1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-10 04:52:38 +01:00

add progressbar and remove tab indicator

This commit is contained in:
Allan Wang 2017-06-01 17:45:42 -07:00
parent d924fd96dd
commit 266569a14f
24 changed files with 536 additions and 256 deletions

View File

@ -121,7 +121,7 @@
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -112,6 +112,8 @@ dependencies {
compile("com.mikepenz:materialdrawer:${MATERIAL_DRAWER}@aar") {
transitive = true
}
compile 'com.jude:swipebackhelper:3.1.2'
}
kapt {

View File

@ -87,6 +87,9 @@
android:scheme="https" />
</intent-filter>
</activity>
<activity
android:name=".WebOverlayActivity"
android:theme="@style/AppTheme.Overlay" />
<receiver
android:name=".services.NotificationReceiver"

View File

@ -11,6 +11,7 @@ import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.view.Menu
import android.view.MenuItem
import android.view.ViewTreeObserver
import butterknife.ButterKnife
import com.mikepenz.materialdrawer.Drawer
import com.pitchedapps.frost.dbflow.loadFbTabs
@ -18,9 +19,7 @@ import com.pitchedapps.frost.dbflow.saveAsync
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.fragments.BaseFragment
import com.pitchedapps.frost.fragments.WebFragment
import com.pitchedapps.frost.utils.Changelog
import com.pitchedapps.frost.utils.bindView
import com.pitchedapps.frost.utils.toDrawable
import com.pitchedapps.frost.utils.*
class MainActivity : AppCompatActivity() {
@ -33,6 +32,8 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// SwipeBackHelper.onCreate(this)
// SwipeBackHelper.getCurrentPage(this).setSwipeBackEnable(false)
setContentView(R.layout.activity_main)
ButterKnife.bind(this)
setSupportActionBar(toolbar)
@ -40,6 +41,20 @@ class MainActivity : AppCompatActivity() {
adapter = SectionsPagerAdapter(supportFragmentManager, loadFbTabs())
viewPager.adapter = adapter
viewPager.offscreenPageLimit = 5
viewPager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
val delta: Float by lazy { positionOffset * (255 - 128).toFloat() }
(0 until tabs.tabCount).asSequence().forEach {
i ->
tabs.getTabAt(i)?.icon?.alpha = when (i) {
position -> (255.0 - delta).toInt()
position + 1 -> (128.0 + delta).toInt()
else -> 128
}
}
}
})
setupTabs()
fab.setOnClickListener { view ->
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
@ -47,6 +62,17 @@ class MainActivity : AppCompatActivity() {
}
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// SwipeBackHelper.onPostCreate(this)
}
override fun onDestroy() {
super.onDestroy()
// SwipeBackHelper.onDestroy(this)
}
fun setupTabs() {
viewPager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tabs))
@ -72,8 +98,7 @@ class MainActivity : AppCompatActivity() {
// finish()
}
R.id.action_changelog -> Changelog.show(this)
R.id.action_call -> {
}
R.id.action_call -> WebOverlayActivity.newInstance(this, "https://www.google.ca")
R.id.action_db -> adapter.pages.saveAsync(this)
R.id.action_restart -> {
finish();

View File

@ -3,12 +3,17 @@ package com.pitchedapps.frost
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.support.v4.app.ActivityOptionsCompat
import android.support.v4.content.ContextCompat
import android.support.v4.widget.SwipeRefreshLayout
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import butterknife.ButterKnife
import com.jude.swipbackhelper.SwipeBackHelper
import com.pitchedapps.frost.utils.bindView
import com.pitchedapps.frost.web.FrostWebView
import com.pitchedapps.frost.web.FrostWebViewCore
/**
* Created by Allan Wang on 2017-06-01.
@ -16,15 +21,15 @@ import com.pitchedapps.frost.web.FrostWebView
class WebOverlayActivity : AppCompatActivity() {
val toolbar: Toolbar by bindView(R.id.toolbar)
val refresh: SwipeRefreshLayout by bindView(R.id.swipe_refresh)
val web: FrostWebView by bindView(R.id.frost_webview)
val frostWeb:FrostWebView by bindView(R.id.frost_webview)
companion object {
private const val ARG_URL = "arg_url"
fun newInstance(context: Context, url: String) {
val intent = Intent(context, WebOverlayActivity::class.java)
intent.putExtra(ARG_URL, url)
context.startActivity(intent)
val bundle = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.slide_in_right, R.anim.slide_out_right).toBundle()
ContextCompat.startActivity(context, intent, bundle)
}
}
@ -33,5 +38,23 @@ class WebOverlayActivity : AppCompatActivity() {
setContentView(R.layout.activity_web_overlay)
ButterKnife.bind(this)
setSupportActionBar(toolbar)
frostWeb.baseUrl = intent.extras.getString(ARG_URL)
SwipeBackHelper.onCreate(this)
SwipeBackHelper.getCurrentPage(this)
.setSwipeBackEnable(true)
.setSwipeSensitivity(0.5f)
.setSwipeRelateEnable(true)
.setSwipeRelateOffset(300)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
SwipeBackHelper.onPostCreate(this)
}
override fun onDestroy() {
super.onDestroy()
SwipeBackHelper.onDestroy(this)
}
}

View File

@ -1,6 +1,6 @@
package com.pitchedapps.frost.events
import com.pitchedapps.frost.web.FrostWebView
import com.pitchedapps.frost.web.FrostWebViewCore
/**
* Created by Allan Wang on 2017-05-31.
@ -12,7 +12,7 @@ class WebEvent(val key: Int, val urlMatch: String? = null) {
const val REFRESH_BASE = 1
}
fun execute(webView: FrostWebView) {
fun execute(webView: FrostWebViewCore) {
if (urlMatch != null && !webView.url.contains(urlMatch)) return
when (key) {
REFRESH -> webView.reload()

View File

@ -5,34 +5,26 @@ import android.support.v4.widget.SwipeRefreshLayout
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import butterknife.ButterKnife
import butterknife.Unbinder
import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.bindView
import com.pitchedapps.frost.utils.putString
import com.pitchedapps.frost.web.FrostWebView
import com.pitchedapps.frost.web.WebStatus
import com.pitchedapps.frost.web.FrostWebViewCore
/**
* Created by Allan Wang on 2017-05-29.
*/
class WebFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener {
override fun onRefresh() {
web.reload()
}
class WebFragment : BaseFragment() {
companion object {
private const val ARG_URL = "arg_url"
fun newInstance(position: Int, url: String) = BaseFragment.newInstance(WebFragment(), position).putString(ARG_URL, url)
}
val refresh: SwipeRefreshLayout by bindView(R.id.swipe_refresh)
val web: FrostWebView by bindView(R.id.frost_webview)
val refresh: SwipeRefreshLayout by lazy { frostWebView.refresh }
val web: FrostWebViewCore by lazy { frostWebView.web }
lateinit var url: String
private lateinit var unbinder: Unbinder
lateinit private var frostWebView: FrostWebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -41,39 +33,10 @@ class WebFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
super.onCreateView(inflater, container, savedInstanceState)
val view = inflater.inflate(R.layout.swipe_webview, container, false)
unbinder = ButterKnife.bind(this, view)
return view
frostWebView = FrostWebView(context)
frostWebView.baseUrl = url
return frostWebView
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
web.baseUrl = url
web.observable.subscribe {
t: WebStatus ->
when (t) {
WebStatus.LOADED, WebStatus.ERROR -> refresh.isRefreshing = false
WebStatus.LOADING -> refresh.isRefreshing = true
}
}
refresh.setOnRefreshListener(this)
// refresh.shouldSwipe = {
// L.e("Y ${web.scrollY}")
// SwipeRefreshBase.shouldScroll(web)
// }
web.loadUrl(url)
}
override fun onDestroyView() {
super.onDestroyView()
unbinder.unbind()
}
override fun onBackPressed(): Boolean {
if (web.canGoBack()) {
web.goBack()
return true
}
return false
}
override fun onBackPressed() = frostWebView.onBackPressed()
}

View File

@ -1,13 +1,19 @@
package com.pitchedapps.frost.utils
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.support.annotation.ColorRes
import android.support.annotation.ColorInt
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
/**
* Created by Allan Wang on 2017-05-29.
*/
fun IIcon.toDrawable(c: Context, sizeDp: Int = 24, @ColorRes color: Int = android.R.color.white): Drawable
= IconicsDrawable(c).icon(this).colorRes(color).sizeDp(sizeDp)
fun IIcon.toDrawable(c: Context, sizeDp: Int = 24, @ColorInt color: Int = Color.WHITE): Drawable {
val state = ColorStateList.valueOf(color)
val icon = IconicsDrawable(c).icon(this).sizeDp(sizeDp)
icon.setTintList(state)
return icon
}

View File

@ -6,7 +6,7 @@ import io.reactivex.subjects.Subject
* Created by Allan Wang on 2017-05-29.
*/
interface ObservableContainer<T> {
val observable: Subject<T>
val progressObservable: Subject<T>
}
interface KeyPairObservable : ObservableContainer<Pair<Int, Int>>

View File

@ -0,0 +1,107 @@
package com.pitchedapps.frost.views
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.view.View
import android.view.ViewAnimationUtils
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.view.animation.DecelerateInterpolator
/**
* Created by Allan Wang on 2017-06-01.
*/
fun View.rootCircularReveal(x: Int = 0, y: Int = 0, duration: Long = 500L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
this.addOnLayoutChangeListener(object : View.OnLayoutChangeListener {
override fun onLayoutChange(v: View, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int,
oldRight: Int, oldBottom: Int) {
v.removeOnLayoutChangeListener(this)
var x2 = x
var y2 = y
if (x2 > right) x2 = 0
if (y2 > bottom) y2 = 0
val radius = Math.hypot(Math.max(x2, right - x2).toDouble(), Math.max(y2, bottom - y2).toDouble()).toInt()
val reveal = ViewAnimationUtils.createCircularReveal(v, x2, y2, 0f, radius.toFloat())
reveal.interpolator = DecelerateInterpolator(1f)
reveal.duration = duration
reveal.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
visibility = View.VISIBLE
onStart?.invoke()
}
override fun onAnimationEnd(animation: Animator?) = onFinish?.invoke() ?: Unit
override fun onAnimationCancel(animation: Animator?) = onFinish?.invoke() ?: Unit
})
reveal.start()
}
})
}
fun View.circularReveal(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float = -1.0f, duration: Long = 500L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
if (!isAttachedToWindow) {
visibility = View.VISIBLE
return
}
var r = radius
if (r < 0.0f) {
r = Math.max(Math.hypot(x.toDouble(), y.toDouble()), Math.hypot((width - x.toDouble()), (height - y.toDouble()))).toFloat()
}
val anim = ViewAnimationUtils.createCircularReveal(this, x, y, 0f, r).setDuration(duration)
anim.startDelay = offset
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
visibility = View.VISIBLE
onStart?.invoke()
}
override fun onAnimationEnd(animation: Animator?) = onFinish?.invoke() ?: Unit
override fun onAnimationCancel(animation: Animator?) = onFinish?.invoke() ?: Unit
})
anim.start()
}
fun View.fadeIn(offset: Long = 0L, duration: Long = 200L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
if (!isAttachedToWindow) {
visibility = View.VISIBLE
return
}
if (isAttachedToWindow) {
val anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_in)
anim.startOffset = offset
anim.duration = duration
anim.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation?) {}
override fun onAnimationEnd(animation: Animation?) = onFinish?.invoke() ?: Unit
override fun onAnimationStart(animation: Animation?) {
visibility = View.VISIBLE
onStart?.invoke()
}
})
startAnimation(anim)
}
}
fun View.fadeOut(offset: Long = 0L, duration: Long = 200L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) {
if (!isAttachedToWindow) {
visibility = View.INVISIBLE
return
}
val anim = AnimationUtils.loadAnimation(context, android.R.anim.fade_out)
anim.startOffset = offset
anim.duration = duration
anim.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation?) {}
override fun onAnimationEnd(animation: Animation?) {
visibility = View.INVISIBLE
onFinish?.invoke()
}
override fun onAnimationStart(animation: Animation?) {
onStart?.invoke()
}
})
startAnimation(anim)
}

