aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt33
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt50
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/VideoActivity.kt33
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt20
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewerContract.kt54
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Animator.kt70
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt63
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt49
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt14
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt4
14 files changed, 279 insertions, 129 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt
index ddb42d72..ab08981f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/BaseActivity.kt
@@ -1,10 +1,12 @@
package com.pitchedapps.frost.activities
+import android.content.res.Configuration
import android.os.Bundle
import ca.allanwang.kau.internal.KauBaseActivity
import com.github.pwittchen.reactivenetwork.library.rx2.Connectivity
import com.github.pwittchen.reactivenetwork.library.rx2.ReactiveNetwork
import com.pitchedapps.frost.R
+import com.pitchedapps.frost.contracts.VideoViewerContract
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
import com.pitchedapps.frost.utils.materialDialogThemed
@@ -18,7 +20,10 @@ import io.reactivex.schedulers.Schedulers
*/
abstract class BaseActivity : KauBaseActivity() {
override fun onBackPressed() {
- if (isTaskRoot && Prefs.exitConfirmation) {
+ if (this is MainActivity && searchView?.onBackPressed() == true) return
+ if (this is VideoViewerContract && videoOnBackPress()) return
+ if (this is MainActivity && currentFragment.onBackPressed()) return
+ if (this !is WebOverlayActivityBase && isTaskRoot && Prefs.exitConfirmation) {
materialDialogThemed {
title(R.string.kau_exit)
content(R.string.kau_exit_confirmation)
@@ -27,12 +32,14 @@ abstract class BaseActivity : KauBaseActivity() {
onPositive { _, _ -> super.onBackPressed() }
checkBoxPromptRes(R.string.kau_do_not_show_again, false, { _, b -> Prefs.exitConfirmation = !b })
}
- } else super.onBackPressed()
+ return
+ }
+ super.onBackPressed()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setFrostTheme()
+ if (this !is WebOverlayActivityBase) setFrostTheme()
}
private var networkDisposable: Disposable? = null
@@ -47,8 +54,7 @@ abstract class BaseActivity : KauBaseActivity() {
networkDisposable = ReactiveNetwork.observeNetworkConnectivity(applicationContext)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe {
- connectivity: Connectivity ->
+ .subscribe { connectivity: Connectivity ->
connectivity.apply {
L.d("Network connectivity changed: isAvailable: $isAvailable isRoaming: $isRoaming")
consumer(connectivity)
@@ -64,12 +70,23 @@ abstract class BaseActivity : KauBaseActivity() {
override fun onResume() {
super.onResume()
- disposeNetworkConnectivity()
- observeNetworkConnectivity()
+// disposeNetworkConnectivity()
+// observeNetworkConnectivity()
}
override fun onPause() {
super.onPause()
- disposeNetworkConnectivity()
+// disposeNetworkConnectivity()
+ }
+
+
+ override fun onStop() {
+ if (this is VideoViewerContract) videoOnStop()
+ super.onStop()
+ }
+
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ if (this is VideoViewerContract) videoViewer?.updateLocation()
}
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
index 2f4bd2e1..d4c30547 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
@@ -5,7 +5,6 @@ import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
-import android.content.res.Configuration
import android.graphics.PointF
import android.graphics.drawable.ColorDrawable
import android.net.Uri
@@ -48,6 +47,7 @@ import com.pitchedapps.frost.R
import com.pitchedapps.frost.contracts.ActivityWebContract
import com.pitchedapps.frost.contracts.FileChooserContract
import com.pitchedapps.frost.contracts.FileChooserDelegate
+import com.pitchedapps.frost.contracts.VideoViewerContract
import com.pitchedapps.frost.dbflow.loadFbCookie
import com.pitchedapps.frost.dbflow.loadFbTabs
import com.pitchedapps.frost.enums.MainActivityLayout
@@ -63,7 +63,6 @@ import com.pitchedapps.frost.utils.iab.FrostBilling
import com.pitchedapps.frost.utils.iab.IS_FROST_PRO
import com.pitchedapps.frost.utils.iab.IabMain
import com.pitchedapps.frost.views.BadgedIcon
-import com.pitchedapps.frost.views.FrostVideoContainerContract
import com.pitchedapps.frost.views.FrostVideoViewer
import com.pitchedapps.frost.views.FrostViewPager
import io.reactivex.android.schedulers.AndroidSchedulers
@@ -77,18 +76,18 @@ import java.util.concurrent.TimeUnit
class MainActivity : BaseActivity(),
ActivityWebContract, FileChooserContract by FileChooserDelegate(),
- FrostVideoContainerContract,
+ VideoViewerContract,
FrostBilling by IabMain() {
lateinit var adapter: SectionsPagerAdapter
- val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper)
+ override val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper)
val toolbar: Toolbar by bindView(R.id.toolbar)
val viewPager: FrostViewPager by bindView(R.id.container)
val fab: FloatingActionButton by bindView(R.id.fab)
val tabs: TabLayout by bindView(R.id.tabs)
val appBar: AppBarLayout by bindView(R.id.appbar)
val coordinator: CoordinatorLayout by bindView(R.id.main_content)
- var videoViewer: FrostVideoViewer? = null
+ override var videoViewer: FrostVideoViewer? = null
lateinit var drawer: Drawer
lateinit var drawerHeader: AccountHeader
var webFragmentObservable = PublishSubject.create<Int>()!!
@@ -130,8 +129,7 @@ class MainActivity : BaseActivity(),
"Frost id" to Prefs.frostId)
}
}
- setContentView(R.layout.activity_frame_wrapper)
- frameWrapper.inflate(Prefs.mainActivityLayout.layoutRes, true)
+ setFrameContentView(Prefs.mainActivityLayout.layoutRes)
setSupportActionBar(toolbar)
adapter = SectionsPagerAdapter(supportFragmentManager, loadFbTabs())
viewPager.adapter = adapter
@@ -147,7 +145,7 @@ class MainActivity : BaseActivity(),
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
- val delta: Float by lazy { positionOffset * (255 - 128).toFloat() }
+ val delta = positionOffset * (255 - 128).toFloat()
tabsForEachView { tabPosition, view ->
view.setAllAlpha(when (tabPosition) {
position -> 255.0f - delta
@@ -169,14 +167,6 @@ class MainActivity : BaseActivity(),
onCreateBilling()
}
- fun showVideo(url: String) {
- if (videoViewer != null) {
- videoViewer?.setVideo(url)
- } else {
- videoViewer = FrostVideoViewer.showVideo(url, this)
- }
- }
-
fun tabsForEachView(action: (position: Int, view: BadgedIcon) -> Unit) {
(0 until tabs.tabCount).asSequence().forEach { i ->
action(i, tabs.getTabAt(i)!!.customView as BadgedIcon)
@@ -312,7 +302,7 @@ class MainActivity : BaseActivity(),
}
}
- fun Builder.primaryFrostItem(item: FbItem) = this.primaryItem(item.titleId) {
+ private fun Builder.primaryFrostItem(item: FbItem) = this.primaryItem(item.titleId) {
iicon = item.icon
iconColor = Prefs.textColor.toLong()
textColor = Prefs.textColor.toLong()
@@ -331,7 +321,7 @@ class MainActivity : BaseActivity(),
}
}
- fun Builder.secondaryFrostItem(@StringRes title: Int, onClick: () -> Unit) = this.secondaryItem(title) {
+ private fun Builder.secondaryFrostItem(@StringRes title: Int, onClick: () -> Unit) = this.secondaryItem(title) {
textColor = Prefs.textColor.toLong()
selectedIconColor = Prefs.textColor.toLong()
selectedTextColor = Prefs.textColor.toLong()
@@ -436,23 +426,11 @@ class MainActivity : BaseActivity(),
super.onStart()
}
- override fun onStop() {
- videoViewer?.pause()
- super.onStop()
- }
-
override fun onDestroy() {
onDestroyBilling()
super.onDestroy()
}
- override fun onBackPressed() {
- if (videoViewer?.onBackPressed() == true) return
- if (searchView?.onBackPressed() == true) return
- if (currentFragment.onBackPressed()) return
- super.onBackPressed()
- }
-
inline val currentFragment
get() = supportFragmentManager.findFragmentByTag("android:switcher:${R.id.container}:${viewPager.currentItem}") as WebFragment
@@ -489,16 +467,4 @@ class MainActivity : BaseActivity(),
else
PointF(0f, 0f)
- override val videoContainer: FrameLayout
- get() = frameWrapper
-
- override fun onVideoFinished() {
- L.d("Video view released")
- videoViewer = null
- }
-
- override fun onConfigurationChanged(newConfig: Configuration) {
- super.onConfigurationChanged(newConfig)
- videoViewer?.updateLocation()
- }
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/VideoActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/VideoActivity.kt
deleted file mode 100644
index 5943c73c..00000000
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/VideoActivity.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.pitchedapps.frost.activities
-
-import android.os.Bundle
-import android.view.ViewGroup
-import ca.allanwang.kau.internal.KauBaseActivity
-import ca.allanwang.kau.utils.bindView
-import com.pitchedapps.frost.R
-import com.pitchedapps.frost.utils.L
-import com.pitchedapps.frost.views.FrostVideoView
-
-/**
- * Created by Allan Wang on 2017-06-01.
- */
-class VideoActivity : KauBaseActivity() {
-
- val container: ViewGroup by bindView(R.id.video_container)
- val video: FrostVideoView by bindView(R.id.video)
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.view_video)
- container.setOnTouchListener { _, event ->
- val y = video.shouldParentAcceptTouch(event)
- L.d("Video SPAT $y")
- y
- }
- }
-
- override fun onStop() {
- video.pause()
- super.onStop()
- }
-} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
index c2556563..e20cfbf2 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt
@@ -1,6 +1,7 @@
package com.pitchedapps.frost.activities
import android.content.Intent
+import android.graphics.PointF
import android.net.Uri
import android.os.Bundle
import android.support.design.widget.CoordinatorLayout
@@ -10,6 +11,7 @@ import android.view.Menu
import android.view.MenuItem
import android.webkit.ValueCallback
import android.webkit.WebChromeClient
+import android.widget.FrameLayout
import ca.allanwang.kau.internal.KauBaseActivity
import ca.allanwang.kau.swipe.kauSwipeOnCreate
import ca.allanwang.kau.swipe.kauSwipeOnDestroy
@@ -20,9 +22,11 @@ import com.pitchedapps.frost.R
import com.pitchedapps.frost.contracts.ActivityWebContract
import com.pitchedapps.frost.contracts.FileChooserContract
import com.pitchedapps.frost.contracts.FileChooserDelegate
+import com.pitchedapps.frost.contracts.VideoViewerContract
import com.pitchedapps.frost.enums.OverlayContext
import com.pitchedapps.frost.facebook.*
import com.pitchedapps.frost.utils.*
+import com.pitchedapps.frost.views.FrostVideoViewer
import com.pitchedapps.frost.web.FrostWebView
import io.reactivex.disposables.Disposable
import okhttp3.HttpUrl
@@ -95,9 +99,10 @@ class WebOverlayBasicActivity : WebOverlayActivityBase(true)
*/
class WebOverlayActivity : WebOverlayActivityBase(false)
-open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : KauBaseActivity(),
- ActivityWebContract, FileChooserContract by FileChooserDelegate() {
+open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : BaseActivity(),
+ ActivityWebContract, VideoViewerContract, FileChooserContract by FileChooserDelegate() {
+ override val frameWrapper: FrameLayout by bindView(R.id.frame_wrapper)
val toolbar: Toolbar by bindView(R.id.overlay_toolbar)
val frostWeb: FrostWebView by bindView(R.id.overlay_frost_webview)
val coordinator: CoordinatorLayout by bindView(R.id.overlay_main_content)
@@ -122,7 +127,7 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : KauBas
finish()
return
}
- setContentView(R.layout.activity_web_overlay)
+ setFrameContentView(R.layout.activity_web_overlay)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayShowHomeEnabled(true)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
@@ -219,4 +224,13 @@ open class WebOverlayActivityBase(private val forceBasicAgent: Boolean) : KauBas
}
return true
}
+
+ /*
+ * ----------------------------------------------------
+ * Video Contract
+ * ----------------------------------------------------
+ */
+ override var videoViewer: FrostVideoViewer? = null
+ override val lowerVideoPadding: PointF = PointF(0f, 0f)
+
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewerContract.kt b/app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewerContract.kt
new file mode 100644
index 00000000..2e6ad04f
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/contracts/VideoViewerContract.kt
@@ -0,0 +1,54 @@
+package com.pitchedapps.frost.contracts
+
+import android.app.Activity
+import android.widget.FrameLayout
+import ca.allanwang.kau.utils.inflate
+import com.pitchedapps.frost.R
+import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.views.FrostVideoContainerContract
+import com.pitchedapps.frost.views.FrostVideoViewer
+
+/**
+ * Created by Allan Wang on 2017-11-10.
+ */
+interface VideoViewerContract : FrameWrapper, FrostVideoContainerContract {
+
+ var videoViewer: FrostVideoViewer?
+
+ fun showVideo(url: String)
+ = showVideo(url, false)
+
+ /**
+ * Create new viewer and reuse existing one
+ * The url will be formatted upon loading
+ */
+ fun showVideo(url: String, repeat: Boolean) {
+ if (videoViewer != null)
+ videoViewer?.setVideo(url, repeat)
+ else
+ videoViewer = FrostVideoViewer.showVideo(url, repeat, this)
+ }
+
+ fun videoOnStop() = videoViewer?.pause()
+
+ fun videoOnBackPress() = videoViewer?.onBackPressed() ?: false
+
+ override val videoContainer: FrameLayout
+ get() = frameWrapper
+
+ override fun onVideoFinished() {
+ L.d("Video view released")
+ videoViewer = null
+ }
+}
+
+interface FrameWrapper {
+
+ val frameWrapper: FrameLayout
+
+ fun Activity.setFrameContentView(layoutRes: Int) {
+ setContentView(R.layout.activity_frame_wrapper)
+ frameWrapper.inflate(layoutRes, true)
+ }
+
+}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
index 65fae493..64f0b652 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbConst.kt
@@ -5,7 +5,7 @@ package com.pitchedapps.frost.facebook
*/
const val HTTPS_FACEBOOK_COM = "https://facebook.com"
const val FACEBOOK_COM = "facebook.com"
-const val FB_URL_BASE = "https://m.facebook.com/"
+const val FB_URL_BASE = "https://m.$FACEBOOK_COM/"
fun PROFILE_PICTURE_URL(id: Long) = "https://graph.facebook.com/$id/picture?type=large"
const val USER_AGENT_FULL = "Mozilla/5.0 (Linux; Android 4.4.2; en-us; SAMSUNG SM-G900T Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.6 Chrome/28.0.1500.94 Mobile Safari/537.36"
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
index 22fc275f..0e74bb59 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
@@ -31,7 +31,7 @@ class FbUrlFormatter(url: String) {
var cleanedUrl = url
discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "", true) }
converter.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
- if (cleanedUrl != url) cleanedUrl = cleanedUrl.replaceFirst("&", "?")
+ if (cleanedUrl != url && !cleanedUrl.contains("?")) cleanedUrl = cleanedUrl.replaceFirst("&", "?")
cleanedUrl = URLDecoder.decode(cleanedUrl, StandardCharsets.UTF_8.name())
val qm = cleanedUrl.indexOf("?")
if (qm > -1) {
@@ -70,6 +70,8 @@ class FbUrlFormatter(url: String) {
}
companion object {
+
+ const val VIDEO_REDIRECT = "/video_redirect/?src="
/**
* Items here are explicitly removed from the url
* Taken from FaceSlim
@@ -82,7 +84,7 @@ class FbUrlFormatter(url: String) {
"https://m.facebook.com/l.php?u=",
"http://touch.facebook.com/l.php?u=",
"https://touch.facebook.com/l.php?u=",
- "/video_redirect/?src="
+ VIDEO_REDIRECT
)
val misc = arrayOf("&amp;" to "&")
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Animator.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Animator.kt
new file mode 100644
index 00000000..da852e6e
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Animator.kt
@@ -0,0 +1,70 @@
+package com.pitchedapps.frost.utils
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.view.animation.Interpolator
+
+/**
+ * Created by Allan Wang on 2017-11-10.
+ */
+class ProgressAnimator private constructor(private vararg val values: Float) {
+
+ companion object {
+ inline fun ofFloat(crossinline builder: ProgressAnimator.() -> Unit) = ofFloat(0f, 1f) { builder() }
+
+ fun ofFloat(vararg values: Float, builder: ProgressAnimator.() -> Unit) = ProgressAnimator(*values).apply {
+ builder()
+ build()
+ }
+ }
+
+ private val animators: MutableList<(Float) -> Unit> = mutableListOf()
+ private val startActions: MutableList<() -> Unit> = mutableListOf()
+ private val endActions: MutableList<() -> Unit> = mutableListOf()
+
+ var duration: Long = -1L
+ var interpolator: Interpolator? = null
+
+ /**
+ * Add more changes to the [ValueAnimator] before running
+ */
+ var extraConfigs: ValueAnimator.() -> Unit = {}
+
+ fun withAnimator(from: Float, to: Float, animator: (Float) -> Unit) = animators.add {
+ val range = to - from
+ animator(range * it + from)
+ }
+
+ fun withAnimator(animator: (Float) -> Unit) = animators.add(animator)
+
+ fun withAnimatorInv(animator: (Float) -> Unit) = animators.add { animator(1f - it) }
+
+ fun withStartAction(action: () -> Unit) = startActions.add(action)
+
+ fun withEndAction(action: () -> Unit) = endActions.add(action)
+
+ fun build() {
+ ValueAnimator.ofFloat(*values).apply {
+ if (this@ProgressAnimator.duration > 0L)
+ duration = this@ProgressAnimator.duration
+ if (this@ProgressAnimator.interpolator != null)
+ interpolator = this@ProgressAnimator.interpolator
+ addUpdateListener {
+ val progress = it.animatedValue as Float
+ animators.forEach { it(progress) }
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?) {
+ startActions.forEach { it() }
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ endActions.forEach { it() }
+ }
+ })
+ extraConfigs()
+ start()
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
index 6d6c5381..22c77f5f 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
@@ -30,6 +30,7 @@ import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.*
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.*
+import com.pitchedapps.frost.facebook.FbUrlFormatter.Companion.VIDEO_REDIRECT
import com.pitchedapps.frost.utils.iab.IS_FROST_PRO
import org.jsoup.Jsoup
import org.jsoup.nodes.Element
@@ -200,6 +201,9 @@ fun Context.resolveActivityForUri(uri: Uri): Boolean {
inline val String?.isFacebookUrl
get() = this != null && this.contains(FACEBOOK_COM)
+inline val String?.isVideoUrl
+ get() = this != null && this.startsWith(VIDEO_REDIRECT)
+
fun Context.frostChangelog() = showChangelog(R.xml.frost_changelog, Prefs.textColor) {
theme()
if (System.currentTimeMillis() - Prefs.installDate > 2592000000) { //show after 1 month
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt
index c5508a4d..639dc9ba 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt
@@ -8,10 +8,12 @@ import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
+import ca.allanwang.kau.utils.AnimHolder
import ca.allanwang.kau.utils.dpToPx
import ca.allanwang.kau.utils.scaleXY
import com.devbrackets.android.exomedia.ui.widget.VideoView
import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.utils.ProgressAnimator
/**
* Created by Allan Wang on 2017-10-13.
@@ -29,10 +31,10 @@ class FrostVideoView @JvmOverloads constructor(
private inline val v
get() = videoViewImpl
- var backgroundView: View? = null
var onFinishedListener: () -> Unit = {}
- lateinit var viewerContract: FrostVideoViewerContract
+ private lateinit var viewerContract: FrostVideoViewerContract
lateinit var containerContract: FrostVideoContainerContract
+ var repeat: Boolean = false
private val videoDimensions = PointF(0f, 0f)
@@ -47,8 +49,8 @@ class FrostVideoView @JvmOverloads constructor(
private val SWIPE_TO_CLOSE_HORIZONTAL_THRESHOLD = 2f.dpToPx
private val SWIPE_TO_CLOSE_VERTICAL_THRESHOLD = 5f.dpToPx
private val SWIPE_TO_CLOSE_OFFSET_THRESHOLD = 75f.dpToPx
- val ANIMATION_DURATION = 300L
- private val FAST_ANIMATION_DURATION = 100L
+ const val ANIMATION_DURATION = 200L
+ private const val FAST_ANIMATION_DURATION = 100L
}
private var videoBounds = RectF()
@@ -59,19 +61,32 @@ class FrostVideoView @JvmOverloads constructor(
if (videoDimensions.x <= 0f || videoDimensions.y <= 0f)
return L.d("Attempted to toggle video expansion when points have not been finalized")
field = value
+ val origX = translationX
+ val origY = translationY
+ val origScale = scaleX
if (field) {
- animate().scaleXY(1f).translationX(0f).translationY(0f).setDuration(ANIMATION_DURATION).withStartAction {
- backgroundView?.animate()?.alpha(1f)?.setDuration(ANIMATION_DURATION)
- viewerContract.onFade(1f, ANIMATION_DURATION)
- }.withEndAction {
- if (!isPlaying) showControls()
+ ProgressAnimator.ofFloat {
+ duration = ANIMATION_DURATION
+ interpolator = AnimHolder.fastOutSlowInInterpolator(context)
+ withAnimator { viewerContract.onExpand(it) }
+ withAnimator(origScale, 1f) { scaleXY = it }
+ withAnimator(origX, 0f) { translationX = it }
+ withAnimator(origY, 0f) { translationY = it }
+ withEndAction {
+ if (!isPlaying) showControls()
+ else viewerContract.onControlsHidden()
+ }
}
} else {
hideControls()
val (scale, tX, tY) = mapBounds()
- animate().scaleXY(scale).translationX(tX).translationY(tY).setDuration(ANIMATION_DURATION).withStartAction {
- backgroundView?.animate()?.alpha(0f)?.setDuration(ANIMATION_DURATION)
- viewerContract.onFade(0f, ANIMATION_DURATION)
+ ProgressAnimator.ofFloat {
+ duration = ANIMATION_DURATION
+ interpolator = AnimHolder.fastOutSlowInInterpolator(context)
+ withAnimatorInv { viewerContract.onExpand(it) }
+ withAnimator(origScale, scale) { scaleXY = it }
+ withAnimator(origX, tX) { translationX = it }
+ withAnimator(origY, tY) { translationY = it }
}
}
}
@@ -110,16 +125,28 @@ class FrostVideoView @JvmOverloads constructor(
if (isExpanded) showControls()
}
setOnCompletionListener {
- viewerContract.onVideoComplete()
+ if (repeat) restart()
+ else viewerContract.onVideoComplete()
}
setOnTouchListener(FrameTouchListener(context))
v.setOnTouchListener(VideoTouchListener(context))
setOnVideoSizedChangedListener { intrinsicWidth, intrinsicHeight ->
val ratio = Math.min(width.toFloat() / intrinsicWidth, height.toFloat() / intrinsicHeight.toFloat())
+ /**
+ * Only remap if not expanded and if dimensions have changed
+ */
+ val shouldRemap = !isExpanded
+ && (videoDimensions.x != ratio * intrinsicWidth || videoDimensions.y != ratio * intrinsicHeight)
videoDimensions.set(ratio * intrinsicWidth, ratio * intrinsicHeight)
+ if (shouldRemap) updateLocation()
}
}
+ fun setViewerContract(contract: FrostVideoViewerContract) {
+ this.viewerContract = contract
+ videoControls?.setVisibilityListener(viewerContract)
+ }
+
fun jumpToStart() {
pause()
v.seekTo(0)
@@ -136,7 +163,7 @@ class FrostVideoView @JvmOverloads constructor(
override fun restart(): Boolean {
videoUri ?: return false
- if (videoViewImpl.restart() && isExpanded) {
+ if (videoViewImpl.restart() && isExpanded && !repeat) {
videoControls?.showLoading(true)
return true
}
@@ -163,9 +190,11 @@ class FrostVideoView @JvmOverloads constructor(
fun destroy() {
stopPlayback()
if (alpha > 0f)
- animate().alpha(0f).setDuration(FAST_ANIMATION_DURATION).withEndAction { onFinishedListener() }.withStartAction {
- viewerContract.onFade(0f, FAST_ANIMATION_DURATION)
- }.start()
+ ProgressAnimator.ofFloat(alpha, 0f) {
+ duration = FAST_ANIMATION_DURATION
+ withAnimator { alpha = it }
+ withEndAction { onFinishedListener() }
+ }
else
onFinishedListener()
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
index bf4df8fe..3a773288 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
@@ -13,6 +13,7 @@ import android.view.ViewTreeObserver
import android.widget.FrameLayout
import android.widget.ImageView
import ca.allanwang.kau.utils.*
+import com.devbrackets.android.exomedia.listener.VideoControlsVisibilityListener
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.formattedFbUrl
@@ -35,17 +36,21 @@ class FrostVideoViewer @JvmOverloads constructor(
companion object {
/**
+ * Matches VideoControls.CONTROL_VISIBILITY_ANIMATION_LENGTH
+ */
+ private const val CONTROL_ANIMATION_DURATION = 300L
+
+ /**
* Simplified binding to add video to layout, and remove it when finished
* This is under the assumption that the container allows for overlays,
* such as a FrameLayout
*/
- fun showVideo(url: String, contract: FrostVideoContainerContract): FrostVideoViewer {
+ fun showVideo(url: String, repeat: Boolean, contract: FrostVideoContainerContract): FrostVideoViewer {
val container = contract.videoContainer
val videoViewer = FrostVideoViewer(container.context)
container.addView(videoViewer)
videoViewer.bringToFront()
- L.d("Create video view", url)
- videoViewer.setVideo(url)
+ videoViewer.setVideo(url, repeat)
videoViewer.video.containerContract = contract
videoViewer.video.onFinishedListener = { container.removeView(videoViewer); contract.onVideoFinished() }
return videoViewer
@@ -56,11 +61,9 @@ class FrostVideoViewer @JvmOverloads constructor(
inflate(R.layout.view_video, true)
alpha = 0f
background.setBackgroundColor(if (Prefs.bgColor.isColorDark) Prefs.bgColor.withMinAlpha(200) else Color.BLACK)
- video.backgroundView = background
- video.viewerContract = this
+ video.setViewerContract(this)
video.pause()
toolbar.inflateMenu(R.menu.menu_video)
- toolbar.setBackgroundColor(Prefs.headerColor)
context.setMenuIcons(toolbar.menu, Prefs.iconColor,
R.id.action_pip to GoogleMaterial.Icon.gmd_picture_in_picture_alt,
R.id.action_download to GoogleMaterial.Icon.gmd_file_download
@@ -77,12 +80,14 @@ class FrostVideoViewer @JvmOverloads constructor(
video.restart()
restarter.fadeOut { restarter.gone() }
}
-// toolbar.setOnTouchListener { _, event -> video.shouldParentAcceptTouch(event) }
}
- fun setVideo(url: String) {
+ fun setVideo(url: String, repeat: Boolean = false) {
+ val formattedUrl = url.formattedFbUrl
+ L.d("Load video view; repeat: $repeat", url)
animate().alpha(1f).setDuration(FrostVideoView.ANIMATION_DURATION).start()
- video.setVideoURI(Uri.parse(url.formattedFbUrl))
+ video.setVideoURI(Uri.parse(formattedUrl))
+ video.repeat = repeat
}
/**
@@ -106,10 +111,9 @@ class FrostVideoViewer @JvmOverloads constructor(
* -------------------------------------------------------------
*/
- override fun onFade(alpha: Float, duration: Long) {
- toolbar.visible().animate().alpha(alpha).setDuration(duration).withEndAction {
- if (alpha == 0f) toolbar.gone()
- }
+ override fun onExpand(progress: Float) {
+ toolbar.goneIf(progress == 0f).alpha = progress
+ background.alpha = progress
}
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
@@ -134,11 +138,26 @@ class FrostVideoViewer @JvmOverloads constructor(
})
}
+ override fun onControlsShown() {
+ if (video.isExpanded)
+ toolbar.fadeIn(duration = CONTROL_ANIMATION_DURATION, onStart = { toolbar.visible() })
+ }
+
+ override fun onControlsHidden() {
+ if (!toolbar.isGone)
+ toolbar.fadeOut(duration = CONTROL_ANIMATION_DURATION) { toolbar.gone() }
+ }
+
}
-interface FrostVideoViewerContract {
+interface FrostVideoViewerContract : VideoControlsVisibilityListener {
fun onSingleTapConfirmed(event: MotionEvent): Boolean
- fun onFade(alpha: Float, duration: Long)
+ /**
+ * Process of expansion
+ * 1f represents an expanded view, 0f represents a minimized view
+ */
+ fun onExpand(progress: Float)
+
fun onVideoComplete()
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
index 4700c894..71ceb4ca 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostJSI.kt
@@ -4,6 +4,7 @@ import android.content.Context
import android.support.v4.widget.SwipeRefreshLayout
import android.webkit.JavascriptInterface
import com.pitchedapps.frost.activities.MainActivity
+import com.pitchedapps.frost.contracts.VideoViewerContract
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.utils.*
@@ -36,9 +37,10 @@ class FrostJSI(val webView: FrostWebViewCore) {
= if (url == null) false else webView.requestWebOverlay(url)
@JavascriptInterface
- fun loadVideo(url: String?) {
+ fun loadVideo(url: String?, isGif: Boolean) {
if (url != null)
- webView.post { activity?.showVideo(url) }
+ webView.post { (context as? VideoViewerContract)?.showVideo(url, isGif)
+ ?: L.d("Could not load video; contract not implemented") }
}
@JavascriptInterface
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt
index ac5cde29..334ef51b 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt
@@ -3,13 +3,12 @@ package com.pitchedapps.frost.web
import com.pitchedapps.frost.activities.WebOverlayActivity
import com.pitchedapps.frost.activities.WebOverlayActivityBase
import com.pitchedapps.frost.activities.WebOverlayBasicActivity
+import com.pitchedapps.frost.contracts.VideoViewerContract
import com.pitchedapps.frost.facebook.FB_URL_BASE
import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
import com.pitchedapps.frost.facebook.formattedFbUrl
-import com.pitchedapps.frost.utils.L
-import com.pitchedapps.frost.utils.isFacebookUrl
-import com.pitchedapps.frost.utils.launchWebOverlay
+import com.pitchedapps.frost.utils.*
/**
* Created by Allan Wang on 2017-08-15.
@@ -26,6 +25,12 @@ import com.pitchedapps.frost.utils.launchWebOverlay
*/
fun FrostWebViewCore.requestWebOverlay(url: String): Boolean {
if (url == "#") return false
+ if (url.isVideoUrl && context is VideoViewerContract) {
+ L.i("Found video", url)
+ (context as VideoViewerContract).showVideo(url)
+ return true
+ }
+ if (!Prefs.overlayEnabled) return false
if (context is WebOverlayActivityBase) {
L.v("Check web request from overlay", url)
//already overlay; manage user agent
@@ -73,7 +78,8 @@ fun FrostWebViewCore.requestWebOverlay(url: String): Boolean {
val messageWhitelist = setOf(FbItem.MESSAGES, FbItem.CHAT, FbItem.FEED_MOST_RECENT, FbItem.FEED_TOP_STORIES).map { it.url }.toSet()
val String.shouldUseBasicAgent
- get() = (messageWhitelist.any { contains(it) }) || this == FB_URL_BASE
+ get() = !contains("story.php") //we will use basic agent for anything that isn't a comment section
+// get() = (messageWhitelist.any { contains(it) }) || this == FB_URL_BASE
/**
* The following components should never be launched in a new overlay
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
index e3803134..c8c7e2e7 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
@@ -106,11 +106,11 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
injectBackgroundColor()
webCore.jsInject(
JsActions.LOGIN_CHECK,
- JsAssets.CLICK_A.maybe(Prefs.overlayEnabled),
+ JsAssets.CLICK_A,
JsAssets.TEXTAREA_LISTENER,
CssHider.ADS.maybe(!Prefs.showFacebookAds && IS_FROST_PRO),
JsAssets.CONTEXT_A,
- JsAssets.MEDIA.maybe(webCore.baseEnum != null),
+ JsAssets.MEDIA,
JsAssets.HEADER_BADGES.maybe(webCore.baseEnum != null)
)
}