Add universal links for android

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-06-27 15:09:48 +02:00
parent 9df15ffc78
commit b17d9bab37
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
12 changed files with 215 additions and 13 deletions

View File

@ -20,3 +20,4 @@ GROUP_CHATS_ENABLED=0
USE_SYM_KEY=0
MAINNET_WARNING_ENABLED=1
SPAM_BUTTON_DETECTION_ENABLED=1
UNIVERSAL_LINKS_ENABLED=0

View File

@ -54,6 +54,14 @@
<data android:scheme="app-settings"
android:host="notification" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="get.status.im" />
<data android:scheme="https" android:host="get.status.im" />
<data android:scheme="app" android:host="get.status.im" />
</intent-filter>
</activity>
<service
android:name=".module.StatusService"

View File

@ -96,6 +96,7 @@ public class MainActivity extends ReactActivity
@Override
public void onNewIntent(final Intent intent) {
super.onNewIntent(intent);
if (intent.getDataString() != null && intent.getData().getScheme().startsWith("app-settings")) {
startActivity(createNotificationSettingsIntent());
}

View File

@ -374,15 +374,18 @@
:confirm-button-text (i18n/label :t/clear)
:on-accept #(re-frame/dispatch [:clear-history])}}))
(handlers/register-handler-fx
:create-new-public-chat
[re-frame/trim-v]
(fn [{:keys [db now] :as cofx} [topic]]
(defn create-new-public-chat [topic {:keys [db now] :as cofx}]
(handlers-macro/merge-fx cofx
(models/add-public-chat topic)
(navigation/navigate-to-clean :home)
(navigate-to-chat topic {})
(public-chat/join-public-chat topic))))
(public-chat/join-public-chat topic)))
(handlers/register-handler-fx
:create-new-public-chat
[re-frame/trim-v]
(fn [cofx [topic]]
(create-new-public-chat topic cofx)))
(defn- group-name-from-contacts [selected-contacts all-contacts username]
(->> selected-contacts
@ -407,13 +410,16 @@
(navigate-to-chat random-id {})
(transport.message/send (group-chat/GroupAdminUpdate. chat-name selected-contacts) random-id)))))
(defn show-profile [identity {:keys [db] :as cofx}]
(handlers-macro/merge-fx cofx
{:db (assoc db :contacts/identity identity)}
(navigation/navigate-forget :profile)))
(handlers/register-handler-fx
:show-profile
[re-frame/trim-v]
(fn [{:keys [db] :as cofx} [identity]]
(handlers-macro/merge-fx cofx
{:db (assoc db :contacts/identity identity)}
(navigation/navigate-forget :profile))))
(fn [cofx [identity]]
(show-profile identity cofx)))
(handlers/register-handler-fx
:resend-message

View File

@ -150,6 +150,10 @@
(spec/def ::device-UUID (spec/nilable string?))
;;;;UNIVERSAL LINKS
(spec/def :universal-links/url (spec/nilable string?))
(spec/def ::db (allowed-keys
:opt
[:contacts/contacts
@ -187,6 +191,7 @@
:inbox/last-received
:inbox/current-id
:inbox/fetching?
:universal-links/url
:browser/browsers
:browser/options
:new/open-dapp

View File

@ -13,6 +13,9 @@
status-im.ui.screens.group.chat-settings.events
status-im.ui.screens.group.events
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.universal-links.core :as universal-links]
status-im.utils.universal-links.events
status-im.ui.screens.add-new.new-chat.navigation
status-im.ui.screens.network-settings.events
status-im.ui.screens.profile.events
@ -315,7 +318,7 @@
(handlers/register-handler-fx
:initialize-account
(fn [_ [_ address events-after]]
(fn [cofx [_ address events-after]]
{:dispatch-n (cond-> [[:initialize-account-db address]
[:initialize-protocol address]
[:fetch-web3-node-version]
@ -329,7 +332,8 @@
[:update-transactions]
[:get-fcm-token]
[:update-sign-in-time]
[:show-mainnet-is-default-alert]]
[:show-mainnet-is-default-alert]
(universal-links/stored-url-event cofx)]
(seq events-after) (into events-after))}))
(handlers/register-handler-fx

View File

@ -2,6 +2,7 @@
(:require-macros [status-im.utils.views :refer [defview letsubs] :as views])
(:require [re-frame.core :refer [dispatch]]
[status-im.utils.platform :refer [android?]]
[status-im.utils.universal-links.core :as utils.universal-links]
[status-im.ui.components.react :refer [view modal create-main-screen-view] :as react]
[status-im.ui.components.styles :as common-styles]
[status-im.ui.screens.main-tabs.views :refer [main-tabs]]
@ -134,7 +135,9 @@
(defview main []
(letsubs [signed-up? [:signed-up?]
view-id [:get :view-id]]
{:component-will-update (fn [] (react/dismiss-keyboard!))}
{:component-did-mount utils.universal-links/initialize
:component-will-unmount utils.universal-links/finalize
:component-will-update (fn [] (react/dismiss-keyboard!))}
(when view-id
(let [component (get-main-component view-id)
main-screen-view (create-main-screen-view view-id)]

View File

@ -22,6 +22,7 @@
(def stub-status-go? (enabled? (get-config :STUB_STATUS_GO 0)))
(def offline-inbox-enabled? (enabled? (get-config :OFFLINE_INBOX_ENABLED "1")))
(def bootnodes-settings-enabled? (enabled? (get-config :BOOTNODES_SETTINGS_ENABLED "1")))
(def universal-links-enabled? (enabled? (get-config :UNIVERSAL_LINK_ENABLED "1")))
(def log-level
(-> (get-config :LOG_LEVEL "error")
string/lower-case

View File

@ -0,0 +1,100 @@
(ns status-im.utils.universal-links.core
(:require
[taoensso.timbre :as log]
[re-frame.core :as re-frame]
[status-im.utils.config :as config]
[status-im.chat.events :as chat.events]
[status-im.models.account :as models.account]
[status-im.ui.components.react :as react]))
(def public-chat-regex #".*/chat/public/(.*)$")
(def profile-regex #".*/user/(.*)$")
(defn handle-public-chat [public-chat cofx]
(log/info "universal-links: handling public chat " public-chat)
(chat.events/create-new-public-chat public-chat cofx))
(defn handle-view-profile [profile-id cofx]
(log/info "universal links: handling view profile" profile-id)
(chat.events/show-profile profile-id cofx))
(defn handle-not-found [full-url]
(log/info "universal links: no handler for " full-url))
(defn match-url [url regex]
(some->> url
(re-matches regex)
peek))
(defn stored-url-event
"Return an event description for processing a url if in the database"
[{:keys [db]}]
(when-let [url (:universal-links/url db)]
[:handle-universal-link url]))
(defn dispatch-url
"Dispatch url so we can get access to re-frame/db"
[url]
(if-not (nil? url)
(re-frame/dispatch [:handle-universal-link url])
(log/debug "universal links: no url")))
(defn store-url-for-later
"Store the url in the db to be processed on login"
[url {:keys [db]}]
(assoc-in {:db db} [:db :universal-links/url] url))
(defn clear-url
"Remove a url from the db"
[{:keys [db]}]
(update {:db db} :db dissoc :universal-links/url))
(defn route-url
"Match a url against a list of routes and handle accordingly"
[url cofx]
(cond
(match-url url public-chat-regex)
(handle-public-chat (match-url url public-chat-regex) cofx)
(match-url url profile-regex)
(handle-view-profile (match-url url profile-regex) cofx)
:else (handle-not-found url)))
(defn handle-url
"Store url in the database if the user is not logged in, to be processed
on login, otherwise just handle it"
[url cofx]
(if (models.account/logged-in? cofx)
(do
(clear-url cofx)
(route-url url cofx))
(store-url-for-later url cofx)))
(defn unwrap-js-url [e]
(-> e
(js->clj :keywordize-keys true)
:url))
(def url-event-listener
(comp dispatch-url unwrap-js-url))
(defn initialize
"Add an event listener for handling background->foreground transition
and handles incoming url if the app has been started by clicking on a link"
[]
(when config/universal-links-enabled?
(log/debug "universal-links: initializing")
(.. react/linking
(getInitialURL)
(then dispatch-url))
(.. react/linking
(addEventListener "url" url-event-listener))))
(defn finalize
"Remove event listener for url"
[]
(when config/universal-links-enabled?
(log/debug "universal-links: finalizing")
(.. react/linking
(removeEventListener "url" url-event-listener))))

View File

@ -0,0 +1,14 @@
(ns status-im.utils.universal-links.events
(:require [re-frame.core :as re-frame]
[taoensso.timbre :as log]
[status-im.utils.config :as config]
[status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.universal-links.core :as universal-links]))
(handlers/register-handler-fx
:handle-universal-link
(fn [cofx [_ url]]
(log/debug "universal links: event received for " url)
(when config/universal-links-enabled?
(universal-links/handle-url url cofx))))

View File

@ -42,6 +42,7 @@
[status-im.test.utils.mixpanel]
[status-im.test.utils.prices]
[status-im.test.utils.keychain.core]
[status-im.test.utils.universal-links.core]
[status-im.test.ui.screens.accounts.login.events]))
(enable-console-print!)
@ -95,4 +96,5 @@
'status-im.test.utils.mixpanel
'status-im.test.utils.prices
'status-im.test.utils.keychain.core
'status-im.test.utils.universal-links.core
'status-im.test.ui.screens.accounts.login.events)

View File

@ -0,0 +1,57 @@
(ns status-im.test.utils.universal-links.core
(:require [cljs.test :refer-macros [deftest is testing]]
[re-frame.core :as re-frame]
[status-im.utils.universal-links.core :as links]))
(deftest handle-url-test
(testing "the user is not logged in"
(testing "it stores the url for later processing"
(is (= {:db {:universal-links/url "some-url"}}
(links/handle-url "some-url" {:db {}})))))
(testing "the user is logged in"
(let [db {:account/account {:public-key "pk"}
:universal-links/url "some-url"}]
(testing "it clears the url"
(is (nil? (get-in (links/handle-url "some-url"
{:db db})
[:db :universal-links/url]))))
(testing "a public chat link"
(testing "it joins the chat"
(is (get-in (links/handle-url "app://get.status.im/chat/public/status"
{:db db})
[:db :chats "status"]))))
(testing "a user profile link"
(testing "it loads the profile"
(let [actual (links/handle-url "app://get.status.im/user/profile-id"
{:db db})]
(is (= "profile-id" (get-in actual [:db :contacts/identity])))
(is (= :profile (get-in actual [:db :view-id]))))))
(testing "a not found url"
(testing "it does nothing"
(is (nil? (links/handle-url "app://get.status.im/not-existing"
{:db db}))))))))
(deftest url-event-listener
(testing "the url is not nil"
(testing "it dispatches the url"
(let [actual (atom nil)]
(with-redefs [re-frame/dispatch #(reset! actual %)]
(links/url-event-listener #js {:url "some-url"})
(is (= [:handle-universal-link "some-url"] @actual))))))
(testing "the url is nil"
(testing "it does not dispatches the url"
(let [actual (atom nil)]
(with-redefs [re-frame/dispatch #(reset! actual %)]
(links/url-event-listener #js {})
(is (= nil @actual)))))))
(deftest stored-url-event
(testing "the url is in the database"
(testing "it returns the event"
(= [:handle-universal-link "some-url"]
(links/stored-url-event {:db {:universal-links/url "some-url"}}))))
(testing "the url is not in the database"
(testing "it returns nil"
(= nil
(links/stored-url-event {:db {}})))))