1
0
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:
Allan Wang 2017-11-13 05:09:24 -05:00 committed by GitHub
parent 4aed05a892
commit 63d8779ad4
15 changed files with 78 additions and 108 deletions

View File

@ -54,7 +54,8 @@ android {
disable 'TrustAllX509TrustManager',
'UnusedResources',
'ContentDescription',
'RtlSymmetry'
'RtlSymmetry',
'MissingTranslation'
xmlReport false
textReport true

View File

@ -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"

View File

@ -25,6 +25,8 @@ if (!window.hasOwnProperty('frost_click_a')) {
e.stopPropagation();
e.preventDefault();
}
} else {
console.log('Click Intercept Prevented');
}
}
}

View File

@ -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())

View File

@ -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)

View File

@ -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=",

View File

@ -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 {

View File

@ -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()
}
}
}

View File

@ -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"

View File

@ -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()

View File

@ -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 }

View File

@ -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

View File

@ -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>

View File

@ -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)
}
}

View File

@ -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