forked from recloudstream/cloudstream
account switch
This commit is contained in:
parent
3a6814f808
commit
a34c79e4a9
10 changed files with 269 additions and 27 deletions
|
@ -29,6 +29,7 @@ import com.lagradost.cloudstream3.mvvm.logError
|
|||
import com.lagradost.cloudstream3.network.initRequestClient
|
||||
import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver
|
||||
import com.lagradost.cloudstream3.syncproviders.OAuth2Interface.Companion.OAuth2Apis
|
||||
import com.lagradost.cloudstream3.syncproviders.OAuth2Interface.Companion.OAuth2accountApis
|
||||
import com.lagradost.cloudstream3.syncproviders.OAuth2Interface.Companion.appString
|
||||
import com.lagradost.cloudstream3.ui.APIRepository
|
||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
|
||||
|
@ -371,6 +372,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// init accounts
|
||||
for (api in OAuth2accountApis) {
|
||||
api.init(this)
|
||||
}
|
||||
|
||||
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
val currentTheme = when (settingsManager.getString(getString(R.string.app_theme_key), "Black")) {
|
||||
|
@ -593,6 +599,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
|
|||
}
|
||||
APIRepository.dubStatusActive = getApiDubstatusSettings()
|
||||
|
||||
|
||||
/*
|
||||
val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar
|
||||
val displayName = "output.dex" //""output.dex"
|
||||
|
|
|
@ -17,33 +17,34 @@ import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
|||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKeys
|
||||
import com.lagradost.cloudstream3.utils.DataStore.removeKeys
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
|
||||
import java.net.URL
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class AniListApi(var accountId: String) : OAuth2Interface {
|
||||
class AniListApi(index : Int) : OAuth2Interface.AccountManager(index) {
|
||||
override val name: String
|
||||
get() = "AniList"
|
||||
override val key: String
|
||||
get() = "6871"
|
||||
override val redirectUrl: String
|
||||
get() = "anilistlogin"
|
||||
|
||||
override fun logOut(context: Context) {
|
||||
context.removeKeys(accountId)
|
||||
}
|
||||
override val idPrefix: String
|
||||
get() = "anilist"
|
||||
|
||||
override fun loginInfo(context: Context): OAuth2Interface.LoginInfo? {
|
||||
// context.getUser(true)?.
|
||||
context.getKey<AniListUser>(accountId, ANILIST_USER_KEY)?.let { user ->
|
||||
return OAuth2Interface.LoginInfo(profilePicture = user.picture, name = user.name)
|
||||
return OAuth2Interface.LoginInfo(profilePicture = user.picture, name = user.name, accountIndex = accountIndex)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun logOut(context: Context) {
|
||||
context.removeAccountKeys()
|
||||
}
|
||||
|
||||
override fun authenticate(context: Context) {
|
||||
val request = "https://anilist.co/api/v2/oauth/authorize?client_id=$key&response_type=token"
|
||||
context.openBrowser(request)
|
||||
|
@ -58,6 +59,7 @@ class AniListApi(var accountId: String) : OAuth2Interface {
|
|||
|
||||
val endTime = unixTime + expiresIn.toLong()
|
||||
|
||||
context.switchToNewAccount()
|
||||
context.setKey(accountId, ANILIST_UNIXTIME_KEY, endTime)
|
||||
context.setKey(accountId, ANILIST_TOKEN_KEY, token)
|
||||
context.setKey(ANILIST_SHOULD_UPDATE_LIST, true)
|
||||
|
@ -552,7 +554,7 @@ class AniListApi(var accountId: String) : OAuth2Interface {
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.getUser(setSettings: Boolean = true): AniListUser? {
|
||||
private fun Context.getUser(setSettings: Boolean = true): AniListUser? {
|
||||
val q = """
|
||||
{
|
||||
Viewer {
|
||||
|
@ -582,6 +584,7 @@ class AniListApi(var accountId: String) : OAuth2Interface {
|
|||
)
|
||||
if (setSettings) {
|
||||
setKey(accountId, ANILIST_USER_KEY, user)
|
||||
registerAccount()
|
||||
}
|
||||
/* // TODO FIX FAVS
|
||||
for(i in u.favourites.anime.nodes) {
|
||||
|
|
|
@ -19,7 +19,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
|||
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
|
||||
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.removeKeys
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
|
||||
import java.net.URL
|
||||
|
@ -28,22 +27,24 @@ import java.text.ParseException
|
|||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class MALApi(var accountId: String) : OAuth2Interface {
|
||||
class MALApi(index : Int) : OAuth2Interface.AccountManager(index) {
|
||||
override val name: String
|
||||
get() = "MAL"
|
||||
override val key: String
|
||||
get() = "1714d6f2f4f7cc19644384f8c4629910"
|
||||
override val redirectUrl: String
|
||||
get() = "mallogin"
|
||||
override val idPrefix: String
|
||||
get() = "mal"
|
||||
|
||||
override fun logOut(context: Context) {
|
||||
context.removeKeys(accountId)
|
||||
context.removeAccountKeys()
|
||||
}
|
||||
|
||||
override fun loginInfo(context: Context): OAuth2Interface.LoginInfo? {
|
||||
//context.getMalUser(true)?
|
||||
context.getKey<MalUser>(accountId, MAL_USER_KEY)?.let { user ->
|
||||
return OAuth2Interface.LoginInfo(profilePicture = user.picture, name = user.name)
|
||||
return OAuth2Interface.LoginInfo(profilePicture = user.picture, name = user.name, accountIndex = accountIndex)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
@ -84,6 +85,7 @@ class MALApi(var accountId: String) : OAuth2Interface {
|
|||
}
|
||||
|
||||
if (res != "") {
|
||||
context.switchToNewAccount()
|
||||
context.storeToken(res)
|
||||
context.getMalUser()
|
||||
context.setKey(MAL_SHOULD_UPDATE_LIST, true)
|
||||
|
@ -383,7 +385,7 @@ class MALApi(var accountId: String) : OAuth2Interface {
|
|||
}
|
||||
}
|
||||
|
||||
fun Context.getMalUser(setSettings: Boolean = true): MalUser? {
|
||||
private fun Context.getMalUser(setSettings: Boolean = true): MalUser? {
|
||||
checkMalToken()
|
||||
return try {
|
||||
val res = get(
|
||||
|
@ -399,6 +401,7 @@ class MALApi(var accountId: String) : OAuth2Interface {
|
|||
val user = mapper.readValue<MalUser>(res)
|
||||
if (setSettings) {
|
||||
setKey(accountId, MAL_USER_KEY, user)
|
||||
registerAccount()
|
||||
}
|
||||
user
|
||||
} catch (e: Exception) {
|
||||
|
|
|
@ -1,31 +1,99 @@
|
|||
package com.lagradost.cloudstream3.syncproviders
|
||||
|
||||
import android.content.Context
|
||||
import com.lagradost.cloudstream3.utils.DataStore.getKey
|
||||
import com.lagradost.cloudstream3.utils.DataStore.removeKeys
|
||||
import com.lagradost.cloudstream3.utils.DataStore.setKey
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
interface OAuth2Interface {
|
||||
val key : String
|
||||
val name : String
|
||||
val redirectUrl : String
|
||||
val key: String
|
||||
val name: String
|
||||
val redirectUrl: String
|
||||
|
||||
fun handleRedirect(context: Context, url : String)
|
||||
fun handleRedirect(context: Context, url: String)
|
||||
fun authenticate(context: Context)
|
||||
|
||||
fun loginInfo(context: Context) : LoginInfo?
|
||||
fun loginInfo(context: Context): LoginInfo?
|
||||
fun logOut(context: Context)
|
||||
|
||||
class LoginInfo(
|
||||
val profilePicture : String?,
|
||||
val name : String?,
|
||||
val profilePicture: String?,
|
||||
val name: String?,
|
||||
|
||||
val accountIndex: Int,
|
||||
)
|
||||
|
||||
companion object {
|
||||
val malApi = MALApi("mal_account_0")
|
||||
val aniListApi = AniListApi("anilist_account_0")
|
||||
abstract class AccountManager(private val defIndex: Int) : OAuth2Interface {
|
||||
// don't change this as all keys depend on it
|
||||
open val idPrefix: String
|
||||
get() {
|
||||
throw(NotImplementedError())
|
||||
}
|
||||
|
||||
val OAuth2Apis get() = listOf(
|
||||
malApi, aniListApi
|
||||
)
|
||||
var accountIndex = defIndex
|
||||
protected val accountId get() = "${idPrefix}_account_$accountIndex"
|
||||
private val accountActiveKey get() = "${idPrefix}_active"
|
||||
|
||||
// int array of all accounts indexes
|
||||
private val accountsKey get() = "${idPrefix}_accounts"
|
||||
|
||||
protected fun Context.removeAccountKeys() {
|
||||
this.removeKeys(accountId)
|
||||
val accounts = getAccounts(this).toMutableList()
|
||||
accounts.remove(accountIndex)
|
||||
this.setKey(accountsKey, accounts.toIntArray())
|
||||
|
||||
init(this)
|
||||
}
|
||||
|
||||
fun getAccounts(context: Context): IntArray {
|
||||
return context.getKey(accountsKey, intArrayOf())!!
|
||||
}
|
||||
|
||||
fun init(context: Context) {
|
||||
accountIndex = context.getKey(accountActiveKey, defIndex)!!
|
||||
val accounts = getAccounts(context)
|
||||
if (accounts.isNotEmpty() && this.loginInfo(context) == null) {
|
||||
accountIndex = accounts.first()
|
||||
}
|
||||
}
|
||||
|
||||
protected fun Context.switchToNewAccount() {
|
||||
val accounts = getAccounts(this)
|
||||
accountIndex = (accounts.maxOrNull() ?: 0) + 1
|
||||
}
|
||||
|
||||
protected fun Context.registerAccount() {
|
||||
this.setKey(accountActiveKey, accountIndex)
|
||||
val accounts = getAccounts(this).toMutableList()
|
||||
if (!accounts.contains(accountIndex)) {
|
||||
accounts.add(accountIndex)
|
||||
}
|
||||
|
||||
this.setKey(accountsKey, accounts.toIntArray())
|
||||
}
|
||||
|
||||
fun changeAccount(context: Context, index: Int) {
|
||||
accountIndex = index
|
||||
context.setKey(accountActiveKey, index)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val malApi = MALApi(0)
|
||||
val aniListApi = AniListApi(0)
|
||||
|
||||
val OAuth2Apis
|
||||
get() = listOf<OAuth2Interface>(
|
||||
malApi, aniListApi
|
||||
)
|
||||
|
||||
// this needs init with context
|
||||
val OAuth2accountApis
|
||||
get() = listOf<AccountManager>(
|
||||
malApi, aniListApi
|
||||
)
|
||||
|
||||
const val appString = "cloudstreamapp"
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
package com.lagradost.cloudstream3.ui.settings
|
||||
|
||||
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.syncproviders.OAuth2Interface
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.setImage
|
||||
|
||||
class AccountClickCallback(val action: Int, val view : View, val card: OAuth2Interface.LoginInfo)
|
||||
|
||||
class AccountAdapter(
|
||||
val cardList: List<OAuth2Interface.LoginInfo>,
|
||||
val layout: Int = R.layout.account_single,
|
||||
private val clickCallback: (AccountClickCallback) -> Unit
|
||||
) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return CardViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(layout, parent, false), clickCallback
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is CardViewHolder -> {
|
||||
holder.bind(cardList[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return cardList.size
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return cardList[position].accountIndex.toLong()
|
||||
}
|
||||
|
||||
class CardViewHolder
|
||||
constructor(itemView: View, private val clickCallback: (AccountClickCallback) -> Unit) :
|
||||
RecyclerView.ViewHolder(itemView) {
|
||||
private val pfp: ImageView = itemView.findViewById(R.id.account_profile_picture)!!
|
||||
private val accountName: TextView = itemView.findViewById(R.id.account_name)!!
|
||||
|
||||
fun bind(card: OAuth2Interface.LoginInfo) {
|
||||
// just in case name is null account index will show, should never happened
|
||||
accountName.text = card.name ?: "%s %d".format(accountName.context.getString(R.string.account), card.accountIndex)
|
||||
if(card.profilePicture.isNullOrEmpty()) {
|
||||
pfp.isVisible = false
|
||||
} else {
|
||||
pfp.isVisible = true
|
||||
pfp.setImage(card.profilePicture)
|
||||
}
|
||||
|
||||
itemView.setOnClickListener {
|
||||
clickCallback.invoke(AccountClickCallback(0, itemView, card))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@ import androidx.appcompat.app.AlertDialog
|
|||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.hippo.unifile.UniFile
|
||||
import com.lagradost.cloudstream3.APIHolder.apis
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||
|
@ -119,7 +120,37 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
Triple("🇹🇷", "Turkish", "tr")
|
||||
).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top
|
||||
|
||||
private fun showLoginInfo(context: Context, api: OAuth2Interface, info: OAuth2Interface.LoginInfo) {
|
||||
private fun showAccountSwitch(context: Context, api: OAuth2Interface.AccountManager) {
|
||||
val builder =
|
||||
AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_switch)
|
||||
val dialog = builder.show()
|
||||
|
||||
val accounts = api.getAccounts(context)
|
||||
dialog.findViewById<TextView>(R.id.account_add)?.setOnClickListener {
|
||||
api.authenticate(it.context)
|
||||
}
|
||||
|
||||
val ogIndex = api.accountIndex
|
||||
|
||||
val items = ArrayList<OAuth2Interface.LoginInfo>()
|
||||
|
||||
for (index in accounts) {
|
||||
api.accountIndex = index
|
||||
val accountInfo = api.loginInfo(context)
|
||||
if (accountInfo != null) {
|
||||
items.add(accountInfo)
|
||||
}
|
||||
}
|
||||
api.accountIndex = ogIndex
|
||||
val adapter = AccountAdapter(items, R.layout.account_single) {
|
||||
dialog?.dismiss()
|
||||
api.changeAccount(it.view.context, it.card.accountIndex)
|
||||
}
|
||||
val list = dialog.findViewById<RecyclerView>(R.id.account_list)
|
||||
list?.adapter = adapter
|
||||
}
|
||||
|
||||
private fun showLoginInfo(context: Context, api: OAuth2Interface.AccountManager, info: OAuth2Interface.LoginInfo) {
|
||||
val builder =
|
||||
AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_managment)
|
||||
val dialog = builder.show()
|
||||
|
@ -134,6 +165,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
|
|||
|
||||
dialog.findViewById<TextView>(R.id.account_name)?.text = info.name ?: context.getString(R.string.no_data)
|
||||
dialog.findViewById<TextView>(R.id.account_site)?.text = api.name
|
||||
dialog.findViewById<TextView>(R.id.account_switch_account)?.setOnClickListener {
|
||||
dialog.dismiss()
|
||||
showAccountSwitch(it.context, api)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
|
|
|
@ -56,6 +56,11 @@
|
|||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/account_switch_account"
|
||||
android:text="@string/switch_account"
|
||||
style="@style/SettingsItem">
|
||||
</TextView>
|
||||
<TextView
|
||||
android:id="@+id/account_logout"
|
||||
android:text="@string/logout"
|
||||
|
|
29
app/src/main/res/layout/account_single.xml
Normal file
29
app/src/main/res/layout/account_single.xml
Normal file
|
@ -0,0 +1,29 @@
|
|||
<LinearLayout
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:orientation="horizontal"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_marginStart="10dp"
|
||||
app:cardCornerRadius="100dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp">
|
||||
<ImageView
|
||||
android:id="@+id/account_profile_picture"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:ignore="ContentDescription">
|
||||
</ImageView>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<TextView
|
||||
android:foreground="@null"
|
||||
android:id="@+id/account_name"
|
||||
tools:text="Account 1"
|
||||
style="@style/SettingsItem">
|
||||
</TextView>
|
||||
</LinearLayout>
|
||||
|
24
app/src/main/res/layout/account_switch.xml
Normal file
24
app/src/main/res/layout/account_switch.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/account_list"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
android:background="?attr/primaryBlackBackground"
|
||||
tools:listitem="@layout/account_single"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_rowWeight="1"
|
||||
android:layout_height="wrap_content">
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
<TextView
|
||||
android:id="@+id/account_add"
|
||||
android:text="@string/add_account"
|
||||
style="@style/SettingsItem">
|
||||
<requestFocus/>
|
||||
</TextView>
|
||||
</LinearLayout>
|
|
@ -326,6 +326,8 @@
|
|||
<string name="account">account</string>
|
||||
<string name="logout">Logout</string>
|
||||
<string name="login">Login</string>
|
||||
<string name="switch_account">Switch account</string>
|
||||
<string name="add_account">Add account</string>
|
||||
<!-- ============ -->
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue