account switch

This commit is contained in:
LagradOst 2021-11-08 19:13:39 +01:00
parent 3a6814f808
commit a34c79e4a9
10 changed files with 269 additions and 27 deletions

View file

@ -29,6 +29,7 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.network.initRequestClient import com.lagradost.cloudstream3.network.initRequestClient
import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver import com.lagradost.cloudstream3.receivers.VideoDownloadRestartReceiver
import com.lagradost.cloudstream3.syncproviders.OAuth2Interface.Companion.OAuth2Apis 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.syncproviders.OAuth2Interface.Companion.appString
import com.lagradost.cloudstream3.ui.APIRepository import com.lagradost.cloudstream3.ui.APIRepository
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO import com.lagradost.cloudstream3.ui.download.DOWNLOAD_NAVIGATE_TO
@ -371,6 +372,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
// init accounts
for (api in OAuth2accountApis) {
api.init(this)
}
val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this)
val currentTheme = when (settingsManager.getString(getString(R.string.app_theme_key), "Black")) { val currentTheme = when (settingsManager.getString(getString(R.string.app_theme_key), "Black")) {
@ -593,6 +599,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
} }
APIRepository.dubStatusActive = getApiDubstatusSettings() APIRepository.dubStatusActive = getApiDubstatusSettings()
/* /*
val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar val relativePath = (Environment.DIRECTORY_DOWNLOADS) + File.separatorChar
val displayName = "output.dex" //""output.dex" val displayName = "output.dex" //""output.dex"

View file

@ -17,33 +17,34 @@ import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.getKey
import com.lagradost.cloudstream3.utils.DataStore.getKeys 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.setKey
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
import java.net.URL import java.net.URL
import java.util.* import java.util.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class AniListApi(var accountId: String) : OAuth2Interface { class AniListApi(index : Int) : OAuth2Interface.AccountManager(index) {
override val name: String override val name: String
get() = "AniList" get() = "AniList"
override val key: String override val key: String
get() = "6871" get() = "6871"
override val redirectUrl: String override val redirectUrl: String
get() = "anilistlogin" get() = "anilistlogin"
override val idPrefix: String
override fun logOut(context: Context) { get() = "anilist"
context.removeKeys(accountId)
}
override fun loginInfo(context: Context): OAuth2Interface.LoginInfo? { override fun loginInfo(context: Context): OAuth2Interface.LoginInfo? {
// context.getUser(true)?. // context.getUser(true)?.
context.getKey<AniListUser>(accountId, ANILIST_USER_KEY)?.let { user -> 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 return null
} }
override fun logOut(context: Context) {
context.removeAccountKeys()
}
override fun authenticate(context: Context) { override fun authenticate(context: Context) {
val request = "https://anilist.co/api/v2/oauth/authorize?client_id=$key&response_type=token" val request = "https://anilist.co/api/v2/oauth/authorize?client_id=$key&response_type=token"
context.openBrowser(request) context.openBrowser(request)
@ -58,6 +59,7 @@ class AniListApi(var accountId: String) : OAuth2Interface {
val endTime = unixTime + expiresIn.toLong() val endTime = unixTime + expiresIn.toLong()
context.switchToNewAccount()
context.setKey(accountId, ANILIST_UNIXTIME_KEY, endTime) context.setKey(accountId, ANILIST_UNIXTIME_KEY, endTime)
context.setKey(accountId, ANILIST_TOKEN_KEY, token) context.setKey(accountId, ANILIST_TOKEN_KEY, token)
context.setKey(ANILIST_SHOULD_UPDATE_LIST, true) 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 = """ val q = """
{ {
Viewer { Viewer {
@ -582,6 +584,7 @@ class AniListApi(var accountId: String) : OAuth2Interface {
) )
if (setSettings) { if (setSettings) {
setKey(accountId, ANILIST_USER_KEY, user) setKey(accountId, ANILIST_USER_KEY, user)
registerAccount()
} }
/* // TODO FIX FAVS /* // TODO FIX FAVS
for(i in u.favourites.anime.nodes) { for(i in u.favourites.anime.nodes) {

View file

@ -19,7 +19,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.utils.AppUtils.splitQuery import com.lagradost.cloudstream3.utils.AppUtils.splitQuery
import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.ioSafe
import com.lagradost.cloudstream3.utils.DataStore.getKey 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.setKey
import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject import com.lagradost.cloudstream3.utils.DataStore.toKotlinObject
import java.net.URL import java.net.URL
@ -28,22 +27,24 @@ import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
class MALApi(var accountId: String) : OAuth2Interface { class MALApi(index : Int) : OAuth2Interface.AccountManager(index) {
override val name: String override val name: String
get() = "MAL" get() = "MAL"
override val key: String override val key: String
get() = "1714d6f2f4f7cc19644384f8c4629910" get() = "1714d6f2f4f7cc19644384f8c4629910"
override val redirectUrl: String override val redirectUrl: String
get() = "mallogin" get() = "mallogin"
override val idPrefix: String
get() = "mal"
override fun logOut(context: Context) { override fun logOut(context: Context) {
context.removeKeys(accountId) context.removeAccountKeys()
} }
override fun loginInfo(context: Context): OAuth2Interface.LoginInfo? { override fun loginInfo(context: Context): OAuth2Interface.LoginInfo? {
//context.getMalUser(true)? //context.getMalUser(true)?
context.getKey<MalUser>(accountId, MAL_USER_KEY)?.let { user -> 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 return null
} }
@ -84,6 +85,7 @@ class MALApi(var accountId: String) : OAuth2Interface {
} }
if (res != "") { if (res != "") {
context.switchToNewAccount()
context.storeToken(res) context.storeToken(res)
context.getMalUser() context.getMalUser()
context.setKey(MAL_SHOULD_UPDATE_LIST, true) 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() checkMalToken()
return try { return try {
val res = get( val res = get(
@ -399,6 +401,7 @@ class MALApi(var accountId: String) : OAuth2Interface {
val user = mapper.readValue<MalUser>(res) val user = mapper.readValue<MalUser>(res)
if (setSettings) { if (setSettings) {
setKey(accountId, MAL_USER_KEY, user) setKey(accountId, MAL_USER_KEY, user)
registerAccount()
} }
user user
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -1,6 +1,9 @@
package com.lagradost.cloudstream3.syncproviders package com.lagradost.cloudstream3.syncproviders
import android.content.Context 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 import java.util.concurrent.TimeUnit
interface OAuth2Interface { interface OAuth2Interface {
@ -17,13 +20,78 @@ interface OAuth2Interface {
class LoginInfo( class LoginInfo(
val profilePicture: String?, val profilePicture: String?,
val name: String?, val name: String?,
val accountIndex: Int,
) )
companion object { abstract class AccountManager(private val defIndex: Int) : OAuth2Interface {
val malApi = MALApi("mal_account_0") // don't change this as all keys depend on it
val aniListApi = AniListApi("anilist_account_0") open val idPrefix: String
get() {
throw(NotImplementedError())
}
val OAuth2Apis get() = listOf( 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 malApi, aniListApi
) )

View file

@ -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))
}
}
}
}

View file

@ -16,6 +16,7 @@ import androidx.appcompat.app.AlertDialog
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView
import com.hippo.unifile.UniFile import com.hippo.unifile.UniFile
import com.lagradost.cloudstream3.APIHolder.apis import com.lagradost.cloudstream3.APIHolder.apis
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
@ -119,7 +120,37 @@ class SettingsFragment : PreferenceFragmentCompat() {
Triple("🇹🇷", "Turkish", "tr") Triple("🇹🇷", "Turkish", "tr")
).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top ).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 = val builder =
AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_managment) AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_managment)
val dialog = builder.show() 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_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_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?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {

View file

@ -56,6 +56,11 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/account_switch_account"
android:text="@string/switch_account"
style="@style/SettingsItem">
</TextView>
<TextView <TextView
android:id="@+id/account_logout" android:id="@+id/account_logout"
android:text="@string/logout" android:text="@string/logout"

View 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>

View 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>

View file

@ -326,6 +326,8 @@
<string name="account">account</string> <string name="account">account</string>
<string name="logout">Logout</string> <string name="logout">Logout</string>
<string name="login">Login</string> <string name="login">Login</string>
<string name="switch_account">Switch account</string>
<string name="add_account">Add account</string>
<!-- ============ --> <!-- ============ -->
</resources> </resources>