View File

@ -1,14 +1,30 @@
package com.pitchedapps.frost.views
import android.content.res.ColorStateList
import android.graphics.PorterDuff
import android.support.annotation.ColorInt
import android.support.annotation.ColorRes
import android.support.v4.content.ContextCompat
import android.view.View
import android.view.ViewGroup
import android.widget.ProgressBar
/**
* Created by Allan Wang on 2017-05-31.
*/
fun View.matchParent() {
with (layoutParams) {
with(layoutParams) {
height = ViewGroup.LayoutParams.MATCH_PARENT
width = ViewGroup.LayoutParams.MATCH_PARENT
}
}
fun ProgressBar.tintRes(@ColorRes id: Int) = tint(ContextCompat.getColor(context, id))
fun ProgressBar.tint(@ColorInt color: Int) {
val sl = ColorStateList.valueOf(color)
progressTintList = sl
secondaryProgressTintList = sl
indeterminateTintList = sl
}

View File

@ -2,14 +2,28 @@ package com.pitchedapps.frost.web
import android.webkit.ConsoleMessage
import android.webkit.WebChromeClient
import android.webkit.WebView
import com.pitchedapps.frost.utils.L
import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-05-31.
*/
class FrostChromeClient:WebChromeClient() {
class FrostChromeClient(val progressObservable: Subject<Int>) : WebChromeClient() {
override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
L.d("Console ${consoleMessage.lineNumber()}: ${consoleMessage.message()}")
return super.onConsoleMessage(consoleMessage)
}
override fun onReceivedTitle(view: WebView?, title: String?) {
super.onReceivedTitle(view, title)
L.d("Title $title")
}
override fun onProgressChanged(view: WebView, newProgress: Int) {
super.onProgressChanged(view, newProgress)
progressObservable.onNext(newProgress)
}
}

View File

@ -0,0 +1,13 @@
package com.pitchedapps.frost.web
enum class FrostWebEnums {
LOADING, LOADED, ERROR
}
enum class FrostWebOverlay(val match: String) {
STORY("story.php?story_fbid");
companion object {
val values = values()
}
}

View File

@ -1,141 +1,62 @@
package com.pitchedapps.frost.web
import android.annotation.SuppressLint
import android.content.Context
import android.support.v4.view.MotionEventCompat
import android.support.v4.view.NestedScrollingChild
import android.support.v4.view.NestedScrollingChildHelper
import android.support.v4.view.ViewCompat
import android.os.Build
import android.support.v4.widget.SwipeRefreshLayout
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.webkit.WebView
import com.pitchedapps.frost.events.WebEvent
import com.pitchedapps.frost.utils.ObservableContainer
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
enum class WebStatus {
LOADING, LOADED, ERROR
}
import android.widget.FrameLayout
import android.widget.ProgressBar
import butterknife.ButterKnife
import com.pitchedapps.frost.R
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.bindView
import io.reactivex.android.schedulers.AndroidSchedulers
/**
* Created by Allan Wang on 2017-05-29.
*
* Courtesy of takahirom
*
* https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
* Created by Allan Wang on 2017-06-01.
*/
class FrostWebView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : WebView(context, attrs, defStyleAttr), NestedScrollingChild, ObservableContainer<WebStatus> {
private val childHelper = NestedScrollingChildHelper(this)
private var lastY: Int = 0
private val scrollOffset = IntArray(2)
private val scrollConsumed = IntArray(2)
private var nestedOffsetY: Int = 0
override val observable: Subject<WebStatus> //TODO see if we need this
var baseUrl: String? = null
class FrostWebView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, defStyleRes: Int = 0)
: FrameLayout(context, attrs, defStyleAttr, defStyleRes), SwipeRefreshLayout.OnRefreshListener {
var baseUrl: String?
get() = web.baseUrl
set(value) {
web.baseUrl = value
if (value != null) web.loadUrl(value)
}
val refresh: SwipeRefreshLayout by bindView(R.id.swipe_refresh)
val web: FrostWebViewCore by bindView(R.id.frost_webview_core)
val progress: ProgressBar by bindView(R.id.progressBar)
init {
isNestedScrollingEnabled = true
observable = BehaviorSubject.create<WebStatus>()
setupWebview()
inflate(getContext(), R.layout.swipe_webview, this)
ButterKnife.bind(this)
web.progressObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
val loaded = it == 100
refresh.isRefreshing = !loaded
progress.visibility = if (loaded) View.INVISIBLE else View.VISIBLE
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) progress.setProgress(it, true)
else progress.progress = it
}
refresh.setOnRefreshListener(this)
addOnAttachStateChangeListener(object : OnAttachStateChangeListener {
override fun onViewDetachedFromWindow(v: View) {
web.visibility = View.VISIBLE
}
@SuppressLint("SetJavaScriptEnabled")
fun setupWebview() {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
setLayerType(View.LAYER_TYPE_HARDWARE, null)
setWebViewClient(FrostWebViewClient(observable))
setWebChromeClient(FrostChromeClient())
override fun onViewAttachedToWindow(v: View) {}
})
}
override fun loadUrl(url: String?) {
if (url != null)
super.loadUrl(url)
override fun onRefresh() {
web.reload()
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
val event = MotionEvent.obtain(ev)
val action = MotionEventCompat.getActionMasked(event)
if (action == MotionEvent.ACTION_DOWN)
nestedOffsetY = 0
val eventY = event.y.toInt()
event.offsetLocation(0f, nestedOffsetY.toFloat())
val returnValue: Boolean
when (action) {
MotionEvent.ACTION_MOVE -> {
var deltaY = lastY - eventY
// NestedPreScroll
if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, -scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
fun onBackPressed(): Boolean {
if (web.canGoBack()) {
web.goBack()
return true
}
lastY = eventY - scrollOffset[1]
returnValue = super.onTouchEvent(event)
// NestedScroll
if (dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
lastY -= scrollOffset[1]
return false
}
}
MotionEvent.ACTION_DOWN -> {
returnValue = super.onTouchEvent(event)
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
returnValue = super.onTouchEvent(event)
stopNestedScroll()
}
else -> return false
}
return returnValue
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
EventBus.getDefault().register(this);
}
override fun onDetachedFromWindow() {
EventBus.getDefault().unregister(this)
super.onDetachedFromWindow()
}
@Subscribe
fun webEvent(event: WebEvent) = event.execute(this)
// Nested Scroll implements
override fun setNestedScrollingEnabled(enabled: Boolean) {
childHelper.isNestedScrollingEnabled = enabled
}
override fun isNestedScrollingEnabled() = childHelper.isNestedScrollingEnabled
override fun startNestedScroll(axes: Int) = childHelper.startNestedScroll(axes)
override fun stopNestedScroll() = childHelper.stopNestedScroll()
override fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent()
override fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?)
= childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow)
override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?)
= childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)
override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean)
= childHelper.dispatchNestedFling(velocityX, velocityY, consumed)
override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float)
= childHelper.dispatchNestedPreFling(velocityX, velocityY)
}

