diff --git a/app/build.gradle b/app/build.gradle
index 62529e10d..591ed9ba5 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 5acf19d6d..136a467f0 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" />
-
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=",
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 fda5ebf58..520d750f2 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 OkHttp3 sample
*/
+@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 da852e6e3..000000000
--- 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 3e1e1ddea..e6db8eee2 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 22c77f5f8..c644499ea 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 639dc9baa..9d5e199a0 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 ca88a23e0..e8f9fee9f 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 cf1a48cb1..884286527 100644
--- a/app/src/main/res/values/strings_errors.xml
+++ b/app/src/main/res/values/strings_errors.xml
@@ -4,4 +4,6 @@
The url could not be loaded properly. Would you like to send it for debugging?
Invalid Share Url
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.
+ No Download Manager
+ The download manager is not enabled. Would you like to enable it to allow downloads?
\ 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 79cde1377..62b7cac2b 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
diff --git a/gradle.properties b/gradle.properties
index 03b000afb..d5013a4d5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -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