aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md5
-rw-r--r--app/src/main/AndroidManifest.xml5
-rw-r--r--app/src/main/assets/css/core/main.compact.css6
-rw-r--r--app/src/main/assets/css/core/main.scss8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt18
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt19
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt1
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/NotificationReceiver.kt34
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt4
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt163
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt26
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt9
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt12
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt8
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt2
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt78
-rw-r--r--app/src/main/res/drawable/ic_action_cancel.xml9
-rw-r--r--app/src/main/res/values/strings.xml8
-rw-r--r--app/src/main/res/xml/frost_changelog.xml9
-rw-r--r--docs/Changelog.md8
-rw-r--r--generate-apk-release.sh15
-rw-r--r--gradle.properties2
28 files changed, 353 insertions, 113 deletions
diff --git a/README.md b/README.md
index cf2a0fb4..17e27b64 100644
--- a/README.md
+++ b/README.md
@@ -15,3 +15,8 @@ It contains many features, including:
* Native image viewer and downloader via long press
* Reactive based loading
* The transparency of open sourced development
+
+For testers and users without a play store account, test builds can be found [here](https://github.com/AllanWang/Frost-for-Facebook-APK-Builder/releases).
+Note that these builds occur for every commit, including unstable ones.
+You can find the release numbers for the master branch under the [Travis](https://travis-ci.org/AllanWang/Frost-for-Facebook/branches).
+Those builds are likely more stable as they are pushed out to the alpha stream on the play store. \ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c4b5b9dc..769dd14d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -137,6 +137,7 @@
android:enabled="true"
android:label="@string/frost_notifications"
android:permission="android.permission.BIND_JOB_SERVICE" />
+
<receiver
android:name=".services.UpdateReceiver"
android:enabled="true">
@@ -144,6 +145,10 @@
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
+ <receiver
+ android:name=".services.NotificationReceiver"
+ android:enabled="true" />
+
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
diff --git a/app/src/main/assets/css/core/main.compact.css b/app/src/main/assets/css/core/main.compact.css
index ca6a9d3a..c91e892e 100644
--- a/app/src/main/assets/css/core/main.compact.css
+++ b/app/src/main/assets/css/core/main.compact.css
@@ -1,6 +1,6 @@
#viewport { background: #451515 !important; }
-body, #root, #header, [style*="background-color"], ._55wo, ._1upc, input, ._2f9r, ._59e9, ._5pz4, ._5lp4, ._5lp5, .container, .subpage, ._5n_f, #static_templates, .tlBody, #timelineBody, .timelineX, .timeline, .feed, .tlPrelude, .tlFeedPlaceholder, ._4_d0, .al, ._1gkq, ._5c5b, ._1qxg, ._5luf, ._2new, ._cld, ._3zvb, ._2nk0, ._11ub, ._5p7j, ._55wm, ._5rgs, ._5xuj, ._1sv1, ._45fu, ._18qg, ._1_ac, ._5w3g, ._3e18, ._10c_, ._2jl2, ._5q_r, ._5yt8, ._idb, ._2ip_, ._f6s, ._2l5v, ._8i2, ._d4i, ._577z, ._2u4w, ._3u9p, ._3u9t, ._2v9s, ._cw4, ._5_y-, ._5_y_, ._5_z3, ._cwy, ._5_z0, ._5_z1, ._5_z2, ._2mtc, ._206a, ._1_-1, ._1ybg, .appCenterCategorySelectorButton, ._5c9u, div._5y57::before, ._59f6._55so::before, .structuredPublisher, ._94v, ._vqv, ._5lp5, ._55wm, ._2om3, ._2ol-, ._1f9d, ._vee, ._31a-, ._3r8b, ._3r9d, .acw, ._4_xl, ._1p70, ._1p70, ._1ih_, ._51v6, ._u2c, ._484w, ._3ils, ._rm7, ._32qk, ._d01, ._2y60, ._5fu3, ._2foa, ._2y5_, ._38o9, ._1kb, .mAppCenterFatLabel, ._3f50, .mentions-placeholder, .mentions, .mentions-shadow, .mentions-measurer, .acg, ._59tu, ._52z5, ._4l9b, ._4gj3, .groupChromeView, ._uww, textarea, ._15n_, ._skt, ._5f28, ._14_j, ._3bg5, ._53_-, ._52x1 { background: rgba(255, 0, 255, 0.02) !important; }
+body, #root, #header, [style*="background-color"], ._55wo, ._1upc, input, ._2f9r, ._59e9, ._5pz4, ._5lp4, ._5lp5, .container, .subpage, ._5n_f, #static_templates, ._22_8, .tlBody, #timelineBody, .timelineX, .timeline, .feed, .tlPrelude, .tlFeedPlaceholder, ._4_d0, .al, ._1gkq, ._5c5b, ._1qxg, ._5luf, ._2new, ._cld, ._3zvb, ._2nk0, .btnD, .btnI, ._11ub, ._5p7j, ._55wm, ._5rgs, ._5xuj, ._1sv1, ._45fu, ._18qg, ._1_ac, ._5w3g, ._3e18, ._10c_, ._2jl2, ._5q_r, ._5yt8, ._idb, ._2ip_, ._f6s, ._2l5v, ._8i2, ._d4i, ._577z, ._2u4w, ._3u9p, ._3u9t, ._2v9s, ._cw4, ._5_y-, ._5_y_, ._5_z3, ._cwy, ._5_z0, ._5_z1, ._5_z2, ._2mtc, ._206a, ._1_-1, ._1ybg, .appCenterCategorySelectorButton, ._5c9u, div._5y57::before, ._59f6._55so::before, .structuredPublisher, ._94v, ._vqv, ._5lp5, ._55wm, ._2om3, ._2ol-, ._1f9d, ._vee, ._31a-, ._3r8b, ._3r9d, .acw, ._4_xl, ._1p70, ._1p70, ._1ih_, ._51v6, ._u2c, ._484w, ._3ils, ._rm7, ._32qk, ._d01, ._2y60, ._5fu3, ._2foa, ._2y5_, ._38o9, ._1kb, .mAppCenterFatLabel, ._3f50, .mentions-placeholder, .mentions, .mentions-shadow, .mentions-measurer, .acg, ._59tu, ._52z5, ._4l9b, ._4gj3, .groupChromeView, ._uww, textarea, ._15n_, ._skt, ._5f28, ._14_j, ._3bg5, ._53_-, ._52x1 { background: rgba(255, 0, 255, 0.02) !important; }
._cv_, ._2sq8 { background-color: rgba(255, 0, 255, 0.02) !important; }
@@ -12,7 +12,7 @@ body, #root, #header, [style*="background-color"], ._55wo, ._1upc, input, ._2f9r
button:not([style*=image]), button::before, .touch ._56bt, ._56be::before, .btnS, .touch::before, ._590n, ._4g8h, ._2cpp, ._5xo2, ._5u5a::before, ._4u3j, ._15ks, ._5hua, ._59tt, ._41ft, .jx-tokenizer, ._55fj, .excessItem, ._4e8n, ._5pxa._3uj9, ._5n_5, ._u2d, ._56bu::before, ._5h8f, ._d00, ._2066, ._2k51, ._4qax, .aclb, ._4756, ._w34, ._56bv::before, ._5769, ._34iv, ._z-w, .acbk { background: rgba(199, 70, 70, 0.2) !important; }
-[style*="color"], body, input, ._42rv, ._4qau, ._z-z, ._z-v, ._1e8d, ._36nl, ._36nm, ._2_11, ._2_rf, ._2ip_, ._403p, ._5xu2, ._3ml8, ._3mla, ._43mh, .touch .btn, p, span, .fcg, button, ._52j9, ._52jb, ._52ja, ._5j35, ._rnk, ._24u0, ._1g06, ._14ye, .fcb, ._56cz._56c_, ._1gk_, ._55fj, ._45fu, ._18qg, ._1_ac, textarea, ._24pi, ._4en9, ._1kb, ._5p7j, ._2klz, ._5780, ._5781, ._5782, ._3u9u, ._3u9_, ._3u9s, ._1hcx, ._2066, ._1_-1, ._cv_, ._1nbx, ._2cuh, ._4ms9, ._4ms5, ._4ms6, ._31b4, ._31b5, ._5q_r, ._idb, ._27vp, ._4nwe, ._4nw9, ._27vi, .appCenterAppInfo, .appCenterPermissions, ._3c9l, ._3c9m, ._4jn_, ._32qt, ._3mom, ._3moo, ._-7o, ._d00, ._d01, ._559g, ._2new, .appCenterCategorySelectorButton, .mentions-input, .mentions-placeholder, .fcw, ._5-7t, .fcl, ._4qas, .thread-title, .title, ._46pa, ._336p, ._1rrd, ._2om4, ._3m1m, ._2om2, ._5n_e, .appListExplanation, ._5yt8, ._8he, ._2luw, ._5rgs, h1, h2, h3, h4, h5, h6 { color: #d7b0d7 !important; }
+[style*="color"], body, input, ._42rv, ._4qau, ._dwm .descArea, ._z-z, ._z-v, ._1e8d, ._36nl, ._36nm, ._2_11, ._2_rf, ._2ip_, ._403p, ._5xu2, ._3ml8, ._3mla, ._43mh, .touch .btn, p, span, .fcg, button, ._52j9, ._52jb, ._52ja, ._5j35, ._rnk, ._24u0, ._1g06, ._14ye, .fcb, ._56cz._56c_, ._1gk_, ._55fj, ._45fu, ._18qg, ._1_ac, textarea, ._24pi, ._4en9, ._1kb, ._5p7j, ._2klz, ._5780, ._5781, ._5782, ._3u9u, ._3u9_, ._3u9s, ._1hcx, ._2066, ._1_-1, ._cv_, ._1nbx, ._2cuh, ._4ms9, ._4ms5, ._4ms6, ._31b4, ._31b5, ._5q_r, ._idb, ._27vp, ._4nwe, ._4nw9, ._27vi, .appCenterAppInfo, .appCenterPermissions, ._3c9l, ._3c9m, ._4jn_, ._32qt, ._3mom, ._3moo, ._-7o, ._d00, ._d01, ._559g, ._2new, .appCenterCategorySelectorButton, .mentions-input, .mentions-placeholder, .fcw, ._5-7t, .fcl, ._4qas, .thread-title, .title, ._46pa, ._336p, ._1rrd, ._2om4, ._3m1m, ._2om2, ._5n_e, .appListExplanation, ._5yt8, ._8he, ._2luw, ._5rgs, h1, h2, h3, h4, h5, h6 { color: #d7b0d7 !important; }
._15kl::before, ._5j35::after, ._2k4b, ._3to7, ._4nw8 { border-left: 1px solid rgba(215, 176, 215, 0.3) !important; }
@@ -20,7 +20,7 @@ button:not([style*=image]), button::before, .touch ._56bt, ._56be::before, .btnS
._1mx0, ._1rbr, ._5yt8, ._idb, ._cld, ._15n_, ._3-2-, ._27ve, ._2s20, ._gui, ._2s21 > *::after, ._32qk, ._d00, ._d01, ._38o9, ._2u4w, ._3u9t, ._55fj, ._52x1, ._3wjp, ._usq, ._2cul:before, ._13e_, .jewel .flyout, ._3bg5 ._52x6, ._3on6, ._2om3, ._2ol-, ._56d8, .al, ._1gkq, ._5fjv, ._5fjw, ._4z83 { border-top: 1px solid rgba(215, 176, 215, 0.3) !important; }
-._15ny::after, ._z-w, ._8i2, ._2nk0, ._2u4w, ._577z:not(:last-child) ._ygd, ._3u9u, ._3mgz, ._52x6, ._2066, ._5luf, .mAppCenterFatLabel, .appCenterCategorySelectorButton, ._1q6v, ._5q_r, ._5yt8, ._ap1, ._52x1, ._59tu, ._usq, ._13e_, ._59f6._55so::before, ._4gj3, .jx-result, ._2om3, ._2ol-, ._1f9d, ._vef, ._55x2 > *, .al, ._44qk, ._1gkq, ._5rgs, ._5xuj, ._1sv1, ._idb, ._5lp5, ._3-2-, ._3to6, ._ir5, ._4nw6, ._4nwh, ._27ve, div._51v6::before, ._3c9h::before, ._2s20, ._gui, ._5jku, ._2foa, ._2y60, ._5fu3, ._4en9, ._1kb:not(:last-child) ._1kc, ._5pz4, ._5lp4, ._5lp5, ._3on6, ._5h6z, ._5h6x, ._2om4, ._5fjw > div, ._5fjv > :first-child, ._5fjw > :first-child, ._5fjv, ._5fjw, ._4z83 { border-bottom: 1px solid rgba(215, 176, 215, 0.3) !important; }
+._15ny::after, ._z-w, ._8i2, ._2nk0, ._22_8, ._2u4w, ._577z:not(:last-child) ._ygd, ._3u9u, ._3mgz, ._52x6, ._2066, ._5luf, .mAppCenterFatLabel, .appCenterCategorySelectorButton, ._1q6v, ._5q_r, ._5yt8, ._ap1, ._52x1, ._59tu, ._usq, ._13e_, ._59f6._55so::before, ._4gj3, .jx-result, ._2om3, ._2ol-, ._1f9d, ._vef, ._55x2 > *, .al, ._44qk, ._1gkq, ._5rgs, ._5xuj, ._1sv1, ._idb, ._5lp5, ._3-2-, ._3to6, ._ir5, ._4nw6, ._4nwh, ._27ve, div._51v6::before, ._3c9h::before, ._2s20, ._gui, ._5jku, ._2foa, ._2y60, ._5fu3, ._4en9, ._1kb:not(:last-child) ._1kc, ._5pz4, ._5lp4, ._5lp5, ._3on6, ._5h6z, ._5h6x, ._2om4, ._5fjw > div, ._5fjv > :first-child, ._5fjw > :first-child, ._5fjv, ._5fjw, ._4z83 { border-bottom: 1px solid rgba(215, 176, 215, 0.3) !important; }
._d4i, ._f6s, .mentions-suggest-item, .mentions-suggest, ._1_y5, ._lr0, ._5hgt, ._2cpp, ._4e8n, ._uww, .mentions-placeholder, .mentions-shadow, .mentions-measurer, ._5whq, ._59tt, ._41ft::after, .jx-tokenizer, ._3uqf, ._4756, ._1rrd, ._5n_f { border: 1px solid rgba(215, 176, 215, 0.3) !important; }
diff --git a/app/src/main/assets/css/core/main.scss b/app/src/main/assets/css/core/main.scss
index 5f532cf4..85ce793f 100644
--- a/app/src/main/assets/css/core/main.scss
+++ b/app/src/main/assets/css/core/main.scss
@@ -5,8 +5,8 @@
background: $background !important;
}
-body, #root, #header, [style*="background-color"], ._55wo, ._1upc, input, ._2f9r, ._59e9, ._5pz4, ._5lp4, ._5lp5, .container, .subpage, ._5n_f, #static_templates,
-.tlBody, #timelineBody, .timelineX, .timeline, .feed, .tlPrelude, .tlFeedPlaceholder, ._4_d0, .al, ._1gkq, ._5c5b, ._1qxg, ._5luf, ._2new, ._cld, ._3zvb, ._2nk0,
+body, #root, #header, [style*="background-color"], ._55wo, ._1upc, input, ._2f9r, ._59e9, ._5pz4, ._5lp4, ._5lp5, .container, .subpage, ._5n_f, #static_templates, ._22_8,
+.tlBody, #timelineBody, .timelineX, .timeline, .feed, .tlPrelude, .tlFeedPlaceholder, ._4_d0, .al, ._1gkq, ._5c5b, ._1qxg, ._5luf, ._2new, ._cld, ._3zvb, ._2nk0, .btnD, .btnI,
._11ub, ._5p7j, ._55wm, ._5rgs, ._5xuj, ._1sv1, ._45fu, ._18qg, ._1_ac, ._5w3g, ._3e18, ._10c_, ._2jl2, ._5q_r, ._5yt8, ._idb, ._2ip_, ._f6s, ._2l5v, ._8i2,
._d4i, ._577z, ._2u4w, ._3u9p, ._3u9t, ._2v9s, ._cw4, ._5_y-, ._5_y_, ._5_z3, ._cwy, ._5_z0, ._5_z1, ._5_z2, ._2mtc, ._206a, ._1_-1, ._1ybg, .appCenterCategorySelectorButton,
._5c9u, div._5y57::before, ._59f6._55so::before, .structuredPublisher, ._94v, ._vqv, ._5lp5, ._55wm, ._2om3, ._2ol-, ._1f9d, ._vee, ._31a-, ._3r8b, ._3r9d,
@@ -41,7 +41,7 @@ button:not([style*=image]), button::before, .touch ._56bt, ._56be::before, .btnS
background: $background2 !important;
}
-[style*="color"], body, input, ._42rv, ._4qau,
+[style*="color"], body, input, ._42rv, ._4qau, ._dwm .descArea,
._z-z, ._z-v, ._1e8d, ._36nl, ._36nm, ._2_11, ._2_rf, ._2ip_, ._403p, ._5xu2, ._3ml8, ._3mla,
._43mh, .touch .btn, p, span, .fcg, button, ._52j9, ._52jb, ._52ja, ._5j35, ._rnk, ._24u0, ._1g06, ._14ye, .fcb, ._56cz._56c_, ._1gk_, ._55fj, ._45fu, ._18qg, ._1_ac,
textarea, ._24pi, ._4en9, ._1kb, ._5p7j, ._2klz, ._5780, ._5781, ._5782, ._3u9u, ._3u9_, ._3u9s, ._1hcx, ._2066, ._1_-1, ._cv_, ._1nbx, ._2cuh, ._4ms9, ._4ms5, ._4ms6, ._31b4, ._31b5, ._5q_r, ._idb,
@@ -68,7 +68,7 @@ h1, h2, h3, h4, h5, h6 {
border-top: 1px solid $divider !important;
}
-._15ny::after, ._z-w, ._8i2, ._2nk0,
+._15ny::after, ._z-w, ._8i2, ._2nk0, ._22_8,
._2u4w, ._577z:not(:last-child) ._ygd, ._3u9u, ._3mgz, ._52x6, ._2066, ._5luf, .mAppCenterFatLabel, .appCenterCategorySelectorButton, ._1q6v, ._5q_r, ._5yt8,
._ap1, ._52x1, ._59tu, ._usq, ._13e_, ._59f6._55so::before, ._4gj3, .jx-result, ._2om3, ._2ol-, ._1f9d, ._vef, ._55x2 > *, .al, ._44qk, ._1gkq, ._5rgs, ._5xuj, ._1sv1, ._idb,
._5lp5, ._3-2-, ._3to6, ._ir5, ._4nw6, ._4nwh, ._27ve, div._51v6::before, ._3c9h::before, ._2s20, ._gui, ._5jku, ._2foa, ._2y60, ._5fu3, ._4en9, ._1kb:not(:last-child) ._1kc,
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
index c2b3cc0f..5de07b7a 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt
@@ -21,7 +21,7 @@ class StartActivity : KauBaseActivity() {
FbCookie.switchBackUser {
loadFbCookiesAsync {
cookies ->
- L.d("Cookies loaded ${System.currentTimeMillis()}", cookies.toString())
+ L.d("Cookies loaded at time ${System.currentTimeMillis()}", cookies.toString())
if (cookies.isNotEmpty())
launchNewTask(if (Prefs.userId != -1L) MainActivity::class.java else SelectorActivity::class.java, ArrayList(cookies))
else
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
index a1717de1..fbcd12cc 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/AboutActivity.kt
@@ -73,7 +73,7 @@ class AboutActivity : AboutActivityBase(null, {
* Frost may not be a library but we're conveying the same info
*/
val frost = Library().apply {
- libraryName = string(R.string.app_name)
+ libraryName = string(R.string.frost_name)
author = "Pitched Apps"
libraryWebsite = "https://github.com/AllanWang/Frost-for-Facebook"
isOpenSource = true
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
index e419c21c..6a39b269 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/ImageActivity.kt
@@ -148,7 +148,7 @@ class ImageActivity : KauBaseActivity() {
private fun saveTempImage(resource: Bitmap, callback: (uri: Uri?) -> Unit) {
var photoFile: File? = null
try {
- photoFile = createImageFile()
+ photoFile = createPrivateMediaFile(".png")
} catch (ignored: IOException) {
} finally {
if (photoFile == null) {
@@ -166,27 +166,13 @@ class ImageActivity : KauBaseActivity() {
}
}
- @Throws(IOException::class)
- private fun createImageFile(): File {
- // Create an image file name
- val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
- val imageFileName = "Frost_" + timeStamp + "_"
- val storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
- return File.createTempFile(imageFileName, ".png", storageDir)
- }
-
internal fun downloadImage() {
kauRequestPermissions(PERMISSION_WRITE_EXTERNAL_STORAGE) {
granted, _ ->
L.d("Download image callback granted: $granted")
if (granted) {
doAsync {
- val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
- val imageFileName = "Frost_" + timeStamp + "_"
- val storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
- val frostDir = File(storageDir, "Frost")
- if (!frostDir.exists()) frostDir.mkdirs()
- val destination = File.createTempFile(imageFileName, ".png", frostDir)
+ val destination = createMediaFile(".png")
downloadPath = destination.absolutePath
var success = true
try {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt
index 8503145e..47c286fa 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/LoginActivity.kt
@@ -1,5 +1,6 @@
package com.pitchedapps.frost.activities
+import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.os.Handler
@@ -27,7 +28,6 @@ import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.BiFunction
import io.reactivex.internal.operators.single.SingleToObservable
-import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.SingleSubject
@@ -42,8 +42,6 @@ class LoginActivity : BaseActivity() {
val textview: AppCompatTextView by bindView(R.id.textview)
val profile: ImageView by bindView(R.id.profile)
- val loginObservable = SingleSubject.create<CookieModel>()
- val progressObservable = BehaviorSubject.create<Int>()!!
val profileObservable = SingleSubject.create<Boolean>()
val usernameObservable = SingleSubject.create<String>()
@@ -62,17 +60,14 @@ class LoginActivity : BaseActivity() {
setSupportActionBar(toolbar)
setTitle(R.string.kau_login)
setFrostColors(toolbar)
- web.loginObservable = loginObservable
- web.progressObservable = progressObservable
- loginObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
+ web.loadLogin({ refresh = it != 100 }) {
cookie ->
+ L.d("Login found")
web.fadeOut(onFinish = {
profile.fadeIn()
loadInfo(cookie)
})
}
- progressObservable.observeOn(AndroidSchedulers.mainThread()).subscribe { refresh = it != 100 }
- web.loadLogin()
}
fun loadInfo(cookie: CookieModel) {
@@ -124,4 +119,12 @@ class LoginActivity : BaseActivity() {
fun loadUsername(cookie: CookieModel) {
cookie.fetchUsername { usernameObservable.onSuccess(it) }
}
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ if (requestCode == 999) {
+ L.d("Result found for activity with result $resultCode")
+ L.d("Intent data ${data?.extras.toString()}")
+ } else
+ super.onActivityResult(requestCode, resultCode, data)
+ }
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
index df1228bd..e8148b55 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/activities/MainActivity.kt
@@ -350,7 +350,6 @@ class MainActivity : BaseActivity(), SearchWebView.SearchContract,
hiddenSearchView?.dispose()
hiddenSearchView = null
searchView = null
- //todo remove true searchview and add contract
}
override fun emitSearchResponse(items: List<SearchItem>) {
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt
index 7e9fdcad..0387bb99 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/injectors/JsInjector.kt
@@ -52,6 +52,7 @@ interface InjectorContract {
*/
fun WebView.jsInject(vararg injectors: InjectorContract, callback: ((Array<String>) -> Unit) = {}) {
val validInjectors = injectors.filter { it != JsActions.EMPTY }
+ if (validInjectors.isEmpty()) return callback(emptyArray())
val observables = Array(validInjectors.size, { SingleSubject.create<String>() })
Observable.zip<String, Array<String>>(observables.map { it.toObservable() }, { it.map { it.toString() }.toTypedArray() }).subscribeOn(AndroidSchedulers.mainThread()).subscribe({
callback(it)
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 d9b91225..2453d3b0 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/FrostNotifications.kt
@@ -22,7 +22,7 @@ import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.FrostWebActivity
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.dbflow.fetchUsername
-import com.pitchedapps.frost.facebook.FB_URL_BASE
+import com.pitchedapps.frost.facebook.formattedFbUrl
import com.pitchedapps.frost.utils.ARG_USER_ID
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
@@ -40,7 +40,7 @@ val Context.frostNotification: NotificationCompat.Builder
}
@Suppress("DEPRECATION")
-//The update feature is for Android O and seems to still be in beta
+ //The update feature is for Android O and seems to still be in beta
fun Notification.frostConfig() = apply {
if (Prefs.notificationVibrate) defaults = defaults or Notification.DEFAULT_VIBRATE
if (Prefs.notificationSound) defaults = defaults or Notification.DEFAULT_SOUND
@@ -86,12 +86,12 @@ data class NotificationContent(val data: CookieModel,
}
} else {
val intent = Intent(context, FrostWebActivity::class.java)
- intent.data = Uri.parse("${FB_URL_BASE}$href")
+ intent.data = Uri.parse(href.formattedFbUrl)
intent.putExtra(ARG_USER_ID, data.id)
val group = "frost_${data.id}"
val pendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
val notifBuilder = context.frostNotification
- .setContentTitle(title ?: context.string(R.string.app_name))
+ .setContentTitle(title ?: context.string(R.string.frost_name))
.setContentText(text)
.setContentIntent(pendingIntent)
.setCategory(Notification.CATEGORY_SOCIAL)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationReceiver.kt b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationReceiver.kt
new file mode 100644
index 00000000..c903ff72
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationReceiver.kt
@@ -0,0 +1,34 @@
+package com.pitchedapps.frost.services
+
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.support.v4.app.NotificationManagerCompat
+import com.pitchedapps.frost.utils.L
+
+/**
+ * Created by Allan Wang on 2017-08-04.
+ *
+ * Cancels a notification
+ */
+private const val NOTIF_TAG_TO_CANCEL = "notif_tag_to_cancel"
+private const val NOTIF_ID_TO_CANCEL = "notif_id_to_cancel"
+
+class NotificationReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ L.d("NotificationReceiver triggered")
+ val notifTag = intent.getStringExtra(NOTIF_TAG_TO_CANCEL)
+ val notifId = intent.getIntExtra(NOTIF_ID_TO_CANCEL, -1)
+ if (notifId != -1) {
+ L.d("NotificationReceiver: Cancelling $notifTag $notifId")
+ NotificationManagerCompat.from(context).cancel(notifTag, notifId)
+ }
+ }
+}
+
+fun Context.getNotificationPendingCancelIntent(tag: String?, notifId: Int): PendingIntent {
+ val cancelIntent = Intent(this, NotificationReceiver::class.java)
+ .putExtra(NOTIF_TAG_TO_CANCEL, tag).putExtra(NOTIF_ID_TO_CANCEL, notifId)
+ return PendingIntent.getBroadcast(this, 0, cancelIntent, 0)
+} \ No newline at end of file
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 3ddad869..fe7758cc 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/services/NotificationService.kt
@@ -182,7 +182,7 @@ class NotificationService : JobService() {
private fun Context.debugNotification(text: String) {
if (!BuildConfig.DEBUG) return
val notifBuilder = frostNotification
- .setContentTitle(string(R.string.app_name))
+ .setContentTitle(string(R.string.frost_name))
.setContentText(text)
NotificationManagerCompat.from(this).notify(999, notifBuilder.build().frostConfig())
}
@@ -190,7 +190,7 @@ class NotificationService : JobService() {
fun summaryNotification(userId: Long, count: Int) {
if (count <= 1) return
val notifBuilder = frostNotification
- .setContentTitle(string(R.string.app_name))
+ .setContentTitle(string(R.string.frost_name))
.setContentText("$count notifications")
.setGroup("frost_$userId")
.setGroupSummary(true)
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt
new file mode 100644
index 00000000..35f69bca
--- /dev/null
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Downloader.kt
@@ -0,0 +1,163 @@
+package com.pitchedapps.frost.utils
+
+import android.app.Notification
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.support.v4.app.NotificationCompat
+import android.support.v4.app.NotificationManagerCompat
+import android.support.v4.content.FileProvider
+import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE
+import ca.allanwang.kau.permissions.kauRequestPermissions
+import ca.allanwang.kau.utils.copyFromInputStream
+import ca.allanwang.kau.utils.string
+import com.pitchedapps.frost.BuildConfig
+import com.pitchedapps.frost.R
+import com.pitchedapps.frost.services.frostConfig
+import com.pitchedapps.frost.services.frostNotification
+import com.pitchedapps.frost.services.getNotificationPendingCancelIntent
+import okhttp3.MediaType
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.ResponseBody
+import okio.*
+import org.jetbrains.anko.AnkoAsyncContext
+import org.jetbrains.anko.doAsync
+import java.io.File
+import java.io.IOException
+import java.lang.ref.WeakReference
+
+/**
+ * Created by Allan Wang on 2017-08-04.
+ *
+ * With reference to the <a href="https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/Progress.java">OkHttp3 sample</a>
+ */
+fun Context.frostDownload(url: String) {
+ L.d("Received download request", "Download $url")
+ val type = if (url.contains("video")) DownloadType.VIDEO
+ else return L.d("Download request does not match any type")
+ kauRequestPermissions(PERMISSION_WRITE_EXTERNAL_STORAGE) {
+ granted, _ ->
+ if (granted) doAsync { frostDownloadImpl(url, type) }
+ }
+}
+
+private const val MAX_PROGRESS = 1000
+//private val DOWNLOAD_GROUP: String? = null
+private const val DOWNLOAD_GROUP = "frost_downloads"
+
+private enum class DownloadType(val downloadingRes: Int, val downloadedRes: Int) {
+ VIDEO(R.string.downloading_video, R.string.downloaded_video),
+ FILE(R.string.downloading_file, R.string.downloaded_file);
+
+ fun getPendingIntent(context: Context, file: File): PendingIntent {
+ val uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
+ val type = context.contentResolver.getType(uri)
+ L.d("DownloadType: retrieved pending intent - $uri $type")
+ val intent = Intent(Intent.ACTION_VIEW, uri)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ .setDataAndType(uri, type)
+ return PendingIntent.getActivity(context, 0, intent, 0)
+ }
+}
+
+private fun AnkoAsyncContext<Context>.frostDownloadImpl(url: String, type: DownloadType) {
+ L.d("Starting download request")
+ val notifId = Math.abs(url.hashCode() + System.currentTimeMillis().toInt())
+ var notifBuilderAttempt: NotificationCompat.Builder? = null
+ weakRef.get()?.apply {
+ notifBuilderAttempt = frostNotification
+ .setContentTitle(string(type.downloadingRes))
+ .setCategory(Notification.CATEGORY_PROGRESS)
+ .setWhen(System.currentTimeMillis())
+ .setProgress(MAX_PROGRESS, 0, false)
+ .setOngoing(true)
+ .addAction(R.drawable.ic_action_cancel, string(R.string.kau_cancel), getNotificationPendingCancelIntent(DOWNLOAD_GROUP, notifId))
+ .setGroup(DOWNLOAD_GROUP)
+ }
+ val notifBuilder = notifBuilderAttempt ?: return
+ notifBuilder.show(weakRef, notifId)
+
+ val request: Request = Request.Builder()
+ .url(url)
+ .tag(url)
+ .build()
+
+ var client: OkHttpClient? = null
+ client = OkHttpClient.Builder()
+ .addNetworkInterceptor {
+ chain ->
+ val original = chain.proceed(chain.request())
+ return@addNetworkInterceptor original.newBuilder().body(ProgressResponseBody(original.body()!!) {
+ bytesRead, contentLength, done ->
+ //cancel request if context reference is now invalid
+ if (weakRef.get() == null) {
+ client?.cancel(url)
+ }
+ val ctx = weakRef.get() ?: return@ProgressResponseBody client?.cancel(url) ?: Unit
+ val percentage = bytesRead.toFloat() / contentLength.toFloat() * MAX_PROGRESS
+ L.v("Download request progress: $percentage")
+ notifBuilder.setProgress(MAX_PROGRESS, percentage.toInt(), false)
+ if (done) {
+ notifBuilder.setFinished(ctx, type)
+ L.d("Download request finished")
+ }
+ notifBuilder.show(weakRef, notifId)
+ }).build()
+ }
+ .build()
+ client.newCall(request).execute().use {
+ response ->
+ if (!response.isSuccessful) throw IOException("Unexpected code $response")
+ val stream = response.body()?.byteStream()
+ if (stream != null) {
+ val destination = createMediaFile(".mp4")
+ destination.copyFromInputStream(stream)
+ weakRef.get()?.apply {
+ notifBuilder.setContentIntent(type.getPendingIntent(this, destination))
+ notifBuilder.show(weakRef, notifId)
+ }
+ }
+ }
+}
+
+private fun NotificationCompat.Builder.setFinished(context: Context, type: DownloadType)
+ = setContentTitle(context.string(type.downloadedRes))
+ .setProgress(0, 0, false).setOngoing(false).setAutoCancel(true)
+ .apply { mActions.clear() }
+
+private fun OkHttpClient.cancel(url: String) {
+ val call = dispatcher().runningCalls().firstOrNull { it.request().tag() == url }
+ if (call != null && !call.isCanceled) call.cancel()
+}
+
+private fun NotificationCompat.Builder.show(weakRef: WeakReference<Context>, notifId: Int) {
+ val c = weakRef.get() ?: return
+ NotificationManagerCompat.from(c).notify(DOWNLOAD_GROUP, notifId, build().frostConfig())
+}
+
+private class ProgressResponseBody(
+ val responseBody: ResponseBody,
+ val listener: (bytesRead: Long, contentLength: Long, done: Boolean) -> Unit) : ResponseBody() {
+
+ private val bufferedSource: BufferedSource by lazy { Okio.buffer(source(responseBody.source())) }
+
+ override fun contentLength(): Long = responseBody.contentLength()
+
+ override fun contentType(): MediaType? = responseBody.contentType()
+
+ override fun source(): BufferedSource = bufferedSource
+
+ private fun source(source: Source): Source = object : ForwardingSource(source) {
+
+ private var totalBytesRead = 0L
+
+ override fun read(sink: Buffer?, byteCount: Long): Long {
+ val bytesRead = super.read(sink, byteCount)
+ // read() returns the number of bytes read, or -1 if this source is exhausted.
+ totalBytesRead += if (bytesRead != -1L) bytesRead else 0
+ listener(totalBytesRead, responseBody.contentLength(), bytesRead == -1L)
+ return bytesRead
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
index cc3ea52e..496a6b5b 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt
@@ -2,8 +2,10 @@ package com.pitchedapps.frost.utils
import android.app.Activity
import android.content.Context
+import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
+import android.net.Uri
import android.support.annotation.StringRes
import android.support.design.internal.SnackbarContentLayout
import android.support.design.widget.Snackbar
@@ -22,8 +24,11 @@ import com.pitchedapps.frost.BuildConfig
import com.pitchedapps.frost.R
import com.pitchedapps.frost.activities.*
import com.pitchedapps.frost.dbflow.CookieModel
+import com.pitchedapps.frost.facebook.FACEBOOK_COM
import com.pitchedapps.frost.facebook.FbTab
import com.pitchedapps.frost.facebook.formattedFbUrl
+import java.io.IOException
+import java.util.*
/**
* Created by Allan Wang on 2017-06-03.
@@ -145,4 +150,23 @@ fun Activity.frostNavigationBar() {
navigationBarColor = if (Prefs.tintNavBar) Prefs.headerColor else Color.BLACK
}
-fun <T> RequestBuilder<T>.withRoundIcon() = apply(RequestOptions().transform(CircleCrop()))!! \ No newline at end of file
+fun <T> RequestBuilder<T>.withRoundIcon() = apply(RequestOptions().transform(CircleCrop()))!!
+
+@Throws(IOException::class)
+fun createMediaFile(extension: String) = createMediaFile("Frost", extension)
+
+@Throws(IOException::class)
+fun Context.createPrivateMediaFile(extension: String) = createPrivateMediaFile("Frost", extension)
+
+/**
+ * Tries to send the uri to the proper activity via an intent
+ * @returns {@code true} if activity is resolved, {@code false} otherwise
+ */
+fun Context.resolveActivityForUri(uri: Uri): Boolean {
+ if (uri.toString().contains(FACEBOOK_COM) && !uri.toString().contains("intent:")) return false //ignore response as we will be triggering ourself
+ val intent = Intent(Intent.ACTION_VIEW, uri)
+ if (intent.resolveActivity(packageManager) == null) return false
+ startActivity(intent)
+ return true
+}
+
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt
index dc5ac6ac..4b6c9e4e 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/AccountItem.kt
@@ -6,9 +6,7 @@ import android.support.v7.widget.RecyclerView
import android.view.View
import android.widget.ImageView
import ca.allanwang.kau.iitems.KauIItem
-import ca.allanwang.kau.utils.bindView
-import ca.allanwang.kau.utils.fadeIn
-import ca.allanwang.kau.utils.toDrawable
+import ca.allanwang.kau.utils.*
import com.bumptech.glide.Glide
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
@@ -30,7 +28,7 @@ class AccountItem(val cookie: CookieModel?) : KauIItem<AccountItem, AccountItem.
override fun bindView(viewHolder: ViewHolder, payloads: List<Any>?) {
super.bindView(viewHolder, payloads)
with(viewHolder) {
- text.visibility = View.INVISIBLE
+ text.invisible()
text.setTextColor(Prefs.textColor)
if (cookie != null) {
text.text = cookie.name
@@ -46,10 +44,9 @@ class AccountItem(val cookie: CookieModel?) : KauIItem<AccountItem, AccountItem.
}
}).into(image)
} else {
- text.visibility = View.VISIBLE
+ text.visible()
image.setImageDrawable(GoogleMaterial.Icon.gmd_add_circle_outline.toDrawable(itemView.context, 100, Prefs.textColor))
text.text = itemView.context.getString(R.string.kau_add_account)
- //todo add plus image
}
}
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt
index 367495b5..d692c7aa 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostViewPager.kt
@@ -1,5 +1,6 @@
package com.pitchedapps.frost.views
+import android.annotation.SuppressLint
import android.content.Context
import android.support.v4.view.ViewPager
import android.util.AttributeSet
@@ -16,5 +17,6 @@ class FrostViewPager @JvmOverloads constructor(context: Context, attrs: Attribut
override fun onInterceptTouchEvent(ev: MotionEvent?) = Prefs.viewpagerSwipe && enableSwipe && super.onInterceptTouchEvent(ev)
+ @SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent?): Boolean = Prefs.viewpagerSwipe && enableSwipe && super.onTouchEvent(ev)
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
index 343674d5..6bc27256 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostChromeClients.kt
@@ -70,4 +70,6 @@ class FrostChromeClient(webCore: FrostWebViewCore) : WebChromeClient() {
callback(origin, granted, true)
}
}
+
+
} \ No newline at end of file
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
index 1679d7a3..79ca1fdf 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebView.kt
@@ -9,15 +9,12 @@ import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import android.widget.ProgressBar
-import ca.allanwang.kau.utils.bindView
-import ca.allanwang.kau.utils.tint
-import ca.allanwang.kau.utils.visible
-import ca.allanwang.kau.utils.withAlpha
+import ca.allanwang.kau.utils.*
import com.pitchedapps.frost.R
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.frostDownload
import io.reactivex.android.schedulers.AndroidSchedulers
/**
@@ -37,7 +34,7 @@ class FrostWebView @JvmOverloads constructor(
refresh.setColorSchemeColors(Prefs.iconColor)
refresh.setProgressBackgroundColorSchemeColor(Prefs.headerColor.withAlpha(255))
web.progressObservable.observeOn(AndroidSchedulers.mainThread()).subscribe {
- progress.visibility = if (it == 100) View.INVISIBLE else View.VISIBLE
+ progress.invisibleIf(it == 100)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) progress.setProgress(it, true)
else progress.progress = it
}
@@ -62,7 +59,7 @@ class FrostWebView @JvmOverloads constructor(
baseEnum = enum
with(settings) {
javaScriptEnabled = true
- if (url.contains("/message"))
+ if (url.contains("facebook.com/message"))
userAgentString = USER_AGENT_BASIC
allowFileAccess = true
textZoom = Prefs.webTextScaling
@@ -73,6 +70,7 @@ class FrostWebView @JvmOverloads constructor(
webChromeClient = FrostChromeClient(this)
addJavascriptInterface(FrostJSI(this), "Frost")
setBackgroundColor(Color.TRANSPARENT)
+ setDownloadListener { downloadUrl, _, _, _, _ -> context.frostDownload(downloadUrl) }
}
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
index 9f7dd916..94bff3c3 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewClients.kt
@@ -132,13 +132,7 @@ open class FrostWebViewClient(val webCore: FrostWebViewCore) : BaseWebViewClient
if (path.startsWith("/composer/")) return launchRequest(request)
if (request.url.toString().contains("scontent-sea1-1.xx.fbcdn.net") && (path.endsWith(".jpg") || path.endsWith(".png")))
return launchImage(request)
- if (!request.url.toString().contains(FACEBOOK_COM)) {
- val intent = Intent(Intent.ACTION_VIEW, request.url)
- if (intent.resolveActivity(view.context.packageManager) != null) {
- view.context.startActivity(Intent(Intent.ACTION_VIEW, request.url))
- return true
- }
- }
+ if (view.context.resolveActivityForUri(request.url)) return true
return super.shouldOverrideUrlLoading(view, request)
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
index d96fba55..d8edc15c 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/FrostWebViewCore.kt
@@ -1,6 +1,7 @@
package com.pitchedapps.frost.web
import android.animation.ValueAnimator
+import android.annotation.SuppressLint
import android.content.Context
import android.support.v4.view.NestedScrollingChild
import android.support.v4.view.NestedScrollingChildHelper
@@ -94,6 +95,7 @@ class FrostWebViewCore @JvmOverloads constructor(
*
* https://github.com/takahirom/webview-in-coordinatorlayout/blob/master/app/src/main/java/com/github/takahirom/webview_in_coodinator_layout/NestedWebView.java
*/
+ @SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent): Boolean {
val event = MotionEvent.obtain(ev)
val action = event.action
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt
index b178f66c..31be4450 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/web/LoginWebView.kt
@@ -4,23 +4,19 @@ import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.View
-import android.webkit.ConsoleMessage
-import android.webkit.CookieManager
-import android.webkit.WebChromeClient
-import android.webkit.WebView
+import android.webkit.*
import ca.allanwang.kau.utils.fadeIn
-import com.pitchedapps.frost.R
+import ca.allanwang.kau.utils.isVisible
import com.pitchedapps.frost.dbflow.CookieModel
import com.pitchedapps.frost.facebook.FACEBOOK_COM
+import com.pitchedapps.frost.facebook.FB_URL_BASE
import com.pitchedapps.frost.facebook.FbCookie
import com.pitchedapps.frost.injectors.CssHider
import com.pitchedapps.frost.injectors.jsInject
import com.pitchedapps.frost.utils.L
import com.pitchedapps.frost.utils.Prefs
-import com.pitchedapps.frost.utils.frostSnackbar
-import io.reactivex.subjects.PublishSubject
-import io.reactivex.subjects.SingleSubject
-import io.reactivex.subjects.Subject
+import org.jetbrains.anko.doAsync
+import org.jetbrains.anko.uiThread
/**
* Created by Allan Wang on 2017-05-29.
@@ -30,27 +26,15 @@ class LoginWebView @JvmOverloads constructor(
) : WebView(context, attrs, defStyleAttr) {
companion object {
- const val LOGIN_URL = "https://touch.facebook.com/login"
- private val userMatcher: Regex by lazy { Regex("c_user=([0-9]*);") }
+ const val LOGIN_URL = "${FB_URL_BASE}login"
+ private val userMatcher: Regex = Regex("c_user=([0-9]*);")
}
- val cookieObservable = PublishSubject.create<Pair<String, String?>>()
- lateinit var loginObservable: SingleSubject<CookieModel>
- lateinit var progressObservable: Subject<Int>
+ private lateinit var loginCallback: (CookieModel) -> Unit
+ private lateinit var progressCallback: (Int) -> Unit
init {
- FbCookie.reset({
- cookieObservable.filter { (_, cookie) -> cookie?.contains(userMatcher) ?: false }
- .subscribe {
- (url, cookie) ->
- L.d("Checking cookie for login", "$url\n\t$cookie")
- val id = userMatcher.find(cookie!!)?.groups?.get(1)?.value!!
- FbCookie.save(id.toLong())
- cookieObservable.onComplete()
- loginObservable.onSuccess(CookieModel(id.toLong(), "", cookie))
- }
- setupWebview()
- })
+ FbCookie.reset { setupWebview() }
}
@SuppressLint("SetJavaScriptEnabled")
@@ -61,27 +45,39 @@ class LoginWebView @JvmOverloads constructor(
webChromeClient = LoginChromeClient()
}
- fun loadLogin() {
+ fun loadLogin(progressCallback: (Int) -> Unit, loginCallback: (CookieModel) -> Unit) {
+ this.progressCallback = progressCallback
+ this.loginCallback = loginCallback
loadUrl(LOGIN_URL)
}
-
- inner class LoginClient : BaseWebViewClient() {
+ private inner class LoginClient : BaseWebViewClient() {
override fun onPageFinished(view: WebView, url: String?) {
super.onPageFinished(view, url)
- if (url == null || !url.contains(FACEBOOK_COM)) {
- view.frostSnackbar(R.string.no_longer_facebook)
- loadLogin()
- return
+ val containsFacebook = url?.contains(FACEBOOK_COM) ?: false
+ checkForLogin(url) { id, cookie -> loginCallback(CookieModel(id, "", cookie)) }
+ view.jsInject(CssHider.HEADER.maybe(containsFacebook),
+ CssHider.CORE.maybe(containsFacebook),
+ Prefs.themeInjector.maybe(containsFacebook),
+ callback = { if (!view.isVisible) view.fadeIn(offset = 150L) })
+ }
+
+ fun checkForLogin(url: String?, onFound: (id: Long, cookie: String) -> Unit) {
+ doAsync {
+ if (url == null || !url.contains(FACEBOOK_COM)) return@doAsync
+ val cookie = CookieManager.getInstance().getCookie(url) ?: return@doAsync
+ L.d("Checking cookie for login", cookie)
+ val id = userMatcher.find(cookie)?.groups?.get(1)?.value?.toLong() ?: return@doAsync
+ uiThread { onFound(id, cookie) }
}
- cookieObservable.onNext(Pair(url, CookieManager.getInstance().getCookie(url)))
- view.jsInject(CssHider.HEADER, CssHider.CORE,
- Prefs.themeInjector,
- callback = {
- if (view.visibility != View.VISIBLE)
- view.fadeIn(offset = 150L)
- })
+ }
+
+ override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
+ //For now, we will ignore all attempts to launch external apps during login
+ if (request.url == null || request.url.scheme == "intent" || request.url.scheme == "android-app")
+ return true
+ return super.shouldOverrideUrlLoading(view, request)
}
}
@@ -93,7 +89,7 @@ class LoginWebView @JvmOverloads constructor(
override fun onProgressChanged(view: WebView, newProgress: Int) {
super.onProgressChanged(view, newProgress)
- progressObservable.onNext(newProgress)
+ progressCallback(newProgress)
}
}
} \ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_action_cancel.xml b/app/src/main/res/drawable/ic_action_cancel.xml
new file mode 100644
index 00000000..e349d8c4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_action_cancel.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="@color/facebook_blue"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector> \ 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 d4a7c9dc..412d5f34 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -48,9 +48,6 @@
<string name="copy_text">Copy Text</string>
<string name="debug_image_link_subject">Frost for Facebook: Image Link Debug</string>
-
- <string name="no_longer_facebook">No longer under facebook; refreshing…</string>
-
<string name="web_overlay_swipe_hint">Swipe right to go back to the previous window.</string>
<string name="profile_picture">Profile Picture</string>
@@ -79,4 +76,9 @@
<string name="image_download_success">Image downloaded</string>
<string name="image_download_fail">Image failed to download</string>
<string name="image_share_failed">Failed to share image</string>
+
+ <string name="downloading_video">Downloading Video</string>
+ <string name="downloaded_video">Video Downloaded</string>
+ <string name="downloading_file">Downloading File</string>
+ <string name="downloaded_file">File Downloaded</string>
</resources>
diff --git a/app/src/main/res/xml/frost_changelog.xml b/app/src/main/res/xml/frost_changelog.xml
index 8f68de9f..e7681107 100644
--- a/app/src/main/res/xml/frost_changelog.xml
+++ b/app/src/main/res/xml/frost_changelog.xml
@@ -10,7 +10,14 @@
<!--<version title="Beta Updates" />-->
- <version title="v1.4.2"/>
+ <version title="Beta Updates"/>
+ <item text="Fixed notification titles" />
+ <item text="Added support for downloading videos" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+
+ <version title="v1.5.0"/>
<item text="Experimental: Add notifications for messages; report to me if this drains your battery" />
<item text="Add FAQ in the about section" />
<item text="Add video uploading" />
diff --git a/docs/Changelog.md b/docs/Changelog.md
index c4abb9ae..c36d2b7f 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -1,10 +1,16 @@
# Changelog
-## v1.4.2
+## Beta Updates
+* Fixed notification titles
+* Added support for downloading videos
+
+## v1.5.0
* Experimental: Add notifications for messages; report to me if this drains your battery
* Add FAQ in the about section
* Add video uploading
* Add open link option in context menu
+* Add geolocation
+* Update theme
## v1.4.1
* Add intro pages
diff --git a/generate-apk-release.sh b/generate-apk-release.sh
index 830fa07d..6d1c868c 100644
--- a/generate-apk-release.sh
+++ b/generate-apk-release.sh
@@ -3,11 +3,11 @@
# config
# make sure the GITHUB_API_KEY is encrypted and inside the travis file
# travis encrypt GITHUB_API_KEY=super_secret --add env.global
+# Note - gradle 3.0.0 generates outputs in their own folders - ctrl + f > releaseTest
RELEASE_REPO=AllanWang/Frost-for-Facebook-APK-Builder
USER_AUTH=AllanWang
EMAIL=me@allanwang.ca
-APK_NAME=Frost-releaseTest
MODULE_NAME=app
VERSION_KEY=Frost
# Make version key different from module name
@@ -15,7 +15,9 @@ VERSION_KEY=Frost
# create a new directory that will contain our generated apk
mkdir $HOME/$VERSION_KEY/
# copy generated apk from build folder to the folder just created
-cp -R $MODULE_NAME/build/outputs/apk/$APK_NAME.apk $HOME/$VERSION_KEY/
+cp -a $MODULE_NAME/build/outputs/apk/releaseTest/. $HOME/$VERSION_KEY/
+printf "Moved apks\n"
+ls -a $HOME/${VERSION_KEY}
# go to home and setup git
echo "Clone Git"
@@ -41,9 +43,12 @@ API_JSON="$(printf '{"tag_name": "v%s","target_commitish": "master","name": "v%s
newRelease="$(curl --data "$API_JSON" https://api.github.com/repos/$RELEASE_REPO/releases?access_token=$GITHUB_API_KEY)"
rID="$(echo "$newRelease" | jq ".id")"
-cd $HOME
+cd $HOME/${VERSION_KEY}
echo "Push apk to $rID"
-curl "https://uploads.github.com/repos/${RELEASE_REPO}/releases/${rID}/assets?access_token=${GITHUB_API_KEY}&name=${APK_NAME}-v${TRAVIS_BUILD_NUMBER}.apk" --header 'Content-Type: application/zip' --upload-file $VERSION_KEY/$APK_NAME.apk -X POST
-
+for apk in $(find *.apk -type f); do
+ apkName="${apk::-4}"
+ printf "Apk $apkName\n"
+ curl "https://uploads.github.com/repos/${RELEASE_REPO}/releases/${rID}/assets?access_token=${GITHUB_API_KEY}&name=${apkName}-v${TRAVIS_BUILD_NUMBER}.apk" --header 'Content-Type: application/zip' --upload-file $apkName.apk -X POST
+done
echo -e "Done\n" \ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 589a03a0..0a6e4ac0 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,7 +17,7 @@ MIN_SDK=21
TARGET_SDK=26
BUILD_TOOLS=26.0.1
-KAU=ca2cda0
+KAU=9d3169f
KOTLIN=1.1.3-2
CRASHLYTICS=2.6.8
DBFLOW=4.0.5