diff options
author | Allan Wang <me@allanwang.ca> | 2017-12-21 02:16:34 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-21 02:16:34 -0500 |
commit | d683cae6ffe644a9f63eea6cf3b7e59d2bde617b (patch) | |
tree | 517fe1d44c27084ccd87507d9804ba28f15c1647 /app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt | |
parent | 82f9aca96493316bc62008f2b3167d34a6029b38 (diff) | |
download | frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.tar.gz frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.tar.bz2 frost-d683cae6ffe644a9f63eea6cf3b7e59d2bde617b.zip |
Enhancement/fragment interface (#564)
* Begin fragment interfaces and themable contracts
* Prepare swiperefresh interface
* Snapshot
* Add compilable version
* Revamp once more
* Finalize layouts
* Cleanup
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt')
-rw-r--r-- | app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt | 234 |
1 files changed, 234 insertions, 0 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 new file mode 100644 index 00000000..498164c0 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt @@ -0,0 +1,234 @@ +package com.pitchedapps.frost.fragments + +import android.content.Context +import android.os.Bundle +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import ca.allanwang.kau.utils.withArguments +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter +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.FbItem +import com.pitchedapps.frost.parsers.FrostParser +import com.pitchedapps.frost.utils.* +import com.pitchedapps.frost.views.FrostRecyclerView +import com.pitchedapps.frost.views.FrostWebView +import com.pitchedapps.frost.web.FrostWebViewClient +import com.pitchedapps.frost.web.FrostWebViewClientMenu +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.toast + +/** + * Created by Allan Wang on 2017-11-07. + */ +abstract class BaseFragment : Fragment(), FragmentContract, DynamicUiContract { + + companion object { + private const val ARG_URL_ENUM = "arg_url_enum" + private const val ARG_POSITION = "arg_position" + + internal operator fun invoke(base: () -> BaseFragment, data: FbItem, position: Int): BaseFragment { + val fragment = if (Prefs.nativeViews) base() else WebFragment() + val d = if (data == FbItem.FEED) FeedSort(Prefs.feedSort).item else data + fragment.withArguments( + ARG_URL to d.url, + ARG_POSITION to position, + ARG_URL_ENUM to d + ) + return fragment + } + } + + override val baseUrl: String by lazy { arguments!!.getString(ARG_URL) } + override val baseEnum: FbItem by lazy { arguments!!.getSerializable(ARG_URL_ENUM) as FbItem } + override val position: Int by lazy { arguments!!.getInt(ARG_POSITION) } + + override var firstLoad: Boolean = true + private var activityDisposable: Disposable? = null + private var onCreateRunnable: ((FragmentContract) -> Unit)? = null + + override var content: FrostContentParent? = null + + protected abstract val layoutRes: Int + + override final fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val view = inflater.inflate(layoutRes, container, false) + val content = view as? FrostContentParent + ?: throw IllegalArgumentException("layoutRes for fragment must return view implementing FrostContentParent") + this.content = content + content.bind(this) + return view + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + onCreateRunnable?.invoke(this) + onCreateRunnable = null + firstLoadRequest() + } + + override fun setUserVisibleHint(isVisibleToUser: Boolean) { + super.setUserVisibleHint(isVisibleToUser) + firstLoadRequest() + } + + override fun firstLoadRequest() { + if (userVisibleHint && isVisible && firstLoad) { + core?.reloadBase(true) + firstLoad = false + } + } + + override fun post(action: (fragment: FragmentContract) -> Unit) { + onCreateRunnable = action + } + + override fun setTitle(title: String) { + (context as? MainActivityContract)?.setTitle(title) + } + + override fun attachMainObservable(contract: MainActivityContract): Disposable = + contract.fragmentSubject.observeOn(AndroidSchedulers.mainThread()).subscribe { + when (it) { + REQUEST_REFRESH -> { + core?.apply { + reload(true) + clearHistory() + } + } + position -> { + contract.setTitle(baseEnum.titleId) + core?.active = true + } + -(position + 1) -> { + core?.active = false + } + REQUEST_TEXT_ZOOM -> { + reloadTextSize() + } + } + } + + override fun detachMainObservable() { + activityDisposable?.dispose() + } + + override fun onAttach(context: Context) { + super.onAttach(context) + detachMainObservable() + if (context is MainActivityContract) + activityDisposable = attachMainObservable(context) + } + + override fun onDetach() { + detachMainObservable() + super.onDetach() + } + + override fun onDestroyView() { + content?.destroy() + content = null + super.onDestroyView() + } + + override fun reloadTheme() { + reloadThemeSelf() + content?.reloadTextSize() + } + + override fun reloadThemeSelf() { + // intentionally blank + } + + override fun reloadTextSize() { + reloadTextSizeSelf() + content?.reloadTextSize() + } + + override fun reloadTextSizeSelf() { + // intentionally blank + } + + override fun onBackPressed(): Boolean = content?.core?.onBackPressed() ?: false + + override fun onTabClick(): Unit = content?.core?.onTabClicked() ?: Unit +} + +abstract class RecyclerFragment<T, 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> + + abstract val adapter: FastItemAdapter<Item> + + abstract fun toItems(data: T): List<Item> + + override fun bind(recyclerView: FrostRecyclerView) { + recyclerView.adapter = this.adapter + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val tail = tailMapper(baseEnum) + if (tail.isNotEmpty()) { + val baseUrl = baseEnum.url + L.d("Adding $tail to $baseUrl for RecyclerFragment") + arguments!!.putString(ARG_URL, "$baseUrl$tail") + } + } + + private fun tailMapper(item: FbItem) = when (item) { + FbItem.NOTIFICATIONS, FbItem.MESSAGES -> "/?more" + else -> "" + } + + override fun reload(progress: (Int) -> Unit, callback: (Boolean) -> Unit) { + doAsync { + progress(10) + val doc = frostJsoup(baseUrl) + progress(60) + val data = parser.parse(doc) + if (data == null) { + 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(data) + progress(97) + adapter.setNewList(items) + } + } +} + +open class WebFragment : BaseFragment(), FragmentContract { + + override val layoutRes: Int = R.layout.view_content_web + + /** + * Given a webview, output a client + */ + open fun client(web: FrostWebView) = FrostWebViewClient(web) + + override fun innerView(context: Context) = FrostWebView(context) + +} + +class WebFragmentMenu : WebFragment() { + + override fun client(web: FrostWebView) = FrostWebViewClientMenu(web) + +}
\ No newline at end of file |