View File

@ -1,18 +1,20 @@
package com.pitchedapps.frost.web
import android.graphics.Bitmap
import android.view.View
import android.view.KeyEvent
import android.webkit.*
import com.pitchedapps.frost.facebook.FACEBOOK_COM
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.injectors.CssAssets
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.views.circularReveal
import com.pitchedapps.frost.views.fadeOut
import io.reactivex.subjects.Subject
/**
* Created by Allan Wang on 2017-05-31.
*/
class FrostWebViewClient(val observable: Subject<WebStatus>) : WebViewClient() {
class FrostWebViewClient : WebViewClient() {
companion object {
//Collections of jewels mapped with url match -> id
@ -23,31 +25,50 @@ class FrostWebViewClient(val observable: Subject<WebStatus>) : WebViewClient() {
}
}
override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
super.onReceivedError(view, request, error)
observable.onNext(WebStatus.ERROR)
L.e("FWV Error ${request}")
}
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
observable.onNext(WebStatus.LOADING)
L.d("FWV Loading $url")
if (!url.contains(FACEBOOK_COM)) return
if (url.contains("logout.php")) FbCookie.logout()
view.visibility = View.INVISIBLE
view.fadeOut(duration = 200L)
}
override fun onPageFinished(view: WebView, url: String) {
super.onPageFinished(view, url)
if (!url.contains(FACEBOOK_COM)) return
observable.onNext(WebStatus.LOADED)
FbCookie.checkUserId(url, CookieManager.getInstance().getCookie(url))
CssAssets.BASE.inject(view, {
view.visibility = View.VISIBLE
view.circularReveal(offset = 150L)
})
}
override fun shouldOverrideKeyEvent(view: WebView, event: KeyEvent): Boolean {
L.d("Key event ${event.keyCode}")
return super.shouldOverrideKeyEvent(view, event)
}
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
L.d("Hi")
L.d("Url Loading ${request.url?.path}")
return super.shouldOverrideUrlLoading(view, request)
}
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
if (!request.url.host.contains(FACEBOOK_COM)) return super.shouldInterceptRequest(view, request)
L.d("Url intercept ${request.url.path}")
return super.shouldInterceptRequest(view, request)
}
override fun onLoadResource(view: WebView, url: String) {
if (!url.contains(FACEBOOK_COM)) return super.onLoadResource(view, url)
L.d("Resource $url")
FrostWebOverlay.values.forEach {
if (url.contains(it.match))
L.d("Loaded $it")
}
super.onLoadResource(view, url)
}
fun logout() {
}

