diff --git a/.github/workflows/build_check.yaml b/.github/workflows/build_check.yaml
index e06a11aba..4038ad366 100644
--- a/.github/workflows/build_check.yaml
+++ b/.github/workflows/build_check.yaml
@@ -51,7 +51,7 @@ jobs:
uses: actions/upload-artifact@v1
with:
name: apk
- path: animeworld/build/outputs/apk/debug/animeworld-debug.apk
+ path: animeworld/build/outputs/apk/debug/animeworld-debug.apk
- name: Upload APK
uses: actions/upload-artifact@v1
with:
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0bd14c3d3..24b1ecc23 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -27,6 +27,7 @@
+
@@ -43,6 +44,7 @@
+
@@ -59,6 +61,7 @@
+
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/AllFragment.kt b/UIViews/src/main/java/com/programmersbox/uiviews/AllFragment.kt
index 40632ccd8..483bbc58e 100644
--- a/UIViews/src/main/java/com/programmersbox/uiviews/AllFragment.kt
+++ b/UIViews/src/main/java/com/programmersbox/uiviews/AllFragment.kt
@@ -1,159 +1,278 @@
package com.programmersbox.uiviews
import android.os.Bundle
+import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.slideInVertically
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.*
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Cancel
+import androidx.compose.material.icons.filled.CloudOff
+import androidx.compose.material.icons.filled.KeyboardArrowUp
+import androidx.compose.runtime.*
+import androidx.compose.runtime.rxjava2.subscribeAsState
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMaxBy
import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.RecyclerView
+import androidx.navigation.fragment.findNavController
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.google.accompanist.swiperefresh.SwipeRefresh
+import com.google.accompanist.swiperefresh.SwipeRefreshState
+import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.google.android.material.composethemeadapter.MdcTheme
-import com.jakewharton.rxbinding2.widget.textChanges
-import com.programmersbox.dragswipe.DragSwipeAdapter
-import com.programmersbox.dragswipe.DragSwipeDiffUtil
import com.programmersbox.favoritesdatabase.DbModel
import com.programmersbox.favoritesdatabase.ItemDatabase
-import com.programmersbox.helpfulutils.gone
-import com.programmersbox.helpfulutils.runOnUIThread
-import com.programmersbox.helpfulutils.visible
import com.programmersbox.models.ApiService
import com.programmersbox.models.ItemModel
import com.programmersbox.models.sourcePublish
import com.programmersbox.sharedutils.FirebaseDb
-import com.programmersbox.uiviews.databinding.FragmentAllBinding
-import com.programmersbox.uiviews.utils.EndlessScrollingListener
+import com.programmersbox.uiviews.utils.InfiniteListHandler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.Flowables
import io.reactivex.rxkotlin.addTo
import io.reactivex.rxkotlin.subscribeBy
import io.reactivex.schedulers.Schedulers
-import kotlinx.coroutines.delay
+import io.reactivex.subjects.BehaviorSubject
import kotlinx.coroutines.launch
-import java.util.concurrent.TimeUnit
+import org.koin.android.ext.android.inject
/**
* A simple [Fragment] subclass.
* Use the [AllFragment.newInstance] factory method to
* create an instance of this fragment.
*/
-class AllFragment : BaseListFragment() {
+class AllFragment : BaseFragmentCompose() {
private val disposable: CompositeDisposable = CompositeDisposable()
private var count = 1
- override val layoutId: Int get() = R.layout.fragment_all
+ private val info: GenericInfo by inject()
- private val currentList = mutableListOf()
+ private val searchPublisher = BehaviorSubject.createDefault>(emptyList())
+
+ private val sourceList = mutableStateListOf()
+ private val favoriteList = mutableStateListOf()
private val dao by lazy { ItemDatabase.getInstance(requireContext()).itemDao() }
private val itemListener = FirebaseDb.FirebaseListener()
- private lateinit var binding: FragmentAllBinding
+ @ExperimentalAnimationApi
+ @ExperimentalFoundationApi
+ @ExperimentalMaterialApi
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = ComposeView(requireContext())
+ .apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner))
+ setContent { AllView() }
+ }
override fun viewCreated(view: View, savedInstanceState: Bundle?) {
- super.viewCreated(view, savedInstanceState)
-
- binding = FragmentAllBinding.bind(view)
+ sourcePublish
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe {
+ count = 1
+ sourceList.clear()
+ searchPublisher.onNext(emptyList())
+ sourceLoadCompose(it)
+ }
+ .addTo(disposable)
Flowables.combineLatest(
itemListener.getAllShowsFlowable(),
dao.getAllFavorites()
) { f, d -> (f + d).groupBy(DbModel::url).map { it.value.fastMaxBy(DbModel::numChapters)!! } }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe { adapter.update(it) { s, d -> s.url == d.url } }
- .addTo(disposable)
-
- binding.allList.apply {
- adapter = this@AllFragment.adapter
- layoutManager = info.createLayoutManager(this@AllFragment.requireContext())
- addOnScrollListener(object : EndlessScrollingListener(layoutManager!!) {
- override fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?) {
- if (sourcePublish.value!!.canScroll && binding.searchInfo.text.isNullOrEmpty()) {
- count++
- binding.allRefresh.isRefreshing = true
- sourceLoad(sourcePublish.value!!, count)
- }
- }
- })
- }
-
- binding.composeShimmer.setContent { MdcTheme { info.ComposeShimmerItem() } }
-
- ReactiveNetwork.observeInternetConnectivity()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
- binding.offlineView.visibility = if (it) View.GONE else View.VISIBLE
- binding.allRefresh.visibility = if (it) View.VISIBLE else View.GONE
+ favoriteList.clear()
+ favoriteList.addAll(it)
}
.addTo(disposable)
+ }
- sourcePublish
+ private fun sourceLoadCompose(sources: ApiService, page: Int = 1, refreshState: SwipeRefreshState? = null) {
+ sources
+ .getList(page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- binding.composeShimmer.visible()
- count = 1
- adapter.setListNotify(emptyList())
- sourceLoad(it)
- binding.allList.scrollToPosition(0)
+ .doOnSubscribe { refreshState?.isRefreshing = true }
+ .subscribeBy {
+ sourceList.addAll(it)
+ refreshState?.isRefreshing = false
}
.addTo(disposable)
+ }
- binding.scrollToTop.setOnClickListener {
- lifecycleScope.launch {
- activity?.runOnUiThread { binding.allList.smoothScrollToPosition(0) }
- delay(500)
- activity?.runOnUiThread { binding.allList.scrollToPosition(0) }
- }
- }
+ @ExperimentalAnimationApi
+ @ExperimentalMaterialApi
+ @ExperimentalFoundationApi
+ @Composable
+ private fun AllView() {
+ MdcTheme {
+ val isConnected by ReactiveNetwork.observeInternetConnectivity()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeAsState(initial = true)
- binding.searchInfo
- .textChanges()
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .debounce(1000, TimeUnit.MILLISECONDS)
- .flatMapSingle { sourcePublish.value!!.searchList(it, 1, currentList) }
- .onErrorReturnItem(currentList)
- .subscribe {
- adapter.setData(it)
- activity?.runOnUiThread { binding.searchLayout.suffixText = "${it.size}" }
- }
- .addTo(disposable)
+ when {
+ !isConnected -> {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Image(
+ Icons.Default.CloudOff,
+ null,
+ modifier = Modifier.size(50.dp, 50.dp),
+ colorFilter = ColorFilter.tint(MaterialTheme.colors.onBackground)
+ )
+ Text(stringResource(R.string.you_re_offline), style = MaterialTheme.typography.h5)
+ }
+ }
+ else -> {
+ val state = rememberLazyListState()
+ val refresh = rememberSwipeRefreshState(isRefreshing = false)
+ val source by sourcePublish.subscribeAsState(initial = null)
+ val focusManager = LocalFocusManager.current
+ val scaffoldState = rememberBottomSheetScaffoldState()
+ val scope = rememberCoroutineScope()
+ val searchList by searchPublisher.subscribeAsState(initial = emptyList())
+ var searchText by rememberSaveable { mutableStateOf("") }
+ val showButton by remember { derivedStateOf { state.firstVisibleItemIndex > 0 } }
- }
+ BottomSheetScaffold(
+ scaffoldState = scaffoldState,
+ sheetPeekHeight = ButtonDefaults.MinHeight + 4.dp,
+ sheetContent = {
+ Scaffold(
+ topBar = {
+ Column {
+ Button(
+ onClick = {
+ scope.launch {
+ if (scaffoldState.bottomSheetState.isCollapsed) scaffoldState.bottomSheetState.expand()
+ else scaffoldState.bottomSheetState.collapse()
+ }
+ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .heightIn(ButtonDefaults.MinHeight + 4.dp),
+ shape = RoundedCornerShape(0f)
+ ) {
+ Text(
+ stringResource(R.string.search),
+ style = MaterialTheme.typography.button
+ )
+ }
- private fun DragSwipeAdapter.setData(newList: List) {
- val diffCallback = object : DragSwipeDiffUtil(dataList, newList) {
- override fun areContentsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean = oldItem.url == newItem.url
- override fun areItemsTheSame(oldItem: ItemModel, newItem: ItemModel): Boolean = oldItem.url === newItem.url
- }
- val diffResult = DiffUtil.calculateDiff(diffCallback)
- dataList.clear()
- dataList.addAll(newList)
- runOnUIThread { diffResult.dispatchUpdatesTo(this) }
- }
+ OutlinedTextField(
+ value = searchText,
+ onValueChange = { searchText = it },
+ label = { Text(stringResource(R.string.searchFor, source?.serviceName.orEmpty())) },
+ trailingIcon = {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(searchList.size.toString())
+ IconButton(onClick = { searchText = "" }) { Icon(Icons.Default.Cancel, null) }
+ }
+ },
+ modifier = Modifier
+ .padding(5.dp)
+ .fillMaxWidth(),
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
+ keyboardActions = KeyboardActions(onSearch = {
+ focusManager.clearFocus()
+ sourcePublish.value!!.searchList(searchText, 1, sourceList)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .onErrorReturnItem(sourceList)
+ .subscribe(searchPublisher::onNext)
+ .addTo(disposable)
+ })
+ )
+ }
+ }
+ ) { p ->
+ Box(modifier = Modifier.padding(p)) {
+ info.ItemListView(list = searchList, listState = rememberLazyListState(), favorites = favoriteList) {
+ findNavController().navigate(AllFragmentDirections.actionAllFragment2ToDetailsFragment3(it))
+ }
+ }
+ }
+ },
+ floatingActionButton = {
+ AnimatedVisibility(
+ visible = showButton && scaffoldState.bottomSheetState.isCollapsed,
+ enter = slideInVertically({ it / 2 })
+ ) {
+ FloatingActionButton(
+ onClick = { scope.launch { state.animateScrollToItem(0) } },
+ backgroundColor = MaterialTheme.colors.primary
+ ) {
+ Icon(
+ imageVector = Icons.Default.KeyboardArrowUp,
+ contentDescription = null,
+ modifier = Modifier.padding(5.dp),
+ )
+ }
+ }
+ },
+ floatingActionButtonPosition = FabPosition.End
+ ) { p ->
+ SwipeRefresh(
+ modifier = Modifier.padding(p),
+ state = refresh,
+ onRefresh = {
+ source?.let {
+ count = 1
+ sourceList.clear()
+ sourceLoadCompose(it, count, refresh)
+ }
+ }
+ ) {
+ if (sourceList.isEmpty()) {
+ info.ComposeShimmerItem()
+ } else {
+ info.ItemListView(list = sourceList, listState = state, favorites = favoriteList) {
+ findNavController().navigate(AllFragmentDirections.actionAllFragment2ToDetailsFragment3(it))
+ }
+ }
+ }
- private fun sourceLoad(sources: ApiService, page: Int = 1) {
- sources.getList(page)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribeBy {
- adapter.addItems(it)
- currentList.clear()
- currentList.addAll(it)
- binding.allRefresh.isRefreshing = false
- activity?.runOnUiThread {
- binding.searchLayout.editText?.setText("")
- binding.searchLayout.suffixText = "${adapter.dataList.size}"
- binding.searchLayout.hint = getString(R.string.searchFor, sourcePublish.value?.serviceName.orEmpty())
+ if (source?.canScroll == true) {
+ InfiniteListHandler(listState = state, buffer = 1) {
+ source?.let {
+ count++
+ sourceLoadCompose(it, count, refresh)
+ }
+ }
+ }
+
+ }
}
- binding.composeShimmer.gone()
}
- .addTo(disposable)
+ }
}
override fun onDestroy() {
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/BaseFragment.kt b/UIViews/src/main/java/com/programmersbox/uiviews/BaseFragment.kt
index 33c949f93..e7ac4b5a1 100644
--- a/UIViews/src/main/java/com/programmersbox/uiviews/BaseFragment.kt
+++ b/UIViews/src/main/java/com/programmersbox/uiviews/BaseFragment.kt
@@ -41,5 +41,20 @@ abstract class BaseFragment : Fragment() {
}
}
+ abstract fun viewCreated(view: View, savedInstanceState: Bundle?)
+}
+
+abstract class BaseFragmentCompose : Fragment() {
+
+ private var hasInitializedRootView = false
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ if (!hasInitializedRootView) {
+ hasInitializedRootView = true
+ viewCreated(view, savedInstanceState)
+ }
+ }
+
abstract fun viewCreated(view: View, savedInstanceState: Bundle?)
}
\ No newline at end of file
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/BaseListFragment.kt b/UIViews/src/main/java/com/programmersbox/uiviews/BaseListFragment.kt
deleted file mode 100644
index fa5b3a2b9..000000000
--- a/UIViews/src/main/java/com/programmersbox/uiviews/BaseListFragment.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.programmersbox.uiviews
-
-import android.os.Bundle
-import android.view.View
-import androidx.annotation.CallSuper
-import androidx.recyclerview.widget.RecyclerView
-import org.koin.android.ext.android.inject
-
-abstract class BaseListFragment : BaseFragment() {
- protected lateinit var adapter: ItemListAdapter
-
- protected val info: GenericInfo by inject()
-
- @CallSuper
- override fun viewCreated(view: View, savedInstanceState: Bundle?) {
- adapter = info.createAdapter(this@BaseListFragment.requireContext(), this)
- }
-}
\ No newline at end of file
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/GenericInfo.kt b/UIViews/src/main/java/com/programmersbox/uiviews/GenericInfo.kt
index 633fdba27..e31c1a6ca 100644
--- a/UIViews/src/main/java/com/programmersbox/uiviews/GenericInfo.kt
+++ b/UIViews/src/main/java/com/programmersbox/uiviews/GenericInfo.kt
@@ -1,18 +1,18 @@
package com.programmersbox.uiviews
import android.content.Context
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
-import androidx.recyclerview.widget.RecyclerView
+import com.programmersbox.favoritesdatabase.DbModel
import com.programmersbox.models.ApiService
import com.programmersbox.models.ChapterModel
+import com.programmersbox.models.ItemModel
import com.programmersbox.sharedutils.AppUpdate
interface GenericInfo {
val apkString: AppUpdate.AppUpdates.() -> String?
- fun createAdapter(context: Context, baseListFragment: BaseListFragment): ItemListAdapter
- fun createLayoutManager(context: Context): RecyclerView.LayoutManager
fun chapterOnClick(model: ChapterModel, allChapters: List, context: Context)
fun sourceList(): List
fun searchList(): List = sourceList()
@@ -23,4 +23,12 @@ interface GenericInfo {
@Composable
fun ComposeShimmerItem()
+ @Composable
+ fun ItemListView(
+ list: List,
+ favorites: List,
+ listState: LazyListState,
+ onClick: (ItemModel) -> Unit
+ )
+
}
\ No newline at end of file
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/GlobalSearchFragment.kt b/UIViews/src/main/java/com/programmersbox/uiviews/GlobalSearchFragment.kt
index 3607aa306..e8d27dcb8 100644
--- a/UIViews/src/main/java/com/programmersbox/uiviews/GlobalSearchFragment.kt
+++ b/UIViews/src/main/java/com/programmersbox/uiviews/GlobalSearchFragment.kt
@@ -6,7 +6,10 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
@@ -22,7 +25,7 @@ import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cancel
import androidx.compose.material.icons.filled.CloudOff
-import androidx.compose.material.icons.filled.VerticalAlignTop
+import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.*
import androidx.compose.runtime.rxjava2.subscribeAsState
@@ -99,6 +102,7 @@ class GlobalSearchFragment : Fragment() {
var searchText by rememberSaveable { mutableStateOf(args.searchFor) }
val focusManager = LocalFocusManager.current
val listState = rememberLazyListState()
+ val showButton by remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } }
val scope = rememberCoroutineScope()
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = false)
val list by searchPublisher.subscribeAsState(initial = emptyList())
@@ -206,9 +210,15 @@ class GlobalSearchFragment : Fragment() {
) {
Scaffold(
floatingActionButton = {
- FloatingActionButton(
- onClick = { scope.launch { listState.animateScrollToItem(0) } }
- ) { Icon(Icons.Default.VerticalAlignTop, null) }
+ AnimatedVisibility(
+ visible = showButton && searchText.isNotEmpty(),
+ enter = slideInVertically({ it / 2 }),
+ exit = slideOutVertically({ it / 2 })
+ ) {
+ FloatingActionButton(
+ onClick = { scope.launch { listState.animateScrollToItem(0) } }
+ ) { Icon(Icons.Default.KeyboardArrowUp, null) }
+ }
},
floatingActionButtonPosition = FabPosition.End
) {
@@ -227,7 +237,7 @@ class GlobalSearchFragment : Fragment() {
SearchCoverCard(
model = m,
placeHolder = AppCompatResources.getDrawable(LocalContext.current, mainLogo.logoId)
- ) { findNavController().navigate(GlobalSearchFragmentDirections.showDetails(m)) }
+ ) { findNavController().navigate(GlobalNavDirections.showDetails(m)) }
}
}
}
@@ -251,20 +261,6 @@ class GlobalSearchFragment : Fragment() {
}
}
}
-
- /*LaunchedEffect(key1 = searchText) {
- snapshotFlow { searchText }
- .debounce(1000)
- .distinctUntilChanged()
- .collect { s ->
- println("Searching for $s")
- searchForItems(
- searchText = searchText,
- onSubscribe = { swipeRefreshState.isRefreshing = true },
- subscribe = { swipeRefreshState.isRefreshing = false }
- )
- }
- }*/
}
}
}
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/ItemListAdapter.kt b/UIViews/src/main/java/com/programmersbox/uiviews/ItemListAdapter.kt
deleted file mode 100644
index 732f822b8..000000000
--- a/UIViews/src/main/java/com/programmersbox/uiviews/ItemListAdapter.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.programmersbox.uiviews
-
-import android.content.Context
-import android.view.View
-import androidx.navigation.findNavController
-import androidx.recyclerview.widget.RecyclerView
-import com.programmersbox.dragswipe.CheckAdapter
-import com.programmersbox.dragswipe.CheckAdapterInterface
-import com.programmersbox.dragswipe.DragSwipeAdapter
-import com.programmersbox.favoritesdatabase.DbModel
-import com.programmersbox.models.ItemModel
-
-abstract class ItemListAdapter(
- protected val context: Context,
- private val baseListFragment: BaseListFragment,
- check: CheckAdapter = CheckAdapter()
-) : DragSwipeAdapter(), CheckAdapterInterface by check {
- init {
- check.adapter = this
- }
-
- protected fun onClick(v: View, itemModel: ItemModel) {
- val direction = if (baseListFragment is RecentFragment) RecentFragmentDirections.actionRecentFragment2ToDetailsFragment2(itemModel)
- else AllFragmentDirections.actionAllFragment2ToDetailsFragment3(itemModel)
- v.findNavController().navigate(direction)
- }
-}
\ No newline at end of file
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/RecentFragment.kt b/UIViews/src/main/java/com/programmersbox/uiviews/RecentFragment.kt
index 48114092b..f65469bb7 100644
--- a/UIViews/src/main/java/com/programmersbox/uiviews/RecentFragment.kt
+++ b/UIViews/src/main/java/com/programmersbox/uiviews/RecentFragment.kt
@@ -1,138 +1,186 @@
package com.programmersbox.uiviews
import android.os.Bundle
+import android.view.LayoutInflater
import android.view.View
-import androidx.compose.runtime.*
+import android.view.ViewGroup
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.CloudOff
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.rxjava2.subscribeAsState
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastMaxBy
import androidx.fragment.app.Fragment
-import androidx.recyclerview.widget.RecyclerView
+import androidx.navigation.fragment.findNavController
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
+import com.google.accompanist.swiperefresh.SwipeRefresh
+import com.google.accompanist.swiperefresh.SwipeRefreshState
+import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.google.android.material.composethemeadapter.MdcTheme
import com.programmersbox.favoritesdatabase.DbModel
import com.programmersbox.favoritesdatabase.ItemDatabase
-import com.programmersbox.helpfulutils.gone
-import com.programmersbox.helpfulutils.visible
import com.programmersbox.models.ApiService
+import com.programmersbox.models.ItemModel
import com.programmersbox.models.sourcePublish
import com.programmersbox.sharedutils.FirebaseDb
-import com.programmersbox.uiviews.databinding.FragmentRecentBinding
-import com.programmersbox.uiviews.utils.EndlessScrollingListener
+import com.programmersbox.uiviews.utils.InfiniteListHandler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.Flowables
import io.reactivex.rxkotlin.addTo
import io.reactivex.rxkotlin.subscribeBy
import io.reactivex.schedulers.Schedulers
+import org.koin.android.ext.android.inject
/**
* A simple [Fragment] subclass.
* Use the [RecentFragment.newInstance] factory method to
* create an instance of this fragment.
*/
-class RecentFragment : BaseListFragment() {
+class RecentFragment : BaseFragmentCompose() {
- override val layoutId: Int get() = R.layout.fragment_recent
+ private val info: GenericInfo by inject()
private val disposable: CompositeDisposable = CompositeDisposable()
+ private val sourceList = mutableStateListOf()
+ private val favoriteList = mutableStateListOf()
private var count = 1
private val dao by lazy { ItemDatabase.getInstance(requireContext()).itemDao() }
private val itemListener = FirebaseDb.FirebaseListener()
- private lateinit var binding: FragmentRecentBinding
-
- override fun viewCreated(view: View, savedInstanceState: Bundle?) {
- super.viewCreated(view, savedInstanceState)
- binding = FragmentRecentBinding.bind(view)
-
- Flowables.combineLatest(
- itemListener.getAllShowsFlowable(),
- dao.getAllFavorites()
- ) { f, d -> (f + d).groupBy(DbModel::url).map { it.value.fastMaxBy(DbModel::numChapters)!! } }
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe { adapter.update(it) { s, d -> s.url == d.url } }
- .addTo(disposable)
+ companion object {
+ @JvmStatic
+ fun newInstance() = RecentFragment()
+ }
- binding.recentList.apply {
- adapter = this@RecentFragment.adapter
- layoutManager = info.createLayoutManager(this@RecentFragment.requireContext())
- addOnScrollListener(object : EndlessScrollingListener(layoutManager!!) {
- override fun onLoadMore(page: Int, totalItemsCount: Int, view: RecyclerView?) {
- if (sourcePublish.value!!.canScroll) {
- count++
- binding.recentRefresh.isRefreshing = true
- sourceLoad(sourcePublish.value!!, count)
- }
- }
- })
+ @ExperimentalFoundationApi
+ @ExperimentalMaterialApi
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View = ComposeView(requireContext())
+ .apply {
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner))
+ setContent { RecentView() }
}
- ReactiveNetwork.observeInternetConnectivity()
+ override fun viewCreated(view: View, savedInstanceState: Bundle?) {
+ sourcePublish
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
- binding.offlineView.visibility = if (it) View.GONE else View.VISIBLE
- binding.recentRefresh.visibility = if (it) View.VISIBLE else View.GONE
+ count = 1
+ sourceList.clear()
+ sourceLoadCompose(it)
}
.addTo(disposable)
- binding.recentRefresh.setOnRefreshListener {
- binding.composeShimmer.visible()
- count = 1
- adapter.setListNotify(emptyList())
- sourceLoad(sourcePublish.value!!)
- binding.recentList.scrollToPosition(0)
- }
-
- binding.composeShimmer.apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnLifecycleDestroyed(viewLifecycleOwner))
- setContent { MdcTheme { info.ComposeShimmerItem() } }
- //TODO: right now, when going to the detail screen from recent, this disposes
- //TODO: ...which means that when it shows up again, it wont compose again
- //TODO: found out what's causing the compose not reloading issue.
- //TODO: Its due to the BaseFragment and how it doesn't get started again. This is an issue.
- //TODO: It is needed so the api calls don't get refreshed when returning to the screen.
- //TODO: We will see how we can fix this going forward.
- }
-
- sourcePublish
+ Flowables.combineLatest(
+ itemListener.getAllShowsFlowable(),
+ dao.getAllFavorites()
+ ) { f, d -> (f + d).groupBy(DbModel::url).map { it.value.fastMaxBy(DbModel::numChapters)!! } }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
- binding.composeShimmer.visible()
- count = 1
- adapter.setListNotify(emptyList())
- sourceLoad(it)
- binding.recentList.scrollToPosition(0)
+ favoriteList.clear()
+ favoriteList.addAll(it)
}
.addTo(disposable)
+ }
+ override fun onDestroy() {
+ super.onDestroy()
+ disposable.dispose()
+ itemListener.unregister()
}
- private fun sourceLoad(sources: ApiService, page: Int = 1) {
+ private fun sourceLoadCompose(sources: ApiService, page: Int = 1, refreshState: SwipeRefreshState? = null) {
sources
.getRecent(page)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
+ .doOnSubscribe { refreshState?.isRefreshing = true }
.subscribeBy {
- adapter.addItems(it)
- binding.recentRefresh.isRefreshing = false
- binding.composeShimmer.gone()
+ sourceList.addAll(it)
+ refreshState?.isRefreshing = false
}
.addTo(disposable)
}
- override fun onDestroy() {
- super.onDestroy()
- disposable.dispose()
- itemListener.unregister()
- }
+ @ExperimentalMaterialApi
+ @ExperimentalFoundationApi
+ @Composable
+ private fun RecentView() {
+ MdcTheme {
+ val state = rememberLazyListState()
+ val source by sourcePublish.subscribeAsState(initial = null)
+ val refresh = rememberSwipeRefreshState(isRefreshing = false)
+
+ val isConnected by ReactiveNetwork.observeInternetConnectivity()
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeAsState(initial = true)
+
+ when {
+ !isConnected -> {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Image(
+ Icons.Default.CloudOff,
+ null,
+ modifier = Modifier.size(50.dp, 50.dp),
+ colorFilter = ColorFilter.tint(MaterialTheme.colors.onBackground)
+ )
+ Text(stringResource(R.string.you_re_offline), style = MaterialTheme.typography.h5)
+ }
+ }
+ sourceList.isEmpty() -> info.ComposeShimmerItem()
+ else -> {
+ SwipeRefresh(
+ state = refresh,
+ onRefresh = {
+ source?.let {
+ count = 1
+ sourceList.clear()
+ sourceLoadCompose(it, count, refresh)
+ }
+ }
+ ) {
+ info.ItemListView(list = sourceList, listState = state, favorites = favoriteList) {
+ findNavController().navigate(RecentFragmentDirections.actionRecentFragment2ToDetailsFragment2(it))
+ }
+ }
- companion object {
- @JvmStatic
- fun newInstance() = RecentFragment()
+ if (source?.canScroll == true) {
+ InfiniteListHandler(listState = state, buffer = 1) {
+ source?.let {
+ count++
+ sourceLoadCompose(it, count, refresh)
+ }
+ }
+ }
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/utils/BindingUtils.kt b/UIViews/src/main/java/com/programmersbox/uiviews/utils/BindingUtils.kt
index 5dc67427c..1f28092ec 100644
--- a/UIViews/src/main/java/com/programmersbox/uiviews/utils/BindingUtils.kt
+++ b/UIViews/src/main/java/com/programmersbox/uiviews/utils/BindingUtils.kt
@@ -1,136 +1,24 @@
package com.programmersbox.uiviews.utils
import android.animation.ValueAnimator
-import android.content.res.ColorStateList
import android.graphics.*
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
-import android.widget.CheckBox
-import android.widget.ImageView
-import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.NonNull
-import androidx.appcompat.widget.Toolbar
import androidx.core.graphics.drawable.toBitmap
-import androidx.databinding.BindingAdapter
import androidx.palette.graphics.Palette
import androidx.recyclerview.widget.RecyclerView
import com.airbnb.lottie.LottieAnimationView
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.model.KeyPath
-import com.bumptech.glide.Glide
import com.bumptech.glide.ListPreloader
import com.bumptech.glide.RequestBuilder
-import com.bumptech.glide.load.resource.bitmap.RoundedCorners
import com.bumptech.glide.request.target.CustomTarget
import com.bumptech.glide.request.transition.Transition
-import com.google.android.material.appbar.CollapsingToolbarLayout
-import com.google.android.material.button.MaterialButton
-import com.google.android.material.chip.Chip
-import com.google.android.material.chip.ChipGroup
import com.programmersbox.dragswipe.DragSwipeAdapter
-import com.programmersbox.helpfulutils.changeDrawableColor
-import com.programmersbox.models.ChapterModel
-import com.programmersbox.models.SwatchInfo
import kotlin.properties.Delegates
-@BindingAdapter("coverImage", "logoId")
-fun loadImage(view: ImageView, imageUrl: String?, logoId: Int) {
- Glide.with(view)
- .load(imageUrl)
- .override(360, 480)
- .placeholder(logoId)
- .error(logoId)
- .fallback(logoId)
- .transform(RoundedCorners(15))
- .into(view)
-}
-
-@BindingAdapter("otherNames")
-fun otherNames(view: TextView, names: List?) {
- view.text = names?.joinToString("\n\n")
-}
-
-@BindingAdapter("genreList", "swatch")
-fun loadGenres(view: ChipGroup, genres: List?, swatchInfo: SwatchInfo?) {
- view.removeAllViews()
- genres?.forEach {
- view.addView(Chip(view.context).apply {
- text = it
- isCheckable = false
- isClickable = false
- swatchInfo?.rgb?.let { setTextColor(it) }
- swatchInfo?.bodyColor?.let { chipBackgroundColor = ColorStateList.valueOf(it) }
- })
- }
-}
-
-@BindingAdapter("toolbarColors")
-fun toolbarColors(view: Toolbar, swatchInfo: SwatchInfo?) {
- swatchInfo?.titleColor?.let {
- view.setTitleTextColor(it)
- view.navigationIcon?.changeDrawableColor(it)
- view.setSubtitleTextColor(it)
- view.overflowIcon?.changeDrawableColor(it)
- }
- swatchInfo?.rgb?.let { view.setBackgroundColor(it) }
-}
-
-@BindingAdapter("collapsingToolbarColors")
-fun collapsingToolbarColors(view: CollapsingToolbarLayout, swatchInfo: SwatchInfo?) {
- swatchInfo?.titleColor?.let { view.setCollapsedTitleTextColor(it) }
- swatchInfo?.rgb?.let {
- view.setBackgroundColor(it)
- //view.setExpandedTitleColor(it)
- }
-}
-
-@BindingAdapter("titleColor")
-fun titleColor(view: TextView, swatchInfo: SwatchInfo?) {
- swatchInfo?.bodyColor?.let { view.setTextColor(it) }
-}
-
-@BindingAdapter("bodyColor")
-fun bodyColor(view: TextView, swatchInfo: SwatchInfo?) {
- swatchInfo?.bodyColor?.let { view.setTextColor(it) }
-}
-
-@BindingAdapter("linkColor")
-fun linkColor(view: TextView, swatchInfo: SwatchInfo?) {
- swatchInfo?.bodyColor?.let { view.setLinkTextColor(it) }
-}
-
-@BindingAdapter("optionTint")
-fun optionTint(view: MaterialButton, swatchInfo: SwatchInfo?) {
- swatchInfo?.rgb?.let { view.strokeColor = ColorStateList.valueOf(it) }
-}
-
-@BindingAdapter("checkedButtonTint")
-fun buttonTint(view: CheckBox, swatchInfo: SwatchInfo?) {
- swatchInfo?.bodyColor?.let { view.buttonTintList = ColorStateList.valueOf(it) }
- swatchInfo?.bodyColor?.let { view.setTextColor(it) }
-}
-
-@BindingAdapter("startButtonColor")
-fun startButtonColor(view: MaterialButton, swatchInfo: SwatchInfo?) {
- swatchInfo?.bodyColor?.let { view.iconTint = ColorStateList.valueOf(it) }
- swatchInfo?.bodyColor?.let { view.setTextColor(it) }
- swatchInfo?.bodyColor?.let { view.strokeColor = ColorStateList.valueOf(it) }
-}
-
-@BindingAdapter("uploadedText")
-fun uploadedText(view: TextView, chapterModel: ChapterModel) {
- /*if (
- chapterModel.uploadedTime != null &&
- chapterModel.uploadedTime?.isDateBetween(System.currentTimeMillis() - 8.days.inMilliseconds.toLong(), System.currentTimeMillis()) == true
- ) {
- view.setTimeAgo(chapterModel.uploadedTime!!, showSeconds = true, autoUpdate = false)
- } else {
-
- }*/
- view.text = chapterModel.uploaded
-}
-
@DslMarker
annotation class GlideMarker
diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/utils/ComposableUtils.kt b/UIViews/src/main/java/com/programmersbox/uiviews/utils/ComposableUtils.kt
index 1c0b3b492..d6eb13c19 100644
--- a/UIViews/src/main/java/com/programmersbox/uiviews/utils/ComposableUtils.kt
+++ b/UIViews/src/main/java/com/programmersbox/uiviews/utils/ComposableUtils.kt
@@ -56,6 +56,7 @@ import com.skydoves.landscapist.glide.GlideImage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -960,4 +961,28 @@ fun rememberMutableStateListOf(vararg elements: T): SnapshotStateList<
save = { it.toList() },
restore = { it.toMutableStateList() }
)
-) { elements.toList().toMutableStateList() }
\ No newline at end of file
+) { elements.toList().toMutableStateList() }
+
+@Composable
+fun InfiniteListHandler(
+ listState: LazyListState,
+ buffer: Int = 2,
+ onLoadMore: () -> Unit
+) {
+ val loadMore = remember {
+ derivedStateOf {
+ val layoutInfo = listState.layoutInfo
+ val totalItemsNumber = layoutInfo.totalItemsCount
+ val lastVisibleItemIndex = (layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0) + 1
+
+ lastVisibleItemIndex > (totalItemsNumber - buffer)
+ }
+ }
+
+ LaunchedEffect(loadMore) {
+ snapshotFlow { loadMore.value }
+ .drop(1)
+ .distinctUntilChanged()
+ .collect { onLoadMore() }
+ }
+}
\ No newline at end of file
diff --git a/UIViews/src/main/res/layout/fragment_all.xml b/UIViews/src/main/res/layout/fragment_all.xml
deleted file mode 100644
index dcc3c864d..000000000
--- a/UIViews/src/main/res/layout/fragment_all.xml
+++ /dev/null
@@ -1,124 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/UIViews/src/main/res/layout/fragment_recent.xml b/UIViews/src/main/res/layout/fragment_recent.xml
deleted file mode 100644
index b1516d0f0..000000000
--- a/UIViews/src/main/res/layout/fragment_recent.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/UIViews/src/main/res/navigation/all_nav.xml b/UIViews/src/main/res/navigation/all_nav.xml
index 613bb1c64..c60102067 100644
--- a/UIViews/src/main/res/navigation/all_nav.xml
+++ b/UIViews/src/main/res/navigation/all_nav.xml
@@ -1,15 +1,13 @@
+ android:label="fragment_all">
@@ -10,8 +9,7 @@
+ android:label="fragment_recent">
(context, baseListFragment) {
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimeHolder =
- AnimeHolder(AnimeListItemBinding.inflate(context.layoutInflater, parent, false))
-
- override fun AnimeHolder.onBind(item: ItemModel, position: Int) {
- bind(item, currentList)
- itemView.setOnClickListener { onClick(it, item) }
- }
-}
-
-class AnimeHolder(private val binding: AnimeListItemBinding) : RecyclerView.ViewHolder(binding.root) {
-
- fun bind(info: ItemModel, list: List) {
- binding.show = info
- binding.root.toolTipText(info.title)
- binding.favoriteHeart.changeTint(binding.animeTitle.currentTextColor)
- binding.favoriteHeart.check(false)
- binding.favoriteHeart.check(list.fastAny { it.url == info.url })
- binding.executePendingBindings()
- }
-
-}
\ No newline at end of file
diff --git a/animeworld/src/main/java/com/programmersbox/animeworld/CustomFetchNotificationManager.kt b/animeworld/src/main/java/com/programmersbox/animeworld/CustomFetchNotificationManager.kt
index 99e440922..84314533b 100644
--- a/animeworld/src/main/java/com/programmersbox/animeworld/CustomFetchNotificationManager.kt
+++ b/animeworld/src/main/java/com/programmersbox/animeworld/CustomFetchNotificationManager.kt
@@ -74,10 +74,12 @@ class CustomFetchNotificationManager(context: Context) : FetchNotificationManage
} else {
null
}
- if (channel == null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
- val channelName = context.getString(R.string.fetch_notification_default_channel_name)
- channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
- notificationManager.createNotificationChannel(channel)
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
+ if (channel == null) {
+ val channelName = context.getString(R.string.fetch_notification_default_channel_name)
+ channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT)
+ notificationManager.createNotificationChannel(channel)
+ }
}
}
diff --git a/animeworld/src/main/java/com/programmersbox/animeworld/GenericAnime.kt b/animeworld/src/main/java/com/programmersbox/animeworld/GenericAnime.kt
index 9fd8a1822..77625b596 100644
--- a/animeworld/src/main/java/com/programmersbox/animeworld/GenericAnime.kt
+++ b/animeworld/src/main/java/com/programmersbox/animeworld/GenericAnime.kt
@@ -5,27 +5,27 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.Toast
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.Card
-import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.Icon
-import androidx.compose.material.Text
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.items
+import androidx.compose.material.*
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastAny
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import androidx.mediarouter.app.MediaRouteDialogFactory
import androidx.preference.Preference
import androidx.preference.SwitchPreference
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
import com.google.accompanist.placeholder.PlaceholderHighlight
import com.google.accompanist.placeholder.material.placeholder
import com.google.accompanist.placeholder.material.shimmer
@@ -39,18 +39,18 @@ import com.programmersbox.anime_sources.anime.WcoStream
import com.programmersbox.anime_sources.anime.Yts
import com.programmersbox.animeworld.cast.ExpandedControlsActivity
import com.programmersbox.animeworld.ytsdatabase.Torrent
+import com.programmersbox.favoritesdatabase.DbModel
import com.programmersbox.gsonutils.fromJson
import com.programmersbox.helpfulutils.requestPermissions
import com.programmersbox.helpfulutils.runOnUIThread
import com.programmersbox.helpfulutils.sharedPrefNotNullDelegate
import com.programmersbox.models.ApiService
import com.programmersbox.models.ChapterModel
+import com.programmersbox.models.ItemModel
import com.programmersbox.models.sourcePublish
import com.programmersbox.sharedutils.AppUpdate
import com.programmersbox.sharedutils.MainLogo
-import com.programmersbox.uiviews.BaseListFragment
import com.programmersbox.uiviews.GenericInfo
-import com.programmersbox.uiviews.ItemListAdapter
import com.programmersbox.uiviews.SettingsDsl
import com.programmersbox.uiviews.utils.NotificationLogo
import com.tonyodev.fetch2.*
@@ -75,11 +75,6 @@ class GenericAnime(val context: Context) : GenericInfo {
override val apkString: AppUpdate.AppUpdates.() -> String? get() = { anime_file }
- override fun createAdapter(context: Context, baseListFragment: BaseListFragment): ItemListAdapter =
- (AnimeAdapter(context, baseListFragment) as ItemListAdapter)
-
- override fun createLayoutManager(context: Context): RecyclerView.LayoutManager = LinearLayoutManager(context)
-
override fun downloadChapter(chapterModel: ChapterModel, title: String) {
if ((chapterModel.source as? ShowApi)?.canStream == false) {
Toast.makeText(context, context.getString(R.string.source_no_stream, chapterModel.source.serviceName), Toast.LENGTH_SHORT).show()
@@ -329,7 +324,6 @@ class GenericAnime(val context: Context) : GenericInfo {
.fillMaxWidth()
.padding(5.dp)
) {
-
Row(
modifier = Modifier
.fillMaxWidth()
@@ -354,4 +348,40 @@ class GenericAnime(val context: Context) : GenericInfo {
}
}
+ @ExperimentalMaterialApi
+ @ExperimentalFoundationApi
+ @Composable
+ override fun ItemListView(
+ list: List,
+ favorites: List,
+ listState: LazyListState,
+ onClick: (ItemModel) -> Unit
+ ) {
+ LazyColumn(state = listState) {
+ items(list) {
+ Card(
+ onClick = { onClick(it) },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(5.dp),
+ elevation = 5.dp
+ ) {
+ ListItem(
+ icon = {
+ Icon(
+ if (favorites.fastAny { f -> f.url == it.url }) Icons.Default.Favorite else Icons.Default.FavoriteBorder,
+ contentDescription = null,
+ )
+ },
+ text = { Text(it.title) },
+ overlineText = { Text(it.source.serviceName) },
+ secondaryText = if (it.description.isNotEmpty()) {
+ { Text(it.description) }
+ } else null
+ )
+ }
+ }
+ }
+ }
+
}
diff --git a/animeworld/src/main/res/layout/anime_list_item.xml b/animeworld/src/main/res/layout/anime_list_item.xml
deleted file mode 100644
index 855405a87..000000000
--- a/animeworld/src/main/res/layout/anime_list_item.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/mangaworld/src/main/java/com/programmersbox/mangaworld/GenericManga.kt b/mangaworld/src/main/java/com/programmersbox/mangaworld/GenericManga.kt
index 6099f7266..bff70f6c3 100644
--- a/mangaworld/src/main/java/com/programmersbox/mangaworld/GenericManga.kt
+++ b/mangaworld/src/main/java/com/programmersbox/mangaworld/GenericManga.kt
@@ -7,6 +7,7 @@ import android.content.Intent
import android.os.Environment
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.lazy.GridCells
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyVerticalGrid
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
@@ -15,22 +16,16 @@ import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.preference.Preference
import androidx.preference.SwitchPreference
-import androidx.recyclerview.widget.GridLayoutManager
-import androidx.recyclerview.widget.RecyclerView
+import com.programmersbox.favoritesdatabase.DbModel
import com.programmersbox.gsonutils.toJson
import com.programmersbox.helpfulutils.downloadManager
import com.programmersbox.helpfulutils.requestPermissions
import com.programmersbox.manga_sources.Sources
import com.programmersbox.manga_sources.utilities.NetworkHelper
-import com.programmersbox.models.ApiService
-import com.programmersbox.models.ChapterModel
-import com.programmersbox.models.Storage
-import com.programmersbox.models.sourcePublish
+import com.programmersbox.models.*
import com.programmersbox.sharedutils.AppUpdate
import com.programmersbox.sharedutils.MainLogo
-import com.programmersbox.uiviews.BaseListFragment
import com.programmersbox.uiviews.GenericInfo
-import com.programmersbox.uiviews.ItemListAdapter
import com.programmersbox.uiviews.SettingsDsl
import com.programmersbox.uiviews.utils.*
import io.reactivex.disposables.CompositeDisposable
@@ -53,12 +48,6 @@ class GenericManga(val context: Context) : GenericInfo {
override val apkString: AppUpdate.AppUpdates.() -> String? get() = { manga_file }
- override fun createAdapter(context: Context, baseListFragment: BaseListFragment): ItemListAdapter =
- (MangaGalleryAdapter(context, baseListFragment) as ItemListAdapter)
-
- override fun createLayoutManager(context: Context): RecyclerView.LayoutManager =
- AutoFitGridLayoutManager(context, 360).apply { orientation = GridLayoutManager.VERTICAL }
-
override fun chapterOnClick(model: ChapterModel, allChapters: List, context: Context) {
context.startActivity(
Intent(context, ReadActivity::class.java).apply {
@@ -157,4 +146,25 @@ class GenericManga(val context: Context) : GenericInfo {
}
}
+ @ExperimentalMaterialApi
+ @ExperimentalFoundationApi
+ @Composable
+ override fun ItemListView(
+ list: List,
+ favorites: List,
+ listState: LazyListState,
+ onClick: (ItemModel) -> Unit
+ ) {
+ LazyVerticalGrid(
+ cells = GridCells.Adaptive(ComposableUtils.IMAGE_WIDTH),
+ state = listState
+ ) {
+ items(list.size) { i ->
+ list.getOrNull(i)?.let {
+ CoverCard(imageUrl = it.imageUrl, name = it.title, placeHolder = R.drawable.manga_world_round_logo) { onClick(it) }
+ }
+ }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/mangaworld/src/main/java/com/programmersbox/mangaworld/MangaGalleryAdapter.kt b/mangaworld/src/main/java/com/programmersbox/mangaworld/MangaGalleryAdapter.kt
deleted file mode 100644
index a5fcb7433..000000000
--- a/mangaworld/src/main/java/com/programmersbox/mangaworld/MangaGalleryAdapter.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.programmersbox.mangaworld
-
-import android.content.Context
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-import com.bumptech.glide.Glide
-import com.bumptech.glide.load.resource.bitmap.RoundedCorners
-import com.programmersbox.favoritesdatabase.DbModel
-import com.programmersbox.helpfulutils.layoutInflater
-import com.programmersbox.mangaworld.databinding.MangaGalleryItemBinding
-import com.programmersbox.models.ItemModel
-import com.programmersbox.uiviews.BaseListFragment
-import com.programmersbox.uiviews.ItemListAdapter
-import com.programmersbox.uiviews.utils.toolTipText
-
-class MangaGalleryAdapter(context: Context, baseListFragment: BaseListFragment) :
- ItemListAdapter(context, baseListFragment) {
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GalleryHolder =
- GalleryHolder(MangaGalleryItemBinding.inflate(context.layoutInflater, parent, false))
-
- override fun GalleryHolder.onBind(item: ItemModel, position: Int) {
- bind(item)
- itemView.setOnClickListener { onClick(it, item) }
- Glide.with(context)
- .asBitmap()
- .load(item.imageUrl)
- //.override(360, 480)
- .fitCenter()
- .transform(RoundedCorners(15))
- .fallback(R.drawable.manga_world_round_logo)
- .placeholder(R.drawable.manga_world_round_logo)
- .error(R.drawable.manga_world_round_logo)
- .into(cover)
- /*.into {
- resourceReady { image, _ ->
- //cover.setImageBitmap(image.glowEffect(10, title.currentTextColor) ?: image)
- cover.setImageBitmap(image)
- *//*if (context.usePalette) {
- swatch = image.getPalette().vibrantSwatch
- }*//*
- }
- }*/
- }
-
- override val currentList: MutableList get() = mutableListOf()
- override val previousList: MutableList get() = mutableListOf()
- override fun update(list: List, check: (ItemModel, DbModel) -> Boolean) {}
-}
-
-class GalleryHolder(private val binding: MangaGalleryItemBinding) : RecyclerView.ViewHolder(binding.root) {
- val cover = binding.galleryListCover
- val title = binding.galleryListTitle
- val layout = binding.galleryListLayout
-
- fun bind(item: ItemModel) {
- binding.model = item
- binding.root.toolTipText(item.title)
- binding.executePendingBindings()
- }
-}
\ No newline at end of file
diff --git a/mangaworld/src/main/res/layout/manga_gallery_item.xml b/mangaworld/src/main/res/layout/manga_gallery_item.xml
deleted file mode 100644
index 11eadec48..000000000
--- a/mangaworld/src/main/res/layout/manga_gallery_item.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/novelworld/build.gradle b/novelworld/build.gradle
index 88752c604..b80d74ccd 100644
--- a/novelworld/build.gradle
+++ b/novelworld/build.gradle
@@ -2,6 +2,7 @@ plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
+ id 'androidx.navigation.safeargs.kotlin'
id 'com.google.gms.google-services'
id 'com.google.firebase.crashlytics'
id 'com.mikepenz.aboutlibraries.plugin'
diff --git a/novelworld/src/main/java/com/programmersbox/novelworld/GenericNovel.kt b/novelworld/src/main/java/com/programmersbox/novelworld/GenericNovel.kt
index 13cbe2764..f005cb5e4 100644
--- a/novelworld/src/main/java/com/programmersbox/novelworld/GenericNovel.kt
+++ b/novelworld/src/main/java/com/programmersbox/novelworld/GenericNovel.kt
@@ -2,27 +2,29 @@ package com.programmersbox.novelworld
import android.content.Context
import android.content.Intent
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.items
import androidx.compose.material.Card
import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.ListItem
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
import com.google.accompanist.placeholder.material.placeholder
+import com.programmersbox.favoritesdatabase.DbModel
import com.programmersbox.gsonutils.toJson
import com.programmersbox.models.ApiService
import com.programmersbox.models.ChapterModel
+import com.programmersbox.models.ItemModel
import com.programmersbox.novel_sources.Sources
import com.programmersbox.sharedutils.AppUpdate
import com.programmersbox.sharedutils.MainLogo
-import com.programmersbox.uiviews.BaseListFragment
import com.programmersbox.uiviews.GenericInfo
-import com.programmersbox.uiviews.ItemListAdapter
import com.programmersbox.uiviews.utils.ChapterModelSerializer
import com.programmersbox.uiviews.utils.NotificationLogo
import org.koin.dsl.module
@@ -34,10 +36,6 @@ val appModule = module {
}
class GenericNovel(val context: Context) : GenericInfo {
- override fun createAdapter(context: Context, baseListFragment: BaseListFragment): ItemListAdapter =
- (NovelAdapter(context, baseListFragment) as ItemListAdapter)
-
- override fun createLayoutManager(context: Context): RecyclerView.LayoutManager = LinearLayoutManager(context)
override fun chapterOnClick(model: ChapterModel, allChapters: List, context: Context) {
context.startActivity(
@@ -83,4 +81,18 @@ class GenericNovel(val context: Context) : GenericInfo {
}
}
+ @ExperimentalMaterialApi
+ @ExperimentalFoundationApi
+ @Composable
+ override fun ItemListView(
+ list: List,
+ favorites: List,
+ listState: LazyListState,
+ onClick: (ItemModel) -> Unit
+ ) {
+ LazyColumn(state = listState) {
+ items(list) { ListItem(text = { Text(it.title) }) }
+ }
+ }
+
}
\ No newline at end of file
diff --git a/novelworld/src/main/java/com/programmersbox/novelworld/NovelAdapter.kt b/novelworld/src/main/java/com/programmersbox/novelworld/NovelAdapter.kt
deleted file mode 100644
index 7c811b368..000000000
--- a/novelworld/src/main/java/com/programmersbox/novelworld/NovelAdapter.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.programmersbox.novelworld
-
-import android.content.Context
-import android.view.ViewGroup
-import androidx.recyclerview.widget.RecyclerView
-import com.programmersbox.favoritesdatabase.DbModel
-import com.programmersbox.helpfulutils.layoutInflater
-import com.programmersbox.models.ItemModel
-import com.programmersbox.novelworld.databinding.NovelListItemBinding
-import com.programmersbox.uiviews.BaseListFragment
-import com.programmersbox.uiviews.ItemListAdapter
-import com.programmersbox.uiviews.utils.toolTipText
-
-class NovelAdapter(
- context: Context,
- baseListFragment: BaseListFragment,
-) : ItemListAdapter(context, baseListFragment) {
-
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NovelHolder =
- NovelHolder(NovelListItemBinding.inflate(context.layoutInflater, parent, false))
-
- override fun NovelHolder.onBind(item: ItemModel, position: Int) {
- bind(item, currentList)
- itemView.setOnClickListener { onClick(it, item) }
- }
-}
-
-class NovelHolder(private val binding: NovelListItemBinding) : RecyclerView.ViewHolder(binding.root) {
-
- fun bind(info: ItemModel, list: List) {
- binding.show = info
- binding.root.toolTipText(info.title)
- /*binding.favoriteHeart.changeTint(binding.animeTitle.currentTextColor)
- binding.favoriteHeart.check(false)
- binding.favoriteHeart.check(list.any { it.url == info.url })*/
- binding.executePendingBindings()
- }
-
-}
\ No newline at end of file
diff --git a/novelworld/src/main/res/layout/novel_list_item.xml b/novelworld/src/main/res/layout/novel_list_item.xml
deleted file mode 100644
index fca076ff4..000000000
--- a/novelworld/src/main/res/layout/novel_list_item.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/otakumanager/src/main/java/com/programmersbox/otakumanager/OtakuManagerApp.kt b/otakumanager/src/main/java/com/programmersbox/otakumanager/OtakuManagerApp.kt
index 833077625..aa5559ad2 100644
--- a/otakumanager/src/main/java/com/programmersbox/otakumanager/OtakuManagerApp.kt
+++ b/otakumanager/src/main/java/com/programmersbox/otakumanager/OtakuManagerApp.kt
@@ -1,17 +1,17 @@
package com.programmersbox.otakumanager
import android.content.Context
+import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
-import androidx.recyclerview.widget.RecyclerView
+import com.programmersbox.favoritesdatabase.DbModel
import com.programmersbox.manga_sources.utilities.NetworkHelper
import com.programmersbox.models.ApiService
import com.programmersbox.models.ChapterModel
+import com.programmersbox.models.ItemModel
import com.programmersbox.sharedutils.AppUpdate
import com.programmersbox.sharedutils.FirebaseDb
import com.programmersbox.sharedutils.MainLogo
-import com.programmersbox.uiviews.BaseListFragment
import com.programmersbox.uiviews.GenericInfo
-import com.programmersbox.uiviews.ItemListAdapter
import com.programmersbox.uiviews.OtakuApp
import com.programmersbox.uiviews.utils.NotificationLogo
import com.programmersbox.uiviews.utils.shouldCheck
@@ -43,14 +43,6 @@ val appModule = module {
object : GenericInfo {
override val apkString: AppUpdate.AppUpdates.() -> String? get() = { otakumanager_file }
- override fun createAdapter(context: Context, baseListFragment: BaseListFragment): ItemListAdapter {
- throw Exception("This should not be seen")
- }
-
- override fun createLayoutManager(context: Context): RecyclerView.LayoutManager {
- throw Exception("This should not be seen")
- }
-
override fun chapterOnClick(model: ChapterModel, allChapters: List, context: Context) {
throw Exception("This should not be seen")
}
@@ -77,6 +69,15 @@ val appModule = module {
}
+ @Composable
+ override fun ItemListView(
+ list: List,
+ favorites: List,
+ listState: LazyListState,
+ onClick: (ItemModel) -> Unit
+ ) {
+ }
+
}
}
}
\ No newline at end of file
diff --git a/sharedutils/build.gradle b/sharedutils/build.gradle
index 6c94cd311..d385c4ec9 100644
--- a/sharedutils/build.gradle
+++ b/sharedutils/build.gradle
@@ -47,7 +47,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-auth:19.2.0'
implementation 'com.firebaseui:firebase-ui-auth:8.0.0'
implementation 'com.google.firebase:firebase-firestore-ktx:23.0.3'
- implementation 'com.google.firebase:firebase-database-ktx:20.0.1'
+ implementation 'com.google.firebase:firebase-database-ktx:20.0.2'
implementation coroutinesCore
implementation coroutinesAndroid