mirror of
https://github.com/AllanWang/Frost-for-Facebook.git
synced 2024-11-08 20:12:39 +01:00
Enhancement/video (#484)
* Fix more parsing issues * Try catch decoder resolves #456 * Fix unit test and add null check for images, resolves #458 * Remove downloadservice, resolves #459 * Clean up progress animator * Check for download manager before download attempt * Update strings
This commit is contained in:
parent
4aed05a892
commit
63d8779ad4
@ -54,7 +54,8 @@ android {
|
||||
disable 'TrustAllX509TrustManager',
|
||||
'UnusedResources',
|
||||
'ContentDescription',
|
||||
'RtlSymmetry'
|
||||
'RtlSymmetry',
|
||||
'MissingTranslation'
|
||||
|
||||
xmlReport false
|
||||
textReport true
|
||||
|
@ -145,11 +145,6 @@
|
||||
android:enabled="true"
|
||||
android:label="@string/frost_notifications"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||
<service
|
||||
android:name=".services.DownloadService"
|
||||
android:enabled="true"
|
||||
android:label="@string/frost_notifications"
|
||||
android:permission="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<receiver
|
||||
android:name=".services.UpdateReceiver"
|
||||
|
@ -25,6 +25,8 @@ if (!window.hasOwnProperty('frost_click_a')) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
} else {
|
||||
console.log('Click Intercept Prevented');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
app/src/main/assets/js/click_a.min.js
vendored
2
app/src/main/assets/js/click_a.min.js
vendored
@ -4,7 +4,7 @@ window.frost_click_a=!0
|
||||
;var prevented=!1,_frostAClick=function(e){
|
||||
var t=e.target||e.srcElement
|
||||
;if("A"!==t.tagName&&(t=t.parentNode),"A"!==t.tagName&&(t=t.parentNode),
|
||||
"A"===t.tagName&&!prevented){
|
||||
"A"===t.tagName)if(prevented)console.log("Click Intercept Prevented");else{
|
||||
var o=t.getAttribute("href")
|
||||
;console.log("Click Intercept",o),"undefined"!=typeof Frost&&Frost.loadUrl(o)&&(e.stopPropagation(),
|
||||
e.preventDefault())
|
||||
|
@ -76,6 +76,7 @@ class ImageActivity : KauBaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
intent?.extras ?: return finish()
|
||||
L.i("Displaying image", imageUrl)
|
||||
val layout = if (!text.isNullOrBlank()) R.layout.activity_image else R.layout.activity_image_textless
|
||||
setContentView(layout)
|
||||
|
@ -26,30 +26,39 @@ class FbUrlFormatter(url: String) {
|
||||
* 4. Url is split into sections
|
||||
*/
|
||||
init {
|
||||
if (url.isBlank()) cleaned = ""
|
||||
else {
|
||||
var cleanedUrl = url
|
||||
discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "", true) }
|
||||
converter.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
|
||||
if (cleanedUrl != url && !cleanedUrl.contains("?")) cleanedUrl = cleanedUrl.replaceFirst("&", "?")
|
||||
cleaned = clean(url)
|
||||
}
|
||||
|
||||
fun clean(url: String): String {
|
||||
if (url.isBlank()) return ""
|
||||
var cleanedUrl = url
|
||||
discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "", true) }
|
||||
val changed = cleanedUrl != url
|
||||
converter.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
|
||||
try {
|
||||
cleanedUrl = URLDecoder.decode(cleanedUrl, StandardCharsets.UTF_8.name())
|
||||
val qm = cleanedUrl.indexOf("?")
|
||||
if (qm > -1) {
|
||||
cleanedUrl.substring(qm + 1).split("&").forEach {
|
||||
val p = it.split("=")
|
||||
queries.put(p[0], p.elementAtOrNull(1) ?: "")
|
||||
}
|
||||
cleanedUrl = cleanedUrl.substring(0, qm)
|
||||
}
|
||||
discardableQueries.forEach { queries.remove(it) }
|
||||
//final cleanup
|
||||
misc.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
|
||||
if (cleanedUrl.startsWith("#!")) cleanedUrl = cleanedUrl.substring(2)
|
||||
if (cleanedUrl.startsWith("/")) cleanedUrl = FB_URL_BASE + cleanedUrl.substring(1)
|
||||
cleanedUrl = cleanedUrl.replaceFirst(".facebook.com//", ".facebook.com/") //sometimes we are given a bad url
|
||||
L.v(null, "Formatted url from $url to $cleanedUrl")
|
||||
cleaned = cleanedUrl
|
||||
} catch (e: Exception) {
|
||||
L.e(e, "Failed url formatting")
|
||||
return url
|
||||
}
|
||||
if (changed && !cleanedUrl.contains("?")) //ensure we aren't missing '?'
|
||||
cleanedUrl = cleanedUrl.replaceFirst("&", "?")
|
||||
val qm = cleanedUrl.indexOf("?")
|
||||
if (qm > -1) {
|
||||
cleanedUrl.substring(qm + 1).split("&").forEach {
|
||||
val p = it.split("=")
|
||||
queries.put(p[0], p.elementAtOrNull(1) ?: "")
|
||||
}
|
||||
cleanedUrl = cleanedUrl.substring(0, qm)
|
||||
}
|
||||
discardableQueries.forEach { queries.remove(it) }
|
||||
//final cleanup
|
||||
misc.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
|
||||
if (cleanedUrl.startsWith("#!")) cleanedUrl = cleanedUrl.substring(2)
|
||||
if (cleanedUrl.startsWith("/")) cleanedUrl = FB_URL_BASE + cleanedUrl.substring(1)
|
||||
cleanedUrl = cleanedUrl.replaceFirst(".facebook.com//", ".facebook.com/") //sometimes we are given a bad url
|
||||
L.v(null, "Formatted url from $url to $cleanedUrl")
|
||||
return cleanedUrl
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@ -76,6 +85,10 @@ class FbUrlFormatter(url: String) {
|
||||
* Items here are explicitly removed from the url
|
||||
* Taken from FaceSlim
|
||||
* https://github.com/indywidualny/FaceSlim/blob/master/app/src/main/java/org/indywidualni/fblite/util/Miscellany.java
|
||||
*
|
||||
* Note: Typically, in this case, the redirect url should have all the necessary queries
|
||||
* I am unsure how Facebook reacts in all cases, so the ones after the redirect are appended on afterwards
|
||||
* That shouldn't break anything
|
||||
*/
|
||||
val discardable = arrayOf(
|
||||
"http://lm.facebook.com/l.php?u=",
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.pitchedapps.frost.services
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.IntentService
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
@ -26,11 +27,14 @@ import java.io.File
|
||||
/**
|
||||
* Created by Allan Wang on 2017-08-08.
|
||||
*
|
||||
* Not in use
|
||||
*
|
||||
* Background file downloader
|
||||
* All we are given is a link and a mime type
|
||||
*
|
||||
* With reference to the <a href="https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java">OkHttp3 sample</a>
|
||||
*/
|
||||
@SuppressLint("Registered")
|
||||
class DownloadService : IntentService("FrostVideoDownloader") {
|
||||
|
||||
companion object {
|
||||
|
@ -1,70 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -8,10 +8,15 @@ import android.os.Environment
|
||||
import android.webkit.URLUtil
|
||||
import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE
|
||||
import ca.allanwang.kau.permissions.kauRequestPermissions
|
||||
import ca.allanwang.kau.utils.isAppEnabled
|
||||
import ca.allanwang.kau.utils.string
|
||||
import com.pitchedapps.frost.R
|
||||
import com.pitchedapps.frost.dbflow.loadFbCookie
|
||||
import com.pitchedapps.frost.facebook.USER_AGENT_BASIC
|
||||
import android.support.v4.content.ContextCompat.startActivity
|
||||
import android.content.Intent
|
||||
import android.content.ActivityNotFoundException
|
||||
import ca.allanwang.kau.utils.showAppInfo
|
||||
|
||||
|
||||
/**
|
||||
@ -37,6 +42,16 @@ fun Context.frostDownload(uri: Uri?,
|
||||
L.d("Received download request", "Download $uri")
|
||||
if (uri.scheme != "http" && uri.scheme != "https")
|
||||
return L.e("Invalid download attempt", uri.toString())
|
||||
if (!isAppEnabled(DOWNLOAD_MANAGER_PACKAGE)) {
|
||||
materialDialogThemed {
|
||||
title(R.string.no_download_manager)
|
||||
content(R.string.no_download_manager_desc)
|
||||
positiveText(R.string.kau_yes)
|
||||
onPositive { _, _ -> showAppInfo(DOWNLOAD_MANAGER_PACKAGE) }
|
||||
negativeText(R.string.kau_no)
|
||||
}
|
||||
return
|
||||
}
|
||||
kauRequestPermissions(PERMISSION_WRITE_EXTERNAL_STORAGE) { granted, _ ->
|
||||
if (!granted) return@kauRequestPermissions
|
||||
val request = DownloadManager.Request(uri)
|
||||
@ -53,4 +68,6 @@ fun Context.frostDownload(uri: Uri?,
|
||||
val dm = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
|
||||
dm.enqueue(request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val DOWNLOAD_MANAGER_PACKAGE = "com.android.providers.downloads"
|
@ -202,7 +202,7 @@ inline val String?.isFacebookUrl
|
||||
get() = this != null && this.contains(FACEBOOK_COM)
|
||||
|
||||
inline val String?.isVideoUrl
|
||||
get() = this != null && this.startsWith(VIDEO_REDIRECT)
|
||||
get() = this != null && (this.startsWith(VIDEO_REDIRECT) || this.startsWith("https://video-"))
|
||||
|
||||
fun Context.frostChangelog() = showChangelog(R.xml.frost_changelog, Prefs.textColor) {
|
||||
theme()
|
||||
|
@ -8,12 +8,12 @@ import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import ca.allanwang.kau.ui.ProgressAnimator
|
||||
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.
|
||||
@ -83,7 +83,7 @@ class FrostVideoView @JvmOverloads constructor(
|
||||
ProgressAnimator.ofFloat {
|
||||
duration = ANIMATION_DURATION
|
||||
interpolator = AnimHolder.fastOutSlowInInterpolator(context)
|
||||
withAnimatorInv { viewerContract.onExpand(it) }
|
||||
withAnimator { viewerContract.onExpand(1f - it) }
|
||||
withAnimator(origScale, scale) { scaleXY = it }
|
||||
withAnimator(origX, tX) { translationX = it }
|
||||
withAnimator(origY, tY) { translationY = it }
|
||||
|
@ -3,11 +3,13 @@ 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.VideoViewHolder
|
||||
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.*
|
||||
import org.jetbrains.anko.runOnUiThread
|
||||
|
||||
/**
|
||||
* Created by Allan Wang on 2017-08-15.
|
||||
@ -18,6 +20,9 @@ import com.pitchedapps.frost.utils.*
|
||||
* This helper method will collect all known cases and launch the overlay accordingly
|
||||
* Returns {@code true} (default) if action is consumed, {@code false} otherwise
|
||||
*
|
||||
* Note that this is not always called on the main thread!
|
||||
* UI related methods should always be posted or they may not be properly executed.
|
||||
*
|
||||
* If the request already comes from an instance of [WebOverlayActivity], we will then judge
|
||||
* whether the user agent string should be changed. All propagated results will return false,
|
||||
* as we have no need of sending a new intent to the same activity
|
||||
@ -26,7 +31,7 @@ fun FrostWebViewCore.requestWebOverlay(url: String): Boolean {
|
||||
if (url == "#") return false
|
||||
if (url.isVideoUrl && context is VideoViewHolder) {
|
||||
L.i("Found video", url)
|
||||
(context as VideoViewHolder).showVideo(url)
|
||||
context.runOnUiThread { (context as VideoViewHolder).showVideo(url) }
|
||||
return true
|
||||
}
|
||||
if (!Prefs.overlayEnabled) return false
|
||||
|
@ -4,4 +4,6 @@
|
||||
<string name="bad_image_overlay">The url could not be loaded properly. Would you like to send it for debugging?</string>
|
||||
<string name="invalid_share_url">Invalid Share Url</string>
|
||||
<string name="invalid_share_url_desc">You have shared a block of text that is not a url. The text has been copied to your clipboard, so you may share it manually yourself.</string>
|
||||
<string name="no_download_manager">No Download Manager</string>
|
||||
<string name="no_download_manager_desc">The download manager is not enabled. Would you like to enable it to allow downloads?</string>
|
||||
</resources>
|
@ -9,7 +9,8 @@ import kotlin.test.assertEquals
|
||||
*/
|
||||
class FbUrlTest {
|
||||
|
||||
fun assertFbFormat(expected: String, url: String) {
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun assertFbFormat(expected: String, url: String) {
|
||||
val fbUrl = FbUrlFormatter(url)
|
||||
assertEquals(expected, fbUrl.toString(), "FbUrl Mismatch:\n${fbUrl.toLogList().joinToString("\n\t")}")
|
||||
}
|
||||
@ -52,9 +53,8 @@ class FbUrlTest {
|
||||
fun video() {
|
||||
//note that the video numbers have been changed to maintain privacy
|
||||
val url = "/video_redirect/?src=https%3A%2F%2Fvideo-yyz1-1.xx.fbcdn.net%2Fv%2Ft42.1790-2%2F2349078999904_n.mp4%3Fefg%3DeyJ87J9%26oh%3Df5777784%26oe%3D56FD4&source=media_collage&id=1735049&refid=8&_ft_=qid.6484464%3Amf_story_key.-43172431214%3Atop_level_post_id.102773&__tn__=FEH-R"
|
||||
val expected = "https://video-yyz1-1.xx.fbcdn.net/v/t42.1790-2/2349078999904_n.mp4?efg=eyJ87J9&oh=f5777784&oe=56FD4?source&id=1735049&_ft_=qid.6484464:mf_story_key.-43172431214:top_level_post_id.102773&__tn__=FEH-R"
|
||||
val expected = "https://video-yyz1-1.xx.fbcdn.net/v/t42.1790-2/2349078999904_n.mp4?efg=eyJ87J9&oh=f5777784&oe=56FD4&source=media_collage&id=1735049&_ft_=qid.6484464:mf_story_key.-43172431214:top_level_post_id.102773&__tn__=FEH-R"
|
||||
assertFbFormat(expected, url)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -17,7 +17,7 @@ MIN_SDK=21
|
||||
TARGET_SDK=26
|
||||
BUILD_TOOLS=26.0.2
|
||||
|
||||
KAU=1b7368f
|
||||
KAU=1bc48a6
|
||||
KOTLIN=1.1.51
|
||||
|
||||
COMMONS_TEXT=1.1
|
||||
|
Loading…
Reference in New Issue
Block a user