added local accounts

This commit is contained in:
LagradOst 2023-08-02 05:30:50 +02:00
parent b06f098447
commit 180987e2d0
17 changed files with 693 additions and 29 deletions

View file

@ -148,6 +148,14 @@ class AcraApplication : Application() {
_context = WeakReference(value) _context = WeakReference(value)
} }
fun <T : Any> getKeyClass(path: String, valueType: Class<T>): T? {
return context?.getKey(path, valueType)
}
fun <T : Any> setKeyClass(path: String, value: T) {
context?.setKey(path, value)
}
fun removeKeys(folder: String): Int? { fun removeKeys(folder: String): Int? {
return context?.removeKeys(folder) return context?.removeKeys(folder)
} }

View file

@ -0,0 +1,106 @@
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)
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
}
}

View file

@ -40,6 +40,7 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showOptionSelectStringRes
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarMargin
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbarView
import com.lagradost.cloudstream3.utils.UIHelper.setImage import com.lagradost.cloudstream3.utils.UIHelper.setImage
@ -250,6 +251,11 @@ class HomeParentItemAdapterPreview(
private var bookmarkRecyclerView: RecyclerView = private var bookmarkRecyclerView: RecyclerView =
itemView.findViewById(R.id.home_bookmarked_child_recyclerview) itemView.findViewById(R.id.home_bookmarked_child_recyclerview)
private var homeAccount: View? =
itemView.findViewById(R.id.home_switch_account)
private var topPadding : View? = itemView.findViewById(R.id.home_padding)
private val homeNonePadding: View = itemView.findViewById(R.id.home_none_padding) private val homeNonePadding: View = itemView.findViewById(R.id.home_none_padding)
private val previewCallback: ViewPager2.OnPageChangeCallback = private val previewCallback: ViewPager2.OnPageChangeCallback =
@ -432,6 +438,8 @@ class HomeParentItemAdapterPreview(
resumeRecyclerView.setLinearListLayout() resumeRecyclerView.setLinearListLayout()
bookmarkRecyclerView.setLinearListLayout() bookmarkRecyclerView.setLinearListLayout()
fixPaddingStatusbarMargin(topPadding)
for ((chip, watch) in toggleList) { for ((chip, watch) in toggleList) {
chip.isChecked = false chip.isChecked = false
chip.setOnCheckedChangeListener { _, isChecked -> chip.setOnCheckedChangeListener { _, isChecked ->
@ -445,6 +453,10 @@ class HomeParentItemAdapterPreview(
} }
} }
homeAccount?.setOnClickListener { v ->
DataStoreHelper.showWhoIsWatching(v?.context ?: return@setOnClickListener)
}
(binding as? FragmentHomeHeadTvBinding)?.apply { (binding as? FragmentHomeHeadTvBinding)?.apply {
homePreviewChangeApi.setOnClickListener { view -> homePreviewChangeApi.setOnClickListener { view ->
view.context.selectHomepage(viewModel.repo?.name) { api -> view.context.selectHomepage(viewModel.repo?.name) { api ->
@ -485,8 +497,6 @@ class HomeParentItemAdapterPreview(
} }
(binding as? FragmentHomeHeadBinding)?.apply { (binding as? FragmentHomeHeadBinding)?.apply {
fixPaddingStatusbar(binding.homeSearch)
homeSearch.setOnQueryTextListener(object : SearchView.OnQueryTextListener { homeSearch.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
viewModel.queryTextSubmit(query) viewModel.queryTextSubmit(query)

View file

@ -96,8 +96,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
// protected var currentPrefQuality = // protected var currentPrefQuality =
// Qualities.P2160.value // preferred maximum quality, used for ppl w bad internet or on cell // Qualities.P2160.value // preferred maximum quality, used for ppl w bad internet or on cell
protected var fastForwardTime = 10000L protected var fastForwardTime = 10000L
protected var androidTVInterfaceOffSeekTime = 10000L; protected var androidTVInterfaceOffSeekTime = 10000L
protected var androidTVInterfaceOnSeekTime = 30000L; protected var androidTVInterfaceOnSeekTime = 30000L
protected var swipeHorizontalEnabled = false protected var swipeHorizontalEnabled = false
protected var swipeVerticalEnabled = false protected var swipeVerticalEnabled = false
protected var playBackSpeedEnabled = false protected var playBackSpeedEnabled = false

View file

@ -1252,7 +1252,6 @@ class GeneratorPlayer : FullScreenPlayer() {
private fun displayTimeStamp(show: Boolean) { private fun displayTimeStamp(show: Boolean) {
if (timestampShowState == show) return if (timestampShowState == show) return
skipIndex++ skipIndex++
println("displayTimeStamp = $show")
timestampShowState = show timestampShowState = show
playerBinding?.skipChapterButton?.apply { playerBinding?.skipChapterButton?.apply {
val showWidth = 170.toPx val showWidth = 170.toPx
@ -1294,7 +1293,6 @@ class GeneratorPlayer : FullScreenPlayer() {
override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) { override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) {
if (timestamp != null) { if (timestamp != null) {
println("timestamp: $timestamp")
playerBinding?.skipChapterButton?.setText(timestamp.uiText) playerBinding?.skipChapterButton?.setText(timestamp.uiText)
displayTimeStamp(true) displayTimeStamp(true)
val currentIndex = skipIndex val currentIndex = skipIndex

View file

@ -6,7 +6,12 @@ import androidx.preference.PreferenceManager
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.json.JsonMapper import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.module.kotlin.KotlinModule import com.fasterxml.jackson.module.kotlin.KotlinModule
import com.lagradost.cloudstream3.AcraApplication.Companion.getKeyClass
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey
import com.lagradost.cloudstream3.AcraApplication.Companion.setKeyClass
import com.lagradost.cloudstream3.mvvm.logError import com.lagradost.cloudstream3.mvvm.logError
import kotlin.reflect.KClass
import kotlin.reflect.KProperty
const val DOWNLOAD_HEADER_CACHE = "download_header_cache" const val DOWNLOAD_HEADER_CACHE = "download_header_cache"
@ -20,6 +25,31 @@ const val PREFERENCES_NAME = "rebuild_preference"
// TODO degelgate by value for get & set // TODO degelgate by value for get & set
class PreferenceDelegate<T : Any>(
val key: String, val default: T //, private val klass: KClass<T>
) {
private val klass: KClass<out T> = default::class
// simple cache to make it not get the key every time it is accessed, however this requires
// that ONLY this changes the key
private var cache: T? = null
operator fun getValue(self: Any?, property: KProperty<*>) =
cache ?: getKeyClass(key, klass.java).also { newCache -> cache = newCache } ?: default
operator fun setValue(
self: Any?,
property: KProperty<*>,
t: T?
) {
cache = t
if (t == null) {
removeKey(key)
} else {
setKeyClass(key, t)
}
}
}
object DataStore { object DataStore {
val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule()) val mapper: JsonMapper = JsonMapper.builder().addModule(KotlinModule())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).build()
@ -89,7 +119,7 @@ object DataStore {
} }
fun Context.removeKeys(folder: String): Int { fun Context.removeKeys(folder: String): Int {
val keys = getKeys(folder) val keys = getKeys("$folder/")
keys.forEach { value -> keys.forEach { value ->
removeKey(value) removeKey(value)
} }
@ -106,6 +136,15 @@ object DataStore {
} }
} }
fun <T> Context.getKey(path: String, valueType: Class<T>): T? {
try {
val json: String = getSharedPrefs().getString(path, null) ?: return null
return json.toKotlinObject(valueType)
} catch (e: Exception) {
return null
}
}
fun <T> Context.setKey(folder: String, path: String, value: T) { fun <T> Context.setKey(folder: String, path: String, value: T) {
setKey(getFolderName(folder, path), value) setKey(getFolderName(folder, path), value)
} }
@ -114,6 +153,10 @@ object DataStore {
return mapper.readValue(this, T::class.java) return mapper.readValue(this, T::class.java)
} }
fun <T> String.toKotlinObject(valueType: Class<T>): T {
return mapper.readValue(this, valueType)
}
// GET KEY GIVEN PATH AND DEFAULT VALUE, NULL IF ERROR // GET KEY GIVEN PATH AND DEFAULT VALUE, NULL IF ERROR
inline fun <reified T : Any> Context.getKey(path: String, defVal: T?): T? { inline fun <reified T : Any> Context.getKey(path: String, defVal: T?): T? {
try { try {

View file

@ -1,6 +1,14 @@
package com.lagradost.cloudstream3.utils package com.lagradost.cloudstream3.utils
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.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.unixTimeMS import com.lagradost.cloudstream3.APIHolder.unixTimeMS
import com.lagradost.cloudstream3.AcraApplication.Companion.getKey import com.lagradost.cloudstream3.AcraApplication.Companion.getKey
@ -8,10 +16,20 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.getKeys
import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey 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.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.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
const val VIDEO_POS_DUR = "video_pos_dur" const val VIDEO_POS_DUR = "video_pos_dur"
const val VIDEO_WATCH_STATE = "video_watch_state" const val VIDEO_WATCH_STATE = "video_watch_state"
@ -26,6 +44,197 @@ const val RESULT_SEASON = "result_season"
const val RESULT_DUB = "result_dub" const val RESULT_DUB = "result_dub"
object DataStoreHelper { object DataStoreHelper {
// be aware, don't change the index of these as Account uses the index for the art
private val profileImages = arrayOf(
R.drawable.profile_bg_dark_blue,
R.drawable.profile_bg_blue,
R.drawable.profile_bg_orange,
R.drawable.profile_bg_pink,
R.drawable.profile_bg_purple,
R.drawable.profile_bg_red,
R.drawable.profile_bg_teal
)
data class Account(
@JsonProperty("keyIndex")
val keyIndex: Int,
@JsonProperty("name")
val name: String,
@JsonProperty("customImage")
val customImage: String? = null,
@JsonProperty("defaultImageIndex")
val defaultImageIndex: Int,
) {
val image: UiImage
get() = customImage?.let { UiImage.Image(it) } ?: UiImage.Drawable(
profileImages.getOrNull(defaultImageIndex) ?: profileImages.first()
)
}
const val TAG = "data_store_helper"
private var accounts by PreferenceDelegate("$TAG/account", arrayOf<Account>())
var selectedKeyIndex by PreferenceDelegate("$TAG/account_key_index", 0)
val currentAccount: String get() = selectedKeyIndex.toString()
private fun setAccount(account: Account) {
selectedKeyIndex = account.keyIndex
showToast(account.name)
MainActivity.bookmarksUpdatedEvent(true)
}
private fun editAccount(context: Context, account: Account, isNewAccount: Boolean) {
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))
MainActivity.bookmarksUpdatedEvent(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 {
// rolls the image forwards once
currentEditAccount =
currentEditAccount.copy(defaultImageIndex = (currentEditAccount.defaultImageIndex + 1) % profileImages.size)
binding.profilePic.setImage(currentEditAccount.image)
}
binding.applyBtt.setOnClickListener {
val currentAccounts = accounts.toMutableList()
val overrideIndex =
currentAccounts.indexOfFirst { it.keyIndex == currentEditAccount.keyIndex }
// if an account is found that has the same keyIndex then override that one, if not then append it
if (overrideIndex != -1) {
currentAccounts[overrideIndex] = currentEditAccount
} else {
currentAccounts.add(currentEditAccount)
}
// set the new default account as well as add the key for the new account
setAccount(currentEditAccount)
accounts = currentAccounts.toTypedArray()
dialog.dismissSafe()
}
}
private fun getDefaultAccount(context: Context): Account {
return accounts.let { currentAccounts ->
currentAccounts.getOrNull(currentAccounts.indexOfFirst { it.keyIndex == 0 }) ?: Account(
keyIndex = 0,
name = context.getString(R.string.default_account),
defaultImageIndex = 0
)
}
}
fun showWhoIsWatching(context: Context) {
val binding: WhoIsWatchingBinding = WhoIsWatchingBinding.inflate(
LayoutInflater.from(context)
)
val showAccount = accounts.toMutableList().apply {
val item = getDefaultAccount(context)
remove(item)
add(0, item)
}
val builder =
BottomSheetDialog(context)
builder.setContentView(binding.root)
val accountName = context.getString(R.string.account)
binding.profilesRecyclerview.setLinearListLayout(isHorizontal = true)
binding.profilesRecyclerview.adapter = WhoIsWatchingAdapter(
selectCallBack = { account ->
setAccount(account)
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
@ -117,7 +326,6 @@ object DataStoreHelper {
/** /**
* A datastore wide account for future implementations of a multiple account system * A datastore wide account for future implementations of a multiple account system
**/ **/
var currentAccount: String = "0" //TODO ACCOUNT IMPLEMENTATION
fun getAllWatchStateIds(): List<Int>? { fun getAllWatchStateIds(): List<Int>? {
val folder = "$currentAccount/$RESULT_WATCH_STATE" val folder = "$currentAccount/$RESULT_WATCH_STATE"

View file

@ -15,6 +15,7 @@ import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import android.view.ViewGroup.MarginLayoutParams
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.ImageView import android.widget.ImageView
import android.widget.ListAdapter import android.widget.ListAdapter
@ -33,6 +34,10 @@ import androidx.core.graphics.blue
import androidx.core.graphics.drawable.toBitmapOrNull import androidx.core.graphics.drawable.toBitmapOrNull
import androidx.core.graphics.green import androidx.core.graphics.green
import androidx.core.graphics.red import androidx.core.graphics.red
import androidx.core.view.marginBottom
import androidx.core.view.marginLeft
import androidx.core.view.marginRight
import androidx.core.view.marginTop
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
@ -55,7 +60,6 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.ui.result.UiImage import com.lagradost.cloudstream3.ui.result.UiImage
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
import jp.wasabeef.glide.transformations.BlurTransformation import jp.wasabeef.glide.transformations.BlurTransformation
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -77,8 +81,8 @@ object UIHelper {
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) || Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
} }
fun populateChips(view: ChipGroup?, tags : List<String>) { fun populateChips(view: ChipGroup?, tags: List<String>) {
if(view == null) return if (view == null) return
view.removeAllViews() view.removeAllViews()
val context = view.context ?: return val context = view.context ?: return
@ -304,7 +308,7 @@ object UIHelper {
else req else req
} }
if(radius > 0) { if (radius > 0) {
builder = builder.apply(bitmapTransform(BlurTransformation(radius, sample))) builder = builder.apply(bitmapTransform(BlurTransformation(radius, sample)))
} }
@ -496,6 +500,22 @@ object UIHelper {
) )
} }
fun fixPaddingStatusbarMargin(v: View?) {
if (v == null) return
val ctx = v.context ?: return
v.layoutParams = v.layoutParams.apply {
if (this is MarginLayoutParams) {
setMargins(
v.marginLeft,
v.marginTop + ctx.getStatusBarHeight(),
v.marginRight,
v.marginBottom
)
}
}
}
fun fixPaddingStatusbarView(v: View?) { fun fixPaddingStatusbarView(v: View?) {
if (v == null) return if (v == null) return
val ctx = v.context ?: return val ctx = v.context ?: return

View file

@ -1,6 +1,13 @@
<vector android:height="24dp" android:tint="#000000" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:height="24dp"
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM7.35,18.5C8.66,17.56 10.26,17 12,17s3.34,0.56 4.65,1.5C15.34,19.44 13.74,20 12,20S8.66,19.44 7.35,18.5zM18.14,17.12L18.14,17.12C16.45,15.8 14.32,15 12,15s-4.45,0.8 -6.14,2.12l0,0C4.7,15.73 4,13.95 4,12c0,-4.42 3.58,-8 8,-8s8,3.58 8,8C20,13.95 19.3,15.73 18.14,17.12z"/> android:tint="?attr/white"
<path android:fillColor="@android:color/white" android:pathData="M12,6c-1.93,0 -3.5,1.57 -3.5,3.5S10.07,13 12,13s3.5,-1.57 3.5,-3.5S13.93,6 12,6zM12,11c-0.83,0 -1.5,-0.67 -1.5,-1.5S11.17,8 12,8s1.5,0.67 1.5,1.5S12.83,11 12,11z"/> android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.48 10,-10S17.52,2 12,2zM7.35,18.5C8.66,17.56 10.26,17 12,17s3.34,0.56 4.65,1.5C15.34,19.44 13.74,20 12,20S8.66,19.44 7.35,18.5zM18.14,17.12L18.14,17.12C16.45,15.8 14.32,15 12,15s-4.45,0.8 -6.14,2.12l0,0C4.7,15.73 4,13.95 4,12c0,-4.42 3.58,-8 8,-8s8,3.58 8,8C20,13.95 19.3,15.73 18.14,17.12z" />
<path
android:fillColor="@android:color/white"
android:pathData="M12,6c-1.93,0 -3.5,1.57 -3.5,3.5S10.07,13 12,13s3.5,-1.57 3.5,-3.5S13.93,6 12,6zM12,11c-0.83,0 -1.5,-0.67 -1.5,-1.5S11.17,8 12,8s1.5,0.67 1.5,1.5S12.83,11 12,11z" />
</vector> </vector>

View file

@ -7,15 +7,14 @@
<stroke <stroke
android:width="2dp" android:width="2dp"
android:color="@android:color/white" /> android:color="@android:color/white" />
<corners android:radius="2dp" /> <corners android:radius="@dimen/rounded_image_radius" />
</shape> </shape>
</item> </item>
<item android:id="@android:id/mask"> <item android:id="@android:id/mask">
<shape android:shape="rectangle"> <shape android:shape="rectangle">
<corners android:radius="2dp" /> <corners android:radius="@dimen/rounded_image_radius" />
<solid android:color="?attr/iconGrayBackground" /> <solid android:color="?attr/iconGrayBackground" />
</shape> </shape>
</item> </item>
</ripple> </ripple>

View file

@ -38,16 +38,21 @@
<LinearLayout <LinearLayout
android:id="@+id/home_padding" android:id="@+id/home_padding"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="50dp"
android:orientation="vertical"> android:gravity="center"
android:orientation="horizontal">
<androidx.appcompat.widget.SearchView <androidx.appcompat.widget.SearchView
android:id="@+id/home_search" android:id="@+id/home_search"
android:nextFocusRight="@id/home_switch_account"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="center_vertical"
android:padding="0dp"
android:layout_marginEnd="50dp"
android:editTextColor="@color/white" android:editTextColor="@color/white"
android:gravity="start" android:gravity="center_vertical"
android:iconifiedByDefault="true" android:iconifiedByDefault="true"
android:textColor="@color/white" android:textColor="@color/white"
android:textColorHint="@color/white" android:textColorHint="@color/white"
@ -57,6 +62,19 @@
app:queryHint="@string/search_hint" app:queryHint="@string/search_hint"
app:searchIcon="@drawable/search_icon" app:searchIcon="@drawable/search_icon"
tools:ignore="RtlSymmetry" /> tools:ignore="RtlSymmetry" />
<ImageView
android:background="?android:attr/selectableItemBackgroundBorderless"
android:nextFocusLeft="@id/home_search"
android:id="@+id/home_switch_account"
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginStart="-50dp"
android:contentDescription="@string/account"
android:padding="10dp"
android:src="@drawable/ic_outline_account_circle_24" />
</LinearLayout> </LinearLayout>
<!-- <!--

View file

@ -34,7 +34,7 @@
android:id="@+id/outline" android:id="@+id/outline"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/outline" android:background="@drawable/outline_card"
android:visibility="gone" android:visibility="gone"
tools:visibility="visible" /> tools:visibility="visible" />

View file

@ -0,0 +1,35 @@
<?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:layout_marginBottom="10dp"
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" />
<androidx.recyclerview.widget.RecyclerView
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>

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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:id="@+id/card_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:animateLayoutChanges="true"
android:backgroundTint="?attr/primaryGrayBackground"
android:foreground="?attr/selectableItemBackgroundBorderless"
app:cardCornerRadius="@dimen/rounded_image_radius"
android:layout_margin="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/profile_image_background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.4"
android:contentDescription="@string/profile_background_des"
android:scaleType="centerCrop" />
<View
android:id="@+id/outline"
tools:visibility="visible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/outline_card"
android:visibility="gone" />
<TextView
android:id="@+id/profile_text"
tools:text="@string/mobile_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="10dp"
android:textSize="16sp" />
</androidx.cardview.widget.CardView>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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:id="@+id/card_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:animateLayoutChanges="true"
android:backgroundTint="?attr/primaryGrayBackground"
android:foreground="?attr/selectableItemBackgroundBorderless"
app:cardCornerRadius="@dimen/rounded_image_radius"
android:layout_margin="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_gravity="center"
android:src="@drawable/ic_baseline_add_24"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="centerCrop"
android:contentDescription="@string/add_account" />
</androidx.cardview.widget.CardView>

View file

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/text1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_rowWeight="1"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:text="@string/create_account"
android:textColor="?attr/textColor"
android:textSize="20sp"
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>
<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
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="10dp"
android:layout_marginBottom="60dp"
android:orientation="vertical">
<EditText
android:id="@+id/account_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="username"
android:hint="@string/default_account"
android:inputType="text"
android:nextFocusLeft="@id/apply_btt"
android:nextFocusRight="@id/cancel_btt"
android:nextFocusDown="@id/site_url_input"
android:requiresFadingEdge="vertical"
android:textColorHint="?attr/grayTextColor"
tools:ignore="LabelFor" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="@dimen/rounded_image_radius">
<ImageView
android:id="@+id/profile_pic"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_gravity="center"
android:contentDescription="@string/preview_background_img_des"
android:scaleType="centerCrop"
android:src="@drawable/profile_bg_blue" />
</androidx.cardview.widget.CardView>
</LinearLayout>
<RelativeLayout
android:id="@+id/apply_btt_holder"
android:layout_width="match_parent"
android:layout_height="60dp"
android:layout_gravity="bottom"
android:layout_marginTop="-60dp"
android:orientation="horizontal"
android:padding="10dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/delete_btt"
style="@style/BlackButton"
android:layout_width="wrap_content"
android:layout_alignParentStart="true"
android:layout_gravity="center_vertical"
android:nextFocusRight="@id/apply_btt"
android:text="@string/delete" />
<com.google.android.material.button.MaterialButton
android:id="@+id/apply_btt"
style="@style/WhiteButton"
android:layout_width="wrap_content"
android:layout_gravity="center_vertical|end"
android:layout_toStartOf="@+id/cancel_btt"
android:nextFocusLeft="@id/delete_btt"
android:nextFocusRight="@id/cancel_btt"
android:text="@string/sort_apply" />
<com.google.android.material.button.MaterialButton
android:id="@+id/cancel_btt"
style="@style/BlackButton"
android:layout_width="wrap_content"
android:layout_alignParentEnd="true"
android:layout_gravity="center_vertical|end"
android:nextFocusLeft="@id/apply_btt"
android:text="@string/sort_cancel" />
</RelativeLayout>
</LinearLayout>

View file

@ -307,6 +307,7 @@
<string name="queued">queued</string> <string name="queued">queued</string>
<string name="no_subtitles">No Subtitles</string> <string name="no_subtitles">No Subtitles</string>
<string name="default_subtitles">Default</string> <string name="default_subtitles">Default</string>
<string name="default_account">@string/default_subtitles</string>
<string name="free_storage">Free</string> <string name="free_storage">Free</string>
<string name="used_storage">Used</string> <string name="used_storage">Used</string>
<string name="app_storage">App</string> <string name="app_storage">App</string>