View File

@ -0,0 +1,137 @@
package com.pitchedapps.frost.web
import android.annotation.SuppressLint
import android.content.Context
import android.support.v4.view.MotionEventCompat
import android.support.v4.view.NestedScrollingChild
import android.support.v4.view.NestedScrollingChildHelper
import android.support.v4.view.ViewCompat
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.webkit.WebView
import com.pitchedapps.frost.events.WebEvent
import com.pitchedapps.frost.utils.ObservableContainer
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
/**
* Created by Allan Wang on 2017-05-29.
*
* Courtesy of takahirom
*
* https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
*/
class FrostWebViewCore @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : WebView(context, attrs, defStyleAttr), NestedScrollingChild, ObservableContainer<Int> {
private val childHelper = NestedScrollingChildHelper(this)
private var lastY: Int = 0
private val scrollOffset = IntArray(2)
private val scrollConsumed = IntArray(2)
private var nestedOffsetY: Int = 0
override val progressObservable: Subject<Int> //TODO see if we need this
var baseUrl: String? = null
init {
isNestedScrollingEnabled = true
progressObservable = BehaviorSubject.create<Int>()
setupWebview()
}
@SuppressLint("SetJavaScriptEnabled")
fun setupWebview() {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
setLayerType(View.LAYER_TYPE_HARDWARE, null)
setWebViewClient(FrostWebViewClient())
setWebChromeClient(FrostChromeClient(progressObservable))
}
override fun loadUrl(url: String?) {
if (url != null)
super.loadUrl(url)
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
val event = MotionEvent.obtain(ev)
val action = MotionEventCompat.getActionMasked(event)
if (action == MotionEvent.ACTION_DOWN)
nestedOffsetY = 0
val eventY = event.y.toInt()
event.offsetLocation(0f, nestedOffsetY.toFloat())
val returnValue: Boolean
when (action) {
MotionEvent.ACTION_MOVE -> {
var deltaY = lastY - eventY
// NestedPreScroll
if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, -scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
}
lastY = eventY - scrollOffset[1]
returnValue = super.onTouchEvent(event)
// NestedScroll
if (dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
lastY -= scrollOffset[1]
}
}
MotionEvent.ACTION_DOWN -> {
returnValue = super.onTouchEvent(event)
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
returnValue = super.onTouchEvent(event)
stopNestedScroll()
}
else -> return false
}
return returnValue
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
EventBus.getDefault().register(this);
}
override fun onDetachedFromWindow() {
EventBus.getDefault().unregister(this)
super.onDetachedFromWindow()
}
@Subscribe
fun webEvent(event: WebEvent) = event.execute(this)
// Nested Scroll implements
override fun setNestedScrollingEnabled(enabled: Boolean) {
childHelper.isNestedScrollingEnabled = enabled
}
override fun isNestedScrollingEnabled() = childHelper.isNestedScrollingEnabled
override fun startNestedScroll(axes: Int) = childHelper.startNestedScroll(axes)
override fun stopNestedScroll() = childHelper.stopNestedScroll()
override fun hasNestedScrollingParent() = childHelper.hasNestedScrollingParent()
override fun dispatchNestedScroll(dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, offsetInWindow: IntArray?)
= childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow)
override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?)
= childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)
override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean)
= childHelper.dispatchNestedFling(velocityX, velocityY, consumed)
override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float)
= childHelper.dispatchNestedPreFling(velocityX, velocityY)
}

