1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-09 20:42:34 +01:00

working injectors and redid tabs db

This commit is contained in:
Allan Wang 2017-06-01 00:21:04 -07:00
parent 8618670b82
commit 4cbcabb122
30 changed files with 406 additions and 444 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@
.externalNativeBuild
/app/src/main/res/values/strings_facebook.xml
/app/src/main/kotlin/com/pitchedapps/frost/facebook/Private.kt
*.min.css

View File

@ -1,71 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.25" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="false" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_EMOJI_URL" value="false" />
<option name="GITHUB_LISTS" value="true" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
</project>

View File

@ -3,6 +3,74 @@
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="MarkdownProjectSettings">
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.25" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="LINE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true">
<PanelProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
</PanelProvider>
</PreviewSettings>
<ParserSettings gitHubSyntaxChange="false">
<PegdownExtensions>
<option name="ABBREVIATIONS" value="false" />
<option name="ANCHORLINKS" value="true" />
<option name="ASIDE" value="false" />
<option name="ATXHEADERSPACE" value="true" />
<option name="AUTOLINKS" value="true" />
<option name="DEFINITIONS" value="false" />
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
<option name="FENCED_CODE_BLOCKS" value="true" />
<option name="FOOTNOTES" value="false" />
<option name="HARDWRAPS" value="false" />
<option name="INSERTED" value="false" />
<option name="QUOTES" value="false" />
<option name="RELAXEDHRULES" value="true" />
<option name="SMARTS" value="false" />
<option name="STRIKETHROUGH" value="true" />
<option name="SUBSCRIPT" value="false" />
<option name="SUPERSCRIPT" value="false" />
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
<option name="SUPPRESS_INLINE_HTML" value="false" />
<option name="TABLES" value="true" />
<option name="TASKLISTITEMS" value="true" />
<option name="TOC" value="false" />
<option name="WIKILINKS" value="true" />
</PegdownExtensions>
<ParserOptions>
<option name="COMMONMARK_LISTS" value="false" />
<option name="DUMMY" value="false" />
<option name="EMOJI_SHORTCUTS" value="true" />
<option name="FLEXMARK_FRONT_MATTER" value="false" />
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="false" />
<option name="GFM_TABLE_RENDERING" value="true" />
<option name="GITBOOK_URL_ENCODING" value="false" />
<option name="GITHUB_EMOJI_URL" value="false" />
<option name="GITHUB_LISTS" value="true" />
<option name="GITHUB_WIKI_LINKS" value="true" />
<option name="JEKYLL_FRONT_MATTER" value="false" />
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
</ParserOptions>
</ParserSettings>
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true">
<GeneratorProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
</GeneratorProvider>
<headerTop />
<headerBottom />
<bodyTop />
<bodyBottom />
</HtmlSettings>
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssTextEnabled="false" isDynamicPageWidth="true">
<StylesheetProvider>
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
</StylesheetProvider>
<ScriptProviders />
<cssText />
</CssSettings>
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" />
<LinkMapSettings>
<textMaps />
</LinkMapSettings>
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
@ -27,6 +95,22 @@
</value>
</option>
</component>
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id />
</State>
</expanded-state>
<selected-state>
<State>
<id>Android</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
@ -43,20 +127,4 @@
<component name="ProjectType">
<option name="id" value="Android" />
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>1.8</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

3
.idea/scopes/CSS.xml Normal file
View File

@ -0,0 +1,3 @@
<component name="DependencyValidationManager">
<scope name="CSS" pattern="file[Frost-for-Facebook]:scss/*" />
</component>

View File

@ -93,14 +93,12 @@ dependencies {
compile "org.jsoup:jsoup:${JSOUP}"
compile "com.facebook.android:facebook-android-sdk:${FB_SDK}"
compile "org.jetbrains.anko:anko:${ANKO}"
compile "com.squareup.retrofit2:retrofit:${RETROFIT}"
compile "com.squareup.retrofit2:adapter-rxjava2:${RETROFIT}"
compile "com.squareup.retrofit2:converter-gson:${RETROFIT}"
compile "com.squareup.okhttp3:logging-interceptor:${OKHTTP_INTERCEPTOR}"
// compile "com.squareup.retrofit2:retrofit:${RETROFIT}"
// compile "com.squareup.retrofit2:adapter-rxjava2:${RETROFIT}"
// compile "com.squareup.retrofit2:converter-gson:${RETROFIT}"
// compile "com.squareup.okhttp3:logging-interceptor:${OKHTTP_INTERCEPTOR}"
compile "com.github.bumptech.glide:glide:${GLIDE}"
annotationProcessor "com.github.bumptech.glide:compiler:${GLIDE}"

View File

@ -2,10 +2,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.pitchedapps.frost">
<!-- To auto-complete the email text field in the login form with the user's emails -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

View File

@ -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;
}

View File

@ -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}")
}
}

View File

@ -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()
}

View File

@ -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<LoginResult> {
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)
}
}

View File

@ -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)
}
}

View File

@ -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)
data class FbTabModel(@PrimaryKey var position: Int = -1, var tab: FbTab = FbTab.FEED) : BaseModel()
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())
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<FbTab> {
val tabs: List<FbTabModel>? = 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<FbTab> {
val tabs: List<FbTabModel>? = 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<FbTab>.saveAsync(c: Context) {
map { FbTabModel(it) }.replace(c, FbTabsDb.NAME)
mapIndexed { index, fbTab -> FbTabModel(index, fbTab) }.replace(c, FbTabsDb.NAME)
}

View File

@ -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

View File

@ -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/"

View File

@ -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() {
}

View File

@ -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<Cookie>) {
L.e("COOKIES")
L.e(url.toString())
cookies.forEach { c -> L.e(c.toString()) }
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
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)
}
}

View File

@ -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)

View File

@ -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())
}
}

View File

@ -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<ResponseBody>
}
fun <T> Call<T>.enqueueFrost(success: (call: Call<T>, response: Response<T>) -> Unit) {
this.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>?, t: Throwable?) {
L.e("Frost enqueue error")
}
override fun onResponse(call: Call<T>, response: Response<T>) {
if (response.isSuccessful && !call.isCanceled)
success.invoke(call, response)
else
L.e("Frost response received but not successful")
}
})
}

View File

@ -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}")
}
}

View File

@ -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()
}

View File

@ -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<String, MutableSet<JsActions>> = 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) })
}
}

View File

@ -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
}
}

View File

@ -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<Ripple> = 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
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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?) {

View File

@ -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<WebStatus>) : WebViewClient() {
private var injectionCount: Int = 0
companion object {
//Collections of jewels mapped with url match -> id
val jewelMap: Map<String, String> = mapOf("a" to "b")
fun test() {
}
@ -29,16 +31,21 @@ class FrostWebViewClient(val observable: Subject<WebStatus>) : 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() {

View File

@ -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)
}
}

View File

@ -31,7 +31,6 @@ RX_JAVA=2.1.0
RX_ANDROID=2.0.1
RX_BINDING=2.0.0
JSOUP=1.10.2
FB_SDK=[4,5)
STETHO=1.5.0
ANKO=0.10.0
GLIDE=4.0.0-RC0