From 4cbcabb122e4bdf5d8e3eb67213ec7270d7aa9f0 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 1 Jun 2017 00:21:04 -0700 Subject: working injectors and redid tabs db --- app/src/main/AndroidManifest.xml | 4 -- app/src/main/assets/facebook.scss | 11 +++ .../kotlin/com/pitchedapps/frost/FbActivity.kt | 21 ------ .../main/kotlin/com/pitchedapps/frost/FrostApp.kt | 2 - .../kotlin/com/pitchedapps/frost/LoginActivity.kt | 64 ----------------- .../kotlin/com/pitchedapps/frost/MainActivity.kt | 21 +++--- .../com/pitchedapps/frost/dbflow/FbTabsDb.kt | 62 +++------------- .../com/pitchedapps/frost/facebook/FbCookie.kt | 1 - .../com/pitchedapps/frost/facebook/FbTabs.kt | 24 +++++++ .../com/pitchedapps/frost/facebook/FbToken.kt | 13 ---- .../pitchedapps/frost/facebook/retro/FrostApi.kt | 64 ----------------- .../pitchedapps/frost/facebook/retro/FrostData.kt | 8 --- .../frost/facebook/retro/FrostInterceptor.kt | 29 -------- .../com/pitchedapps/frost/facebook/retro/IFrost.kt | 36 ---------- .../com/pitchedapps/frost/injectors/CssAssets.kt | 25 +++++++ .../com/pitchedapps/frost/injectors/JsBuilder.kt | 36 ---------- .../com/pitchedapps/frost/injectors/JsInjector.kt | 71 +++++++++++++++++- .../pitchedapps/frost/settings/SettingsFragment.kt | 31 ++++++++ .../com/pitchedapps/frost/views/RippleCanvas.kt | 83 ++++++++++++++++++++++ .../com/pitchedapps/frost/views/ViewUtils.kt | 14 ++++ .../com/pitchedapps/frost/web/FrostChromeClient.kt | 15 ++++ .../com/pitchedapps/frost/web/FrostWebView.kt | 2 + .../pitchedapps/frost/web/FrostWebViewClient.kt | 13 +++- .../pitchedapps/frost/injector/JsBuilderTest.kt | 14 ++++ 24 files changed, 314 insertions(+), 350 deletions(-) create mode 100644 app/src/main/assets/facebook.scss delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/FbActivity.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/FbTabs.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/FbToken.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostData.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostInterceptor.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt delete mode 100644 app/src/main/kotlin/com/pitchedapps/frost/injectors/JsBuilder.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/settings/SettingsFragment.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/views/ViewUtils.kt create mode 100644 app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt create mode 100644 app/src/test/kotlin/com/pitchedapps/frost/injector/JsBuilderTest.kt (limited to 'app/src') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d44aa950..efda18a2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,10 +2,6 @@ - - - - diff --git a/app/src/main/assets/facebook.scss b/app/src/main/assets/facebook.scss new file mode 100644 index 00000000..0798eb8a --- /dev/null +++ b/app/src/main/assets/facebook.scss @@ -0,0 +1,11 @@ +$text: #fff; +$background: #000; +$transparent: transparent; + +#header[data-sigil="MTopBlueBarHeader"], #header-notices { + display: None !important; +} + +textarea, ._2gn4 { + background-color: $transparent !important; +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FbActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/FbActivity.kt deleted file mode 100644 index 0132a4db..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/FbActivity.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.pitchedapps.frost - -import android.os.Bundle -import android.support.annotation.CallSuper -import android.support.v7.app.AppCompatActivity -import com.facebook.AccessToken -import com.pitchedapps.frost.utils.L - -/** - * Created by Allan Wang on 2017-05-29. - */ -open class FbActivity : AppCompatActivity() { - var accessToken: AccessToken? = null - - @CallSuper - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - accessToken = AccessToken.getCurrentAccessToken() - L.e("Access ${accessToken?.token}") - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index b5e7f5df..068cf739 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -2,7 +2,6 @@ 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.GlideUtils import com.pitchedapps.frost.utils.Prefs @@ -23,7 +22,6 @@ class FrostApp : Application() { FlowManager.init(FlowConfig.Builder(this).build()) Prefs(this) GlideUtils(this) - FrostApi(this) FbCookie() super.onCreate() } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt deleted file mode 100644 index 8ef60991..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.pitchedapps.frost - -import android.content.Intent -import android.content.pm.PackageInstaller -import android.os.Bundle -import android.widget.Button -import com.facebook.CallbackManager -import com.facebook.FacebookCallback -import com.facebook.FacebookException -import com.facebook.login.LoginBehavior -import com.facebook.login.LoginManager -import com.facebook.login.LoginResult -import com.facebook.login.widget.LoginButton -import com.pitchedapps.frost.utils.L -import java.util.* - - -/** - * Created by Allan Wang on 2017-05-29. - */ -class LoginActivity : FbActivity() { - lateinit var callback: CallbackManager - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.login_teest) - val loginButton = findViewById(R.id.login_button) as LoginButton - loginButton.loginBehavior = LoginBehavior.WEB_VIEW_ONLY - loginButton.setReadPermissions("email") - val switchh = findViewById(R.id.switchh) as Button - switchh.setOnClickListener { - startActivity(Intent(this, MainActivity::class.java)) - finish() - } - // If using in a fragment -// loginButton.setFragment(this) - // Other app specific specialization - - // Callback registration - callback = CallbackManager.Factory.create() - loginButton.registerCallback(callback, object : FacebookCallback { - override fun onSuccess(loginResult: LoginResult) { - L.e("Success") - L.e("Success ${loginResult.accessToken.token}") - } - - override fun onCancel() { - // App code - L.e("Cancel") - } - - override fun onError(exception: FacebookException) { - // App code - L.e("Error") - } - }) - -// LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("public_profile")); - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - callback.onActivityResult(requestCode, resultCode, data) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt index 7bff3b58..41de3578 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt @@ -1,6 +1,5 @@ package com.pitchedapps.frost -import android.content.Intent import android.os.Bundle import android.support.design.widget.FloatingActionButton import android.support.design.widget.Snackbar @@ -13,16 +12,14 @@ import android.support.v7.widget.Toolbar import android.view.Menu import android.view.MenuItem import butterknife.ButterKnife -import com.pitchedapps.frost.dbflow.FbTab import com.pitchedapps.frost.dbflow.loadFbTabs import com.pitchedapps.frost.dbflow.saveAsync -import com.pitchedapps.frost.facebook.retro.FrostApi.frostApi -import com.pitchedapps.frost.facebook.retro.enqueueFrost +import com.pitchedapps.frost.facebook.FbTab import com.pitchedapps.frost.fragments.BaseFragment import com.pitchedapps.frost.fragments.WebFragment -import com.pitchedapps.frost.utils.* -import io.reactivex.subjects.PublishSubject -import io.reactivex.subjects.Subject +import com.pitchedapps.frost.utils.Changelog +import com.pitchedapps.frost.utils.bindView +import com.pitchedapps.frost.utils.toDrawable class MainActivity : AppCompatActivity() { @@ -38,7 +35,7 @@ class MainActivity : AppCompatActivity() { ButterKnife.bind(this) setSupportActionBar(toolbar) - adapter = SectionsPagerAdapter(supportFragmentManager, loadFbTabs(this@MainActivity)) + adapter = SectionsPagerAdapter(supportFragmentManager, loadFbTabs()) viewPager.adapter = adapter viewPager.offscreenPageLimit = 5 setupTabs() @@ -69,11 +66,11 @@ class MainActivity : AppCompatActivity() { // as you specify a parent activity in AndroidManifest.xml. when (item.itemId) { R.id.action_settings -> { - startActivity(Intent(this, LoginActivity::class.java)) - finish() +// startActivity(Intent(this, LoginActivity::class.java)) +// finish() } R.id.action_changelog -> Changelog.show(this) - R.id.action_call -> frostApi.me().enqueueFrost { _, response -> L.e(response.toString()) } + R.id.action_call -> {} R.id.action_db -> adapter.pages.saveAsync(this) R.id.action_restart -> { finish(); @@ -104,6 +101,6 @@ class MainActivity : AppCompatActivity() { override fun getCount() = pages.size - override fun getPageTitle(position: Int): CharSequence = pages[position].title + override fun getPageTitle(position: Int): CharSequence = getString(pages[position].titleId) } } 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 bfd0b0fe..bed50527 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/FbTabsDb.kt @@ -1,15 +1,9 @@ package com.pitchedapps.frost.dbflow import android.content.Context -import android.support.annotation.StringRes -import com.mikepenz.community_material_typeface_library.CommunityMaterial -import com.mikepenz.google_material_typeface_library.GoogleMaterial -import com.mikepenz.iconics.typeface.IIcon -import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic -import com.pitchedapps.frost.R +import com.pitchedapps.frost.facebook.FbTab import com.pitchedapps.frost.utils.L 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.from @@ -26,61 +20,21 @@ object FbTabsDb { const val VERSION = 1 } -data class FbTab(val title: String, val icon: IIcon, val url: String) - -@Table(database = FbTabsDb::class, allFields = true) -data class FbTabModel( - var title: String = "", - @ForeignKey(saveForeignKeyModel = true, deleteForeignKeyModel = false) var icon: IIconModel = IIconModel(), - @PrimaryKey var url: String = "") : BaseModel() { - constructor(fbTab: FbTab) : this(fbTab.title, IIconModel(fbTab.icon), fbTab.url) - - fun toFbTab() = FbTab(title, icon.toIIcon(), url) -} - @Table(database = FbTabsDb::class, allFields = true) -data class IIconModel(var type: Int = -1, @PrimaryKey var name: String = "") { - constructor(icon: IIcon) : this(when (icon) { - is CommunityMaterial.Icon -> 0 - is GoogleMaterial.Icon -> 1 - is MaterialDesignIconic.Icon -> 2 - else -> -1 - }, icon.toString()) +data class FbTabModel(@PrimaryKey var position: Int = -1, var tab: FbTab = FbTab.FEED) : BaseModel() - fun toIIcon(): IIcon = when (type) { - 0 -> CommunityMaterial.Icon.valueOf(name) - 1 -> GoogleMaterial.Icon.valueOf(name) - 2 -> MaterialDesignIconic.Icon.valueOf(name) - else -> CommunityMaterial.Icon.cmd_newspaper - } -} - -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?disable_interstitial=1"), - 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) -} - //BOOKMARKS("https://touch.facebook.com/bookmarks"), //SEARCH("https://touch.facebook.com/search"), -fun loadFbTabs(c: Context): List { - val tabs: List? = SQLite.select().from(FbTabModel::class).queryList() - if (tabs?.isNotEmpty() ?: false) return tabs!!.map { it.toFbTab() } - L.e("No tabs; loading default") - return listOf(FbUrl.FEED, FbUrl.MESSAGES, FbUrl.FRIENDS, FbUrl.NOTIFICATIONS).map { it.tabInfo(c) } +fun loadFbTabs(): List { + val tabs: List? = SQLite.select().from(FbTabModel::class).orderBy(FbTabModel_Table.position, true).queryList() + if (tabs?.isNotEmpty() ?: false) return tabs!!.map { it.tab } + L.d("No tabs; loading default") + return listOf(FbTab.FEED, FbTab.MESSAGES, FbTab.FRIENDS, FbTab.NOTIFICATIONS) } fun List.saveAsync(c: Context) { - map { FbTabModel(it) }.replace(c, FbTabsDb.NAME) + mapIndexed { index, fbTab -> FbTabModel(index, fbTab) }.replace(c, FbTabsDb.NAME) } \ 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 index 3316bb65..e44b872a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbCookie.kt @@ -1,7 +1,6 @@ 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.removeCookie import com.pitchedapps.frost.dbflow.saveFbCookie diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbTabs.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbTabs.kt new file mode 100644 index 00000000..d391d20f --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbTabs.kt @@ -0,0 +1,24 @@ +package com.pitchedapps.frost.facebook + +import android.content.Context +import android.support.annotation.StringRes +import com.mikepenz.community_material_typeface_library.CommunityMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial +import com.mikepenz.iconics.typeface.IIcon +import com.mikepenz.material_design_iconic_typeface_library.MaterialDesignIconic +import com.pitchedapps.frost.R + +enum class FbTab(@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?disable_interstitial=1&rdr"), + NOTIFICATIONS(R.string.notifications, MaterialDesignIconic.Icon.gmi_globe, "notifications"); + + val url = "$FB_URL_BASE$relativeUrl" +} + +const val FACEBOOK_COM = "facebook.com" +const val FB_URL_BASE = "https://m.facebook.com/" \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbToken.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbToken.kt deleted file mode 100644 index 22dc25f7..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbToken.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.pitchedapps.frost.facebook - -import com.facebook.AccessToken - -/** - * Created by Allan Wang on 2017-05-30. - */ -val token: String? - get() = AccessToken.getCurrentAccessToken()?.token - -fun setToken() { - -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt deleted file mode 100644 index f82c041c..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.pitchedapps.frost.facebook.retro - -import android.content.Context -import com.facebook.stetho.okhttp3.StethoInterceptor -import com.google.gson.GsonBuilder -import com.pitchedapps.frost.BuildConfig -import com.pitchedapps.frost.utils.L -import io.reactivex.schedulers.Schedulers -import okhttp3.* -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory -import retrofit2.converter.gson.GsonConverterFactory -import java.io.File - -/** - * Created by Allan Wang on 2017-05-30. - * - * API for data retrieval - */ -object FrostApi { - - lateinit var frostApi: IFrost - - operator fun invoke(context: Context) { - val cacheDir = File(context.cacheDir, "responses") - val cacheSize = 5L * 1024 * 1024 //10MiB - val cache = Cache(cacheDir, cacheSize) - - val client = OkHttpClient.Builder() - .addInterceptor(FrostInterceptor(context)) - .cookieJar(object : CookieJar { - override fun saveFromResponse(url: HttpUrl, cookies: List) { - L.e("COOKIES") - L.e(url.toString()) - cookies.forEach { c -> L.e(c.toString()) } - } - - override fun loadForRequest(url: HttpUrl): List { - TODO("not implemented") //To change body of created functions use File | Settings | File Templates. - } - - }) - .cache(cache) - - - //add logger and stetho last - - if (BuildConfig.DEBUG || BuildConfig.BUILD_TYPE == "releaseTest") { //log if not full release - client.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - client.addNetworkInterceptor(StethoInterceptor()) - } - - val gson = GsonBuilder().setLenient() - - val retrofit = Retrofit.Builder() - .baseUrl("https://touch.facebook.com/") - .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) - .addConverterFactory(GsonConverterFactory.create(gson.create())) - .client(client.build()) - .build(); - frostApi = retrofit.create(IFrost::class.java) - } -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostData.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostData.kt deleted file mode 100644 index 07d686a7..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostData.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.pitchedapps.frost.facebook.retro - -/** - * Created by Allan Wang on 2017-05-30. - * - * Collection of Graph API outputs - */ -data class Me(val name: String, val id: String) \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostInterceptor.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostInterceptor.kt deleted file mode 100644 index f745aedf..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostInterceptor.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.pitchedapps.frost.facebook.retro - -import android.content.Context -import com.pitchedapps.frost.facebook.token -import com.pitchedapps.frost.utils.Utils -import okhttp3.Interceptor -import okhttp3.Response - -/** - * Created by Allan Wang on 2017-05-30. - */ -private val maxStale = 60 * 60 * 24 * 28 //maxAge to get from cache if online (4 weeks) -const val ACCESS_TOKEN = "access_token" - -class FrostInterceptor(val context: Context) : Interceptor { - - override fun intercept(chain: Interceptor.Chain): Response? { - val request = chain.request() - val requestBuilder = request.newBuilder() - val urlBase = request.url() - val urlWithToken = urlBase.newBuilder() - if (urlBase.queryParameter(ACCESS_TOKEN) == null && token != null) - urlWithToken.addQueryParameter(ACCESS_TOKEN, token) - requestBuilder.url(urlWithToken.build()) - if (!Utils.isNetworkAvailable(context)) requestBuilder.addHeader("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) - return chain.proceed(requestBuilder.build()) - } - -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt deleted file mode 100644 index b9048fe0..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.pitchedapps.frost.facebook.retro - -import com.pitchedapps.frost.facebook.token -import com.pitchedapps.frost.utils.L -import okhttp3.ResponseBody -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response -import retrofit2.http.GET -import retrofit2.http.Query - -/** - * Created by Allan Wang on 2017-05-30. - */ -interface IFrost { - - @GET("me") - fun me(): Call - - -} - -fun Call.enqueueFrost(success: (call: Call, response: Response) -> Unit) { - this.enqueue(object : Callback { - override fun onFailure(call: Call?, t: Throwable?) { - L.e("Frost enqueue error") - } - - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful && !call.isCanceled) - success.invoke(call, response) - else - L.e("Frost response received but not successful") - } - }) -} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt new file mode 100644 index 00000000..74a55888 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssAssets.kt @@ -0,0 +1,25 @@ +package com.pitchedapps.frost.injectors + +import android.webkit.WebView +import com.pitchedapps.frost.utils.L + +/** + * Created by Allan Wang on 2017-05-31. + */ +enum class CssAssets(val file: String) { + BASE("facebook.min.css"); + + var content: String? = null + var injector: JsInjector? = null + + fun inject(webView: WebView, callback: ((String) -> Unit)?) { + if (injector == null) { + if (content == null) + content = webView.context.assets.open(file).bufferedReader().use { it.readText() } + injector = JsBuilder().css(content!!).build() + } + injector!!.inject(webView, callback) + L.d("CSS ${injector!!.function}") + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsBuilder.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsBuilder.kt deleted file mode 100644 index 9844e5d5..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsBuilder.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.pitchedapps.frost.injectors - -import android.webkit.WebView - -/** - * Created by Allan Wang on 2017-05-31. - */ -class JsBuilder { - 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()) - } - - fun removeAllStyles() { - - } - - override fun toString() = build() -} \ 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 index 2d1659cc..c7b4eaf8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt @@ -5,8 +5,73 @@ import android.webkit.WebView /** * Created by Allan Wang on 2017-05-31. */ -object JsInjector { - fun inject(webView: WebView) { +enum class JsActions(val function: String) { + HIDE("style.display='none'"), + REMOVE("remove()") +} +class VariableGenerator { + + var count = 0 + + val next: String + get() { + var key = count + count++ + if (key == 0) return "a" + val name = StringBuilder() + while (key > 0) { + name.append(((key % 26) + 'a'.toInt()).toChar()) + key /= 26 + } + return name.toString() + } + + fun reset() { + count = 0 + } +} + +class JsBuilder { + + private val map: MutableMap> = mutableMapOf() + private val v = VariableGenerator() + private val css: StringBuilder by lazy { StringBuilder() } + + fun append(action: JsActions, vararg ids: String): JsBuilder { + ids.forEach { id -> map[id]?.add(action) ?: map.put(id, mutableSetOf(action)) } + return this + } + + fun css(css: String): JsBuilder { + this.css.append(css.trim()) + return this + } + + fun build() = JsInjector(toString()) + + override fun toString(): String { + v.reset() + val builder = StringBuilder().append("!function(){") + map.forEach { id, actions -> + if (actions.size == 1) { + builder.append("document.getElementById('$id').${actions.first().function};") + } else { + val name = v.next + builder.append("var $name=document.getElementById('$id');") + actions.forEach { a -> builder.append("$name.${a.function};") } + } + } + if (css.isNotBlank()) { + val name = v.next + builder.append("var $name=document.createElement('style');$name.innerHTML='$css';document.head.appendChild($name);") + } + return builder.append("}()").toString() + } +} + +class JsInjector(val function: String) { + fun inject(webView: WebView, callback: ((String) -> Unit)? = null) { + webView.evaluateJavascript(function, { value -> callback?.invoke(value) }) } -} \ No newline at end of file +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/SettingsFragment.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/SettingsFragment.kt new file mode 100644 index 00000000..78ad324a --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/SettingsFragment.kt @@ -0,0 +1,31 @@ +package com.pitchedapps.frost.settings + +import android.os.Bundle +import android.support.v7.preference.AndroidResources +import android.support.v7.preference.PreferenceFragmentCompat +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.pitchedapps.frost.views.RippleCanvas +import com.pitchedapps.frost.views.matchParent + +/** + * Created by Allan Wang on 2017-05-31. + */ +class SettingsFragment : PreferenceFragmentCompat() { + + lateinit var ripple: RippleCanvas + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { + val view = super.onCreateView(inflater, container, savedInstanceState)!! + val frame = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER) as ViewGroup + ripple = RippleCanvas(frame.context) + ripple.matchParent() + frame.addView(ripple, 0) + return view + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt new file mode 100644 index 00000000..719a01cc --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt @@ -0,0 +1,83 @@ +package com.pitchedapps.frost.views + +import android.animation.ValueAnimator +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.util.AttributeSet +import android.view.View + +/** + * Created by Allan Wang on 2016-11-17. + * + * + * Canvas drawn ripples that keep the previous color + * Extends to view dimensions + * Supports multiple ripples from varying locations + */ +class RippleCanvas @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : View(context, attrs, defStyleAttr) { + val paint: Paint = Paint() + var baseColor = Color.TRANSPARENT + val ripples: MutableList = mutableListOf() + + init { + paint.isAntiAlias = true + paint.style = Paint.Style.FILL + } + + override fun onDraw(canvas: Canvas) { + canvas.drawColor(baseColor) + val itr = ripples.iterator() + while (itr.hasNext()) { + val r = itr.next() + paint.color = r.color + canvas.drawCircle(r.x, r.y, r.radius, paint) + if (r.radius == r.maxRadius) { + itr.remove() + baseColor = r.color + } + } + } + + @JvmOverloads fun ripple(color: Int, startX: Float = 0f, startY: Float = 0f, duration: Int = 1000) { + var x = startX + var y = startY + val w = width.toFloat() + val h = height.toFloat() + if (x == MIDDLE) + x = w / 2 + else if (x > w) x = 0f + if (y == MIDDLE) + y = h / 2 + else if (y > h) y = 0f + val maxRadius = Math.hypot(Math.max(x, w - x).toDouble(), Math.max(y, h - y).toDouble()).toFloat() + val ripple = Ripple(color, x, y, 0f, maxRadius) + ripples.add(ripple) + val animator = ValueAnimator.ofFloat(0f, maxRadius) + animator.duration = duration.toLong() + animator.addUpdateListener { animation -> + ripple.setRadius(animation.animatedValue as Float) + invalidate() + } + animator.start() + } + + fun set(color: Int) { + baseColor = color + ripples.clear() + invalidate() + } + + inner class Ripple internal constructor(val color: Int, val x: Float, val y: Float, var radius: Float, val maxRadius: Float) { + internal fun setRadius(r: Float) { + radius = r + } + } + + companion object { + val MIDDLE = -1.0f + } +} diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/ViewUtils.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/ViewUtils.kt new file mode 100644 index 00000000..513287ef --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/ViewUtils.kt @@ -0,0 +1,14 @@ +package com.pitchedapps.frost.views + +import android.view.View +import android.view.ViewGroup + +/** + * Created by Allan Wang on 2017-05-31. + */ +fun View.matchParent() { + with (layoutParams) { + height = ViewGroup.LayoutParams.MATCH_PARENT + width = ViewGroup.LayoutParams.MATCH_PARENT + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt new file mode 100644 index 00000000..8367255a --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClient.kt @@ -0,0 +1,15 @@ +package com.pitchedapps.frost.web + +import android.webkit.ConsoleMessage +import android.webkit.WebChromeClient +import com.pitchedapps.frost.utils.L + +/** + * Created by Allan Wang on 2017-05-31. + */ +class FrostChromeClient:WebChromeClient() { + override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean { + L.d("Console ${consoleMessage.lineNumber()}: ${consoleMessage.message()}") + return super.onConsoleMessage(consoleMessage) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt index 27312763..de7860da 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt @@ -49,8 +49,10 @@ class FrostWebView @JvmOverloads constructor( @SuppressLint("SetJavaScriptEnabled") fun setupWebview() { settings.javaScriptEnabled = true + settings.domStorageEnabled = true setLayerType(View.LAYER_TYPE_HARDWARE, null) setWebViewClient(FrostWebViewClient(observable)) + setWebChromeClient(FrostChromeClient()) } override fun loadUrl(url: String?) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt index 14fcc22a..379bb22d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt @@ -1,8 +1,11 @@ package com.pitchedapps.frost.web import android.graphics.Bitmap +import android.view.View import android.webkit.* +import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FbCookie +import com.pitchedapps.frost.injectors.CssAssets import com.pitchedapps.frost.utils.L import io.reactivex.subjects.Subject @@ -11,11 +14,10 @@ import io.reactivex.subjects.Subject */ class FrostWebViewClient(val observable: Subject) : WebViewClient() { - private var injectionCount: Int = 0 - companion object { //Collections of jewels mapped with url match -> id val jewelMap: Map = mapOf("a" to "b") + fun test() { } @@ -29,16 +31,21 @@ class FrostWebViewClient(val observable: Subject) : WebViewClient() { override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { super.onPageStarted(view, url, favicon) - injectionCount = 0 observable.onNext(WebStatus.LOADING) L.d("FWV Loading $url") + if (!url.contains(FACEBOOK_COM)) return if (url.contains("logout.php")) FbCookie.logout() + view.visibility = View.INVISIBLE } override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) + if (!url.contains(FACEBOOK_COM)) return observable.onNext(WebStatus.LOADED) FbCookie.checkUserId(url, CookieManager.getInstance().getCookie(url)) + CssAssets.BASE.inject(view, { + view.visibility = View.VISIBLE + }) } fun logout() { diff --git a/app/src/test/kotlin/com/pitchedapps/frost/injector/JsBuilderTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/injector/JsBuilderTest.kt new file mode 100644 index 00000000..d5fcdc50 --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/injector/JsBuilderTest.kt @@ -0,0 +1,14 @@ +package com.pitchedapps.frost.injector + +import com.pitchedapps.frost.injectors.JsInjector +import org.junit.Test + +/** + * Created by Allan Wang on 2017-05-31. + */ +class JsBuilderTest { + @Test + fun misc() { + println(JsInjector.idRemovals.function) + } +} \ No newline at end of file -- cgit v1.2.3