diff options
author | Allan Wang <me@allanwang.ca> | 2017-07-13 13:50:00 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-13 13:50:00 -0700 |
commit | 91119de328bf5f4e8c945f8fb470453319b9f0ed (patch) | |
tree | 9ba1786f9cd8488a0cc0dfb247e1b387a4161cfb | |
parent | de34d09f975079d5c044eae6da7ed92605009faf (diff) | |
download | frost-91119de328bf5f4e8c945f8fb470453319b9f0ed.tar.gz frost-91119de328bf5f4e8c945f8fb470453319b9f0ed.tar.bz2 frost-91119de328bf5f4e8c945f8fb470453319b9f0ed.zip |
Dev 1.2.2 - Add framework for messenger notifications (#47)
* Update KAU to v2.0
* Only inject theme for facebook and inject js after
* Clean up menu loading logic
* Add path null check
* Remove .idea files
* Add url formatter testers
* Update tests and check url nullability - Fixes
* Create instant messaging parser
* Shorted notification log and remove unnecessary null checks
* Make migration buildable
* Test message parser
* finalize messenger notifs for now
45 files changed, 234 insertions, 487 deletions
@@ -1,16 +1,10 @@ *.iml .gradle /local.properties -/.idea/workspace.xml -/.idea/libraries +/.idea .DS_Store /build /captures .externalNativeBuild *.min.css .sass-cache/ -files/gplay-keys.json -files/play.keystore -files/play.properties -files/test.keystore -files/update-dev.sh diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 43b000d9..00000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CompilerConfiguration"> - <resourceExtensions /> - <wildcardResourcePatterns> - <entry name="!?*.java" /> - <entry name="!?*.form" /> - <entry name="!?*.class" /> - <entry name="!?*.groovy" /> - <entry name="!?*.scala" /> - <entry name="!?*.flex" /> - <entry name="!?*.kt" /> - <entry name="!?*.clj" /> - <entry name="!?*.aj" /> - </wildcardResourcePatterns> - <annotationProcessing> - <profile default="true" name="Default" enabled="true"> - <processorPath useClasspath="true" /> - </profile> - </annotationProcessing> - </component> -</project>
\ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf33..00000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ -<component name="CopyrightManager"> - <settings default="" /> -</component>
\ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index 97626ba4..00000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="Encoding"> - <file url="PROJECT" charset="UTF-8" /> - </component> -</project>
\ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 7ac24c77..00000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="GradleSettings"> - <option name="linkedExternalProjectsSettings"> - <GradleProjectSettings> - <option name="distributionType" value="DEFAULT_WRAPPED" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="modules"> - <set> - <option value="$PROJECT_DIR$" /> - <option value="$PROJECT_DIR$/app" /> - </set> - </option> - <option name="resolveModulePerSourceSet" value="false" /> - </GradleProjectSettings> - </option> - </component> -</project>
\ No newline at end of file diff --git a/.idea/markdown-navigator.xml b/.idea/markdown-navigator.xml deleted file mode 100644 index 838ef398..00000000 --- a/.idea/markdown-navigator.xml +++ /dev/null @@ -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>
\ No newline at end of file diff --git a/.idea/markdown-navigator/profiles_settings.xml b/.idea/markdown-navigator/profiles_settings.xml deleted file mode 100644 index 57927c5a..00000000 --- a/.idea/markdown-navigator/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ -<component name="MarkdownNavigator.ProfileManager"> - <settings default="" pdf-export="" /> -</component>
\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 085136f8..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,78 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="EntryPointsManager"> - <entry_points version="2.0" /> - </component> - <component name="NullableNotNullManager"> - <option name="myDefaultNullable" value="android.support.annotation.Nullable" /> - <option name="myDefaultNotNull" value="android.support.annotation.NonNull" /> - <option name="myNullables"> - <value> - <list size="4"> - <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" /> - <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" /> - <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" /> - <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" /> - </list> - </value> - </option> - <option name="myNotNulls"> - <value> - <list size="4"> - <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" /> - <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" /> - <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" /> - <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" /> - </list> - </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" /> - <OptionsSetting value="true" id="Checkout" /> - <OptionsSetting value="true" id="Update" /> - <OptionsSetting value="true" id="Status" /> - <OptionsSetting value="true" id="Edit" /> - <ConfirmationsSetting value="0" id="Add" /> - <ConfirmationsSetting value="0" id="Remove" /> - </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK"> - <output url="file://$PROJECT_DIR$/build/classes" /> - </component> - <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>
\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 7451d719..00000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="ProjectModuleManager"> - <modules> - <module fileurl="file://$PROJECT_DIR$/Frost-for-Facebook.iml" filepath="$PROJECT_DIR$/Frost-for-Facebook.iml" /> - <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" /> - </modules> - </component> -</project>
\ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 7f68460d..00000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="RunConfigurationProducerService"> - <option name="ignoredProducers"> - <set> - <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" /> - <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" /> - <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" /> - </set> - </option> - </component> -</project>
\ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f..00000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="VcsDirectoryMappings"> - <mapping directory="$PROJECT_DIR$" vcs="Git" /> - </component> -</project>
\ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 50015adb..9066b498 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ script: - cd $TRAVIS_BUILD_DIR/ - printf "Starting script\n" - ./gradlew --quiet androidGitVersion -- if [[ "$TRAVIS_BRANCH" == "master" ]]; then ./gradlew publishRelease; else ./gradlew assembleReleaseTest; fi +- if [[ "$TRAVIS_BRANCH" == "master" ]]; then ./gradlew publishRelease; else ./gradlew testReleaseUnitTest assembleReleaseTest; fi notifications: email: false slack: @@ -1,6 +1,5 @@ # Frost-for-Facebook - <a href='https://play.google.com/store/apps/details?id=com.pitchedapps.frost&utm_source=github'><img alt='Get it on Google Play' width="30%" src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png'/></a> Frost is a third party Facebook wrapper geared towards design and functionality. @@ -8,5 +7,7 @@ It contains many features, including: * Support for multiple accounts and fast switching * Full theming across all activities * Overlaying browser to read posts and get right back to your previous task -* [WIP] Notifications and filtering -* [WIP] Full customization on tab and drawer layouts +* Extensive notification support, with bundling, filtering, battery friendly scheduling, icons, and multi user support +* Context menu from any link through long press +* Reactive based loading +* The transparency of open sourced development diff --git a/app/build.gradle b/app/build.gradle index 7df9c5d7..45bdaa16 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,12 +26,6 @@ android { prefix 'v' } - playAccountConfigs { - defaultAccountConfig { - jsonFile = file('../files/gplay-keys.json') - } - } - defaultConfig { applicationId "${project.APP_GROUP}." + project.APP_ID.toLowerCase() minSdkVersion Integer.parseInt(project.MIN_SDK) @@ -124,8 +118,11 @@ dependencies { testCompile 'junit:junit:4.12' testCompile "org.robolectric:robolectric:${ROBOELECTRIC}" - - compile "ca.allanwang.kau:core:${KAU}" + compile "ca.allanwang.kau:about:$KAU" + compile "ca.allanwang.kau:colorpicker:$KAU" +// compile "ca.allanwang.kau:imagepicker:$KAU" + compile "ca.allanwang.kau:kpref-activity:$KAU" + compile "ca.allanwang.kau:searchview:$KAU" compile "org.jetbrains.kotlin:kotlin-stdlib:${KOTLIN}" testCompile "org.jetbrains.kotlin:kotlin-test-junit:${KOTLIN}" diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 9df5ace7..7d6324cb 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -22,14 +22,6 @@ -keeppackagenames org.jsoup.nodes # IAB -keep class com.android.vending.billing.** -# About libs -#-keep class .R -#-keep class **.R$* { -# <fields>; -#} --keepclasseswithmembers class **.R$* { - public static final int define_*; -} # Glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public class * extends com.bumptech.glide.AppGlideModule diff --git a/app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java deleted file mode 100644 index 9e27deaf..00000000 --- a/app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.pitchedapps.frost; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.pitchedapps.frost", appContext.getPackageName()); - } -} diff --git a/app/src/main/assets/js/menu.js b/app/src/main/assets/js/menu.js index f8cd03c9..7394c824 100644 --- a/app/src/main/assets/js/menu.js +++ b/app/src/main/assets/js/menu.js @@ -27,7 +27,6 @@ if (!window.hasOwnProperty('frost_menu')) { setTimeout(function() { y.disconnect(); console.log('Unhook styler'); - Frost.handleHtml(document.documentElement.outerHTML); }, 500); } }); diff --git a/app/src/main/assets/js/menu.min.js b/app/src/main/assets/js/menu.min.js index ce878b88..5403e03a 100644 --- a/app/src/main/assets/js/menu.min.js +++ b/app/src/main/assets/js/menu.min.js @@ -15,8 +15,7 @@ var o=document.querySelector(".mSideMenu") for(x.disconnect(),console.log("Found side menu");root.firstChild;)root.removeChild(root.firstChild) ;for(;o.childNodes.length;)root.appendChild(o.childNodes[0]) ;Frost.emit(0),setTimeout(function(){ -y.disconnect(),console.log("Unhook styler"), -Frost.handleHtml(document.documentElement.outerHTML) +y.disconnect(),console.log("Unhook styler") },500) } }) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt index 36809535..6cab2a59 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt @@ -7,10 +7,10 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import ca.allanwang.kau.about.AboutActivityBase +import ca.allanwang.kau.about.LibraryIItem import ca.allanwang.kau.adapters.FastItemThemedAdapter import ca.allanwang.kau.adapters.ThemableIItem import ca.allanwang.kau.adapters.ThemableIItemDelegate -import ca.allanwang.kau.iitems.LibraryIItem import ca.allanwang.kau.utils.* import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.entity.Library diff --git a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt index 66c541ed..e0be330d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt @@ -5,12 +5,12 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import ca.allanwang.kau.changelog.showChangelog -import ca.allanwang.kau.kpref.CoreAttributeContract -import ca.allanwang.kau.kpref.KPrefActivity -import ca.allanwang.kau.kpref.KPrefAdapterBuilder -import ca.allanwang.kau.kpref.items.KPrefItemBase +import ca.allanwang.kau.kpref.activity.CoreAttributeContract +import ca.allanwang.kau.kpref.activity.KPrefActivity +import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder +import ca.allanwang.kau.kpref.activity.items.KPrefItemBase +import ca.allanwang.kau.ui.views.RippleCanvas import ca.allanwang.kau.utils.* -import ca.allanwang.kau.views.RippleCanvas import com.mikepenz.community_material_typeface_library.CommunityMaterial import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.pitchedapps.frost.settings.* diff --git a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt index dda6c066..d197918d 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt @@ -41,6 +41,9 @@ open class WebOverlayActivity : AppCompatActivity(), const val ARG_USER_ID = "arg_user_id" } + val urlTest: String? + get() = intent.extras?.getString(ARG_URL) ?: intent.dataString + open val url: String get() = (intent.extras?.getString(ARG_URL) ?: intent.dataString).formattedFbUrl @@ -49,6 +52,12 @@ open class WebOverlayActivity : AppCompatActivity(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + if (urlTest == null) { + L.eThrow("Empty link on web overlay") + toast(R.string.null_url_overlay) + finish() + return + } setContentView(R.layout.activity_web_overlay) setSupportActionBar(toolbar) supportActionBar?.setDisplayShowHomeEnabled(true) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt index 1585e425..66f5ae64 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt @@ -1,11 +1,10 @@ package com.pitchedapps.frost.dbflow import com.pitchedapps.frost.utils.L -import com.raizlabs.android.dbflow.annotation.ConflictAction -import com.raizlabs.android.dbflow.annotation.Database -import com.raizlabs.android.dbflow.annotation.PrimaryKey -import com.raizlabs.android.dbflow.annotation.Table +import com.raizlabs.android.dbflow.annotation.* import com.raizlabs.android.dbflow.kotlinextensions.* +import com.raizlabs.android.dbflow.sql.SQLiteType +import com.raizlabs.android.dbflow.sql.migration.AlterTableMigration import com.raizlabs.android.dbflow.structure.BaseModel /** @@ -15,13 +14,22 @@ import com.raizlabs.android.dbflow.structure.BaseModel @Database(name = NotificationDb.NAME, version = NotificationDb.VERSION) object NotificationDb { const val NAME = "Notifications" - const val VERSION = 1 + const val VERSION = 2 +} + +@Migration(version = 2, database = NotificationDb::class) +class NotificationMigration2(modelClass: Class<NotificationModel>) : AlterTableMigration<NotificationModel>(modelClass) { + override fun onPreMigrate() { + super.onPreMigrate() + addColumn(SQLiteType.INTEGER, "epochIm") + L.d("Added column") + } } @Table(database = NotificationDb::class, allFields = true, primaryKeyConflict = ConflictAction.REPLACE) -data class NotificationModel(@PrimaryKey var id: Long = -1L, var epoch: Long = -1L) : BaseModel() +data class NotificationModel(@PrimaryKey var id: Long = -1L, var epoch: Long = -1L, var epochIm: Long = -1) : BaseModel() -fun lastNotificationTime(id: Long): Long = (select from NotificationModel::class where (NotificationModel_Table.id eq id)).querySingle()?.epoch ?: -1L +fun lastNotificationTime(id: Long): NotificationModel = (select from NotificationModel::class where (NotificationModel_Table.id eq id)).querySingle() ?: NotificationModel(id = id) fun saveNotificationTime(notificationModel: NotificationModel, callback: (() -> Unit)? = null) { notificationModel.async save { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt index 1090f1f3..e53bb202 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt @@ -15,8 +15,9 @@ class FbUrlFormatter(url: String) { init { var cleanedUrl = url discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "") } + val changed = cleanedUrl != url //note that discardables strip away the first ? decoder.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v) } - val qm = cleanedUrl.indexOf("?") + val qm = cleanedUrl.indexOf(if (changed)"&" else "?") if (qm > -1) { cleanedUrl.substring(qm + 1).split("&").forEach { val p = it.split("=") @@ -58,7 +59,8 @@ class FbUrlFormatter(url: String) { "http://m.facebook.com/l.php?u=", "https://m.facebook.com/l.php?u=", "http://touch.facebook.com/l.php?u=", - "https://touch.facebook.com/l.php?u=" + "https://touch.facebook.com/l.php?u=", + "/video_redirect/?src=" ) @JvmStatic val discardableQueries = arrayOf("ref", "refid") diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt index a83e87dc..cb33e527 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt @@ -15,7 +15,8 @@ enum class CssHider(vararg val items: String) : InjectorContract { "article[data-xt*=sponsor]", "article[data-store*=sponsor]" ), - PEOPLE_YOU_MAY_KNOW("article._d2r") + PEOPLE_YOU_MAY_KNOW("article._d2r"), + MESSENGER("._s15", "[data-testid=info_panel]", "js_i") ; val injector: JsInjector by lazy { JsBuilder().css("${items.joinToString(separator = ",")}{display:none!important}").build() } 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 6af6b1db..2cea55b4 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt @@ -64,6 +64,7 @@ class FrostNotificationTarget(val context: Context, data class NotificationContent(val data: CookieModel, val notifId: Int, val href: String, + val title: String? = null, val text: String, val timestamp: Long, val profileUrl: String) { @@ -81,7 +82,7 @@ data class NotificationContent(val data: CookieModel, val group = "frost_${data.id}" val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0) val notifBuilder = context.frostNotification - .setContentTitle(context.string(R.string.app_name)) + .setContentTitle(title ?: context.string(R.string.app_name)) .setContentText(text) .setContentIntent(pendingIntent) .setCategory(Notification.CATEGORY_SOCIAL) @@ -133,7 +134,7 @@ const val NOTIFICATION_JOB_NOW = 6 /** * Run notification job right now */ -fun Context.fetchNotifications():Boolean { +fun Context.fetchNotifications(): Boolean { val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val serviceComponent = ComponentName(this, NotificationService::class.java) val builder = JobInfo.Builder(NOTIFICATION_JOB_NOW, serviceComponent) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt index 4c03f056..f9d0c63c 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt @@ -7,9 +7,13 @@ import android.support.v4.app.NotificationManagerCompat import ca.allanwang.kau.utils.string import com.pitchedapps.frost.BuildConfig import com.pitchedapps.frost.R -import com.pitchedapps.frost.dbflow.* +import com.pitchedapps.frost.dbflow.CookieModel +import com.pitchedapps.frost.dbflow.lastNotificationTime +import com.pitchedapps.frost.dbflow.loadFbCookie +import com.pitchedapps.frost.dbflow.loadFbCookiesSync import com.pitchedapps.frost.facebook.FACEBOOK_COM import com.pitchedapps.frost.facebook.FbTab +import com.pitchedapps.frost.facebook.USER_AGENT_BASIC import com.pitchedapps.frost.utils.L import com.pitchedapps.frost.utils.Prefs import com.pitchedapps.frost.utils.frostAnswersCustom @@ -31,6 +35,7 @@ class NotificationService : JobService() { companion object { val epochMatcher: Regex by lazy { Regex(":([0-9]*?),") } val notifIdMatcher: Regex by lazy { Regex("notif_id\":([0-9]*?),") } + val messageNotifIdMatcher: Regex by lazy { Regex("thread_fbid_([0-9]+)") } val profMatcher: Regex by lazy { Regex("url\\(\"(.*?)\"\\)") } } @@ -59,14 +64,26 @@ class NotificationService : JobService() { return true } + fun logNotif(text: String): NotificationContent? { + L.eThrow("NotificationService: $text") + return null + } + fun fetchNotifications(data: CookieModel) { + fetchGeneralNotifications(data) +// fetchMessageNotifications(data) + debugNotification("Hello") + } + + fun fetchGeneralNotifications(data: CookieModel) { L.i("Notif fetch for $data") - val doc = Jsoup.connect(FbTab.NOTIFICATIONS.url).cookie(FACEBOOK_COM, data.cookie).get() + val doc = Jsoup.connect(FbTab.NOTIFICATIONS.url).cookie(FACEBOOK_COM, data.cookie).userAgent(USER_AGENT_BASIC).get() //aclb for unread, acw for read - val unreadNotifications = doc.getElementById("notifications_list").getElementsByClass("aclb") + val unreadNotifications = (doc.getElementById("notifications_list") ?: return L.eThrow("Notification list not found")).getElementsByClass("aclb") var notifCount = 0 // val prevLatestEpoch = 1498931565L // for testing - val prevLatestEpoch = lastNotificationTime(data.id) + val prevNotifTime = lastNotificationTime(data.id) + val prevLatestEpoch = prevNotifTime.epoch L.v("Notif Prev Latest Epoch $prevLatestEpoch") var newLatestEpoch = prevLatestEpoch unreadNotifications.forEach unread@ { @@ -79,31 +96,77 @@ class NotificationService : JobService() { newLatestEpoch = notif.timestamp notifCount++ } - if (newLatestEpoch != prevLatestEpoch) saveNotificationTime(NotificationModel(data.id, newLatestEpoch)) - frostAnswersCustom("Notifications") { putCustomAttribute("Count", notifCount) } + if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epoch = newLatestEpoch).update() + frostAnswersCustom("Notifications") { + putCustomAttribute("Type", "General") + putCustomAttribute("Count", notifCount) + } summaryNotification(data.id, notifCount) } fun parseNotification(data: CookieModel, element: Element): NotificationContent? { - val a = element.getElementsByTag("a").first() ?: return null - //fetch id - val dataStore = a.attr("data-store") - val notifId = if (dataStore == null) System.currentTimeMillis() - else notifIdMatcher.find(dataStore)?.groups?.get(1)?.value?.toLong() ?: System.currentTimeMillis() + val a = element.getElementsByTag("a").first() ?: return logNotif("IM No a tag") val abbr = element.getElementsByTag("abbr") - val timeString = abbr?.text() - var text = a.text().replace("\u00a0", " ") //remove + val epoch = epochMatcher.find(abbr.attr("data-store"))?.groups?.get(1)?.value?.toLong() ?: return logNotif("IM No epoch") + //fetch id + val notifId = notifIdMatcher.find(a.attr("data-store"))?.groups?.get(1)?.value?.toLong() ?: System.currentTimeMillis() + val timeString = abbr.text() + val text = a.text().replace("\u00a0", " ").removeSuffix(timeString).trim() //remove if (Prefs.notificationKeywords.any { text.contains(it, ignoreCase = true) }) return null //notification filtered out - if (timeString != null) text = text.removeSuffix(timeString) - text = text.trim() - //fetch epoch - val abbrData = abbr?.attr("data-store") - val epoch = if (abbrData == null) -1L else epochMatcher.find(abbrData)?.groups?.get(1)?.value?.toLong() ?: -1L //fetch profpic val p = element.select("i.img[style*=url]") - val pUrl = profMatcher.find(p.getOrNull(0)?.attr("style") ?: "")?.groups?.get(1)?.value ?: "" - return NotificationContent(data, notifId.toInt(), a.attr("href"), text, epoch, pUrl) + val pUrl = profMatcher.find(p.attr("style"))?.groups?.get(1)?.value ?: "" + return NotificationContent(data, notifId.toInt(), a.attr("href"), null, text, epoch, pUrl) + } + + fun fetchMessageNotifications(data: CookieModel) { + if (!Prefs.notificationsInstantMessages) return + L.i("Notif IM fetch for $data") + val doc = Jsoup.connect(FbTab.MESSAGES.url).cookie(FACEBOOK_COM, data.cookie).userAgent(USER_AGENT_BASIC).get() + val unreadNotifications = (doc.getElementById("threadlist_rows") ?: return L.eThrow("Notification messages not found")).getElementsByClass("aclb") + var notifCount = 0 + L.d("IM notif count ${unreadNotifications.size}") + unreadNotifications.forEach { + with(it) { + L.d("notif ${id()} ${className()}") + } + } + val prevNotifTime = lastNotificationTime(data.id) + val prevLatestEpoch = prevNotifTime.epochIm + L.v("Notif Prev Latest Im Epoch $prevLatestEpoch") + var newLatestEpoch = prevLatestEpoch + unreadNotifications.forEach unread@ { + elem -> + val notif = parseMessageNotification(data, elem) ?: return@unread + L.v("Notif im timestamp ${notif.timestamp}") + if (notif.timestamp <= prevLatestEpoch) return@unread + notif.createNotification(this@NotificationService) + if (notif.timestamp > newLatestEpoch) + newLatestEpoch = notif.timestamp + notifCount++ + } +// if (newLatestEpoch != prevLatestEpoch) prevNotifTime.copy(epochIm = newLatestEpoch).update() + frostAnswersCustom("Notifications") { + putCustomAttribute("Type", "Message") + putCustomAttribute("Count", notifCount) + } + summaryNotification(data.id, notifCount) + } + + fun parseMessageNotification(data: CookieModel, element: Element): NotificationContent? { + val a = element.getElementsByTag("a").first() ?: return null + val abbr = element.getElementsByTag("abbr") + val epoch = epochMatcher.find(abbr.attr("data-store"))?.groups?.get(1)?.value?.toLong() ?: return logNotif("No epoch") + val thread = element.getElementsByAttributeValueContaining("id", "thread_fbid_").first() ?: return null + //fetch id + val notifId = messageNotifIdMatcher.find(thread.id())?.groups?.get(1)?.value?.toLong() ?: System.currentTimeMillis() + val text = element.select("span.snippet").firstOrNull()?.text()?.trim() ?: getString(R.string.new_message) + if (Prefs.notificationKeywords.any { text.contains(it, ignoreCase = true) }) return null //notification filtered out + //fetch convo pic + val p = element.select("i.img[style*=url]") + val pUrl = profMatcher.find(p.attr("style"))?.groups?.get(1)?.value ?: "" + return NotificationContent(data, notifId.toInt(), a.attr("href"), a.text(), text, epoch, pUrl) } private fun Context.debugNotification(text: String) { diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt index 68d3fbc6..e05d0dd3 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt @@ -1,10 +1,10 @@ package com.pitchedapps.frost.settings -import ca.allanwang.kau.kpref.KPrefAdapterBuilder -import ca.allanwang.kau.kpref.items.KPrefColorPicker -import ca.allanwang.kau.kpref.items.KPrefSeekbar +import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder +import ca.allanwang.kau.kpref.activity.items.KPrefColorPicker +import ca.allanwang.kau.kpref.activity.items.KPrefSeekbar +import ca.allanwang.kau.ui.views.RippleCanvas import ca.allanwang.kau.utils.string -import ca.allanwang.kau.views.RippleCanvas import com.pitchedapps.frost.MainActivity import com.pitchedapps.frost.R import com.pitchedapps.frost.SettingsActivity 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 2d121073..b912c103 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt @@ -1,6 +1,6 @@ package com.pitchedapps.frost.settings -import ca.allanwang.kau.kpref.KPrefAdapterBuilder +import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder import com.pitchedapps.frost.R import com.pitchedapps.frost.SettingsActivity import com.pitchedapps.frost.utils.Prefs diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt index 6022ee26..86bd356b 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt @@ -1,6 +1,6 @@ package com.pitchedapps.frost.settings -import ca.allanwang.kau.kpref.KPrefAdapterBuilder +import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder import com.pitchedapps.frost.MainActivity import com.pitchedapps.frost.R import com.pitchedapps.frost.SettingsActivity diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt index b8bfb086..29256950 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt @@ -1,6 +1,6 @@ package com.pitchedapps.frost.settings -import ca.allanwang.kau.kpref.KPrefAdapterBuilder +import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder import ca.allanwang.kau.utils.string import com.pitchedapps.frost.MainActivity import com.pitchedapps.frost.R diff --git a/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt b/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt index 5986a998..86cfcc16 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt @@ -1,6 +1,6 @@ package com.pitchedapps.frost.settings -import ca.allanwang.kau.kpref.KPrefAdapterBuilder +import ca.allanwang.kau.kpref.activity.KPrefAdapterBuilder import ca.allanwang.kau.utils.minuteToText import ca.allanwang.kau.utils.snackbar import com.pitchedapps.frost.R 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 7ce8ca10..64ec08cd 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -87,6 +87,8 @@ object Prefs : KPref() { var notificationAllAccounts: Boolean by kpref("notification_all_accounts", true) + var notificationsInstantMessages: Boolean by kpref("notification_im", true) + /** * Cache like value to determine if user has or had pro * In most cases, [com.pitchedapps.frost.utils.iab.IS_FROST_PRO] should be looked at instead diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt index 1d5abdd6..4f65b7f8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt @@ -67,7 +67,7 @@ fun Activity.playStoreNotFound() { } fun Activity.playStoreProNotAvailable() { - playStoreLog("Pro query; store not available") + L.d("Pro query; store not available") materialDialogThemed { title(R.string.uh_oh) content(R.string.play_store_not_found_pro_query) diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt index 1d8f308d..4249cd09 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt @@ -2,7 +2,7 @@ package com.pitchedapps.frost.views import android.annotation.SuppressLint import android.util.TypedValue -import ca.allanwang.kau.kpref.items.KPrefSeekbar +import ca.allanwang.kau.kpref.activity.items.KPrefSeekbar import com.pitchedapps.frost.R /** diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt deleted file mode 100644 index 719a01cc..00000000 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt +++ /dev/null @@ -1,83 +0,0 @@ -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 - } -} 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 16a4a092..0a254c50 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt @@ -45,15 +45,15 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { refreshObservable.onNext(false) return } - view.jsInject(JsActions.LOGIN_CHECK, + view.jsInject( CssAssets.ROUND_ICONS.maybe(Prefs.showRoundedIcons), CssHider.PEOPLE_YOU_MAY_KNOW.maybe(!Prefs.showSuggestedFriends && Prefs.pro), - CssHider.ADS.maybe(!Prefs.showFacebookAds && Prefs.pro), - JsAssets.HEADER_BADGES.maybe(webCore.baseEnum != null)) + CssHider.ADS.maybe(!Prefs.showFacebookAds && Prefs.pro) + ) onPageFinishedActions(url) } - open internal fun onPageFinishedActions(url: String?) { + open internal fun onPageFinishedActions(url: String) { injectAndFinish() } @@ -61,9 +61,15 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { L.d("Page finished reveal") webCore.jsInject(CssHider.HEADER, Prefs.themeInjector, - JsAssets.CLICK_A.maybe(webCore.baseEnum != null), - JsAssets.CONTEXT_A, - callback = { refreshObservable.onNext(false) }) + callback = { + refreshObservable.onNext(false) + webCore.jsInject( + JsActions.LOGIN_CHECK, + JsAssets.CLICK_A.maybe(webCore.baseEnum != null), + JsAssets.CONTEXT_A, + JsAssets.HEADER_BADGES.maybe(webCore.baseEnum != null) + ) + }) } open fun handleHtml(html: String) { @@ -86,7 +92,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : WebViewClient() { override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { L.i("Url Loading ${request.url}") - val path = request.url.path + val path = request.url.path ?: return super.shouldOverrideUrlLoading(view, request) if (path.startsWith("/composer/")) return launchRequest(request) return super.shouldOverrideUrlLoading(view, request) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt index 0f08bcf3..10648e73 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt @@ -1,42 +1,26 @@ package com.pitchedapps.frost.web -import android.graphics.Bitmap import android.webkit.WebView import com.pitchedapps.frost.facebook.FB_URL_BASE import com.pitchedapps.frost.injectors.JsAssets import com.pitchedapps.frost.injectors.jsInject -import com.pitchedapps.frost.utils.L -import io.reactivex.subjects.Subject /** * Created by Allan Wang on 2017-05-31. */ class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(webCore) { - var content: String? = null - val progressObservable: Subject<Int> = webCore.progressObservable - private val contentBaseUrl = "${FB_URL_BASE}notifications" - - override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { - super.onPageStarted(view, url, favicon) - if (content != null) { - when (url.removePrefix(FB_URL_BASE)) { - "settings", - "settings#", - "settings#!/settings?soft=bookmarks" -> { - L.d("Load from stored $url") - view.stopLoading() - view.loadDataWithBaseURL(contentBaseUrl, content, "text/html", "utf-8", "https://google.ca/test") - } - } - } + private val String.shouldInjectMenu + get() = when (removePrefix(FB_URL_BASE)) { + "settings", + "settings#", + "settings#!/settings?soft=bookmarks" -> true + else -> false } override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) - if (url == webCore.baseUrl && content == null) { - jsInject(JsAssets.MENU) - } + if (url.shouldInjectMenu) jsInject(JsAssets.MENU) } override fun emit(flag: Int) { @@ -44,19 +28,8 @@ class FrostWebViewClientMenu(webCore: FrostWebViewCore) : FrostWebViewClient(web super.injectAndFinish() } - override fun onPageFinishedActions(url: String?) { - when (url?.removePrefix(FB_URL_BASE)) { - "settings", - "settings#", - "settings#!/settings?soft=bookmarks" -> { - //do nothing; we will further inject before revealing - } - else -> injectAndFinish() - } + override fun onPageFinishedActions(url: String) { + if (!url.shouldInjectMenu) injectAndFinish() } - override fun handleHtml(html: String) { - super.handleHtml(html) - content = html //we will not save this locally in case things change - } }
\ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 800a8ef7..4d0ed062 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,4 +94,5 @@ <string name="library_dbflow_licenseId">mit</string> <string name="login_id_failed">Login failed; id not found</string> <string name="iab_still_in_progress">IAB query is still in progress</string> + <string name="new_message">New Message</string> </resources> diff --git a/app/src/main/res/values/strings_errors.xml b/app/src/main/res/values/strings_errors.xml new file mode 100644 index 00000000..162114f7 --- /dev/null +++ b/app/src/main/res/values/strings_errors.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="null_url_overlay">Empty url given to overlay; exiting</string> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml index 1a332ea4..8b229dea 100644 --- a/app/src/main/res/xml/changelog.xml +++ b/app/src/main/res/xml/changelog.xml @@ -9,17 +9,22 @@ --> <version title="Beta Updates"/> + <item text="Reduce Menu loading logic" /> + <item text="Load js injectors after showing webview" /> + <item text="Add notifications for messages" /> <item text="" /> - <!--<version title="v1.3"/>--> + <item text="" /> + <item text="" /> + <item text="" /> + <item text="" /> + + <version title="v1.3"/> <item text="Create toggle for notifications only from primary account" /> <item text="Micro string optimizations" /> <item text="Add profile icons to notifications" /> <item text="Make notifications expandable" /> <item text="Add notification trigger in settings" /> <item text="Fix bug where only single latest notification is showing" /> - <item text="" /> - <item text="" /> - <item text="" /> <version title="v1.2"/> <item text="Scale browser on keyboard pop up" /> diff --git a/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt new file mode 100644 index 00000000..d7ec4b46 --- /dev/null +++ b/app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt @@ -0,0 +1,39 @@ +package com.pitchedapps.frost.facebook + +import org.junit.Test +import kotlin.test.assertEquals + + +/** + * Created by Allan Wang on 2017-07-07. + */ +class FbUrlTest { + + @Test + fun base() { + val url = "https://touch.facebook.com/relative/?asdf=1234&hjkl=7890" + assertFbFormat(url, url) + } + + @Test + fun relative() { + val url = "/relative/?asdf=1234&hjkl=7890" + assertFbFormat("$FB_URL_BASE${url.substring(1)}", url) + } + + @Test + fun redirect() { + assertFbFormat("$FB_URL_BASE/relative/?asdf=1234&hjkl=7890", "https://touch.facebook.com/l.php?u=$FB_URL_BASE/relative/&asdf=1234&hjkl=7890") + } + + @Test fun discard() { + val prefix = "$FB_URL_BASE/?test=1234" + val suffix = "&apple=notorange" + assertFbFormat("$prefix$suffix", "$prefix&ref=hello$suffix") + } + + fun assertFbFormat(expected: String, url: String) { + val fbUrl = FbUrlFormatter(url) + assertEquals(expected, fbUrl.toString(), "FbUrl Mismatch:\n${fbUrl.toLogList().joinToString("\n\t")}") + } +}
\ No newline at end of file diff --git a/app/src/test/kotlin/com/pitchedapps/frost/utils/UrlTest.kt b/app/src/test/kotlin/com/pitchedapps/frost/utils/UrlTest.kt deleted file mode 100644 index 3590e625..00000000 --- a/app/src/test/kotlin/com/pitchedapps/frost/utils/UrlTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.pitchedapps.frost.utils - -import com.pitchedapps.frost.facebook.FbUrlFormatter -import org.junit.Test - - -/** - * Created by Allan Wang on 2017-07-07. - */ -class UrlTest { - - @Test - fun testParse() { - val url = FbUrlFormatter(TEST_URL) - url.log() - println(url.toString()) - } - - private fun FbUrlFormatter.log() = toLogList().forEach { println(it) } -} - -const val TEST_URL = "https://touch.facebook.com/ScienceOrientationWeek/?refid=52&_ft_=qid.6440135786032091148%3Amf_story_key.3325631938086219467%3Atop_level_post_id.1555752904487229%3Apage_id.525538540842009&__tn__=C"
\ No newline at end of file diff --git a/docs/Changelog.md b/docs/Changelog.md index e7c3b7ab..9f40685e 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,6 +1,11 @@ # Changelog ## Beta Updates +* Reduce Menu loading logic +* Load js injectors after showing webview +* Add notifications for messages + +## v1.3 * Create toggle for notifications only from primary account * Micro string optimizations * Add profile icons to notifications diff --git a/files/.gitignore b/files/.gitignore new file mode 100644 index 00000000..82c07033 --- /dev/null +++ b/files/.gitignore @@ -0,0 +1,5 @@ +gplay-keys.json +play.keystore +play.properties +test.keystore +update-dev.sh diff --git a/gradle.properties b/gradle.properties index 630f03f1..8dc22a88 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,8 +17,8 @@ MIN_SDK=21 TARGET_SDK=26 BUILD_TOOLS=26.0.0 -KAU=880d433e47 -KOTLIN=1.1.3 +KAU=v2.0 +KOTLIN=1.1.3-2 MATERIAL_DRAWER=5.9.4 MATERIAL_DRAWER_KT=1.0.5 IICON_MATERIAL=2.2.0.3 |