aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-12-29 23:37:10 -0500
committerGitHub <noreply@github.com>2017-12-29 23:37:10 -0500
commit041bafcceadbd5203e95f2692899ac903dd2e883 (patch)
tree9c7294cd32928a6000b04d7ce7b5a3e52cff65c5
parent32e6b5be0e662bbac22806bcc87259fd1a2e2ed0 (diff)
downloadfrost-041bafcceadbd5203e95f2692899ac903dd2e883.tar.gz
frost-041bafcceadbd5203e95f2692899ac903dd2e883.tar.bz2
frost-041bafcceadbd5203e95f2692899ac903dd2e883.zip
Feature/image retrieval (#581)v1.7.3
* Refactor * Attempt new content * Clean up to make compile friendly * Update docs
-rw-r--r--app/build.gradle2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt (renamed from app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRequest.kt)52
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt67
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt27
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt6
-rw-r--r--app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt2
-rw-r--r--app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRegexTest.kt6
-rw-r--r--app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRequestTest.kt11
-rw-r--r--app/src/test/kotlin/com/pitchedapps/frost/internal/Internal.kt2
-rw-r--r--gradle.properties1
12 files changed, 145 insertions, 33 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 1c06cee9..a5b5cbc3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -170,6 +170,8 @@ dependencies {
implementation"com.mikepenz:fastadapter-extensions:${FAST_ADAPTER_EXTENSIONS}@aar"
+ implementation "com.github.bumptech.glide:okhttp3-integration:${GLIDE}"
+
//noinspection GradleDependency
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:${LEAK_CANARY}"
//noinspection GradleDependency
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt
index e2e9d9e5..ad180023 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt
@@ -20,6 +20,7 @@ enum class FbItem(
relativeUrl: String,
val fragmentCreator: () -> BaseFragment = ::WebFragment
) : EnumBundle<FbItem> {
+
ACTIVITY_LOG(R.string.activity_log, GoogleMaterial.Icon.gmd_list, "me/allactivity"),
BIRTHDAYS(R.string.birthdays, GoogleMaterial.Icon.gmd_cake, "events/birthdays"),
CHAT(R.string.chat, GoogleMaterial.Icon.gmd_chat, "buddylist"),
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt
index 24f685be..acc23cad 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRegex.kt
@@ -24,6 +24,7 @@ val FB_EPOCH_MATCHER: Regex by lazy { Regex(":([0-9]+)") }
val FB_NOTIF_ID_MATCHER: Regex by lazy { Regex("notif_([0-9]+)") }
val FB_MESSAGE_NOTIF_ID_MATCHER: Regex by lazy { Regex("[thread|user]_fbid_([0-9]+)") }
val FB_CSS_URL_MATCHER: Regex by lazy { Regex("url\\([\"|']?(.*?)[\"|']?\\)") }
+val FB_JSON_URL_MATCHER: Regex by lazy { Regex("\"(http.*?)\"") }
operator fun MatchResult?.get(groupIndex: Int) = this?.groupValues?.get(groupIndex)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRequest.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
index 51e14097..e3e77c5c 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbRequest.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt
@@ -1,6 +1,7 @@
-package com.pitchedapps.frost.facebook
+package com.pitchedapps.frost.facebook.requests
import com.pitchedapps.frost.BuildConfig
+import com.pitchedapps.frost.facebook.*
import com.pitchedapps.frost.utils.L
import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
@@ -13,7 +14,12 @@ import org.apache.commons.text.StringEscapeUtils
*/
private val authMap: MutableMap<String, RequestAuth> = mutableMapOf()
-fun String.fbRequest(action: RequestAuth.() -> Unit) {
+/**
+ * Synchronously fetch [RequestAuth] from cookie
+ * [action] will only be called if a valid auth is found.
+ * Otherwise, [fail] will be called
+ */
+fun String.fbRequest(fail: () -> Unit = {}, action: RequestAuth.() -> Unit) {
val savedAuth = authMap[this]
if (savedAuth != null) {
savedAuth.action()
@@ -21,7 +27,7 @@ fun String.fbRequest(action: RequestAuth.() -> Unit) {
val auth = getAuth()
if (!auth.isValid) {
L.e("Attempted fbrequest with invalid auth")
- return
+ return fail()
}
authMap.put(this, auth)
L.i(null, "Found auth $auth")
@@ -29,6 +35,9 @@ fun String.fbRequest(action: RequestAuth.() -> Unit) {
}
}
+/**
+ * Underlying container for all fb requests
+ */
data class RequestAuth(val userId: Long = -1,
val cookie: String = "",
val fb_dtsg: String = "",
@@ -40,11 +49,11 @@ data class RequestAuth(val userId: Long = -1,
/**
* Request container with the execution call
*/
-class FrostRequest<out T : Any>(val call: Call, private val invoke: (Call) -> T) {
+class FrostRequest<out T : Any?>(val call: Call, private val invoke: (Call) -> T) {
fun invoke() = invoke(call)
}
-private inline fun <T : Any> RequestAuth.frostRequest(
+internal inline fun <T : Any?> RequestAuth.frostRequest(
noinline invoke: (Call) -> T,
builder: Request.Builder.() -> Request.Builder // to ensure we don't do anything extra at the end
): FrostRequest<T> {
@@ -61,7 +70,7 @@ private val client: OkHttpClient by lazy {
builder.build()
}
-private fun List<Pair<String, Any?>>.toForm(): FormBody {
+internal fun List<Pair<String, Any?>>.toForm(): FormBody {
val builder = FormBody.Builder()
forEach { (key, value) ->
val v = value?.toString() ?: ""
@@ -70,7 +79,7 @@ private fun List<Pair<String, Any?>>.toForm(): FormBody {
return builder.build()
}
-private fun List<Pair<String, Any?>>.withEmptyData(vararg key: String): List<Pair<String, Any?>> {
+internal fun List<Pair<String, Any?>>.withEmptyData(vararg key: String): List<Pair<String, Any?>> {
val newList = toMutableList()
newList.addAll(key.map { it to null })
return newList
@@ -81,7 +90,7 @@ private fun String.requestBuilder() = Request.Builder()
.header("User-Agent", USER_AGENT_BASIC)
.cacheControl(CacheControl.FORCE_NETWORK)
-private fun Request.Builder.call() = client.newCall(build())
+fun Request.Builder.call() = client.newCall(build())!!
fun String.getAuth(): RequestAuth {
var auth = RequestAuth(cookie = this)
@@ -113,22 +122,6 @@ fun String.getAuth(): RequestAuth {
return auth
}
-fun RequestAuth.markNotificationRead(notifId: Long): FrostRequest<Boolean> {
-
- val body = listOf(
- "click_type" to "notification_click",
- "id" to notifId,
- "target_id" to "null",
- "fb_dtsg" to fb_dtsg,
- "__user" to userId
- ).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
-
- return frostRequest(::executeForNoError) {
- url("${FB_URL_BASE}a/jewel_notifications_log.php")
- post(body.toForm())
- }
-}
-
inline fun <T, reified R : Any, O> Array<T>.zip(crossinline mapper: (List<R>) -> O,
crossinline caller: (T) -> R): Single<O> {
val singles = map { Single.fromCallable { caller(it) }.subscribeOn(Schedulers.io()) }
@@ -138,11 +131,6 @@ inline fun <T, reified R : Any, O> Array<T>.zip(crossinline mapper: (List<R>) ->
}
}
-fun RequestAuth.markNotificationsRead(vararg notifId: Long) =
- notifId.toTypedArray().zip<Long, Boolean, Boolean>(
- { it.all { it } },
- { markNotificationRead(it).invoke() })
-
/**
* Execute the call and attempt to check validity
* Valid = not blank & no "error" instance
@@ -158,3 +146,9 @@ fun executeForNoError(call: Call): Boolean {
}
return !empty
}
+
+fun getJsonUrl(call: Call): String? {
+ val body = call.execute().body() ?: return null
+ val url = FB_JSON_URL_MATCHER.find(body.string())[1] ?: return null
+ return StringEscapeUtils.unescapeEcmaScript(url)
+}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
new file mode 100644
index 00000000..61a94ac5
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt
@@ -0,0 +1,67 @@
+package com.pitchedapps.frost.facebook.requests
+
+import com.bumptech.glide.Priority
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.data.DataFetcher
+import com.pitchedapps.frost.facebook.FB_URL_BASE
+import okhttp3.Call
+import okhttp3.Request
+import java.io.IOException
+import java.io.InputStream
+
+/**
+ * Created by Allan Wang on 29/12/17.
+ */
+fun RequestAuth.getFullSizedImage(fbid: Long) = frostRequest(::getJsonUrl) {
+ url("${FB_URL_BASE}photo/view_full_size/?fbid=$fbid&__ajax__=&__user=$userId")
+ get()
+}
+
+class ImageFbidFetcher(private val fbid: Long,
+ private val cookie: String) : DataFetcher<InputStream> {
+
+ @Volatile private var cancelled: Boolean = false
+ private var urlCall: Call? = null
+ private var inputStream: InputStream? = null
+
+ private fun DataFetcher.DataCallback<in InputStream>.fail(msg: String) {
+ onLoadFailed(RuntimeException(msg))
+ }
+
+ override fun getDataClass(): Class<InputStream> = InputStream::class.java
+
+ override fun getDataSource(): DataSource = DataSource.REMOTE
+
+ override fun loadData(priority: Priority, callback: DataFetcher.DataCallback<in InputStream>) {
+ cookie.fbRequest(fail = { callback.fail("Invalid auth") }) {
+ if (cancelled) return@fbRequest callback.fail("Cancelled")
+ val url = getFullSizedImage(fbid).invoke() ?: return@fbRequest callback.fail("Null url")
+ if (cancelled) return@fbRequest callback.fail("Cancelled")
+ urlCall = Request.Builder().url(url).get().call()
+
+ inputStream = try {
+ urlCall?.execute()?.body()?.byteStream()
+ } catch (e: IOException) {
+ null
+ }
+
+ callback.onDataReady(inputStream)
+ }
+ }
+
+ override fun cleanup() {
+ try {
+ inputStream?.close()
+ } catch (e: IOException) {
+ } finally {
+ inputStream = null
+ }
+ }
+
+ override fun cancel() {
+ cancelled = true
+ urlCall?.cancel()
+ urlCall = null
+ cleanup()
+ }
+}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt
new file mode 100644
index 00000000..82a9364b
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt
@@ -0,0 +1,27 @@
+package com.pitchedapps.frost.facebook.requests
+
+import com.pitchedapps.frost.facebook.FB_URL_BASE
+
+/**
+ * Created by Allan Wang on 29/12/17.
+ */
+fun RequestAuth.markNotificationRead(notifId: Long): FrostRequest<Boolean> {
+
+ val body = listOf(
+ "click_type" to "notification_click",
+ "id" to notifId,
+ "target_id" to "null",
+ "fb_dtsg" to fb_dtsg,
+ "__user" to userId
+ ).withEmptyData("m_sess", "__dyn", "__req", "__ajax__")
+
+ return frostRequest(::executeForNoError) {
+ url("${FB_URL_BASE}a/jewel_notifications_log.php")
+ post(body.toForm())
+ }
+}
+
+fun RequestAuth.markNotificationsRead(vararg notifId: Long) =
+ notifId.toTypedArray().zip<Long, Boolean, Boolean>(
+ { it.all { it } },
+ { markNotificationRead(it).invoke() }) \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt
index 74a8b98d..2b407b7d 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt
@@ -9,9 +9,9 @@ import android.content.Context
import android.content.Intent
import android.os.BaseBundle
import android.os.PersistableBundle
-import com.pitchedapps.frost.facebook.RequestAuth
-import com.pitchedapps.frost.facebook.fbRequest
-import com.pitchedapps.frost.facebook.markNotificationRead
+import com.pitchedapps.frost.facebook.requests.RequestAuth
+import com.pitchedapps.frost.facebook.requests.fbRequest
+import com.pitchedapps.frost.facebook.requests.markNotificationRead
import com.pitchedapps.frost.utils.EnumBundle
import com.pitchedapps.frost.utils.EnumBundleCompanion
import com.pitchedapps.frost.utils.EnumCompanion
diff --git a/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt
index 54792086..a565aa7d 100644
--- a/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt
+++ b/app/src/test/kotlin/com/pitchedapps/frost/MiscTest.kt
@@ -1,6 +1,6 @@
package com.pitchedapps.frost
-import com.pitchedapps.frost.facebook.zip
+import com.pitchedapps.frost.facebook.requests.zip
import com.pitchedapps.frost.injectors.CssHider
import org.junit.Test
import kotlin.test.assertTrue
diff --git a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRegexTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRegexTest.kt
index da815b34..a79ccf3f 100644
--- a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRegexTest.kt
+++ b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRegexTest.kt
@@ -41,6 +41,12 @@ class FbRegexTest {
assertEquals(id, FB_MESSAGE_NOTIF_ID_MATCHER.find(data)[1]?.toLong(), "thread_fbid mismatch")
val userData = "threadlist_row_other_user_fbid_${id}thread_fbid_"
assertEquals(id, FB_MESSAGE_NOTIF_ID_MATCHER.find(userData)[1]?.toLong(), "user_fbid mismatch")
+ }
+ @Test
+ fun jsonUrlRegex() {
+ val url = "https://www.hello.world"
+ val data = "\"uri\":\"$url\"}"
+ assertEquals(url, FB_JSON_URL_MATCHER.find(data)[1])
}
} \ No newline at end of file
diff --git a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRequestTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRequestTest.kt
index c3b19727..93f09fc6 100644
--- a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRequestTest.kt
+++ b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbRequestTest.kt
@@ -1,5 +1,8 @@
package com.pitchedapps.frost.facebook
+import com.pitchedapps.frost.facebook.requests.getAuth
+import com.pitchedapps.frost.facebook.requests.getFullSizedImage
+import com.pitchedapps.frost.facebook.requests.markNotificationRead
import com.pitchedapps.frost.internal.AUTH
import com.pitchedapps.frost.internal.COOKIE
import com.pitchedapps.frost.internal.USER_ID
@@ -48,4 +51,12 @@ class FbRequestTest {
AUTH.markNotificationRead(notifId).call.assertNoError()
}
+ @Test
+ fun fullSizeImage() {
+ val fbid = 10155966932992838L // google's current cover photo
+ val url = AUTH.getFullSizedImage(fbid).invoke()
+ println(url)
+ assertTrue(url?.startsWith("https://scontent") == true)
+ }
+
} \ No newline at end of file
diff --git a/app/src/test/kotlin/com/pitchedapps/frost/internal/Internal.kt b/app/src/test/kotlin/com/pitchedapps/frost/internal/Internal.kt
index ed88453a..fb2b2a45 100644
--- a/app/src/test/kotlin/com/pitchedapps/frost/internal/Internal.kt
+++ b/app/src/test/kotlin/com/pitchedapps/frost/internal/Internal.kt
@@ -1,6 +1,8 @@
package com.pitchedapps.frost.internal
import com.pitchedapps.frost.facebook.*
+import com.pitchedapps.frost.facebook.requests.RequestAuth
+import com.pitchedapps.frost.facebook.requests.getAuth
import com.pitchedapps.frost.utils.frostJsoup
import org.junit.Assume
import java.io.File
diff --git a/gradle.properties b/gradle.properties
index adbd6c10..1e7f0d8c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -26,6 +26,7 @@ CRASHLYTICS=2.8.0
DBFLOW=4.2.3
EXOMEDIA=4.1.0
FAST_ADAPTER_EXTENSIONS=3.0.3
+GLIDE=4.4.0
IAB=1.0.44
IICON_COMMUNITY=2.0.46.1
IICON_MATERIAL=2.2.0.4