From 9a41937a33539dbfaae4d072361caaec79865c29 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Wed, 31 May 2017 01:31:02 -0700 Subject: implement cache db and start js injections --- .../main/kotlin/com/pitchedapps/frost/FrostApp.kt | 2 + .../com/pitchedapps/frost/dbflow/CookiesDb.kt | 52 +++++------------- .../com/pitchedapps/frost/dbflow/FbTabsDb.kt | 27 +++++----- .../com/pitchedapps/frost/facebook/CookieMap.kt | 45 ---------------- .../com/pitchedapps/frost/facebook/FbCookie.kt | 63 ++++++++++++++++++++++ .../com/pitchedapps/frost/injectors/JsInjector.kt | 32 +++++++++++ .../kotlin/com/pitchedapps/frost/utils/DbUtils.kt | 19 ++----- .../kotlin/com/pitchedapps/frost/utils/Prefs.kt | 5 +- .../com/pitchedapps/frost/views/FrostWebView.kt | 14 +++-- 9 files changed, 137 insertions(+), 122 deletions(-) delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/CookieMap.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt (limited to 'app/src/main/kotlin/com') diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index 1ea3f0e0..0ad5259d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -1,6 +1,7 @@ package com.pitchedapps.frost import android.app.Application +import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.facebook.retro.FrostApi import com.pitchedapps.frost.utils.CrashReportingTree import com.pitchedapps.frost.utils.Prefs @@ -21,6 +22,7 @@ class FrostApp : Application() { FlowManager.init(FlowConfig.Builder(this).build()) Prefs(this) FrostApi(this) + FbCookie.init() super.onCreate() } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt index 38d55a17..d8f349a0 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/CookiesDb.kt @@ -1,12 +1,14 @@ package com.pitchedapps.frost.dbflow +import com.pitchedapps.frost.facebook.FbCookie +import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.Prefs +import com.raizlabs.android.dbflow.annotation.ConflictAction import com.raizlabs.android.dbflow.annotation.Database -import com.raizlabs.android.dbflow.annotation.ForeignKey import com.raizlabs.android.dbflow.annotation.PrimaryKey import com.raizlabs.android.dbflow.annotation.Table +import com.raizlabs.android.dbflow.kotlinextensions.* import com.raizlabs.android.dbflow.structure.BaseModel -import okhttp3.Cookie -import java.io.Serializable /** * Created by Allan Wang on 2017-05-30. @@ -18,43 +20,13 @@ object CookiesDb { const val VERSION = 1 } -//@Database(name = CookieDb.NAME, version = CookieDb.VERSION) -//object CookieDb { -// const val NAME = "Cookie" -// const val VERSION = 1 -//} +@Table(database = CookiesDb::class, allFields = true, primaryKeyConflict = ConflictAction.REPLACE) +data class CookieModel(@PrimaryKey var id: Long = Prefs.userIdDefault, var cookie: String? = null) : BaseModel() -@Table(database = CookiesDb::class, allFields = true) -data class CookieModel(@PrimaryKey var name: String, - var value: String, - var expiresAt: Long, - var domain: String, - var path: String, - var secure: Boolean, - var httpOnly: Boolean) { +fun loadFbCookie(): CookieModel? = (select from CookieModel::class where (CookieModel_Table.id eq Prefs.userId)).querySingle() - constructor(cookie: Cookie) : this(cookie.name(), cookie.value(), cookie.expiresAt(), cookie.domain(), cookie.path(), cookie.secure(), cookie.httpOnly()) - constructor() : this("", "", 0L, "", "", false, false) - - fun toCookie(): Cookie { - val builder = Cookie.Builder().name(name).value(value).expiresAt(expiresAt).domain(domain).path(path) - if (secure) builder.secure() - if (httpOnly) builder.httpOnly() - return builder.build() +fun saveFbCookie() { + CookieModel(FbCookie.userId, FbCookie.webCookie).async save { + L.d("Fb cookie saved") } - - fun isSecure() = secure - fun isHttpOnly() = httpOnly -} - -//class CookieList(val cookies: List) -//class CookieDbList(val cookies: List) - -//@com.raizlabs.android.dbflow.annotation.TypeConverter -//class CookieTypeConverter() : TypeConverter() { -// override fun getModelValue(data: CookieDbList): CookieList = CookieList(data.cookies.map { it.toCookie() }) -// override fun getDBValue(model: CookieList): CookieDbList = CookieDbList(model.cookies.map { CookieDb(it) }) -//} - -@Table(database = CookiesDb::class) -data class Cookies(@PrimaryKey var url: String = "", @ForeignKey var cookie: CookieModel = CookieModel()) \ No newline at end of file +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt index 662b97e8..067a07bf 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt @@ -16,6 +16,7 @@ import com.raizlabs.android.dbflow.annotation.PrimaryKey import com.raizlabs.android.dbflow.annotation.Table import com.raizlabs.android.dbflow.kotlinextensions.from import com.raizlabs.android.dbflow.sql.language.SQLite +import com.raizlabs.android.dbflow.structure.BaseModel /** * Created by Allan Wang on 2017-05-30. @@ -33,7 +34,7 @@ data class FbTab(val title: String, val icon: IIcon, val url: String) data class FbTabModel( var title: String = "", @ForeignKey(saveForeignKeyModel = true, deleteForeignKeyModel = false) var icon: IIconModel = IIconModel(), - @PrimaryKey var url: String = "") { + @PrimaryKey var url: String = "") : BaseModel() { constructor(fbTab: FbTab) : this(fbTab.title, IIconModel(fbTab.icon), fbTab.url) fun toFbTab() = FbTab(title, icon.toIIcon(), url) @@ -56,15 +57,19 @@ data class IIconModel(var type: Int = -1, @PrimaryKey var name: String = "") { } } -enum class FbUrl(@StringRes val titleId: Int, val icon: IIcon, val url: String) { - LOGIN(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, "https://www.facebook.com/v2.9/dialog/oauth?client_id=${FB_KEY}&redirect_uri=https://touch.facebook.com/&response_type=token,granted_scopes"), - FEED(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, "https://touch.facebook.com/"), - PROFILE(R.string.profile, CommunityMaterial.Icon.cmd_account, "https://touch.facebook.com/me/"), - EVENTS(R.string.events, GoogleMaterial.Icon.gmd_event, "https://touch.facebook.com/events/upcoming"), - FRIENDS(R.string.friends, GoogleMaterial.Icon.gmd_people, "https://touch.facebook.com/friends/center/requests/"), - MESSAGES(R.string.messages, MaterialDesignIconic.Icon.gmi_comments, "https://touch.facebook.com/messages"), - NOTIFICATIONS(R.string.notifications, MaterialDesignIconic.Icon.gmi_globe, "https://touch.facebook.com/notifications"); +const val FB_URL_BASE = "https://m.facebook.com/" +//const val FB_URL_BASE = "https://touch.facebook.com/" +enum class FbUrl(@StringRes val titleId: Int, val icon: IIcon, relativeUrl: String) { +// LOGIN(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, "https://www.facebook.com/v2.9/dialog/oauth?client_id=${FB_KEY}&redirect_uri=https://touch.facebook.com/&response_type=token,granted_scopes"), + FEED(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, ""), + PROFILE(R.string.profile, CommunityMaterial.Icon.cmd_account, "me"), + EVENTS(R.string.events, GoogleMaterial.Icon.gmd_event, "events/upcoming"), + FRIENDS(R.string.friends, GoogleMaterial.Icon.gmd_people, "friends/center/requests"), + MESSAGES(R.string.messages, MaterialDesignIconic.Icon.gmi_comments, "messages"), + NOTIFICATIONS(R.string.notifications, MaterialDesignIconic.Icon.gmi_globe, "notifications"); + + val url = "$FB_URL_BASE$relativeUrl" fun tabInfo(c: Context) = FbTab(c.getString(titleId), icon, url) } @@ -79,7 +84,5 @@ fun loadFbTabs(c: Context): List { } fun List.saveAsync(c: Context) { - map { FbTabModel(it) }.replace(c, FbTabsDb.NAME, { - L.e("Saved successfully") - }) + map { FbTabModel(it) }.replace(c, FbTabsDb.NAME) } \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/CookieMap.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/CookieMap.kt deleted file mode 100644 index 96b1f2de..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/CookieMap.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.pitchedapps.frost.facebook - -import android.webkit.CookieManager -import com.pitchedapps.frost.utils.Prefs - -/** - * Created by Allan Wang on 2017-05-30. - */ -object CookieMap { - - var userId: Int = -1 - private val userMatcher = "c_user=([0-9]*);" - private val map = HashMap() - - operator fun get(key: String) = map[key] - - operator fun set(key: String, value: String) { - map[key] = value - } - - fun put(url: String, cookie: String) { - map.put(url, cookie) - checkUserId(url, cookie) - } - - fun checkUserId(url: String, cookie: String) { - if (userId != -1) return - if (!url.contains("facebook") || !cookie.contains(userMatcher)) return - val id = Regex(userMatcher).find(cookie)?.value - if (id != null) { - userId = id.toInt() - save() - } - } - - fun save() { - Prefs.userId = userId - CookieManager.getInstance().flush() - - } - - fun reset() { - - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt new file mode 100644 index 00000000..80fc3b72 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt @@ -0,0 +1,63 @@ +package com.pitchedapps.frost.facebook + +import android.webkit.CookieManager +import com.pitchedapps.frost.dbflow.FB_URL_BASE +import com.pitchedapps.frost.dbflow.loadFbCookie +import com.pitchedapps.frost.dbflow.saveFbCookie +import com.pitchedapps.frost.utils.L +import com.pitchedapps.frost.utils.Prefs + +/** + * Created by Allan Wang on 2017-05-30. + */ +object FbCookie { + + var userId: Long = Prefs.userIdDefault + var dbCookie: String? = null + var webCookie: String? + get() = CookieManager.getInstance().getCookie(FB_URL_BASE) + set(value) = CookieManager.getInstance().setCookie(FB_URL_BASE, value) + + fun init() { + userId = Prefs.userId + dbCookie = loadFbCookie()?.cookie + if (dbCookie != null && webCookie == null) { + L.d("DbCookie found & WebCookie is null; setting webcookie") + webCookie = dbCookie + } + } + + private val userMatcher: Regex by lazy { Regex("c_user=([0-9]*);") } + + fun checkUserId(url: String, cookie: String?) { + if (userId != Prefs.userIdDefault || cookie == null) return + L.d("Checking cookie for $url\n\t$cookie") + if (!url.contains("facebook") || !cookie.contains(userMatcher)) return + val id = userMatcher.find(cookie)?.groups?.get(1)?.value + if (id != null) { + try { + userId = id.toLong() + save() + } catch (e: NumberFormatException) { + //todo send report that id has changed + } + } + } + + fun save() { + L.d("New cookie found for $userId") + Prefs.userId = userId + CookieManager.getInstance().flush() + saveFbCookie() + } + + //TODO reset when new account is added; reset and clear when account is logged out + fun reset() { + Prefs.userId = Prefs.userIdDefault + userId = Prefs.userIdDefault + with(CookieManager.getInstance()) { + removeAllCookies(null) + flush() + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt new file mode 100644 index 00000000..e7a9df5a --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt @@ -0,0 +1,32 @@ +package com.pitchedapps.frost.injectors + +import android.webkit.WebView + +/** + * Created by Allan Wang on 2017-05-31. + */ +class JsInjector { + private val builder = StringBuilder() + + init { + builder.append("javascript:(function(){") + } + + private fun getElementById(id: String) = "document.getElementById(\"$id\")" + + private fun hideElementById(id: String) { + builder.append(getElementById(id)).append(".style.display=\"none\";") + } + + fun hideElementById(vararg ids: String) { + ids.forEach { hideElementById(it) } + } + + fun build() = builder.toString() + "})()" + + fun inject(webView: WebView) { + webView.loadUrl(build()) + } + + override fun toString() = build() +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt index 5b10e581..32d232b7 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/DbUtils.kt @@ -2,8 +2,8 @@ package com.pitchedapps.frost.utils import android.content.Context import com.raizlabs.android.dbflow.config.FlowManager -import com.raizlabs.android.dbflow.kotlinextensions.processInTransactionAsync -import com.raizlabs.android.dbflow.kotlinextensions.save +import com.raizlabs.android.dbflow.kotlinextensions.* +import com.raizlabs.android.dbflow.structure.database.transaction.FastStoreModelTransaction import com.raizlabs.android.dbflow.structure.database.transaction.Transaction /** @@ -18,19 +18,8 @@ object DbUtils { } -inline fun List.replace(context: Context, dbName: String, - crossinline callback: ((successful: Boolean) -> Unit)) { +inline fun List.replace(context: Context, dbName: String) { L.d("Replacing $dbName.db") DbUtils.db(dbName).reset(context) - this.processInTransactionAsync({ - t, databaseWrapper -> - t.save(databaseWrapper) - }, - Transaction.Success { - callback.invoke(true) - }, - Transaction.Error { _, error -> - L.e(error.message ?: "DbReplace error") - callback.invoke(false) - }) + FastStoreModelTransaction.saveBuilder(FlowManager.getModelAdapter(T::class.java)).addAll(this).build() } \ 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 2ac58113..52a75929 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -25,8 +25,9 @@ object Prefs { get() = sp.getLong(LAST_ACTIVE, -1) set(value) = set(LAST_ACTIVE, System.currentTimeMillis()) - var userId: Int - get() = sp.getInt(USER_ID, -1) + const val userIdDefault = -1L + var userId: Long + get() = sp.getLong(USER_ID, userIdDefault) set(value) = set(USER_ID, value) private fun set(key: String, value: Boolean) = sp.edit().putBoolean(key, value).apply() diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt index c529b224..d537d623 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt @@ -11,6 +11,7 @@ import android.util.AttributeSet import android.view.MotionEvent import android.view.View import android.webkit.* +import com.pitchedapps.frost.facebook.FbCookie import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.ObservableContainer import io.reactivex.subjects.BehaviorSubject @@ -52,22 +53,19 @@ class FrostWebView @JvmOverloads constructor( override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) { super.onReceivedError(view, request, error) observable.onNext(WebStatus.ERROR) - L.e("Error ${request}") + L.e("FWV Error ${request}") } - override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) observable.onNext(WebStatus.LOADING) - L.d("Loading $url") + L.d("FWV Loading $url") } - override fun onPageFinished(view: WebView?, url: String?) { + override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) observable.onNext(WebStatus.LOADED) - val cookie = CookieManager.getInstance().getCookie(url) - L.d("Loaded $url") - L.d("Cookie $cookie") - CookieManager.getInstance().flush() + FbCookie.checkUserId(url, CookieManager.getInstance().getCookie(url)) } }) } -- cgit v1.2.3