aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/fragments
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-12-31 00:42:49 -0500
committerGitHub <noreply@github.com>2017-12-31 00:42:49 -0500
commit3076d9a97c203497aec1415d8ac6037d10eebb46 (patch)
treecdeb914fa95f2b230f6327be3e1527d15b41dc94 /app/src/main/kotlin/com/pitchedapps/frost/fragments
parent041bafcceadbd5203e95f2692899ac903dd2e883 (diff)
downloadfrost-3076d9a97c203497aec1415d8ac6037d10eebb46.tar.gz
frost-3076d9a97c203497aec1415d8ac6037d10eebb46.tar.bz2
frost-3076d9a97c203497aec1415d8ac6037d10eebb46.zip
feature/menu-parser (#582)
* Test menu parser * Add menu fragment implementation * Test proguard * Clean up * Use async * Use invoke * Try without proguard * Try 2 * Add fallback logic * Use normal notification event * Add custom event flag * Add rest of menu fragment data * Ensure fallback works * Update docs
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/fragments')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt119
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt7
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt149
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt41
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt17
5 files changed, 219 insertions, 114 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt
index 963d00bb..8ab775e0 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt
@@ -6,28 +6,15 @@ import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import ca.allanwang.kau.adapters.fastAdapter
import ca.allanwang.kau.utils.withArguments
-import com.mikepenz.fastadapter.FastAdapter
-import com.mikepenz.fastadapter.IItem
-import com.mikepenz.fastadapter.adapters.ItemAdapter
-import com.mikepenz.fastadapter_extensions.items.ProgressItem
-import com.pitchedapps.frost.R
import com.pitchedapps.frost.contracts.DynamicUiContract
import com.pitchedapps.frost.contracts.FrostContentParent
import com.pitchedapps.frost.contracts.MainActivityContract
import com.pitchedapps.frost.enums.FeedSort
-import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
-import com.pitchedapps.frost.parsers.FrostParser
-import com.pitchedapps.frost.parsers.ParseResponse
import com.pitchedapps.frost.utils.*
-import com.pitchedapps.frost.views.FrostRecyclerView
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.toast
-import org.jetbrains.anko.uiThread
/**
* Created by Allan Wang on 2017-11-07.
@@ -39,9 +26,10 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
companion object {
private const val ARG_POSITION = "arg_position"
+ private const val ARG_VALID = "arg_valid"
- internal operator fun invoke(base: () -> BaseFragment, data: FbItem, position: Int): BaseFragment {
- val fragment = if (Prefs.nativeViews) base() else WebFragment()
+ internal operator fun invoke(base: () -> BaseFragment, useFallback: Boolean, data: FbItem, position: Int): BaseFragment {
+ val fragment = if (!useFallback) base() else WebFragment()
val d = if (data == FbItem.FEED) FeedSort(Prefs.feedSort).item else data
fragment.withArguments(
ARG_URL to d.url,
@@ -56,6 +44,17 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
override val baseEnum: FbItem by lazy { FbItem[arguments]!! }
override val position: Int by lazy { arguments!!.getInt(ARG_POSITION) }
+ override var valid: Boolean
+ get() = arguments!!.getBoolean(ARG_VALID, true)
+ set(value) {
+ if (value || this is WebFragment) return
+ arguments!!.putBoolean(ARG_VALID, value)
+ L.e("Invalidating position $position")
+ frostAnswersCustom("Native Fallback",
+ "Item" to baseEnum.name)
+ (context as MainActivityContract).reloadFragment(this)
+ }
+
override var firstLoad: Boolean = true
private var activityDisposable: Disposable? = null
private var onCreateRunnable: ((FragmentContract) -> Unit)? = null
@@ -147,6 +146,7 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
}
override fun onDestroyView() {
+ L.i("Fragment on destroy $position ${hashCode()}")
content?.destroy()
content = null
super.onDestroyView()
@@ -175,92 +175,3 @@ abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract {
override fun onTabClick(): Unit = content?.core?.onTabClicked() ?: Unit
}
-abstract class RecyclerFragment<T : Any, Item : IItem<*, *>> : BaseFragment(), RecyclerContentContract {
-
- override val layoutRes: Int = R.layout.view_content_recycler
-
- /**
- * The parser to make this all happen
- */
- abstract val parser: FrostParser<T>
-
- open fun getDoc(cookie: String?) = frostJsoup(cookie, parser.url)
-
- val adapter: ItemAdapter<Item> = ItemAdapter()
-
- abstract fun toItems(response: ParseResponse<T>): List<Item>
-
- override final fun bind(recyclerView: FrostRecyclerView) {
- recyclerView.adapter = getAdapter()
- recyclerView.onReloadClear = { adapter.clear() }
- bindImpl(recyclerView)
- }
-
- override fun firstLoadRequest() {
- val core = core ?: return
- if (firstLoad) {
- core.reloadBase(true)
- firstLoad = false
- }
- }
-
- /**
- * Anything to call for one time bindings
- * At this stage, all adapters will have FastAdapter references
- */
- open fun bindImpl(recyclerView: FrostRecyclerView) = Unit
-
- /**
- * Create the fast adapter to bind to the recyclerview
- */
- open fun getAdapter(): FastAdapter<IItem<*, *>> = fastAdapter(this.adapter)
-
- override fun reload(progress: (Int) -> Unit, callback: (Boolean) -> Unit) {
- doAsync {
- progress(10)
- val cookie = FbCookie.webCookie
- val doc = getDoc(cookie)
- progress(60)
- val response = parser.parse(cookie, doc)
- if (response == null) {
- uiThread { context?.toast(R.string.error_generic) }
- L.eThrow("RecyclerFragment failed for ${baseEnum.name}")
- Prefs.nativeViews = false
- return@doAsync callback(false)
- }
- progress(80)
- val items = toItems(response)
- progress(97)
- uiThread { adapter.setNewList(items) }
- callback(true)
- }
- }
-}
-
-//abstract class PagedRecyclerFragment<T : Any, Item : IItem<*, *>> : RecyclerFragment<T, Item>() {
-//
-// var allowPagedLoading = true
-//
-// val footerAdapter = ItemAdapter<FrostProgress>()
-//
-// val footerScrollListener = object : EndlessRecyclerOnScrollListener(footerAdapter) {
-// override fun onLoadMore(currentPage: Int) {
-// TODO("not implemented")
-//
-// }
-//
-// }
-//
-// override fun getAdapter() = fastAdapter(adapter, footerAdapter)
-//
-// override fun bindImpl(recyclerView: FrostRecyclerView) {
-// recyclerView.addOnScrollListener(footerScrollListener)
-// }
-//
-// override fun reload(progress: (Int) -> Unit, callback: (Boolean) -> Unit) {
-// footerScrollListener.
-// super.reload(progress, callback)
-// }
-//}
-
-class FrostProgress : ProgressItem() \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt
index a78eb0d0..98a081e6 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentContract.kt
@@ -16,6 +16,13 @@ interface FragmentContract : FrostContentContainer {
val content: FrostContentParent?
/**
+ * Defines whether the fragment is valid in the viewpager
+ * Or if it needs to be recreated
+ * May be called from any thread to toggle status
+ */
+ var valid: Boolean
+
+ /**
* Helper to retrieve the core from [content]
*/
val core: FrostContentCore?
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt
new file mode 100644
index 00000000..c490de60
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragmentBase.kt
@@ -0,0 +1,149 @@
+package com.pitchedapps.frost.fragments
+
+import ca.allanwang.kau.adapters.fastAdapter
+import com.mikepenz.fastadapter.FastAdapter
+import com.mikepenz.fastadapter.IItem
+import com.mikepenz.fastadapter.adapters.ItemAdapter
+import com.mikepenz.fastadapter.adapters.ModelAdapter
+import com.mikepenz.fastadapter_extensions.items.ProgressItem
+import com.pitchedapps.frost.R
+import com.pitchedapps.frost.facebook.FbCookie
+import com.pitchedapps.frost.parsers.FrostParser
+import com.pitchedapps.frost.parsers.ParseResponse
+import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.utils.Prefs
+import com.pitchedapps.frost.utils.frostJsoup
+import com.pitchedapps.frost.views.FrostRecyclerView
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.toast
+import org.jetbrains.anko.uiThread
+
+/**
+ * Created by Allan Wang on 27/12/17.
+ */
+abstract class RecyclerFragment : BaseFragment(), RecyclerContentContract {
+
+ override val layoutRes: Int = R.layout.view_content_recycler
+
+ override fun firstLoadRequest() {
+ val core = core ?: return
+ if (firstLoad) {
+ core.reloadBase(true)
+ firstLoad = false
+ }
+ }
+
+ override final fun reload(progress: (Int) -> Unit, callback: (Boolean) -> Unit) {
+ reloadImpl(progress) {
+ if (it)
+ callback(it)
+ else
+ valid = false
+ }
+ }
+
+ protected abstract fun reloadImpl(progress: (Int) -> Unit, callback: (Boolean) -> Unit)
+}
+
+abstract class GenericRecyclerFragment<T, Item : IItem<*, *>> : RecyclerFragment() {
+
+ abstract fun mapper(data: T): Item
+
+ val adapter: ModelAdapter<T, Item> = ModelAdapter(this::mapper)
+
+ override final fun bind(recyclerView: FrostRecyclerView) {
+ recyclerView.adapter = getAdapter()
+ recyclerView.onReloadClear = { adapter.clear() }
+ bindImpl(recyclerView)
+ }
+
+ /**
+ * Anything to call for one time bindings
+ * At this stage, all adapters will have FastAdapter references
+ */
+ open fun bindImpl(recyclerView: FrostRecyclerView) = Unit
+
+ /**
+ * Create the fast adapter to bind to the recyclerview
+ */
+ open fun getAdapter(): FastAdapter<IItem<*, *>> = fastAdapter(this.adapter)
+
+}
+
+abstract class FrostParserFragment<T : Any, Item : IItem<*, *>> : RecyclerFragment() {
+
+ /**
+ * The parser to make this all happen
+ */
+ abstract val parser: FrostParser<T>
+
+ open fun getDoc(cookie: String?) = frostJsoup(cookie, parser.url)
+
+ abstract fun toItems(response: ParseResponse<T>): List<Item>
+
+ val adapter: ItemAdapter<Item> = ItemAdapter()
+
+ override final fun bind(recyclerView: FrostRecyclerView) {
+ recyclerView.adapter = getAdapter()
+ recyclerView.onReloadClear = { adapter.clear() }
+ bindImpl(recyclerView)
+ }
+
+ /**
+ * Anything to call for one time bindings
+ * At this stage, all adapters will have FastAdapter references
+ */
+ open fun bindImpl(recyclerView: FrostRecyclerView) = Unit
+
+ /**
+ * Create the fast adapter to bind to the recyclerview
+ */
+ open fun getAdapter(): FastAdapter<IItem<*, *>> = fastAdapter(this.adapter)
+
+ override fun reloadImpl(progress: (Int) -> Unit, callback: (Boolean) -> Unit) {
+ doAsync {
+ progress(10)
+ val cookie = FbCookie.webCookie
+ val doc = getDoc(cookie)
+ progress(60)
+ val response = parser.parse(cookie, doc)
+ if (response == null) {
+ L.eThrow("RecyclerFragment failed for ${baseEnum.name}")
+ return@doAsync callback(false)
+ }
+ progress(80)
+ val items = toItems(response)
+ progress(97)
+ uiThread { adapter.setNewList(items) }
+ callback(true)
+ }
+ }
+}
+
+//abstract class PagedRecyclerFragment<T : Any, Item : IItem<*, *>> : RecyclerFragment<T, Item>() {
+//
+// var allowPagedLoading = true
+//
+// val footerAdapter = ItemAdapter<FrostProgress>()
+//
+// val footerScrollListener = object : EndlessRecyclerOnScrollListener(footerAdapter) {
+// override fun onLoadMore(currentPage: Int) {
+// TODO("not implemented")
+//
+// }
+//
+// }
+//
+// override fun getAdapter() = fastAdapter(adapter, footerAdapter)
+//
+// override fun bindImpl(recyclerView: FrostRecyclerView) {
+// recyclerView.addOnScrollListener(footerScrollListener)
+// }
+//
+// override fun reload(progress: (Int) -> Unit, callback: (Boolean) -> Unit) {
+// footerScrollListener.
+// super.reload(progress, callback)
+// }
+//}
+
+class FrostProgress : ProgressItem()
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt
index 4d4a6f8b..ca2912e8 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt
@@ -1,17 +1,22 @@
package com.pitchedapps.frost.fragments
+import com.mikepenz.fastadapter.IItem
+import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.facebook.FbItem
-import com.pitchedapps.frost.iitems.NotificationIItem
+import com.pitchedapps.frost.facebook.requests.*
+import com.pitchedapps.frost.iitems.*
import com.pitchedapps.frost.parsers.FrostNotifs
import com.pitchedapps.frost.parsers.NotifParser
import com.pitchedapps.frost.parsers.ParseResponse
import com.pitchedapps.frost.utils.frostJsoup
import com.pitchedapps.frost.views.FrostRecyclerView
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
/**
* Created by Allan Wang on 27/12/17.
*/
-class NotificationFragment : RecyclerFragment<FrostNotifs, NotificationIItem>() {
+class NotificationFragment : FrostParserFragment<FrostNotifs, NotificationIItem>() {
override val parser = NotifParser
@@ -23,5 +28,37 @@ class NotificationFragment : RecyclerFragment<FrostNotifs, NotificationIItem>()
override fun bindImpl(recyclerView: FrostRecyclerView) {
NotificationIItem.bindEvents(adapter)
}
+}
+class MenuFragment : GenericRecyclerFragment<MenuItemData, IItem<*, *>>() {
+
+ override fun mapper(data: MenuItemData): IItem<*, *> = when (data) {
+ is MenuHeader -> MenuHeaderIItem(data)
+ is MenuItem -> MenuContentIItem(data)
+ is MenuFooterItem ->
+ if (data.isSmall) MenuFooterSmallIItem(data)
+ else MenuFooterIItem(data)
+ else -> throw IllegalArgumentException("Menu item in fragment has invalid type ${data::class.java.simpleName}")
+ }
+
+ override fun bindImpl(recyclerView: FrostRecyclerView) {
+ ClickableIItemContract.bindEvents(adapter)
+ }
+
+ override fun reloadImpl(progress: (Int) -> Unit, callback: (Boolean) -> Unit) {
+ doAsync {
+ val cookie = FbCookie.webCookie
+ progress(10)
+ cookie.fbRequest({ callback(false) }) {
+ progress(30)
+ val data = getMenuData().invoke() ?: return@fbRequest callback(false)
+ if (data.data.isEmpty()) return@fbRequest callback(false)
+ progress(70)
+ val items = data.flatMapValid()
+ progress(90)
+ uiThread { adapter.add(items) }
+ callback(true)
+ }
+ }
+ }
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt
index 2740a36f..cdeea064 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/WebFragments.kt
@@ -1,26 +1,27 @@
package com.pitchedapps.frost.fragments
import com.pitchedapps.frost.R
+import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.views.FrostWebView
import com.pitchedapps.frost.web.FrostWebViewClient
import com.pitchedapps.frost.web.FrostWebViewClientMenu
/**
* Created by Allan Wang on 27/12/17.
+ *
+ * Basic webfragment
+ * Do not extend as this is always a fallback
*/
-open class WebFragment : BaseFragment() {
+class WebFragment : BaseFragment() {
override val layoutRes: Int = R.layout.view_content_web
/**
* Given a webview, output a client
*/
- open fun client(web: FrostWebView) = FrostWebViewClient(web)
-
-}
-
-class WebFragmentMenu : WebFragment() {
-
- override fun client(web: FrostWebView) = FrostWebViewClientMenu(web)
+ fun client(web: FrostWebView) = when (baseEnum) {
+ FbItem.MENU -> FrostWebViewClientMenu(web)
+ else -> FrostWebViewClient(web)
+ }
} \ No newline at end of file