diff options
author | Allan Wang <me@allanwang.ca> | 2017-11-13 05:09:24 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-11-13 05:09:24 -0500 |
commit | 63d8779ad4fab7d2eb762be34eeca04c7debc6f3 (patch) | |
tree | 334f3c0c4c6a717d7c0b0872574cb65b3b50166c /app | |
parent | 4aed05a8923a7f76799bbaa254f407f7e11fef0b (diff) | |
download | frost-63d8779ad4fab7d2eb762be34eeca04c7debc6f3.tar.gz frost-63d8779ad4fab7d2eb762be34eeca04c7debc6f3.tar.bz2 frost-63d8779ad4fab7d2eb762be34eeca04c7debc6f3.zip |
Enhancement/video (#484)v1.6.4
* 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
Diffstat (limited to 'app')
14 files changed, 76 insertions, 106 deletions
diff --git a/app/build.gradle b/app/build.gradle index 62529e10..591ed9ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,7 +54,8 @@ android { disable 'TrustAllX509TrustManager', 'UnusedResources', 'ContentDescription', - 'RtlSymmetry' + 'RtlSymmetry', + 'MissingTranslation' xmlReport false textReport true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5acf19d6..136a467f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -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" diff --git a/app/src/main/assets/js/click_a.js b/app/src/main/assets/js/click_a.js index 2f5d759f..1ced820a 100644 --- a/app/src/main/assets/js/click_a.js +++ b/app/src/main/assets/js/click_a.js @@ -25,6 +25,8 @@ if (!window.hasOwnProperty('frost_click_a')) { e.stopPropagation(); e.preventDefault(); } + } else { + console.log('Click Intercept Prevented'); } } } diff --git a/app/src/main/assets/js/click_a.min.js b/app/src/main/assets/js/click_a.min.js index a0df6912..7abaaac6 100644 --- a/app/src/main/assets/js/click_a.min.js +++ b/app/src/main/assets/js/click_a.min.js @@ -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()) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt index 0a713649..2fe6b8d8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt @@ -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) 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 0e74bb59..22ca3138 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt @@ -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) + } 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) ?: "") } - 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 + 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=", diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt index fda5ebf5..520d750f 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/DownloadService.kt @@ -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 { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Animator.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Animator.kt deleted file mode 100644 index da852e6e..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Animator.kt +++ /dev/null @@ -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() - } - } -}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt index 3e1e1dde..e6db8eee 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt @@ -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) } -}
\ No newline at end of file +} + +private const val DOWNLOAD_MANAGER_PACKAGE = "com.android.providers.downloads"
\ 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 22c77f5f..c644499e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt @@ -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() 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 639dc9ba..9d5e199a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt @@ -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 } 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 ca88a23e..e8f9fee9 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostUrlOverlayValidator.kt @@ -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 diff --git a/app/src/main/res/values/strings_errors.xml b/app/src/main/res/values/strings_errors.xml index cf1a48cb..88428652 100644 --- a/app/src/main/res/values/strings_errors.xml +++ b/app/src/main/res/values/strings_errors.xml @@ -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>
\ No newline at end of file diff --git a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt index 79cde137..62b7cac2 100644 --- a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt +++ b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt @@ -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) } - }
\ No newline at end of file |