aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--.idea/compiler.xml22
-rw-r--r--.idea/copyright/profiles_settings.xml3
-rw-r--r--.idea/encodings.xml6
-rw-r--r--.idea/gradle.xml18
-rw-r--r--.idea/markdown-navigator.xml71
-rw-r--r--.idea/markdown-navigator/profiles_settings.xml3
-rw-r--r--.idea/misc.xml78
-rw-r--r--.idea/modules.xml9
-rw-r--r--.idea/runConfigurations.xml12
-rw-r--r--.idea/vcs.xml6
-rw-r--r--.travis.yml2
-rw-r--r--README.md7
-rw-r--r--app/build.gradle13
-rw-r--r--app/proguard-rules.pro8
-rw-r--r--app/src/androidTest/java/com/pitchedapps/frost/ExampleInstrumentedTest.java26
-rw-r--r--app/src/main/assets/js/menu.js1
-rw-r--r--app/src/main/assets/js/menu.min.js3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/AboutActivity.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/SettingsActivity.kt10
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/WebOverlayActivity.kt9
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/dbflow/NotificationDb.kt22
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt6
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/CssHider.kt3
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt5
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt103
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Appearance.kt8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Behaviour.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Experimental.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Feed.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/settings/Notifications.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/iab/IABDialogs.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/KPrefTextSeekbar.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/RippleCanvas.kt83
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClient.kt22
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClientMenu.kt45
-rw-r--r--app/src/main/res/values/strings.xml1
-rw-r--r--app/src/main/res/values/strings_errors.xml4
-rw-r--r--app/src/main/res/xml/changelog.xml13
-rw-r--r--app/src/test/kotlin/com/pitchedapps/frost/facebook/FbUrlTest.kt39
-rw-r--r--app/src/test/kotlin/com/pitchedapps/frost/utils/UrlTest.kt22
-rw-r--r--docs/Changelog.md5
-rw-r--r--files/.gitignore5
-rw-r--r--gradle.properties4
45 files changed, 234 insertions, 487 deletions
diff --git a/.gitignore b/.gitignore
index d55b28aa..e1fb9d16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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:
diff --git a/README.md b/README.md
index e479b260..7f6203da 100644
--- a/README.md
+++ b/README.md
@@ -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 &nbsp;
+ 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 &nbsp;
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