diff options
Diffstat (limited to 'app')
21 files changed, 8 insertions, 938 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt index 0322a4bb..2f49b235 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/WebOverlayActivity.kt @@ -61,7 +61,6 @@ import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.kotlin.subscribeDuringJob -import com.pitchedapps.frost.services.FrostRunnable import com.pitchedapps.frost.utils.ARG_URL import com.pitchedapps.frost.utils.ARG_USER_ID import com.pitchedapps.frost.utils.BiometricUtils @@ -224,9 +223,6 @@ abstract class WebOverlayActivityBase : BaseActivity(), } } - FrostRunnable.propagate(this, intent) - L.v { "Done propagation" } - swipeBack = kauSwipeOnCreate { if (!Prefs.overlayFullScreenSwipe) edgeSize = 20.dpToPx transitionSystemBars = false 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 a09b5d39..6f726b5b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbItem.kt @@ -23,8 +23,6 @@ import com.mikepenz.iconics.typeface.IIcon import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic import com.pitchedapps.frost.R import com.pitchedapps.frost.fragments.BaseFragment -import com.pitchedapps.frost.fragments.MenuFragment -import com.pitchedapps.frost.fragments.NotificationFragment import com.pitchedapps.frost.fragments.WebFragment import com.pitchedapps.frost.utils.EnumBundle import com.pitchedapps.frost.utils.EnumBundleCompanion @@ -47,15 +45,10 @@ enum class FbItem( FRIENDS(R.string.friends, GoogleMaterial.Icon.gmd_person_add, "friends/center/requests"), GROUPS(R.string.groups, GoogleMaterial.Icon.gmd_group, "groups"), MARKETPLACE(R.string.marketplace, GoogleMaterial.Icon.gmd_store, "marketplace"), - MENU(R.string.menu, GoogleMaterial.Icon.gmd_menu, "settings", ::MenuFragment), + MENU(R.string.menu, GoogleMaterial.Icon.gmd_menu, "settings"), MESSAGES(R.string.messages, MaterialDesignIconic.Icon.gmi_comments, "messages"), NOTES(R.string.notes, CommunityMaterial.Icon2.cmd_note, "notes"), - NOTIFICATIONS( - R.string.notifications, - MaterialDesignIconic.Icon.gmi_globe, - "notifications", - ::NotificationFragment - ), + NOTIFICATIONS(R.string.notifications, MaterialDesignIconic.Icon.gmi_globe, "notifications"), ON_THIS_DAY(R.string.on_this_day, GoogleMaterial.Icon.gmd_today, "onthisday"), PAGES(R.string.pages, GoogleMaterial.Icon.gmd_flag, "pages"), PHOTOS(R.string.photos, GoogleMaterial.Icon.gmd_photo, "me/photos"), diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt index b948506f..53126938 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/FbRequest.kt @@ -17,16 +17,9 @@ package com.pitchedapps.frost.facebook.requests import com.pitchedapps.frost.BuildConfig -import com.pitchedapps.frost.facebook.FB_DTSG_MATCHER import com.pitchedapps.frost.facebook.FB_JSON_URL_MATCHER -import com.pitchedapps.frost.facebook.FB_REV_MATCHER -import com.pitchedapps.frost.facebook.FB_URL_BASE -import com.pitchedapps.frost.facebook.FB_USER_MATCHER import com.pitchedapps.frost.facebook.USER_AGENT import com.pitchedapps.frost.facebook.get -import com.pitchedapps.frost.kotlin.Flyweight -import com.pitchedapps.frost.utils.L -import kotlinx.coroutines.GlobalScope import okhttp3.Call import okhttp3.FormBody import okhttp3.OkHttpClient @@ -35,41 +28,12 @@ import okhttp3.logging.HttpLoggingInterceptor import org.apache.commons.text.StringEscapeUtils /** - * Created by Allan Wang on 21/12/17. - */ -val fbAuth = Flyweight<String, RequestAuth>(GlobalScope, 3600000 /* an hour */) { - it.getAuth() -} - -/** - * Underlying container for all fb requests - */ -data class RequestAuth( - val userId: Long = -1, - val cookie: String = "", - val fb_dtsg: String = "", - val rev: String = "" -) { - val isComplete - get() = userId > 0 && cookie.isNotEmpty() && fb_dtsg.isNotEmpty() && rev.isNotEmpty() -} - -/** * Request container with the execution call */ class FrostRequest<out T : Any?>(val call: Call, private val invoke: (Call) -> T) { fun invoke() = invoke(call) } -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> { - val request = cookie.requestBuilder() - request.builder() - return FrostRequest(request.call(), invoke) -} - val httpClient: OkHttpClient by lazy { val builder = OkHttpClient.Builder() if (BuildConfig.DEBUG) @@ -106,39 +70,6 @@ internal fun String?.requestBuilder(): Request.Builder { fun Request.Builder.call(): Call = httpClient.newCall(build()) -fun String.getAuth(): RequestAuth { - L.v { "Getting auth for ${hashCode()}" } - var auth = RequestAuth(cookie = this) - val id = FB_USER_MATCHER.find(this)[1]?.toLong() ?: return auth - auth = auth.copy(userId = id) - val call = this.requestBuilder() - .url(FB_URL_BASE) - .get() - .call() - call.execute().body()?.charStream()?.useLines { lines -> - lines.forEach { - val text = try { - StringEscapeUtils.unescapeEcmaScript(it) - } catch (ignore: Exception) { - return@forEach - } - val fb_dtsg = FB_DTSG_MATCHER.find(text)[1] - if (fb_dtsg != null) { - auth = auth.copy(fb_dtsg = fb_dtsg) - if (auth.isComplete) return auth - } - - val rev = FB_REV_MATCHER.find(text)[1] - if (rev != null) { - auth = auth.copy(rev = rev) - if (auth.isComplete) return auth - } - } - } - - return auth -} - /** * Execute the call and attempt to check validity * Valid = not blank & no "error" instance 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 index 1b5e8b99..0115d6fc 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Images.kt @@ -16,38 +16,17 @@ */ package com.pitchedapps.frost.facebook.requests -import com.bumptech.glide.Priority -import com.bumptech.glide.RequestBuilder -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.Options -import com.bumptech.glide.load.data.DataFetcher -import com.bumptech.glide.load.model.ModelLoader -import com.bumptech.glide.load.model.ModelLoaderFactory -import com.bumptech.glide.load.model.MultiModelLoaderFactory -import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.request.target.Target -import com.bumptech.glide.signature.ObjectKey -import com.pitchedapps.frost.facebook.FB_IMAGE_ID_MATCHER import com.pitchedapps.frost.facebook.FB_REDIRECT_URL_MATCHER -import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.facebook.formattedFbUrl import com.pitchedapps.frost.facebook.get import com.pitchedapps.frost.utils.L import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout -import okhttp3.Call -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() -} /** * Attempts to get the fbcdn url of the supplied image redirect url @@ -65,106 +44,3 @@ suspend fun String.getFullSizedImageUrl(url: String, timeout: Long = 3000): Stri null } } - -/** - * Request loader for a potentially hd version of a url - * In this case, each url may potentially return an id, - * which may potentially be used to fetch a higher res image url - * The following aims to allow such loading while adhering to Glide's lifecycle - */ -data class HdImageMaybe(val url: String, val cookie: String) { - - val id: Long by lazy { FB_IMAGE_ID_MATCHER.find(url)[1]?.toLongOrNull() ?: -1 } - - val isValid: Boolean by lazy { - id != -1L && cookie.isNotBlank() - } -} - -/* - * The following was a test to see if hd image loading would work - * - * It's working and tested, though the improvements aren't really worth the extra data use - * and reload - */ - -class HdImageLoadingFactory : ModelLoaderFactory<HdImageMaybe, InputStream> { - - override fun build(multiFactory: MultiModelLoaderFactory) = HdImageLoading() - - override fun teardown() = Unit -} - -fun <T> RequestBuilder<T>.loadWithPotentialHd(model: HdImageMaybe) = - thumbnail(clone().load(model.url)) - .load(model) - .apply(RequestOptions().override(Target.SIZE_ORIGINAL)) - -class HdImageLoading : ModelLoader<HdImageMaybe, InputStream> { - - override fun buildLoadData( - model: HdImageMaybe, - width: Int, - height: Int, - options: Options - ): ModelLoader.LoadData<InputStream>? = - if (!model.isValid) null - else ModelLoader.LoadData(ObjectKey(model), HdImageFetcher(model)) - - override fun handles(model: HdImageMaybe) = model.isValid -} - -class HdImageFetcher(private val model: HdImageMaybe) : 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>) { - if (!model.isValid) return callback.fail("Model is invalid") - val result: Result<InputStream?> = runCatching { - runBlocking { - withTimeout(20000L) { - val auth = fbAuth.fetch(model.cookie).await() - if (cancelled) throw RuntimeException("Cancelled") - val url = auth.getFullSizedImage(model.id).invoke() - ?: throw RuntimeException("Null url") - if (cancelled) throw RuntimeException("Cancelled") - if (!url.contains("png") && !url.contains("jpg")) throw RuntimeException("Invalid format") - urlCall?.execute()?.body()?.byteStream() - } - } - } - if (result.isSuccess) - callback.onDataReady(result.getOrNull()) - else - callback.onLoadFailed( - result.exceptionOrNull() as? Exception ?: RuntimeException("Failed") - ) - } - - 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/Menu.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt deleted file mode 100644 index dcb0ce10..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Menu.kt +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2018 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.pitchedapps.frost.facebook.requests - -import com.fasterxml.jackson.annotation.JsonCreator -import com.fasterxml.jackson.annotation.JsonIgnoreProperties -import com.fasterxml.jackson.annotation.JsonProperty -import com.fasterxml.jackson.databind.MapperFeature -import com.fasterxml.jackson.databind.ObjectMapper -import com.pitchedapps.frost.facebook.FB_URL_BASE -import com.pitchedapps.frost.facebook.formattedFbUrl -import com.pitchedapps.frost.utils.L -import okhttp3.Call -import org.apache.commons.text.StringEscapeUtils -import org.jsoup.Jsoup -import java.io.IOException - -/** - * Created by Allan Wang on 29/12/17. - */ -fun RequestAuth.getMenuData(): FrostRequest<MenuData?> { - - val body = listOf( - "fb_dtsg" to fb_dtsg, - "__user" to userId - ).withEmptyData("m_sess", "__dyn", "__req", "__ajax__") - - return frostRequest(::parseMenu) { - url("${FB_URL_BASE}bookmarks/flyout/body/?id=u_0_2") - post(body.toForm()) - } -} - -fun parseMenu(call: Call): MenuData? { - val fullString = call.execute().body()?.string() ?: return null - var jsonString = fullString.substringAfter("bookmarkGroups", "") - .substringAfter("[", "") - - if (jsonString.isBlank()) return null - - jsonString = "{ \"data\" : [${StringEscapeUtils.unescapeEcmaScript(jsonString)}" - - val mapper = ObjectMapper() - .disable(MapperFeature.AUTO_DETECT_SETTERS) - - return try { - val data = mapper.readValue(jsonString, MenuData::class.java) - - // parse footer content - - val footer = fullString.substringAfter("footerMarkup", "") - .substringAfter("{", "") - .substringBefore("}", "") - - val doc = Jsoup.parseBodyFragment( - StringEscapeUtils.unescapeEcmaScript( - StringEscapeUtils.unescapeEcmaScript(footer) - ) - ) - val footerData = mutableListOf<MenuFooterItem>() - val footerSmallData = mutableListOf<MenuFooterItem>() - - doc.select("a[href]").forEach { - val text = it.text() - it.parent() - if (text.isEmpty()) return@forEach - val href = it.attr("href").formattedFbUrl - val item = MenuFooterItem(name = text, url = href) - if (it.parent().tag().name == "span") - footerSmallData.add(item) - else - footerData.add(item) - } - - return data.copy(footer = MenuFooter(footerData, footerSmallData)) - } catch (e: IOException) { - L.e(e) { "Menu parse fail" } - null - } -} - -@JsonIgnoreProperties(ignoreUnknown = true) -data class MenuData( - val data: List<MenuHeader> = emptyList(), - val footer: MenuFooter = MenuFooter() -) { - - @JsonCreator - constructor( - @JsonProperty("data") data: List<MenuHeader>? - ) : this(data ?: emptyList(), MenuFooter()) - - fun flatMapValid(): List<MenuItemData> { - val items = mutableListOf<MenuItemData>() - data.forEach { - if (it.isValid) items.add(it) - items.addAll(it.visible.filter(MenuItem::isValid)) - } - - items.addAll(footer.data.filter(MenuFooterItem::isValid)) - items.addAll(footer.smallData.filter(MenuFooterItem::isValid)) - - return items - } -} - -interface MenuItemData { - val isValid: Boolean -} - -@JsonIgnoreProperties(ignoreUnknown = true) -data class MenuHeader( - val id: String? = null, - val header: String? = null, - val visible: List<MenuItem> = emptyList(), - val all: List<MenuItem> = emptyList() -) : MenuItemData { - - @JsonCreator - constructor( - @JsonProperty("id") id: String?, - @JsonProperty("header") header: String?, - @JsonProperty("visible") visible: List<MenuItem>?, - @JsonProperty("all") all: List<MenuItem>?, - @JsonProperty("fake") fake: Boolean? - ) : this(id, header, visible ?: emptyList(), all ?: emptyList()) - - override val isValid: Boolean - get() = !header.isNullOrBlank() -} - -@JsonIgnoreProperties(ignoreUnknown = true) -data class MenuItem( - val id: String? = null, - val name: String? = null, - val pic: String? = null, - val url: String? = null, - val badge: String? = null, - val countDetails: String? = null -) : MenuItemData { - - @JsonCreator - constructor( - @JsonProperty("id") id: String?, - @JsonProperty("name") name: String?, - @JsonProperty("pic") pic: String?, - @JsonProperty("url") url: String?, - @JsonProperty("count") badge: String?, - @JsonProperty("count_details") countDetails: String?, - @JsonProperty("fake") fake: Boolean? - ) : this( - id, name, pic?.formattedFbUrl, - url?.formattedFbUrl, - if (badge == "0") null else badge, - countDetails - ) - - override val isValid: Boolean - get() = !name.isNullOrBlank() && !url.isNullOrBlank() -} - -data class MenuFooter( - val data: List<MenuFooterItem> = emptyList(), - val smallData: List<MenuFooterItem> = emptyList() -) { - - val hasContent - get() = data.isNotEmpty() || smallData.isNotEmpty() -} - -data class MenuFooterItem( - val name: String? = null, - val url: String? = null, - val isSmall: Boolean = false -) : MenuItemData { - override val isValid: Boolean - get() = name != null && url != null -} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Messages.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Messages.kt deleted file mode 100644 index f350c547..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Messages.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.pitchedapps.frost.facebook.requests - -import com.pitchedapps.frost.facebook.FB_URL_BASE -import okhttp3.Call - -/** - * Created by Allan Wang on 07/01/18. - */ -fun RequestAuth.sendMessage(group: String, content: String): FrostRequest<Boolean> { - - // todo test more; only tested against tids=cid... - val body = listOf( - "tids" to group, - "body" to content, - "fb_dtsg" to fb_dtsg, - "__user" to userId - ).withEmptyData("m_sess", "__dyn", "__req", "__ajax__") - - return frostRequest(::validateMessage) { - url("${FB_URL_BASE}messages/send") - post(body.toForm()) - } -} - -/** - * Messages are a bit weird with their responses - */ -private fun validateMessage(call: Call): Boolean { - val body = call.execute().body() ?: return false - // todo - return true -} 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 deleted file mode 100644 index bf974034..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/requests/Notifications.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2018 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -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()) - } -} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt index 50bae16c..e73acc97 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/FragmentBase.kt @@ -67,7 +67,7 @@ abstract class BaseFragment : Fragment(), CoroutineScope, FragmentContract, Dyna data: FbItem, position: Int ): BaseFragment { - val fragment = if (useFallback || !Prefs.nativeUi) WebFragment() else base() + val fragment = if (useFallback) WebFragment() else base() val d = if (data == FbItem.FEED) FeedSort(Prefs.feedSort).item else data fragment.withArguments( ARG_URL to d.url, diff --git a/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt index 54ea388d..a8a9bf28 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/fragments/RecyclerFragments.kt @@ -16,32 +16,18 @@ */ package com.pitchedapps.frost.fragments -import com.mikepenz.fastadapter.IItem -import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.parsers.FrostNotifs import com.pitchedapps.frost.facebook.parsers.NotifParser import com.pitchedapps.frost.facebook.parsers.ParseResponse -import com.pitchedapps.frost.facebook.requests.MenuFooterItem -import com.pitchedapps.frost.facebook.requests.MenuHeader -import com.pitchedapps.frost.facebook.requests.MenuItem -import com.pitchedapps.frost.facebook.requests.MenuItemData -import com.pitchedapps.frost.facebook.requests.fbAuth -import com.pitchedapps.frost.facebook.requests.getMenuData -import com.pitchedapps.frost.iitems.ClickableIItemContract -import com.pitchedapps.frost.iitems.MenuContentIItem -import com.pitchedapps.frost.iitems.MenuFooterIItem -import com.pitchedapps.frost.iitems.MenuFooterSmallIItem -import com.pitchedapps.frost.iitems.MenuHeaderIItem import com.pitchedapps.frost.iitems.NotificationIItem import com.pitchedapps.frost.utils.frostJsoup import com.pitchedapps.frost.views.FrostRecyclerView -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext /** * Created by Allan Wang on 27/12/17. */ +@Deprecated(message = "Retained as an example; currently does not support marking a notification as read") class NotificationFragment : FrostParserFragment<FrostNotifs, NotificationIItem>() { override val parser = NotifParser @@ -55,33 +41,3 @@ class NotificationFragment : FrostParserFragment<FrostNotifs, NotificationIItem> NotificationIItem.bindEvents(adapter) } } - -class MenuFragment : GenericRecyclerFragment<MenuItemData, IItem<*, *>>() { - - override fun mapper(data: MenuItemData): IItem<*, *> = when (data) { - is MenuHeader -> MenuHeaderIItem(data) - is MenuItem -> MenuContentIItem(data) - is MenuFooterItem -> - if (data.isSmall) MenuFooterSmallIItem(data) - else MenuFooterIItem(data) - else -> throw IllegalArgumentException("Menu item in fragment has invalid type ${data::class.java.simpleName}") - } - - override fun bindImpl(recyclerView: FrostRecyclerView) { - ClickableIItemContract.bindEvents(adapter) - } - - override suspend fun reloadImpl(progress: (Int) -> Unit): List<MenuItemData>? = - withContext(Dispatchers.IO) { - val cookie = FbCookie.webCookie ?: return@withContext null - progress(10) - val auth = fbAuth.fetch(cookie).await() - progress(30) - val data = auth.getMenuData().invoke() ?: return@withContext null - if (data.data.isEmpty()) return@withContext null - progress(70) - val items = data.flatMapValid() - progress(90) - return@withContext items - } -} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/iitems/MenuIItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/iitems/MenuIItem.kt deleted file mode 100644 index ee0cd94c..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/iitems/MenuIItem.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2018 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.pitchedapps.frost.iitems - -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import ca.allanwang.kau.iitems.KauIItem -import ca.allanwang.kau.ui.createSimpleRippleDrawable -import ca.allanwang.kau.utils.bindView -import ca.allanwang.kau.utils.gone -import ca.allanwang.kau.utils.visible -import com.mikepenz.fastadapter.FastAdapter -import com.pitchedapps.frost.R -import com.pitchedapps.frost.facebook.requests.MenuFooterItem -import com.pitchedapps.frost.facebook.requests.MenuHeader -import com.pitchedapps.frost.facebook.requests.MenuItem -import com.pitchedapps.frost.glide.FrostGlide -import com.pitchedapps.frost.glide.GlideApp -import com.pitchedapps.frost.utils.Prefs - -/** - * Created by Allan Wang on 30/12/17. - */ -class MenuContentIItem(val data: MenuItem) : - KauIItem<MenuContentIItem, MenuContentIItem.ViewHolder>(R.layout.iitem_menu, ::ViewHolder), - ClickableIItemContract { - - override val url: String? - get() = data.url - - class ViewHolder(itemView: View) : FastAdapter.ViewHolder<MenuContentIItem>(itemView) { - - val frame: ViewGroup by bindView(R.id.item_frame) - val icon: ImageView by bindView(R.id.item_icon) - val content: TextView by bindView(R.id.item_content) - val badge: TextView by bindView(R.id.item_badge) - - override fun bindView(item: MenuContentIItem, payloads: MutableList<Any>) { - frame.background = createSimpleRippleDrawable(Prefs.textColor, Prefs.nativeBgColor) - content.setTextColor(Prefs.textColor) - badge.setTextColor(Prefs.textColor) - val iconUrl = item.data.pic - if (iconUrl != null) - GlideApp.with(itemView) - .load(iconUrl) - .transform(FrostGlide.circleCrop) - .into(icon.visible()) - else - icon.gone() - content.text = item.data.name - badge.text = item.data.badge - } - - override fun unbindView(item: MenuContentIItem) { - GlideApp.with(itemView).clear(icon) - content.text = null - badge.text = null - } - } -} - -class MenuHeaderIItem(val data: MenuHeader) : HeaderIItem( - data.header, - itemId = R.id.item_menu_header -) - -class MenuFooterIItem(val data: MenuFooterItem) : - TextIItem(data.name, data.url, R.id.item_menu_footer) - -class MenuFooterSmallIItem(val data: MenuFooterItem) : - TextIItem(data.name, data.url, R.id.item_menu_footer_small) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt index cfbb928b..1806d351 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/iitems/NotificationIItem.kt @@ -34,7 +34,6 @@ import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.parsers.FrostNotif import com.pitchedapps.frost.glide.FrostGlide import com.pitchedapps.frost.glide.GlideApp -import com.pitchedapps.frost.services.FrostRunnable import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.isIndependent import com.pitchedapps.frost.utils.launchWebOverlay @@ -53,7 +52,6 @@ class NotificationIItem(val notification: FrostNotif, val cookie: String) : .withOnClickListener { v, _, item, position -> val notif = item.notification if (notif.unread) { - FrostRunnable.markNotificationRead(v!!.context, notif.id, item.cookie) adapter.set( position, NotificationIItem(notif.copy(unread = false), item.cookie) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt index 6ff20bfb..e4d0b485 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt @@ -76,11 +76,7 @@ enum class NotificationType( FbItem.NOTIFICATIONS, NotifParser, Prefs::notificationRingtone - ) { - - override fun bindRequest(content: NotificationContent, cookie: String) = - FrostRunnable.prepareMarkNotificationRead(content.id, cookie) - }, + ), MESSAGE( NOTIF_CHANNEL_MESSAGES, diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt deleted file mode 100644 index 77546971..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostRequestService.kt +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2018 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.pitchedapps.frost.services - -import android.app.job.JobInfo -import android.app.job.JobParameters -import android.app.job.JobScheduler -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.os.BaseBundle -import android.os.PersistableBundle -import com.pitchedapps.frost.facebook.requests.RequestAuth -import com.pitchedapps.frost.facebook.requests.fbAuth -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 -import com.pitchedapps.frost.utils.L -import com.pitchedapps.frost.utils.Prefs -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch - -/** - * Created by Allan Wang on 28/12/17. - */ - -/** - * Private helper data - */ -private enum class FrostRequestCommands : EnumBundle<FrostRequestCommands> { - - NOTIF_READ { - - override fun invoke(auth: RequestAuth, bundle: PersistableBundle) { - val id = bundle.getLong(ARG_0, -1L) - val success = auth.markNotificationRead(id).invoke() - L.d { "Marked notif $id as read: $success" } - } - - override fun propagate(bundle: BaseBundle) = - FrostRunnable.prepareMarkNotificationRead( - bundle.getLong(ARG_0), - bundle.getCookie() - ) - }; - - override val bundleContract: EnumBundleCompanion<FrostRequestCommands> - get() = Companion - - /** - * Call request with arguments inside bundle - */ - abstract fun invoke(auth: RequestAuth, bundle: PersistableBundle) - - /** - * Return bundle builder given arguments in the old bundle - * Must not write to old bundle! - */ - abstract fun propagate(bundle: BaseBundle): BaseBundle.() -> Unit - - companion object : EnumCompanion<FrostRequestCommands>("frost_arg_commands", values()) -} - -private const val ARG_COMMAND = "frost_request_command" -private const val ARG_COOKIE = "frost_request_cookie" -private const val ARG_0 = "frost_request_arg_0" -private const val ARG_1 = "frost_request_arg_1" -private const val ARG_2 = "frost_request_arg_2" -private const val ARG_3 = "frost_request_arg_3" - -private fun BaseBundle.getCookie(): String = getString(ARG_COOKIE)!! -private fun BaseBundle.putCookie(cookie: String) = putString(ARG_COOKIE, cookie) - -/** - * Singleton handler for running requests in [FrostRequestService] - * Requests are typically completely decoupled from the UI, - * and are optional enhancers. - * - * Nothing guarantees the completion time, or whether it even executes at all - * - * Design: - * prepare function - creates a bundle binder - * actor function - calls the service with the given arguments - * - * Global: - * propagator - given a bundle with a command, extracts and executes the requests - */ -object FrostRunnable { - - fun prepareMarkNotificationRead(id: Long, cookie: String): BaseBundle.() -> Unit = { - FrostRequestCommands.NOTIF_READ.put(this) - putLong(ARG_0, id) - putCookie(cookie) - } - - fun markNotificationRead(context: Context, id: Long, cookie: String): Boolean { - if (id <= 0) { - L.d { "Invalid notification id $id for marking as read" } - return false - } - return schedule( - context, FrostRequestCommands.NOTIF_READ, - prepareMarkNotificationRead(id, cookie) - ) - } - - fun propagate(context: Context, intent: Intent?) { - val extras = intent?.extras ?: return - val command = FrostRequestCommands[intent] ?: return - intent.removeExtra(ARG_COMMAND) // reset - L.d { "Propagating command ${command.name}" } - val builder = command.propagate(extras) - schedule(context, command, builder) - } - - private fun schedule( - context: Context, - command: FrostRequestCommands, - bundleBuilder: PersistableBundle.() -> Unit - ): Boolean { - val scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler - val serviceComponent = ComponentName(context, FrostRequestService::class.java) - val bundle = PersistableBundle() - bundle.bundleBuilder() - bundle.putString(ARG_COMMAND, command.name) - - if (bundle.getCookie().isNullOrBlank()) { - L.e { "Scheduled frost request with empty cookie" } - return false - } - - val builder = JobInfo.Builder(REQUEST_SERVICE_BASE + command.ordinal, serviceComponent) - .setMinimumLatency(0L) - .setExtras(bundle) - .setOverrideDeadline(2000L) - .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) - val result = scheduler.schedule(builder.build()) - if (result <= 0) { - L.eThrow("FrostRequestService scheduler failed for ${command.name}") - return false - } - L.d { "Scheduled ${command.name}" } - return true - } -} - -class FrostRequestService : BaseJobService() { - - override fun onStartJob(params: JobParameters?): Boolean { - super.onStartJob(params) - if (!Prefs.authRequests) { - L.i { "Auth requests disabled; skipping request service" } - return false - } - val bundle = params?.extras - if (bundle == null) { - L.eThrow("Launched ${this::class.java.simpleName} without param data") - return false - } - val cookie = bundle.getCookie() - if (cookie.isBlank()) { - L.eThrow("Launched ${this::class.java.simpleName} without cookie") - return false - } - val command = FrostRequestCommands[bundle] - if (command == null) { - L.eThrow("Launched ${this::class.java.simpleName} without command") - return false - } - launch(Dispatchers.IO) { - try { - val auth = fbAuth.fetch(cookie).await() - command.invoke(auth, bundle) - L.d { - "Finished frost service for ${command.name} in ${System.currentTimeMillis() - startTime} ms" - } - } catch (e: Exception) { - L.e(e) { "Failed frost service for ${command.name} in ${System.currentTimeMillis() - startTime} ms" } - } finally { - jobFinished(params, false) - } - } - return true - } -} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt index a0a8117f..ba5b839b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt @@ -79,13 +79,6 @@ fun SettingsActivity.getBehaviourPrefs(): KPrefAdapterBuilder.() -> Unit = { } } - checkbox(R.string.native_ui, Prefs::nativeUi, { - Prefs.nativeUi = it - shouldRestartMain() - }) { - descRes = R.string.native_ui_desc - } - checkbox(R.string.exit_confirmation, Prefs::exitConfirmation, { Prefs.exitConfirmation = it }) { descRes = R.string.exit_confirmation_desc } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt index e2fac291..389cb6b7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/L.kt @@ -84,4 +84,4 @@ fun KauLoggerExtension.test(message: () -> Any?) { if (BuildConfig.DEBUG) { d { "Test1234 ${message()}" } } -}
\ No newline at end of file +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt index 522e1733..3af538dd 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -193,8 +193,6 @@ object Prefs : KPref() { var authRequests: Boolean by kpref("web_requests", false) - var nativeUi: Boolean by kpref("native_ui", true) - inline val mainActivityLayout: MainActivityLayout get() = MainActivityLayout(mainActivityLayoutType) diff --git a/app/src/main/play/en-US/whatsnew b/app/src/main/play/en-US/whatsnew index 21e65341..a3653b1b 100644 --- a/app/src/main/play/en-US/whatsnew +++ b/app/src/main/play/en-US/whatsnew @@ -1,4 +1,4 @@ v2.4.0 -* Removed web only mode for auth requests. Marking notifications as read is now disabled by default to deal with phishing accusations. +* Removed request services, which potentially caused phishing warnings. * Save images with the correct extensions.
\ No newline at end of file diff --git a/app/src/main/res/values/strings_pref_behaviour.xml b/app/src/main/res/values/strings_pref_behaviour.xml index aac56250..32188698 100644 --- a/app/src/main/res/values/strings_pref_behaviour.xml +++ b/app/src/main/res/values/strings_pref_behaviour.xml @@ -17,8 +17,6 @@ <string name="search_bar_desc">Enable the search bar instead of a search overlay</string> <string name="force_message_bottom">Force Message Bottom</string> <string name="force_message_bottom_desc">When loading a message thread, trigger a scroll to the bottom of the page rather than loading the page as is.</string> - <string name="native_ui">Native UI</string> - <string name="native_ui_desc">Enables native UI for certain options in the main activity. Done through parsing.</string> <string name="enable_pip">Enable PIP</string> <string name="enable_pip_desc">Enable picture in picture videos</string> <string name="autoplay_settings">Autoplay Settings</string> diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml index 2fb9984a..3e267f69 100644 --- a/app/src/main/res/xml/frost_changelog.xml +++ b/app/src/main/res/xml/frost_changelog.xml @@ -7,7 +7,7 @@ --> <version title="v2.4.0" /> - <item text="Removed web only mode for auth requests. Marking notifications as read is now disabled by default to deal with phishing accusations." /> + <item text="Removed request services, which potentially caused phishing warnings." /> <item text="Save images with the correct extensions." /> <item text="" /> diff --git a/app/src/test/kotlin/com/pitchedapps/frost/facebook/requests/FbRequestTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/facebook/requests/FbRequestTest.kt deleted file mode 100644 index 67f62a40..00000000 --- a/app/src/test/kotlin/com/pitchedapps/frost/facebook/requests/FbRequestTest.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2018 Allan Wang - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -package com.pitchedapps.frost.facebook.requests - -import com.fasterxml.jackson.databind.ObjectMapper -import com.pitchedapps.frost.internal.AUTH -import com.pitchedapps.frost.internal.COOKIE -import com.pitchedapps.frost.internal.USER_ID -import com.pitchedapps.frost.internal.authDependent -import okhttp3.Call -import org.junit.BeforeClass -import org.junit.Ignore -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertNotNull -import kotlin.test.assertTrue -import kotlin.test.fail - -/** - * Created by Allan Wang on 21/12/17. - */ -class FbRequestTest { - - companion object { - @BeforeClass - @JvmStatic - fun before() { - authDependent() - } - } - - /** - * Used to emulate [executeForNoError] - * Must be consistent with that method - */ - private fun Call.assertNoError() { - val data = execute().body()?.string() ?: fail("Content was null") - println("Call response: $data") - assertTrue(data.isNotEmpty(), "Content was empty") - assertFalse(data.contains("error"), "Content had error") - } - - @Test - fun auth() { - val auth = COOKIE.getAuth() - assertNotNull(auth) - assertEquals(USER_ID, auth.userId) - assertEquals(COOKIE, auth.cookie) - println("Test auth: ${auth.fb_dtsg}") - } - - @Test - @Ignore("Post requests are now experimental") - fun markNotification() { - val notifId = 1514443903880 - AUTH.markNotificationRead(notifId).call.assertNoError() - } - - @Ignore("Broken as of 2019/01/03; however, this was never used in production to begin with") - @Test - fun fullSizeImage() { - val fbid = 10150706277522838L // google's current cover photo - val url = AUTH.getFullSizedImage(fbid).invoke() - println(url) - assertEquals(true, url?.startsWith("https://scontent"), "Bad start for url $url") - } - - @Test - fun testMenu() { - val data = AUTH.getMenuData().invoke() - assertNotNull(data) - println(ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(data)) - assertTrue(data.data.isNotEmpty()) - assertTrue(data.footer.hasContent, "Footer may be badly parsed") - val items = data.flatMapValid() - assertTrue(items.size > 15, "Something may be badly parsed") - } -} 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 41473e86..d0d5c019 100644 --- a/app/src/test/kotlin/com/pitchedapps/frost/internal/Internal.kt +++ b/app/src/test/kotlin/com/pitchedapps/frost/internal/Internal.kt @@ -19,8 +19,6 @@ package com.pitchedapps.frost.internal import com.pitchedapps.frost.facebook.FB_USER_MATCHER import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.get -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 @@ -53,11 +51,6 @@ val PROPS: Properties by lazy { val COOKIE: String by lazy { PROPS.getProperty("COOKIE") ?: "" } val USER_ID: Long by lazy { FB_USER_MATCHER.find(COOKIE)[1]?.toLong() ?: -1 } -val AUTH: RequestAuth by lazy { - COOKIE.getAuth().apply { - println("Auth:\nuser:$userId\nfb_dtsg: $fb_dtsg\nrev: $rev\ncomplete: $isComplete") - } -} private val VALID_COOKIE: Boolean by lazy { val data = testJsoup(FbItem.SETTINGS.url) |