mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
Massive changes to account flow (#741)
* Massive changes to account flow
This commit is contained in:
parent
de61501b22
commit
5bf2b4ead2
24 changed files with 998 additions and 725 deletions
|
@ -167,9 +167,8 @@
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.account.AccountSelectActivity"
|
android:name=".ui.account.AccountSelectActivity"
|
||||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden|navigation"
|
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard|keyboardHidden"
|
||||||
android:exported="true"
|
android:exported="true">
|
||||||
android:resizeableActivity="true">
|
|
||||||
<intent-filter android:exported="true">
|
<intent-filter android:exported="true">
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
|
|
@ -309,9 +309,13 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
||||||
// kinda shitty solution, but cant com main->home otherwise for popups
|
// kinda shitty solution, but cant com main->home otherwise for popups
|
||||||
val bookmarksUpdatedEvent = Event<Boolean>()
|
val bookmarksUpdatedEvent = Event<Boolean>()
|
||||||
/**
|
/**
|
||||||
* Used by data store helper to fully reload home when switching accounts
|
* Used by DataStoreHelper to fully reload home when switching accounts
|
||||||
*/
|
*/
|
||||||
val reloadHomeEvent = Event<Boolean>()
|
val reloadHomeEvent = Event<Boolean>()
|
||||||
|
/**
|
||||||
|
* Used by DataStoreHelper to fully reload library when switching accounts
|
||||||
|
*/
|
||||||
|
val reloadLibraryEvent = Event<Boolean>()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,110 +0,0 @@
|
||||||
package com.lagradost.cloudstream3.ui
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.viewbinding.ViewBinding
|
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingAccountAddBinding
|
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingAccountBinding
|
|
||||||
import com.lagradost.cloudstream3.ui.result.setImage
|
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
|
||||||
|
|
||||||
class WhoIsWatchingAdapter(
|
|
||||||
private val selectCallBack: (DataStoreHelper.Account) -> Unit = { },
|
|
||||||
private val editCallBack: (DataStoreHelper.Account) -> Unit = { },
|
|
||||||
private val addAccountCallback: () -> Unit = {}
|
|
||||||
) :
|
|
||||||
ListAdapter<DataStoreHelper.Account, WhoIsWatchingAdapter.WhoIsWatchingHolder>(DiffCallback()) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val FOOTER = 1
|
|
||||||
const val NORMAL = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
|
||||||
return currentList.size + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int = when (position) {
|
|
||||||
currentList.size -> FOOTER
|
|
||||||
else -> NORMAL
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WhoIsWatchingHolder =
|
|
||||||
WhoIsWatchingHolder(
|
|
||||||
binding = when (viewType) {
|
|
||||||
NORMAL -> WhoIsWatchingAccountBinding.inflate(
|
|
||||||
LayoutInflater.from(parent.context),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
FOOTER -> WhoIsWatchingAccountAddBinding.inflate(
|
|
||||||
LayoutInflater.from(parent.context),
|
|
||||||
parent,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> throw NotImplementedError()
|
|
||||||
},
|
|
||||||
selectCallBack = selectCallBack,
|
|
||||||
addAccountCallback = addAccountCallback,
|
|
||||||
editCallBack = editCallBack,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: WhoIsWatchingHolder, position: Int) =
|
|
||||||
holder.bind(currentList.getOrNull(position))
|
|
||||||
|
|
||||||
class WhoIsWatchingHolder(
|
|
||||||
val binding: ViewBinding,
|
|
||||||
val selectCallBack: (DataStoreHelper.Account) -> Unit,
|
|
||||||
val addAccountCallback: () -> Unit,
|
|
||||||
val editCallBack: (DataStoreHelper.Account) -> Unit
|
|
||||||
) :
|
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
|
||||||
|
|
||||||
fun bind(card: DataStoreHelper.Account?) {
|
|
||||||
when (binding) {
|
|
||||||
is WhoIsWatchingAccountBinding -> binding.apply {
|
|
||||||
if (card == null) return@apply
|
|
||||||
outline.isVisible = card.keyIndex == DataStoreHelper.selectedKeyIndex
|
|
||||||
profileText.text = card.name
|
|
||||||
profileImageBackground.setImage(card.image)
|
|
||||||
|
|
||||||
// Handle the lock indicator
|
|
||||||
val isLocked = card.lockPin != null
|
|
||||||
lockIcon.isVisible = isLocked
|
|
||||||
|
|
||||||
root.setOnClickListener {
|
|
||||||
selectCallBack(card)
|
|
||||||
}
|
|
||||||
root.setOnLongClickListener {
|
|
||||||
editCallBack(card)
|
|
||||||
return@setOnLongClickListener true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is WhoIsWatchingAccountAddBinding -> binding.apply {
|
|
||||||
root.setOnClickListener {
|
|
||||||
addAccountCallback()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DiffCallback : DiffUtil.ItemCallback<DataStoreHelper.Account>() {
|
|
||||||
override fun areItemsTheSame(
|
|
||||||
oldItem: DataStoreHelper.Account,
|
|
||||||
newItem: DataStoreHelper.Account
|
|
||||||
): Boolean = oldItem.keyIndex == newItem.keyIndex
|
|
||||||
|
|
||||||
override fun areContentsTheSame(
|
|
||||||
oldItem: DataStoreHelper.Account,
|
|
||||||
newItem: DataStoreHelper.Account
|
|
||||||
): Boolean = oldItem == newItem
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,55 +2,197 @@ package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountListItemAddBinding
|
||||||
import com.lagradost.cloudstream3.databinding.AccountListItemBinding
|
import com.lagradost.cloudstream3.databinding.AccountListItemBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountListItemEditBinding
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountEditDialog
|
||||||
import com.lagradost.cloudstream3.ui.result.setImage
|
import com.lagradost.cloudstream3.ui.result.setImage
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||||
|
|
||||||
class AccountAdapter(
|
class AccountAdapter(
|
||||||
private val accounts: List<DataStoreHelper.Account>,
|
private val accounts: List<DataStoreHelper.Account>,
|
||||||
private val onItemClick: (DataStoreHelper.Account) -> Unit
|
private val accountSelectCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
private val accountCreateCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
private val accountEditCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
private val accountDeleteCallback: (DataStoreHelper.Account) -> Unit
|
||||||
) : RecyclerView.Adapter<AccountAdapter.AccountViewHolder>() {
|
) : RecyclerView.Adapter<AccountAdapter.AccountViewHolder>() {
|
||||||
|
|
||||||
inner class AccountViewHolder(private val binding: AccountListItemBinding) :
|
companion object {
|
||||||
|
const val VIEW_TYPE_SELECT_ACCOUNT = 0
|
||||||
|
const val VIEW_TYPE_ADD_ACCOUNT = 1
|
||||||
|
const val VIEW_TYPE_EDIT_ACCOUNT = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class AccountViewHolder(private val binding: ViewBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
fun bind(account: DataStoreHelper.Account) {
|
fun bind(account: DataStoreHelper.Account?) {
|
||||||
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
|
when (binding) {
|
||||||
|
is AccountListItemBinding -> binding.apply {
|
||||||
|
if (account == null) return@apply
|
||||||
|
|
||||||
binding.accountName.text = account.name
|
val isTv = isTvSettings() || !root.isInTouchMode
|
||||||
binding.accountImage.setImage(account.image)
|
|
||||||
binding.lockIcon.isVisible = account.lockPin != null
|
|
||||||
binding.outline.isVisible = isLastUsedAccount
|
|
||||||
|
|
||||||
if (isTvSettings()) {
|
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
binding.root.isFocusableInTouchMode = true
|
|
||||||
if (isLastUsedAccount) {
|
accountName.text = account.name
|
||||||
binding.root.requestFocus()
|
accountImage.setImage(account.image)
|
||||||
|
lockIcon.isVisible = account.lockPin != null
|
||||||
|
outline.isVisible = !isTv && isLastUsedAccount
|
||||||
|
|
||||||
|
if (isTv) {
|
||||||
|
// For emulator but this is fine on TV also
|
||||||
|
root.isFocusableInTouchMode = true
|
||||||
|
if (isLastUsedAccount) {
|
||||||
|
root.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
root.foreground = ContextCompat.getDrawable(
|
||||||
|
root.context,
|
||||||
|
R.drawable.outline_drawable
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
root.setOnLongClickListener {
|
||||||
|
showAccountEditDialog(
|
||||||
|
context = root.context,
|
||||||
|
account = account,
|
||||||
|
isNewAccount = false,
|
||||||
|
accountEditCallback = { account -> accountEditCallback.invoke(account) },
|
||||||
|
accountDeleteCallback = { account -> accountDeleteCallback.invoke(account) }
|
||||||
|
)
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
root.setOnClickListener {
|
||||||
|
accountSelectCallback.invoke(account)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
binding.root.setOnClickListener {
|
is AccountListItemEditBinding -> binding.apply {
|
||||||
onItemClick(account)
|
if (account == null) return@apply
|
||||||
|
|
||||||
|
val isTv = isTvSettings() || !root.isInTouchMode
|
||||||
|
|
||||||
|
val isLastUsedAccount = account.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
|
||||||
|
accountName.text = account.name
|
||||||
|
accountImage.setImage(
|
||||||
|
account.image,
|
||||||
|
fadeIn = false,
|
||||||
|
radius = 10
|
||||||
|
)
|
||||||
|
lockIcon.isVisible = account.lockPin != null
|
||||||
|
outline.isVisible = !isTv && isLastUsedAccount
|
||||||
|
|
||||||
|
if (isTv) {
|
||||||
|
// For emulator but this is fine on TV also
|
||||||
|
root.isFocusableInTouchMode = true
|
||||||
|
if (isLastUsedAccount) {
|
||||||
|
root.requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
root.foreground = ContextCompat.getDrawable(
|
||||||
|
root.context,
|
||||||
|
R.drawable.outline_drawable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
root.setOnClickListener {
|
||||||
|
showAccountEditDialog(
|
||||||
|
context = root.context,
|
||||||
|
account = account,
|
||||||
|
isNewAccount = false,
|
||||||
|
accountEditCallback = { account -> accountEditCallback.invoke(account) },
|
||||||
|
accountDeleteCallback = { account -> accountDeleteCallback.invoke(account) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is AccountListItemAddBinding -> binding.apply {
|
||||||
|
root.setOnClickListener {
|
||||||
|
val remainingImages =
|
||||||
|
DataStoreHelper.profileImages.toSet() - accounts.filter { it.customImage == null }
|
||||||
|
.mapNotNull { DataStoreHelper.profileImages.getOrNull(it.defaultImageIndex) }.toSet()
|
||||||
|
|
||||||
|
val image =
|
||||||
|
DataStoreHelper.profileImages.indexOf(remainingImages.randomOrNull() ?: DataStoreHelper.profileImages.random())
|
||||||
|
val keyIndex = (accounts.maxOfOrNull { it.keyIndex } ?: 0) + 1
|
||||||
|
|
||||||
|
val accountName = root.context.getString(R.string.account)
|
||||||
|
|
||||||
|
showAccountEditDialog(
|
||||||
|
root.context,
|
||||||
|
DataStoreHelper.Account(
|
||||||
|
keyIndex = keyIndex,
|
||||||
|
name = "$accountName $keyIndex",
|
||||||
|
customImage = null,
|
||||||
|
defaultImageIndex = image
|
||||||
|
),
|
||||||
|
isNewAccount = true,
|
||||||
|
accountEditCallback = { account -> accountCreateCallback.invoke(account) },
|
||||||
|
accountDeleteCallback = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountViewHolder =
|
||||||
val binding = AccountListItemBinding.inflate(
|
AccountViewHolder(
|
||||||
LayoutInflater.from(parent.context), parent, false
|
binding = when (viewType) {
|
||||||
|
VIEW_TYPE_SELECT_ACCOUNT -> {
|
||||||
|
AccountListItemBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VIEW_TYPE_ADD_ACCOUNT -> {
|
||||||
|
AccountListItemAddBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
VIEW_TYPE_EDIT_ACCOUNT -> {
|
||||||
|
AccountListItemEditBinding.inflate(
|
||||||
|
LayoutInflater.from(parent.context),
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("Invalid view type")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return AccountViewHolder(binding)
|
override fun onBindViewHolder(holder: AccountViewHolder, position: Int) {
|
||||||
|
holder.bind(accounts.getOrNull(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: AccountViewHolder, position: Int) {
|
var viewType = 0
|
||||||
holder.bind(accounts[position])
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
if (viewType != 0 && position != accounts.count()) {
|
||||||
|
return viewType
|
||||||
|
}
|
||||||
|
|
||||||
|
return when (position) {
|
||||||
|
accounts.count() -> VIEW_TYPE_ADD_ACCOUNT
|
||||||
|
else -> VIEW_TYPE_SELECT_ACCOUNT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return accounts.size
|
return accounts.count() + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,132 +0,0 @@
|
||||||
package com.lagradost.cloudstream3.ui.account
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.inputmethod.EditorInfo
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.core.widget.doOnTextChanged
|
|
||||||
import com.lagradost.cloudstream3.R
|
|
||||||
import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
|
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod
|
|
||||||
|
|
||||||
object AccountDialog {
|
|
||||||
// TODO add account creation dialog to allow creating accounts directly from AccountSelectActivity
|
|
||||||
|
|
||||||
fun showPinInputDialog(
|
|
||||||
context: Context,
|
|
||||||
currentPin: String?,
|
|
||||||
editAccount: Boolean,
|
|
||||||
errorText: String? = null,
|
|
||||||
callback: (String?) -> Unit
|
|
||||||
) {
|
|
||||||
fun TextView.visibleWithText(@StringRes textRes: Int) {
|
|
||||||
isVisible = true
|
|
||||||
setText(textRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun TextView.visibleWithText(text: String?) {
|
|
||||||
isVisible = true
|
|
||||||
setText(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
val binding = LockPinDialogBinding.inflate(LayoutInflater.from(context))
|
|
||||||
|
|
||||||
val isPinSet = currentPin != null
|
|
||||||
val isNewPin = editAccount && !isPinSet
|
|
||||||
val isEditPin = editAccount && isPinSet
|
|
||||||
|
|
||||||
val titleRes = if (isEditPin) R.string.enter_current_pin else R.string.enter_pin
|
|
||||||
|
|
||||||
var isPinValid = false
|
|
||||||
|
|
||||||
val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom)
|
|
||||||
.setView(binding.root)
|
|
||||||
.setTitle(titleRes)
|
|
||||||
.setNegativeButton(R.string.cancel) { _, _ ->
|
|
||||||
callback.invoke(null)
|
|
||||||
}
|
|
||||||
.setOnCancelListener {
|
|
||||||
callback.invoke(null)
|
|
||||||
}
|
|
||||||
.setOnDismissListener {
|
|
||||||
if (!isPinValid) {
|
|
||||||
callback.invoke(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewPin) {
|
|
||||||
if (errorText != null) binding.pinEditTextError.visibleWithText(errorText)
|
|
||||||
builder.setPositiveButton(R.string.setup_done) { _, _ ->
|
|
||||||
if (!isPinValid) {
|
|
||||||
// If the done button is pressed and there is an error,
|
|
||||||
// ask again, and mention the error that caused this.
|
|
||||||
showPinInputDialog(
|
|
||||||
context = binding.root.context,
|
|
||||||
currentPin = null,
|
|
||||||
editAccount = true,
|
|
||||||
errorText = binding.pinEditTextError.text.toString(),
|
|
||||||
callback = callback
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val enteredPin = binding.pinEditText.text.toString()
|
|
||||||
callback.invoke(enteredPin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val dialog = builder.create()
|
|
||||||
|
|
||||||
binding.pinEditText.doOnTextChanged { text, _, _, _ ->
|
|
||||||
val enteredPin = text.toString()
|
|
||||||
val isEnteredPinValid = enteredPin.length == 4
|
|
||||||
|
|
||||||
if (isEnteredPinValid) {
|
|
||||||
if (isPinSet) {
|
|
||||||
if (enteredPin != currentPin) {
|
|
||||||
binding.pinEditTextError.visibleWithText(R.string.pin_error_incorrect)
|
|
||||||
binding.pinEditText.text = null
|
|
||||||
isPinValid = false
|
|
||||||
} else {
|
|
||||||
binding.pinEditTextError.isVisible = false
|
|
||||||
isPinValid = true
|
|
||||||
|
|
||||||
callback.invoke(enteredPin)
|
|
||||||
dialog.dismissSafe()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
binding.pinEditTextError.isVisible = false
|
|
||||||
isPinValid = true
|
|
||||||
}
|
|
||||||
} else if (isNewPin) {
|
|
||||||
binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
|
|
||||||
isPinValid = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect IME_ACTION_DONE
|
|
||||||
binding.pinEditText.setOnEditorActionListener { _, actionId, _ ->
|
|
||||||
if (actionId == EditorInfo.IME_ACTION_DONE && isPinValid) {
|
|
||||||
val enteredPin = binding.pinEditText.text.toString()
|
|
||||||
callback.invoke(enteredPin)
|
|
||||||
dialog.dismissSafe()
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't want to accidentally have the dialog dismiss when clicking outside of it.
|
|
||||||
// That is what the cancel button is for.
|
|
||||||
dialog.setCanceledOnTouchOutside(false)
|
|
||||||
|
|
||||||
dialog.show()
|
|
||||||
|
|
||||||
// Auto focus on PIN input and show keyboard
|
|
||||||
binding.pinEditText.requestFocus()
|
|
||||||
binding.pinEditText.postDelayed({
|
|
||||||
showInputMethod(binding.pinEditText)
|
|
||||||
}, 200)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,356 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.content.Intent
|
||||||
|
import android.text.Editable
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isGone
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.doOnTextChanged
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.getActivity
|
||||||
|
import com.lagradost.cloudstream3.MainActivity
|
||||||
|
import com.lagradost.cloudstream3.R
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountEditDialogBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.AccountSelectLinearBinding
|
||||||
|
import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
|
||||||
|
import com.lagradost.cloudstream3.mvvm.logError
|
||||||
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.result.setImage
|
||||||
|
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
||||||
|
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDefaultAccount
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||||
|
import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod
|
||||||
|
|
||||||
|
object AccountHelper {
|
||||||
|
fun showAccountEditDialog(
|
||||||
|
context: Context,
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
isNewAccount: Boolean,
|
||||||
|
accountEditCallback: (DataStoreHelper.Account) -> Unit,
|
||||||
|
accountDeleteCallback: (DataStoreHelper.Account) -> Unit
|
||||||
|
) {
|
||||||
|
val binding = AccountEditDialogBinding.inflate(LayoutInflater.from(context), null, false)
|
||||||
|
val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom)
|
||||||
|
.setView(binding.root)
|
||||||
|
|
||||||
|
var currentEditAccount = account
|
||||||
|
val dialog = builder.show()
|
||||||
|
|
||||||
|
if (!isNewAccount) binding.title.setText(R.string.edit_account)
|
||||||
|
|
||||||
|
// Set up the dialog content
|
||||||
|
binding.accountName.text = Editable.Factory.getInstance()?.newEditable(account.name)
|
||||||
|
binding.accountName.doOnTextChanged { text, _, _, _ ->
|
||||||
|
currentEditAccount = currentEditAccount.copy(name = text?.toString() ?: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.deleteBtt.isGone = isNewAccount
|
||||||
|
binding.deleteBtt.setOnClickListener {
|
||||||
|
val dialogClickListener = DialogInterface.OnClickListener { _, which ->
|
||||||
|
when (which) {
|
||||||
|
DialogInterface.BUTTON_POSITIVE -> {
|
||||||
|
accountDeleteCallback.invoke(account)
|
||||||
|
dialog?.dismissSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogInterface.BUTTON_NEGATIVE -> {
|
||||||
|
dialog?.dismissSafe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
AlertDialog.Builder(context).setTitle(R.string.delete).setMessage(
|
||||||
|
context.getString(R.string.delete_message).format(
|
||||||
|
currentEditAccount.name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setPositiveButton(R.string.delete, dialogClickListener)
|
||||||
|
.setNegativeButton(R.string.cancel, dialogClickListener)
|
||||||
|
.show().setDefaultFocus()
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
logError(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.cancelBtt.setOnClickListener {
|
||||||
|
dialog?.dismissSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the profile picture and its interactions
|
||||||
|
binding.accountImage.setImage(account.image)
|
||||||
|
binding.accountImage.setOnClickListener {
|
||||||
|
// Roll the image forwards once
|
||||||
|
currentEditAccount =
|
||||||
|
currentEditAccount.copy(defaultImageIndex = (currentEditAccount.defaultImageIndex + 1) % DataStoreHelper.profileImages.size)
|
||||||
|
binding.accountImage.setImage(currentEditAccount.image)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle applying changes
|
||||||
|
binding.applyBtt.setOnClickListener {
|
||||||
|
if (currentEditAccount.lockPin != null) {
|
||||||
|
// Ask for the current PIN
|
||||||
|
showPinInputDialog(context, currentEditAccount.lockPin, false) { pin ->
|
||||||
|
if (pin == null) return@showPinInputDialog
|
||||||
|
// PIN is correct, proceed to update the account
|
||||||
|
accountEditCallback.invoke(currentEditAccount)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No lock PIN set, proceed to update the account
|
||||||
|
accountEditCallback.invoke(currentEditAccount)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle setting or changing the PIN
|
||||||
|
if (currentEditAccount.keyIndex == getDefaultAccount(context).keyIndex) {
|
||||||
|
binding.lockProfileCheckbox.isVisible = false
|
||||||
|
if (currentEditAccount.lockPin != null) {
|
||||||
|
currentEditAccount = currentEditAccount.copy(lockPin = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var canSetPin = true
|
||||||
|
|
||||||
|
binding.lockProfileCheckbox.isChecked = currentEditAccount.lockPin != null
|
||||||
|
|
||||||
|
binding.lockProfileCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
if (isChecked) {
|
||||||
|
if (canSetPin) {
|
||||||
|
showPinInputDialog(context, null, true) { pin ->
|
||||||
|
if (pin == null) {
|
||||||
|
binding.lockProfileCheckbox.isChecked = false
|
||||||
|
return@showPinInputDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
currentEditAccount = currentEditAccount.copy(lockPin = pin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentEditAccount.lockPin != null) {
|
||||||
|
// Ask for the current PIN
|
||||||
|
showPinInputDialog(context, currentEditAccount.lockPin, true) { pin ->
|
||||||
|
if (pin == null || pin != currentEditAccount.lockPin) {
|
||||||
|
canSetPin = false
|
||||||
|
binding.lockProfileCheckbox.isChecked = true
|
||||||
|
} else {
|
||||||
|
currentEditAccount = currentEditAccount.copy(lockPin = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canSetPin = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showPinInputDialog(
|
||||||
|
context: Context,
|
||||||
|
currentPin: String?,
|
||||||
|
editAccount: Boolean,
|
||||||
|
forStartup: Boolean = false,
|
||||||
|
errorText: String? = null,
|
||||||
|
callback: (String?) -> Unit
|
||||||
|
) {
|
||||||
|
fun TextView.visibleWithText(@StringRes textRes: Int) {
|
||||||
|
isVisible = true
|
||||||
|
setText(textRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun TextView.visibleWithText(text: String?) {
|
||||||
|
isVisible = true
|
||||||
|
setText(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
val binding = LockPinDialogBinding.inflate(LayoutInflater.from(context))
|
||||||
|
|
||||||
|
val isPinSet = currentPin != null
|
||||||
|
val isNewPin = editAccount && !isPinSet
|
||||||
|
val isEditPin = editAccount && isPinSet
|
||||||
|
|
||||||
|
val titleRes = if (isEditPin) R.string.enter_current_pin else R.string.enter_pin
|
||||||
|
|
||||||
|
var isPinValid = false
|
||||||
|
|
||||||
|
val builder = AlertDialog.Builder(context, R.style.AlertDialogCustom)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setTitle(titleRes)
|
||||||
|
.setNegativeButton(R.string.cancel) { _, _ ->
|
||||||
|
callback.invoke(null)
|
||||||
|
}
|
||||||
|
.setOnCancelListener {
|
||||||
|
callback.invoke(null)
|
||||||
|
}
|
||||||
|
.setOnDismissListener {
|
||||||
|
if (!isPinValid) {
|
||||||
|
callback.invoke(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forStartup) {
|
||||||
|
val currentAccount = DataStoreHelper.accounts.firstOrNull {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setTitle(context.getString(R.string.enter_pin_with_name, currentAccount?.name))
|
||||||
|
builder.setOnDismissListener {
|
||||||
|
if (!isPinValid) {
|
||||||
|
context.getActivity()?.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// So that if they don't know the PIN for the current account,
|
||||||
|
// they don't get completely locked out
|
||||||
|
builder.setNeutralButton(R.string.use_default_account) { _, _ ->
|
||||||
|
val activity = context.getActivity()
|
||||||
|
if (activity is AccountSelectActivity) {
|
||||||
|
isPinValid = true
|
||||||
|
activity.viewModel.handleAccountSelect(getDefaultAccount(context), activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewPin) {
|
||||||
|
if (errorText != null) binding.pinEditTextError.visibleWithText(errorText)
|
||||||
|
builder.setPositiveButton(R.string.setup_done) { _, _ ->
|
||||||
|
if (!isPinValid) {
|
||||||
|
// If the done button is pressed and there is an error,
|
||||||
|
// ask again, and mention the error that caused this.
|
||||||
|
showPinInputDialog(
|
||||||
|
context = binding.root.context,
|
||||||
|
currentPin = null,
|
||||||
|
editAccount = true,
|
||||||
|
errorText = binding.pinEditTextError.text.toString(),
|
||||||
|
callback = callback
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val enteredPin = binding.pinEditText.text.toString()
|
||||||
|
callback.invoke(enteredPin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val dialog = builder.create()
|
||||||
|
|
||||||
|
binding.pinEditText.doOnTextChanged { text, _, _, _ ->
|
||||||
|
val enteredPin = text.toString()
|
||||||
|
val isEnteredPinValid = enteredPin.length == 4
|
||||||
|
|
||||||
|
if (isEnteredPinValid) {
|
||||||
|
if (isPinSet) {
|
||||||
|
if (enteredPin != currentPin) {
|
||||||
|
binding.pinEditTextError.visibleWithText(R.string.pin_error_incorrect)
|
||||||
|
binding.pinEditText.text = null
|
||||||
|
isPinValid = false
|
||||||
|
} else {
|
||||||
|
binding.pinEditTextError.isVisible = false
|
||||||
|
isPinValid = true
|
||||||
|
|
||||||
|
callback.invoke(enteredPin)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binding.pinEditTextError.isVisible = false
|
||||||
|
isPinValid = true
|
||||||
|
}
|
||||||
|
} else if (isNewPin) {
|
||||||
|
binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
|
||||||
|
isPinValid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect IME_ACTION_DONE
|
||||||
|
binding.pinEditText.setOnEditorActionListener { _, actionId, _ ->
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE && isPinValid) {
|
||||||
|
val enteredPin = binding.pinEditText.text.toString()
|
||||||
|
callback.invoke(enteredPin)
|
||||||
|
dialog.dismissSafe()
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want to accidentally have the dialog dismiss when clicking outside of it.
|
||||||
|
// That is what the cancel button is for.
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
|
||||||
|
dialog.show()
|
||||||
|
|
||||||
|
// Auto focus on PIN input and show keyboard
|
||||||
|
binding.pinEditText.requestFocus()
|
||||||
|
binding.pinEditText.postDelayed({
|
||||||
|
showInputMethod(binding.pinEditText)
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity?.showAccountSelectLinear() {
|
||||||
|
val activity = this as? MainActivity ?: return
|
||||||
|
val viewModel = ViewModelProvider(activity)[AccountViewModel::class.java]
|
||||||
|
|
||||||
|
val binding: AccountSelectLinearBinding = AccountSelectLinearBinding.inflate(
|
||||||
|
LayoutInflater.from(activity)
|
||||||
|
)
|
||||||
|
|
||||||
|
val builder = BottomSheetDialog(activity)
|
||||||
|
builder.setContentView(binding.root)
|
||||||
|
builder.show()
|
||||||
|
|
||||||
|
binding.manageAccountsButton.setOnClickListener {
|
||||||
|
val accountSelectIntent = Intent(activity, AccountSelectActivity::class.java)
|
||||||
|
accountSelectIntent.putExtra("isEditingFromMainActivity", true)
|
||||||
|
activity.startActivity(accountSelectIntent)
|
||||||
|
builder.dismissSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
val recyclerView: RecyclerView = binding.accountRecyclerView
|
||||||
|
|
||||||
|
val itemSize = recyclerView.resources.getDimensionPixelSize(
|
||||||
|
R.dimen.account_select_linear_item_size
|
||||||
|
)
|
||||||
|
|
||||||
|
recyclerView.addItemDecoration(AccountSelectLinearItemDecoration(itemSize))
|
||||||
|
|
||||||
|
recyclerView.setLinearListLayout(isHorizontal = true)
|
||||||
|
|
||||||
|
val currentAccount = DataStoreHelper.accounts.firstOrNull {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
} ?: getDefaultAccount(activity)
|
||||||
|
|
||||||
|
// We want to make sure the accounts are up-to-date
|
||||||
|
viewModel.handleAccountSelect(
|
||||||
|
currentAccount,
|
||||||
|
activity,
|
||||||
|
reloadForActivity = true
|
||||||
|
)
|
||||||
|
|
||||||
|
activity.observe(viewModel.accounts) { liveAccounts ->
|
||||||
|
recyclerView.adapter = AccountAdapter(
|
||||||
|
liveAccounts,
|
||||||
|
accountSelectCallback = { account ->
|
||||||
|
viewModel.handleAccountSelect(account, activity)
|
||||||
|
builder.dismissSafe()
|
||||||
|
},
|
||||||
|
accountCreateCallback = { viewModel.handleAccountUpdate(it, activity) },
|
||||||
|
accountEditCallback = { viewModel.handleAccountUpdate(it, activity) },
|
||||||
|
accountDeleteCallback = { viewModel.handleAccountDelete(it, activity) }
|
||||||
|
)
|
||||||
|
|
||||||
|
activity.observe(viewModel.selectedKeyIndex) { selectedKeyIndex ->
|
||||||
|
// Scroll to current account (which is focused by default)
|
||||||
|
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||||
|
layoutManager.scrollToPositionWithOffset(selectedKeyIndex, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,87 +1,163 @@
|
||||||
package com.lagradost.cloudstream3.ui.account
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.lagradost.cloudstream3.CommonActivity
|
import com.lagradost.cloudstream3.CommonActivity
|
||||||
import com.lagradost.cloudstream3.CommonActivity.loadThemes
|
import com.lagradost.cloudstream3.CommonActivity.loadThemes
|
||||||
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.MainActivity
|
import com.lagradost.cloudstream3.MainActivity
|
||||||
import com.lagradost.cloudstream3.R
|
import com.lagradost.cloudstream3.R
|
||||||
import com.lagradost.cloudstream3.databinding.ActivityAccountSelectBinding
|
import com.lagradost.cloudstream3.databinding.ActivityAccountSelectBinding
|
||||||
import com.lagradost.cloudstream3.ui.account.AccountDialog.showPinInputDialog
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
|
import com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_EDIT_ACCOUNT
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountAdapter.Companion.VIEW_TYPE_SELECT_ACCOUNT
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.accounts
|
||||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAccounts
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.selectedKeyIndex
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||||
|
|
||||||
class AccountSelectActivity : AppCompatActivity() {
|
class AccountSelectActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
lateinit var viewModel: AccountViewModel
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
val accounts = getAccounts(this@AccountSelectActivity)
|
|
||||||
|
|
||||||
// Don't show account selection if there is only
|
|
||||||
// one account that exists
|
|
||||||
if (accounts.count() <= 1) {
|
|
||||||
navigateToMainActivity()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
CommonActivity.init(this)
|
|
||||||
loadThemes(this)
|
loadThemes(this)
|
||||||
|
|
||||||
window.navigationBarColor = colorFromAttribute(R.attr.primaryBlackBackground)
|
window.navigationBarColor = colorFromAttribute(R.attr.primaryBlackBackground)
|
||||||
|
|
||||||
val binding = ActivityAccountSelectBinding.inflate(layoutInflater)
|
// Are we editing and coming from MainActivity?
|
||||||
|
val isEditingFromMainActivity = intent.getBooleanExtra(
|
||||||
|
"isEditingFromMainActivity",
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
setContentView(binding.root)
|
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val skipStartup = settingsManager.getBoolean(
|
||||||
|
getString(R.string.skip_startup_account_select_key),
|
||||||
|
false
|
||||||
|
) || accounts.count() <= 1
|
||||||
|
|
||||||
val recyclerView: RecyclerView = binding.accountRecyclerView
|
viewModel = ViewModelProvider(this)[AccountViewModel::class.java]
|
||||||
|
|
||||||
|
// Don't show account selection if there is only
|
||||||
|
// one account that exists
|
||||||
|
if (!isEditingFromMainActivity && skipStartup) {
|
||||||
|
val currentAccount = accounts.firstOrNull { it.keyIndex == selectedKeyIndex }
|
||||||
|
if (currentAccount?.lockPin != null) {
|
||||||
|
CommonActivity.init(this)
|
||||||
|
viewModel.handleAccountSelect(currentAccount, this, true)
|
||||||
|
observe(viewModel.isAllowedLogin) { isAllowedLogin ->
|
||||||
|
if (isAllowedLogin) {
|
||||||
|
// We are allowed to continue to MainActivity
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (accounts.count() > 1) {
|
||||||
|
showToast(this, getString(
|
||||||
|
R.string.logged_account,
|
||||||
|
currentAccount?.name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
val adapter = AccountAdapter(accounts) { selectedAccount ->
|
|
||||||
// Handle the selected account
|
|
||||||
onAccountSelected(selectedAccount)
|
|
||||||
}
|
|
||||||
recyclerView.adapter = adapter
|
|
||||||
|
|
||||||
if (isTvSettings()) {
|
|
||||||
val spanSize = if (accounts.count() <= 6) {
|
|
||||||
accounts.count()
|
|
||||||
} else 6
|
|
||||||
|
|
||||||
recyclerView.layoutManager = GridLayoutManager(this, spanSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onAccountSelected(selectedAccount: DataStoreHelper.Account) {
|
|
||||||
if (selectedAccount.lockPin != null) {
|
|
||||||
// The selected account has a PIN set, prompt the user to enter the PIN
|
|
||||||
showPinInputDialog(this@AccountSelectActivity, selectedAccount.lockPin, false) { pin ->
|
|
||||||
if (pin == null) return@showPinInputDialog
|
|
||||||
// Pin is correct, proceed to main activity
|
|
||||||
setAccount(selectedAccount)
|
|
||||||
navigateToMainActivity()
|
navigateToMainActivity()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// No PIN set for the selected account, proceed to main activity
|
|
||||||
setAccount(selectedAccount)
|
|
||||||
navigateToMainActivity()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setAccount(account: DataStoreHelper.Account) {
|
|
||||||
// Don't reload if it is the same account
|
|
||||||
if (DataStoreHelper.selectedKeyIndex == account.keyIndex) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
DataStoreHelper.selectedKeyIndex = account.keyIndex
|
CommonActivity.init(this)
|
||||||
|
|
||||||
MainActivity.bookmarksUpdatedEvent(true)
|
val binding = ActivityAccountSelectBinding.inflate(layoutInflater)
|
||||||
MainActivity.reloadHomeEvent(true)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
val recyclerView: AutofitRecyclerView = binding.accountRecyclerView
|
||||||
|
|
||||||
|
observe(viewModel.accounts) { liveAccounts ->
|
||||||
|
val adapter = AccountAdapter(
|
||||||
|
liveAccounts,
|
||||||
|
// Handle the selected account
|
||||||
|
accountSelectCallback = {
|
||||||
|
viewModel.handleAccountSelect(it, this)
|
||||||
|
observe(viewModel.isAllowedLogin) { isAllowedLogin ->
|
||||||
|
if (isAllowedLogin) {
|
||||||
|
// We are allowed to continue to MainActivity
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accountCreateCallback = { viewModel.handleAccountUpdate(it, this) },
|
||||||
|
accountEditCallback = {
|
||||||
|
viewModel.handleAccountUpdate(it, this)
|
||||||
|
|
||||||
|
// We came from MainActivity, return there
|
||||||
|
// and switch to the edited account
|
||||||
|
if (isEditingFromMainActivity) {
|
||||||
|
setAccount(it)
|
||||||
|
navigateToMainActivity()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
accountDeleteCallback = { viewModel.handleAccountDelete(it,this) }
|
||||||
|
)
|
||||||
|
|
||||||
|
recyclerView.adapter = adapter
|
||||||
|
|
||||||
|
if (isTvSettings()) {
|
||||||
|
binding.editAccountButton.setBackgroundResource(
|
||||||
|
R.drawable.player_button_tv_attr_no_bg
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.selectedKeyIndex) { selectedKeyIndex ->
|
||||||
|
// Scroll to current account (which is focused by default)
|
||||||
|
val layoutManager = recyclerView.layoutManager as GridLayoutManager
|
||||||
|
layoutManager.scrollToPositionWithOffset(selectedKeyIndex, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
observe(viewModel.isEditing) { isEditing ->
|
||||||
|
if (isEditing) {
|
||||||
|
binding.editAccountButton.setImageResource(R.drawable.ic_baseline_close_24)
|
||||||
|
binding.title.setText(R.string.manage_accounts)
|
||||||
|
adapter.viewType = VIEW_TYPE_EDIT_ACCOUNT
|
||||||
|
} else {
|
||||||
|
binding.editAccountButton.setImageResource(R.drawable.ic_baseline_edit_24)
|
||||||
|
binding.title.setText(R.string.select_an_account)
|
||||||
|
adapter.viewType = VIEW_TYPE_SELECT_ACCOUNT
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditingFromMainActivity) {
|
||||||
|
viewModel.setIsEditing(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.editAccountButton.setOnClickListener {
|
||||||
|
// We came from MainActivity, return there
|
||||||
|
// and resume its state
|
||||||
|
if (isEditingFromMainActivity) {
|
||||||
|
navigateToMainActivity()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.toggleIsEditing()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTvSettings()) {
|
||||||
|
recyclerView.spanCount = if (liveAccounts.count() + 1 <= 6) {
|
||||||
|
liveAccounts.count() + 1
|
||||||
|
} else 6
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navigateToMainActivity() {
|
private fun navigateToMainActivity() {
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
|
class AccountSelectLinearItemDecoration(private val size: Int) : RecyclerView.ItemDecoration() {
|
||||||
|
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||||
|
val layoutParams = view.layoutParams as RecyclerView.LayoutParams
|
||||||
|
layoutParams.width = size
|
||||||
|
layoutParams.height = size
|
||||||
|
view.layoutParams = layoutParams
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.lagradost.cloudstream3.ui.account
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.context
|
||||||
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showPinInputDialog
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getAccounts
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.getDefaultAccount
|
||||||
|
import com.lagradost.cloudstream3.utils.DataStoreHelper.setAccount
|
||||||
|
|
||||||
|
class AccountViewModel : ViewModel() {
|
||||||
|
private fun getAllAccounts(): List<DataStoreHelper.Account> {
|
||||||
|
return context?.let { getAccounts(it) } ?: DataStoreHelper.accounts.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _accounts: MutableLiveData<List<DataStoreHelper.Account>> = MutableLiveData(getAllAccounts())
|
||||||
|
val accounts: LiveData<List<DataStoreHelper.Account>> = _accounts
|
||||||
|
|
||||||
|
private val _isEditing = MutableLiveData(false)
|
||||||
|
val isEditing: LiveData<Boolean> = _isEditing
|
||||||
|
|
||||||
|
private val _isAllowedLogin = MutableLiveData(false)
|
||||||
|
val isAllowedLogin: LiveData<Boolean> = _isAllowedLogin
|
||||||
|
|
||||||
|
private val _selectedKeyIndex = MutableLiveData(
|
||||||
|
getAllAccounts().indexOfFirst {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
}
|
||||||
|
)
|
||||||
|
val selectedKeyIndex: LiveData<Int> = _selectedKeyIndex
|
||||||
|
|
||||||
|
fun setIsEditing(value: Boolean) {
|
||||||
|
_isEditing.postValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toggleIsEditing() {
|
||||||
|
_isEditing.postValue(!(_isEditing.value ?: false))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleAccountUpdate(
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
val currentAccounts = getAccounts(context).toMutableList()
|
||||||
|
|
||||||
|
val overrideIndex = currentAccounts.indexOfFirst { it.keyIndex == account.keyIndex }
|
||||||
|
|
||||||
|
if (overrideIndex != -1) {
|
||||||
|
currentAccounts[overrideIndex] = account
|
||||||
|
} else currentAccounts.add(account)
|
||||||
|
|
||||||
|
val currentHomePage = DataStoreHelper.currentHomePage
|
||||||
|
|
||||||
|
setAccount(account)
|
||||||
|
|
||||||
|
DataStoreHelper.currentHomePage = currentHomePage
|
||||||
|
DataStoreHelper.accounts = currentAccounts.toTypedArray()
|
||||||
|
|
||||||
|
_accounts.postValue(getAccounts(context))
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleAccountDelete(
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
context: Context
|
||||||
|
) {
|
||||||
|
removeKeys(account.keyIndex.toString())
|
||||||
|
|
||||||
|
val currentAccounts = getAccounts(context).toMutableList()
|
||||||
|
|
||||||
|
currentAccounts.removeIf { it.keyIndex == account.keyIndex }
|
||||||
|
|
||||||
|
DataStoreHelper.accounts = currentAccounts.toTypedArray()
|
||||||
|
|
||||||
|
if (account.keyIndex == DataStoreHelper.selectedKeyIndex) {
|
||||||
|
setAccount(getDefaultAccount(context))
|
||||||
|
}
|
||||||
|
|
||||||
|
_accounts.postValue(getAccounts(context))
|
||||||
|
_selectedKeyIndex.postValue(getAllAccounts().indexOfFirst {
|
||||||
|
it.keyIndex == DataStoreHelper.selectedKeyIndex
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleAccountSelect(
|
||||||
|
account: DataStoreHelper.Account,
|
||||||
|
context: Context,
|
||||||
|
forStartup: Boolean = false,
|
||||||
|
reloadForActivity: Boolean = false
|
||||||
|
) {
|
||||||
|
if (reloadForActivity) {
|
||||||
|
_accounts.postValue(getAccounts(context))
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the selected account has a lock PIN set
|
||||||
|
if (account.lockPin != null) {
|
||||||
|
// The selected account has a PIN set, prompt the user to enter the PIN
|
||||||
|
showPinInputDialog(
|
||||||
|
context,
|
||||||
|
account.lockPin,
|
||||||
|
false,
|
||||||
|
forStartup
|
||||||
|
) { pin ->
|
||||||
|
if (pin == null) return@showPinInputDialog
|
||||||
|
// Pin is correct, proceed
|
||||||
|
_isAllowedLogin.postValue(true)
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
setAccount(account)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No PIN set for the selected account, proceed
|
||||||
|
_isAllowedLogin.postValue(true)
|
||||||
|
_selectedKeyIndex.postValue(getAccounts(context).indexOf(account))
|
||||||
|
setAccount(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
import com.lagradost.cloudstream3.ui.APIRepository.Companion.noneApi
|
||||||
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
import com.lagradost.cloudstream3.ui.APIRepository.Companion.randomApi
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLinear
|
||||||
import com.lagradost.cloudstream3.ui.result.txt
|
import com.lagradost.cloudstream3.ui.result.txt
|
||||||
import com.lagradost.cloudstream3.ui.search.*
|
import com.lagradost.cloudstream3.ui.search.*
|
||||||
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
import com.lagradost.cloudstream3.ui.search.SearchHelper.handleSearchClickCallback
|
||||||
|
@ -495,9 +496,10 @@ class HomeFragment : Fragment() {
|
||||||
//homeChangeApiLoading.setOnClickListener(apiChangeClickListener)
|
//homeChangeApiLoading.setOnClickListener(apiChangeClickListener)
|
||||||
homeApiFab.setOnClickListener(apiChangeClickListener)
|
homeApiFab.setOnClickListener(apiChangeClickListener)
|
||||||
homeChangeApi.setOnClickListener(apiChangeClickListener)
|
homeChangeApi.setOnClickListener(apiChangeClickListener)
|
||||||
homeSwitchAccount.setOnClickListener { v ->
|
homeSwitchAccount.setOnClickListener {
|
||||||
DataStoreHelper.showWhoIsWatching(v?.context ?: return@setOnClickListener)
|
activity?.showAccountSelectLinear()
|
||||||
}
|
}
|
||||||
|
|
||||||
homeRandom.setOnClickListener {
|
homeRandom.setOnClickListener {
|
||||||
if (listHomepageItems.isNotEmpty()) {
|
if (listHomepageItems.isNotEmpty()) {
|
||||||
activity.loadSearchResult(listHomepageItems.random())
|
activity.loadSearchResult(listHomepageItems.random())
|
||||||
|
|
|
@ -27,6 +27,7 @@ import com.lagradost.cloudstream3.mvvm.Resource
|
||||||
import com.lagradost.cloudstream3.mvvm.debugException
|
import com.lagradost.cloudstream3.mvvm.debugException
|
||||||
import com.lagradost.cloudstream3.mvvm.observe
|
import com.lagradost.cloudstream3.mvvm.observe
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
|
import com.lagradost.cloudstream3.ui.account.AccountHelper.showAccountSelectLinear
|
||||||
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
|
import com.lagradost.cloudstream3.ui.home.HomeFragment.Companion.selectHomepage
|
||||||
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
import com.lagradost.cloudstream3.ui.result.FOCUS_SELF
|
||||||
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
|
import com.lagradost.cloudstream3.ui.result.ResultViewModel2
|
||||||
|
@ -477,8 +478,8 @@ class HomeParentItemAdapterPreview(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
homeAccount?.setOnClickListener { v ->
|
homeAccount?.setOnClickListener {
|
||||||
DataStoreHelper.showWhoIsWatching(v?.context ?: return@setOnClickListener)
|
activity?.showAccountSelectLinear()
|
||||||
}
|
}
|
||||||
|
|
||||||
(binding as? FragmentHomeHeadTvBinding)?.apply {
|
(binding as? FragmentHomeHeadTvBinding)?.apply {
|
||||||
|
|
|
@ -120,11 +120,11 @@ class LibraryViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
MainActivity.reloadHomeEvent += ::reloadPages
|
MainActivity.reloadLibraryEvent += ::reloadPages
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
MainActivity.reloadHomeEvent -= ::reloadPages
|
MainActivity.reloadLibraryEvent -= ::reloadPages
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,15 +1,7 @@
|
||||||
package com.lagradost.cloudstream3.utils
|
package com.lagradost.cloudstream3.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.text.Editable
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.core.view.isVisible
|
|
||||||
import androidx.core.widget.doOnTextChanged
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
|
||||||
import com.lagradost.cloudstream3.*
|
import com.lagradost.cloudstream3.*
|
||||||
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
import com.lagradost.cloudstream3.APIHolder.filterProviderByPreferredMedia
|
||||||
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
import com.lagradost.cloudstream3.APIHolder.unixTimeMS
|
||||||
|
@ -20,21 +12,12 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
|
||||||
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
||||||
import com.lagradost.cloudstream3.CommonActivity.showToast
|
import com.lagradost.cloudstream3.CommonActivity.showToast
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingAccountEditBinding
|
|
||||||
import com.lagradost.cloudstream3.databinding.WhoIsWatchingBinding
|
|
||||||
import com.lagradost.cloudstream3.mvvm.logError
|
|
||||||
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
import com.lagradost.cloudstream3.syncproviders.AccountManager
|
||||||
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
import com.lagradost.cloudstream3.syncproviders.SyncAPI
|
||||||
import com.lagradost.cloudstream3.ui.WatchType
|
import com.lagradost.cloudstream3.ui.WatchType
|
||||||
import com.lagradost.cloudstream3.ui.WhoIsWatchingAdapter
|
|
||||||
import com.lagradost.cloudstream3.ui.account.AccountDialog.showPinInputDialog
|
|
||||||
import com.lagradost.cloudstream3.ui.library.ListSorting
|
import com.lagradost.cloudstream3.ui.library.ListSorting
|
||||||
import com.lagradost.cloudstream3.ui.result.UiImage
|
import com.lagradost.cloudstream3.ui.result.UiImage
|
||||||
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
import com.lagradost.cloudstream3.ui.result.VideoWatchState
|
||||||
import com.lagradost.cloudstream3.ui.result.setImage
|
|
||||||
import com.lagradost.cloudstream3.ui.result.setLinearListLayout
|
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus
|
|
||||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
@ -75,7 +58,7 @@ class UserPreferenceDelegate<T : Any>(
|
||||||
|
|
||||||
object DataStoreHelper {
|
object DataStoreHelper {
|
||||||
// be aware, don't change the index of these as Account uses the index for the art
|
// be aware, don't change the index of these as Account uses the index for the art
|
||||||
private val profileImages = arrayOf(
|
val profileImages = arrayOf(
|
||||||
R.drawable.profile_bg_dark_blue,
|
R.drawable.profile_bg_dark_blue,
|
||||||
R.drawable.profile_bg_blue,
|
R.drawable.profile_bg_blue,
|
||||||
R.drawable.profile_bg_orange,
|
R.drawable.profile_bg_orange,
|
||||||
|
@ -147,7 +130,7 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
const val TAG = "data_store_helper"
|
const val TAG = "data_store_helper"
|
||||||
private var accounts by PreferenceDelegate("$TAG/account", arrayOf<Account>())
|
var accounts by PreferenceDelegate("$TAG/account", arrayOf<Account>())
|
||||||
var selectedKeyIndex by PreferenceDelegate("$TAG/account_key_index", 0)
|
var selectedKeyIndex by PreferenceDelegate("$TAG/account_key_index", 0)
|
||||||
val currentAccount: String get() = selectedKeyIndex.toString()
|
val currentAccount: String get() = selectedKeyIndex.toString()
|
||||||
|
|
||||||
|
@ -166,156 +149,21 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setAccount(account: Account, refreshHomePage: Boolean) {
|
fun setAccount(account: Account) {
|
||||||
|
val homepage = currentHomePage
|
||||||
|
|
||||||
selectedKeyIndex = account.keyIndex
|
selectedKeyIndex = account.keyIndex
|
||||||
showToast(account.name)
|
showToast(context?.getString(R.string.logged_account, account.name) ?: account.name)
|
||||||
MainActivity.bookmarksUpdatedEvent(true)
|
MainActivity.bookmarksUpdatedEvent(true)
|
||||||
if (refreshHomePage) {
|
MainActivity.reloadLibraryEvent(true)
|
||||||
|
val oldAccount = accounts.find { it.keyIndex == account.keyIndex }
|
||||||
|
if (oldAccount != null && currentHomePage != homepage) {
|
||||||
|
// This is not a new account, and the homepage has changed, reload it
|
||||||
MainActivity.reloadHomeEvent(true)
|
MainActivity.reloadHomeEvent(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun editAccount(context: Context, account: Account, isNewAccount: Boolean) {
|
fun getDefaultAccount(context: Context): Account {
|
||||||
val binding =
|
|
||||||
WhoIsWatchingAccountEditBinding.inflate(LayoutInflater.from(context), null, false)
|
|
||||||
val builder =
|
|
||||||
AlertDialog.Builder(context, R.style.AlertDialogCustom)
|
|
||||||
.setView(binding.root)
|
|
||||||
|
|
||||||
var currentEditAccount = account
|
|
||||||
val dialog = builder.show()
|
|
||||||
binding.accountName.text = Editable.Factory.getInstance()?.newEditable(account.name)
|
|
||||||
binding.accountName.doOnTextChanged { text, _, _, _ ->
|
|
||||||
currentEditAccount = currentEditAccount.copy(name = text?.toString() ?: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.deleteBtt.isGone = isNewAccount
|
|
||||||
binding.deleteBtt.setOnClickListener {
|
|
||||||
val dialogClickListener =
|
|
||||||
DialogInterface.OnClickListener { _, which ->
|
|
||||||
when (which) {
|
|
||||||
DialogInterface.BUTTON_POSITIVE -> {
|
|
||||||
// remove all keys as well as the account, note that default wont get
|
|
||||||
// deleted from currentAccounts, as it is not part of "accounts",
|
|
||||||
// but the watch keys will
|
|
||||||
removeKeys(account.keyIndex.toString())
|
|
||||||
val currentAccounts = accounts.toMutableList()
|
|
||||||
currentAccounts.removeIf { it.keyIndex == account.keyIndex }
|
|
||||||
accounts = currentAccounts.toTypedArray()
|
|
||||||
|
|
||||||
// update UI
|
|
||||||
setAccount(getDefaultAccount(context), true)
|
|
||||||
dialog?.dismissSafe()
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogInterface.BUTTON_NEGATIVE -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
AlertDialog.Builder(context).setTitle(R.string.delete).setMessage(
|
|
||||||
context.getString(R.string.delete_message).format(
|
|
||||||
currentEditAccount.name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.setPositiveButton(R.string.delete, dialogClickListener)
|
|
||||||
.setNegativeButton(R.string.cancel, dialogClickListener)
|
|
||||||
.show().setDefaultFocus()
|
|
||||||
} catch (t: Throwable) {
|
|
||||||
logError(t)
|
|
||||||
// ye you somehow fucked up formatting did you?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.cancelBtt.setOnClickListener {
|
|
||||||
dialog?.dismissSafe()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.profilePic.setImage(account.image)
|
|
||||||
binding.profilePic.setOnClickListener {
|
|
||||||
// Roll the image forwards once
|
|
||||||
currentEditAccount =
|
|
||||||
currentEditAccount.copy(defaultImageIndex = (currentEditAccount.defaultImageIndex + 1) % profileImages.size)
|
|
||||||
binding.profilePic.setImage(currentEditAccount.image)
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.applyBtt.setOnClickListener {
|
|
||||||
if (currentEditAccount.lockPin != null) {
|
|
||||||
// Ask for the current PIN
|
|
||||||
showPinInputDialog(context, currentEditAccount.lockPin, false) { pin ->
|
|
||||||
if (pin == null) return@showPinInputDialog
|
|
||||||
// PIN is correct, proceed to update the account
|
|
||||||
performAccountUpdate(currentEditAccount)
|
|
||||||
dialog.dismissSafe()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No lock PIN set, proceed to update the account
|
|
||||||
performAccountUpdate(currentEditAccount)
|
|
||||||
dialog.dismissSafe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle setting or changing the PIN
|
|
||||||
|
|
||||||
if (currentEditAccount.keyIndex == getDefaultAccount(context).keyIndex) {
|
|
||||||
binding.lockProfileCheckbox.isVisible = false
|
|
||||||
if (currentEditAccount.lockPin != null) {
|
|
||||||
currentEditAccount = currentEditAccount.copy(lockPin = null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var canSetPin = true
|
|
||||||
|
|
||||||
binding.lockProfileCheckbox.isChecked = currentEditAccount.lockPin != null
|
|
||||||
|
|
||||||
binding.lockProfileCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
|
||||||
if (isChecked) {
|
|
||||||
if (canSetPin) {
|
|
||||||
showPinInputDialog(context, null, true) { pin ->
|
|
||||||
if (pin == null) {
|
|
||||||
binding.lockProfileCheckbox.isChecked = false
|
|
||||||
return@showPinInputDialog
|
|
||||||
}
|
|
||||||
|
|
||||||
currentEditAccount = currentEditAccount.copy(lockPin = pin)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (currentEditAccount.lockPin != null) {
|
|
||||||
// Ask for the current PIN
|
|
||||||
showPinInputDialog(context, currentEditAccount.lockPin, true) { pin ->
|
|
||||||
if (pin == null || pin != currentEditAccount.lockPin) {
|
|
||||||
canSetPin = false
|
|
||||||
binding.lockProfileCheckbox.isChecked = true
|
|
||||||
} else {
|
|
||||||
currentEditAccount = currentEditAccount.copy(lockPin = null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
canSetPin = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun performAccountUpdate(account: Account) {
|
|
||||||
val currentAccounts = accounts.toMutableList()
|
|
||||||
|
|
||||||
val overrideIndex = currentAccounts.indexOfFirst { it.keyIndex == account.keyIndex }
|
|
||||||
|
|
||||||
if (overrideIndex != -1) {
|
|
||||||
currentAccounts[overrideIndex] = account
|
|
||||||
} else {
|
|
||||||
currentAccounts.add(account)
|
|
||||||
}
|
|
||||||
|
|
||||||
val currentHomePage = this.currentHomePage
|
|
||||||
setAccount(account, false)
|
|
||||||
this.currentHomePage = currentHomePage
|
|
||||||
accounts = currentAccounts.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getDefaultAccount(context: Context): Account {
|
|
||||||
return accounts.let { currentAccounts ->
|
return accounts.let { currentAccounts ->
|
||||||
currentAccounts.getOrNull(currentAccounts.indexOfFirst { it.keyIndex == 0 }) ?: Account(
|
currentAccounts.getOrNull(currentAccounts.indexOfFirst { it.keyIndex == 0 }) ?: Account(
|
||||||
keyIndex = 0,
|
keyIndex = 0,
|
||||||
|
@ -333,71 +181,6 @@ object DataStoreHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showWhoIsWatching(context: Context) {
|
|
||||||
val binding: WhoIsWatchingBinding = WhoIsWatchingBinding.inflate(LayoutInflater.from(context))
|
|
||||||
val builder = BottomSheetDialog(context)
|
|
||||||
builder.setContentView(binding.root)
|
|
||||||
|
|
||||||
val showAccount = accounts.toMutableList().apply {
|
|
||||||
val item = getDefaultAccount(context)
|
|
||||||
remove(item)
|
|
||||||
add(0, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
val accountName = context.getString(R.string.account)
|
|
||||||
|
|
||||||
binding.profilesRecyclerview.setLinearListLayout(isHorizontal = true)
|
|
||||||
binding.profilesRecyclerview.adapter = WhoIsWatchingAdapter(
|
|
||||||
selectCallBack = { account ->
|
|
||||||
// Check if the selected account has a lock PIN set
|
|
||||||
if (account.lockPin != null) {
|
|
||||||
// Prompt for the lock pin
|
|
||||||
showPinInputDialog(context, account.lockPin, false) { pin ->
|
|
||||||
if (pin == null) return@showPinInputDialog
|
|
||||||
// Pin is correct, unlock the profile
|
|
||||||
setAccount(account, true)
|
|
||||||
builder.dismissSafe()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// No lock PIN set, directly set the account
|
|
||||||
setAccount(account, true)
|
|
||||||
builder.dismissSafe()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addAccountCallback = {
|
|
||||||
val currentAccounts = accounts
|
|
||||||
val remainingImages =
|
|
||||||
profileImages.toSet() - currentAccounts.filter { it.customImage == null }
|
|
||||||
.mapNotNull { profileImages.getOrNull(it.defaultImageIndex) }.toSet()
|
|
||||||
val image =
|
|
||||||
profileImages.indexOf(remainingImages.randomOrNull() ?: profileImages.random())
|
|
||||||
val keyIndex = (currentAccounts.maxOfOrNull { it.keyIndex } ?: 0) + 1
|
|
||||||
|
|
||||||
// create a new dummy account
|
|
||||||
editAccount(
|
|
||||||
context,
|
|
||||||
Account(
|
|
||||||
keyIndex = keyIndex,
|
|
||||||
name = "$accountName $keyIndex",
|
|
||||||
customImage = null,
|
|
||||||
defaultImageIndex = image
|
|
||||||
), isNewAccount = true
|
|
||||||
)
|
|
||||||
builder.dismissSafe()
|
|
||||||
},
|
|
||||||
editCallBack = { account ->
|
|
||||||
editAccount(
|
|
||||||
context, account, isNewAccount = false
|
|
||||||
)
|
|
||||||
builder.dismissSafe()
|
|
||||||
}
|
|
||||||
).apply {
|
|
||||||
submitList(showAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
data class PosDur(
|
data class PosDur(
|
||||||
@JsonProperty("position") val position: Long,
|
@JsonProperty("position") val position: Long,
|
||||||
@JsonProperty("duration") val duration: Long
|
@JsonProperty("duration") val duration: Long
|
||||||
|
|
11
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
11
app/src/main/res/drawable/ic_baseline_edit_24.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||||
|
</vector>
|
|
@ -1,4 +1,5 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
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"
|
||||||
|
@ -14,13 +15,12 @@
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text1"
|
android:id="@+id/title"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_rowWeight="1"
|
android:layout_rowWeight="1"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
android:text="@string/create_account"
|
android:text="@string/create_account"
|
||||||
|
@ -28,34 +28,8 @@
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<!-- <com.google.android.material.button.MaterialButton-->
|
|
||||||
<!-- android:nextFocusDown="@id/repo_name_input"-->
|
|
||||||
<!-- android:id="@+id/list_repositories"-->
|
|
||||||
<!-- android:nextFocusLeft="@id/apply_btt"-->
|
|
||||||
<!-- android:nextFocusRight="@id/cancel_btt"-->
|
|
||||||
<!-- style="@style/WhiteButton"-->
|
|
||||||
<!-- android:layout_width="wrap_content"-->
|
|
||||||
<!-- android:layout_gravity="center_vertical"-->
|
|
||||||
<!-- android:text="@string/view_public_repositories_button_short" />-->
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/text2"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_gravity="center_vertical"
|
|
||||||
|
|
||||||
android:layout_marginBottom="10dp"
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:visibility="gone"
|
|
||||||
tools:text="Gogoanime" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -89,55 +63,55 @@
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius">
|
app:cardCornerRadius="@dimen/rounded_image_radius">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:foreground="@drawable/outline_drawable_forced_round"
|
android:id="@+id/account_image"
|
||||||
android:id="@+id/profile_pic"
|
android:src="@drawable/profile_bg_blue"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="60dp"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:contentDescription="@string/preview_background_img_des"
|
android:contentDescription="@string/preview_background_img_des"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@drawable/profile_bg_blue" />
|
android:foreground="@drawable/outline_drawable_forced_round"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_gravity="center" />
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:id="@+id/apply_btt_holder"
|
android:id="@+id/apply_btt_holder"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="10dp"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:layout_marginTop="-60dp"
|
android:layout_marginTop="-60dp">
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="10dp">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/delete_btt"
|
android:id="@+id/delete_btt"
|
||||||
style="@style/BlackButton"
|
android:text="@string/delete"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:nextFocusRight="@id/apply_btt"
|
android:nextFocusRight="@id/apply_btt"
|
||||||
android:text="@string/delete" />
|
style="@style/BlackButton" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/apply_btt"
|
android:id="@+id/apply_btt"
|
||||||
style="@style/WhiteButton"
|
android:text="@string/sort_apply"
|
||||||
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:layout_toStartOf="@+id/cancel_btt"
|
android:layout_toStartOf="@+id/cancel_btt"
|
||||||
android:nextFocusLeft="@id/delete_btt"
|
android:nextFocusLeft="@id/delete_btt"
|
||||||
android:nextFocusRight="@id/cancel_btt"
|
android:nextFocusRight="@id/cancel_btt"
|
||||||
android:text="@string/sort_apply" />
|
style="@style/WhiteButton" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/cancel_btt"
|
android:id="@+id/cancel_btt"
|
||||||
style="@style/BlackButton"
|
android:text="@string/sort_cancel"
|
||||||
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_gravity="center_vertical|end"
|
android:layout_gravity="center_vertical|end"
|
||||||
android:nextFocusLeft="@id/apply_btt"
|
android:nextFocusLeft="@id/apply_btt"
|
||||||
android:text="@string/sort_cancel" />
|
style="@style/BlackButton" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
|
@ -9,9 +9,9 @@
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:backgroundTint="?attr/primaryGrayBackground"
|
android:backgroundTint="?attr/primaryGrayBackground"
|
||||||
android:foreground="?attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
|
||||||
android:layout_margin="10dp"
|
android:layout_margin="10dp"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintDimensionRatio="1"
|
app:layout_constraintDimensionRatio="1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
@ -19,38 +19,38 @@
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/account_image"
|
android:id="@+id/account_image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:alpha="0.4"
|
android:alpha="0.4"
|
||||||
android:contentDescription="@string/profile_background_des"
|
android:scaleType="centerCrop" />
|
||||||
android:scaleType="centerCrop" />
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/outline"
|
android:id="@+id/outline"
|
||||||
tools:visibility="visible"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:background="@drawable/outline_card"
|
||||||
android:background="@drawable/outline_card"
|
android:visibility="gone"
|
||||||
android:visibility="gone" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/lock_icon"
|
android:id="@+id/lock_icon"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_gravity="top|end"
|
android:layout_gravity="top|end"
|
||||||
android:layout_margin="4dp"
|
android:layout_margin="4dp"
|
||||||
android:src="@drawable/video_locked"
|
android:src="@drawable/video_locked"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/account_name"
|
android:id="@+id/account_name"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
android:textSize="16sp" />
|
android:textSize="16sp" />
|
||||||
|
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
|
@ -2,16 +2,15 @@
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
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"
|
|
||||||
android:id="@+id/card_view"
|
android:id="@+id/card_view"
|
||||||
android:layout_width="100dp"
|
android:layout_width="110dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="110dp"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:backgroundTint="?attr/primaryGrayBackground"
|
android:backgroundTint="?attr/primaryGrayBackground"
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
android:layout_margin="10dp"
|
||||||
android:layout_margin="5dp"
|
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintDimensionRatio="1"
|
app:layout_constraintDimensionRatio="1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
|
@ -4,14 +4,14 @@
|
||||||
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/card_view"
|
android:id="@+id/card_view"
|
||||||
android:layout_width="100dp"
|
android:layout_width="110dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="110dp"
|
||||||
android:animateLayoutChanges="true"
|
android:animateLayoutChanges="true"
|
||||||
android:backgroundTint="?attr/primaryGrayBackground"
|
android:backgroundTint="?attr/primaryGrayBackground"
|
||||||
android:foreground="?attr/selectableItemBackgroundBorderless"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
app:cardCornerRadius="@dimen/rounded_image_radius"
|
android:layout_margin="10dp"
|
||||||
android:layout_margin="5dp"
|
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
app:cardCornerRadius="@dimen/rounded_image_radius"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintDimensionRatio="1"
|
app:layout_constraintDimensionRatio="1"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
@ -20,20 +20,19 @@
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/profile_image_background"
|
android:id="@+id/account_image"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:alpha="0.4"
|
android:alpha="0.1"
|
||||||
android:contentDescription="@string/profile_background_des"
|
|
||||||
android:scaleType="centerCrop" />
|
android:scaleType="centerCrop" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/outline"
|
android:id="@+id/outline"
|
||||||
tools:visibility="visible"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/outline_card"
|
android:background="@drawable/outline_card"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/lock_icon"
|
android:id="@+id/lock_icon"
|
||||||
|
@ -42,11 +41,18 @@
|
||||||
android:layout_gravity="top|end"
|
android:layout_gravity="top|end"
|
||||||
android:layout_margin="4dp"
|
android:layout_margin="4dp"
|
||||||
android:src="@drawable/video_locked"
|
android:src="@drawable/video_locked"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/pencil_icon"
|
||||||
|
android:layout_width="42dp"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:layout_gravity="top|start"
|
||||||
|
android:src="@drawable/ic_baseline_edit_24" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/profile_text"
|
android:id="@+id/account_name"
|
||||||
tools:text="@string/mobile_data"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
44
app/src/main/res/layout/account_select_linear.xml
Normal file
44
app/src/main/res/layout/account_select_linear.xml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?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:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/switch_account"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textColor="?attr/textColor"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_rowWeight="1"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/account_recycler_view"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:descendantFocusability="afterDescendants"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:itemCount="4"
|
||||||
|
tools:listitem="@layout/account_list_item" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/manage_accounts_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:text="@string/manage_accounts"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:icon="@drawable/ic_baseline_edit_24"
|
||||||
|
style="@style/BlackButton" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -1,28 +1,49 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:background="?attr/primaryBlackBackground"
|
android:background="?attr/primaryBlackBackground"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent">
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingTop="36dp"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<TextView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/edit_account_button"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="40dp"
|
||||||
android:text="@string/select_an_account"
|
android:layout_height="40dp"
|
||||||
android:textSize="24sp"
|
android:layout_gravity="end|top"
|
||||||
android:textStyle="bold"
|
android:layout_marginTop="40dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginBottom="16dp" />
|
android:src="@drawable/ic_baseline_edit_24"
|
||||||
|
android:focusable="true"
|
||||||
|
android:nextFocusDown="@id/account_recycler_view"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/manage_accounts"
|
||||||
|
app:tint="@color/player_on_button_tv_attr" />
|
||||||
|
|
||||||
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
<LinearLayout
|
||||||
android:id="@+id/account_recycler_view"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:orientation="vertical"
|
||||||
android:layout_gravity="center_horizontal"
|
android:paddingTop="36dp"
|
||||||
android:paddingLeft="16dp"
|
android:gravity="center">
|
||||||
android:paddingRight="16dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/select_an_account"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||||
|
android:id="@+id/account_recycler_view"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
|
@ -1,55 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/switch_account"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textColor="?attr/textColor"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:text="@string/history"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
|
||||||
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
|
||||||
android:text="@string/error_bookmarks_text"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textColor="?attr/grayTextColor"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_rowWeight="1"
|
|
||||||
android:layout_height="wrap_content" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:descendantFocusability="afterDescendants"
|
|
||||||
android:id="@+id/profiles_recyclerview"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
tools:itemCount="4"
|
|
||||||
tools:listitem="@layout/who_is_watching_account">
|
|
||||||
|
|
||||||
<requestFocus />
|
|
||||||
</androidx.recyclerview.widget.RecyclerView>
|
|
||||||
</LinearLayout>
|
|
|
@ -20,4 +20,6 @@
|
||||||
|
|
||||||
<dimen name="download_size">50dp</dimen>
|
<dimen name="download_size">50dp</dimen>
|
||||||
<dimen name="video_frame_width">1dp</dimen>
|
<dimen name="video_frame_width">1dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="account_select_linear_item_size">100dp</dimen>
|
||||||
</resources>
|
</resources>
|
|
@ -65,6 +65,7 @@
|
||||||
<string name="filter_sub_lang_key" translatable="false">filter_sub_lang_key</string>
|
<string name="filter_sub_lang_key" translatable="false">filter_sub_lang_key</string>
|
||||||
<string name="pref_filter_search_quality_key" translatable="false">pref_filter_search_quality_key</string>
|
<string name="pref_filter_search_quality_key" translatable="false">pref_filter_search_quality_key</string>
|
||||||
<string name="enable_nsfw_on_providers_key" translatable="false">enable_nsfw_on_providers_key</string>
|
<string name="enable_nsfw_on_providers_key" translatable="false">enable_nsfw_on_providers_key</string>
|
||||||
|
<string name="skip_startup_account_select_key" translatable="false">skip_startup_account_select_key</string>
|
||||||
<string name="enable_skip_op_from_database" translatable="false">enable_skip_op_from_database</string>
|
<string name="enable_skip_op_from_database" translatable="false">enable_skip_op_from_database</string>
|
||||||
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
<!-- FORMAT MIGHT TRANSLATE, WILL CAUSE CRASH IF APPLIED WRONG -->
|
||||||
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string>
|
<string name="extra_info_format" formatted="true" translatable="false">%d %s | %s</string>
|
||||||
|
@ -720,10 +721,16 @@
|
||||||
|
|
||||||
|
|
||||||
<string name="enter_pin">Enter PIN</string>
|
<string name="enter_pin">Enter PIN</string>
|
||||||
|
<string name="enter_pin_with_name" formatted="true">Enter PIN for %s</string>
|
||||||
<string name="enter_current_pin">Enter Current PIN</string>
|
<string name="enter_current_pin">Enter Current PIN</string>
|
||||||
<string name="lock_profile">Lock Profile</string>
|
<string name="lock_profile">Lock Profile</string>
|
||||||
<string name="pin">PIN</string>
|
<string name="pin">PIN</string>
|
||||||
<string name="pin_error_incorrect">Incorrect PIN. Please try again.</string>
|
<string name="pin_error_incorrect">Incorrect PIN. Please try again.</string>
|
||||||
<string name="pin_error_length">PIN must be 4 characters</string>
|
<string name="pin_error_length">PIN must be 4 characters</string>
|
||||||
<string name="select_an_account">Select an Account</string>
|
<string name="select_an_account">Select an Account</string>
|
||||||
|
<string name="manage_accounts">Manage Accounts</string>
|
||||||
|
<string name="edit_account">Edit account</string>
|
||||||
|
<string name="logged_account" formatted="true">Logged in as %s</string>
|
||||||
|
<string name="skip_startup_account_select_pref">Skip account selection at startup</string>
|
||||||
|
<string name="use_default_account">Use Default Account</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:icon="@drawable/ic_outline_account_circle_24"
|
||||||
|
android:key="@string/skip_startup_account_select_key"
|
||||||
|
android:title="@string/skip_startup_account_select_pref" />
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:icon="@drawable/mal_logo"
|
android:icon="@drawable/mal_logo"
|
||||||
android:key="@string/mal_key" />
|
android:key="@string/mal_key" />
|
||||||
|
|
Loading…
Reference in a new issue