View File

@ -1,30 +0,0 @@
package com.pitchedapps.frost.web
import android.content.Context
import android.support.v4.widget.SwipeRefreshLayout
import android.util.AttributeSet
import android.view.MotionEvent
import android.webkit.WebView
import com.pitchedapps.frost.utils.Utils
/**
* Created by Allan Wang on 2017-05-28.
*/
class SwipeRefreshBase @JvmOverloads constructor(
context: Context?, attrs: AttributeSet? = null
) : SwipeRefreshLayout(context, attrs) {
lateinit var shouldSwipe: (ev: MotionEvent) -> Boolean
companion object {
private val SCROLL_BUFFER by lazy { Utils.dpToPx(5) }
fun shouldScroll(webview: WebView) = webview.scrollY <= SCROLL_BUFFER
}
// override fun onInterceptTouchEvent(ev: MotionEvent):Boolean {
// val b = shouldSwipe.invoke(ev) && super.onInterceptTouchEvent(ev)
// L.e("Should swipe $b")
// return b
// }
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="100%p"
android:toXDelta="0" />
</set>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="0"
android:toXDelta="100%p" />
</set>

View File

@ -1,18 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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/main_content"
android:layout_width="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" tools:context=".MainActivity">
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout android:id="@+id/appbar"
android:layout_width="match_parent" android:layout_height="wrap_content"
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/appbar_padding_top"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" app:layout_scrollFlags="scroll|enterAlways"
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay">
</android.support.v7.widget.Toolbar>
@ -20,17 +28,23 @@
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
app:tabIndicatorHeight="0dp" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager android:id="@+id/container"
android:layout_width="match_parent" android:layout_height="match_parent"
<android.support.v4.view.ViewPager
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end|bottom" android:layout_margin="@dimen/fab_margin"
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
android:layout_margin="@dimen/fab_margin"
app:srcCompat="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -16,6 +16,9 @@
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay" />
<include layout="@layout/swipe_webview" />
<com.pitchedapps.frost.web.FrostWebView
android:id="@+id/frost_webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -1,15 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="wrap_content">
<com.pitchedapps.frost.web.FrostWebView
android:id="@+id/frost_webview"
<com.pitchedapps.frost.web.FrostWebViewCore
android:id="@+id/frost_webview_core"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:focusableInTouchMode="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.v4.widget.SwipeRefreshLayout>
<ProgressBar
android:id="@+id/progressBar"
style="@style/FrostProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</FrameLayout>

View File

@ -8,5 +8,6 @@
<dimen name="fab_margin">16dp</dimen>
<dimen name="appbar_padding_top">8dp</dimen>
<dimen name="splash_logo">16dp</dimen>
<dimen name="progress_bar_height">1dip</dimen>
</resources>

View File

@ -11,6 +11,13 @@
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.Overlay" parent="AppTheme.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:activityOpenEnterAnimation">@anim/slide_in_right</item>
<item name="android:activityOpenExitAnimation">@anim/slide_out_right</item>
<item name="android:activityCloseEnterAnimation">@anim/slide_in_right</item>
<item name="android:activityCloseExitAnimation">@anim/slide_out_right</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
@ -20,4 +27,9 @@
<item name="colorPrimaryDark">@color/splashBackground</item>
<item name="colorAccent">@color/splashBackground</item>
</style>
<style name="FrostProgressBar" parent="Widget.AppCompat.ProgressBar.Horizontal">
<item name="android:minHeight">@dimen/progress_bar_height</item>
<item name="android:maxHeight">@dimen/progress_bar_height</item>
</style>
</resources>