diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e0d43338..072ebb48 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -96,12 +96,6 @@
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true">
-
-
-
-
-
-
@@ -165,6 +159,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
new file mode 100644
index 00000000..c1566ce4
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountAdapter.kt
@@ -0,0 +1,47 @@
+package com.lagradost.cloudstream3.ui.account
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.core.view.isVisible
+import androidx.recyclerview.widget.RecyclerView
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.ui.result.setImage
+import com.lagradost.cloudstream3.utils.DataStoreHelper
+
+class AccountAdapter(private val accounts: List, private val onItemClick: (DataStoreHelper.Account) -> Unit) :
+ RecyclerView.Adapter() {
+
+ inner class AccountViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+ val accountName: TextView = itemView.findViewById(R.id.account_name)
+ val accountImage: ImageView = itemView.findViewById(R.id.account_image)
+ val lockIcon: ImageView = itemView.findViewById(R.id.lock_icon)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AccountViewHolder {
+ val view = LayoutInflater.from(parent.context).inflate(R.layout.account_list_item, parent, false)
+ return AccountViewHolder(view)
+ }
+
+ override fun onBindViewHolder(holder: AccountViewHolder, position: Int) {
+ val account = accounts[position]
+
+ // Populate data into the UI elements
+ holder.accountName.text = account.name
+ holder.accountImage.setImage(account.image)
+
+ if (account.lockPin != null) {
+ holder.lockIcon.isVisible = true
+ }
+
+ holder.itemView.setOnClickListener {
+ onItemClick(account)
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return accounts.size
+ }
+}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
new file mode 100644
index 00000000..fda311ee
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountDialog.kt
@@ -0,0 +1,108 @@
+package com.lagradost.cloudstream3.ui.account
+
+import android.content.Context
+import android.text.Editable
+import android.text.TextWatcher
+import android.view.LayoutInflater
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.widget.TextView
+import androidx.annotation.StringRes
+import androidx.appcompat.app.AlertDialog
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
+
+object AccountDialog {
+ // TODO add account creation dialog to allow creating accounts directly from AccountSelectActivity
+
+ fun showPinInputDialog(
+ context: Context,
+ currentPin: String?,
+ editAccount: Boolean,
+ callback: (String?) -> Unit
+ ) {
+ fun TextView.visibleWithText(@StringRes textRes: Int) {
+ visibility = View.VISIBLE
+ setText(textRes)
+ }
+
+ fun View.isVisible() = visibility == View.VISIBLE
+
+ val binding = LockPinDialogBinding.inflate(LayoutInflater.from(context))
+
+ binding.pinEditTextError.visibility = View.GONE
+
+ 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
+
+ val dialog = AlertDialog.Builder(context, R.style.AlertDialogCustom)
+ .setView(binding.root)
+ .setTitle(titleRes)
+ .setNegativeButton(R.string.cancel) { _, _ ->
+ callback.invoke(null)
+ }
+ .setOnCancelListener {
+ callback.invoke(null)
+ }
+ .setOnDismissListener {
+ if (binding.pinEditTextError.isVisible()) {
+ callback.invoke(null)
+ }
+ }
+ .create()
+
+ var isPinValid = false
+
+ binding.pinEditText.addTextChangedListener(object : TextWatcher {
+ override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
+
+ override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
+ val enteredPin = s.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.visibility = View.GONE
+ isPinValid = true
+
+ callback.invoke(enteredPin)
+ dialog.dismiss()
+ }
+ } else {
+ binding.pinEditTextError.visibility = View.GONE
+ isPinValid = true
+ }
+ } else if (isNewPin) {
+ binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
+ isPinValid = false
+ }
+ }
+
+ override fun afterTextChanged(s: Editable?) {}
+ })
+
+ // 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.dismiss()
+ }
+ 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()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt
new file mode 100644
index 00000000..9e5da5af
--- /dev/null
+++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountSelectActivity.kt
@@ -0,0 +1,56 @@
+package com.lagradost.cloudstream3.ui.account
+
+import android.content.Intent
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.lagradost.cloudstream3.MainActivity
+import com.lagradost.cloudstream3.R
+import com.lagradost.cloudstream3.ui.account.AccountDialog.showPinInputDialog
+import com.lagradost.cloudstream3.utils.DataStoreHelper
+import com.lagradost.cloudstream3.utils.DataStoreHelper.getAccounts
+
+class AccountSelectActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_account_select)
+
+ val recyclerView: RecyclerView = findViewById(R.id.accountRecyclerView)
+
+ val accounts = getAccounts(this@AccountSelectActivity)
+
+ val adapter = AccountAdapter(accounts) { selectedAccount ->
+ // Handle the selected account
+ onAccountSelected(selectedAccount)
+ }
+ recyclerView.adapter = adapter
+
+ recyclerView.layoutManager = GridLayoutManager(this, 3)
+ }
+
+ 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
+ DataStoreHelper.selectedKeyIndex = selectedAccount.keyIndex
+ navigateToMainActivity()
+ }
+ } else {
+ // No PIN set for the selected account, proceed to main activity
+ DataStoreHelper.selectedKeyIndex = selectedAccount.keyIndex
+ navigateToMainActivity()
+ }
+ }
+
+ private fun navigateToMainActivity() {
+ val mainIntent = Intent(this, MainActivity::class.java)
+ startActivity(mainIntent)
+ finish() // Finish the account selection activity
+ }
+
+
+}
diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
index 1736c5a9..1adcac1c 100644
--- a/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
+++ b/app/src/main/java/com/lagradost/cloudstream3/utils/DataStoreHelper.kt
@@ -3,12 +3,7 @@ package com.lagradost.cloudstream3.utils
import android.content.Context
import android.content.DialogInterface
import android.text.Editable
-import android.text.TextWatcher
import android.view.LayoutInflater
-import android.view.View
-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
@@ -25,7 +20,6 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
import com.lagradost.cloudstream3.CommonActivity.showToast
-import com.lagradost.cloudstream3.databinding.LockPinDialogBinding
import com.lagradost.cloudstream3.databinding.WhoIsWatchingAccountEditBinding
import com.lagradost.cloudstream3.databinding.WhoIsWatchingBinding
import com.lagradost.cloudstream3.mvvm.logError
@@ -33,6 +27,7 @@ import com.lagradost.cloudstream3.syncproviders.AccountManager
import com.lagradost.cloudstream3.syncproviders.SyncAPI
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.result.UiImage
import com.lagradost.cloudstream3.ui.result.VideoWatchState
@@ -330,6 +325,14 @@ object DataStoreHelper {
}
}
+ fun getAccounts(context: Context): List {
+ return accounts.toMutableList().apply {
+ val item = getDefaultAccount(context)
+ remove(item)
+ add(0, item)
+ }
+ }
+
fun showWhoIsWatching(context: Context) {
val binding: WhoIsWatchingBinding = WhoIsWatchingBinding.inflate(LayoutInflater.from(context))
val builder = BottomSheetDialog(context)
@@ -395,97 +398,6 @@ object DataStoreHelper {
builder.show()
}
- private fun showPinInputDialog(
- context: Context,
- currentPin: String?,
- editAccount: Boolean,
- callback: (String?) -> Unit
- ) {
- fun TextView.visibleWithText(@StringRes textRes: Int) {
- visibility = View.VISIBLE
- setText(textRes)
- }
-
- fun View.isVisible() = visibility == View.VISIBLE
-
- val binding = LockPinDialogBinding.inflate(LayoutInflater.from(context))
-
- binding.pinEditTextError.visibility = View.GONE
-
- 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
-
- val dialog = AlertDialog.Builder(context, R.style.AlertDialogCustom)
- .setView(binding.root)
- .setTitle(titleRes)
- .setNegativeButton(R.string.cancel) { _, _ ->
- callback.invoke(null)
- }
- .setOnCancelListener {
- callback.invoke(null)
- }
- .setOnDismissListener {
- if (binding.pinEditTextError.isVisible()) {
- callback.invoke(null)
- }
- }
- .create()
-
- var isPinValid = false
-
- binding.pinEditText.addTextChangedListener(object : TextWatcher {
- override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
-
- override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
- val enteredPin = s.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.visibility = View.GONE
- isPinValid = true
-
- callback.invoke(enteredPin)
- dialog.dismiss()
- }
- } else {
- binding.pinEditTextError.visibility = View.GONE
- isPinValid = true
- }
- } else if (isNewPin) {
- binding.pinEditTextError.visibleWithText(R.string.pin_error_length)
- isPinValid = false
- }
- }
-
- override fun afterTextChanged(s: Editable?) {}
- })
-
- // 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.dismiss()
- }
- 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()
- }
-
data class PosDur(
@JsonProperty("position") val position: Long,
@JsonProperty("duration") val duration: Long
diff --git a/app/src/main/res/layout/account_list_item.xml b/app/src/main/res/layout/account_list_item.xml
new file mode 100644
index 00000000..a6f96242
--- /dev/null
+++ b/app/src/main/res/layout/account_list_item.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_account_select.xml b/app/src/main/res/layout/activity_account_select.xml
new file mode 100644
index 00000000..d7e73f32
--- /dev/null
+++ b/app/src/main/res/layout/activity_account_select.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c5694793..9efca7f2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -722,4 +722,5 @@
PIN
Incorrect PIN. Please try again.
PIN must be 4 characters
+ Select an Account