Runtime permissions (#762)

This commit is contained in:
alwx 2017-03-07 20:29:59 +03:00 committed by Roman Volosovskyi
parent d24e6d5a19
commit 504e3493ef
22 changed files with 268 additions and 79 deletions

View File

@ -106,7 +106,7 @@ android {
defaultConfig {
applicationId "im.status.ethereum"
minSdkVersion 18
targetSdkVersion 22
targetSdkVersion 23
versionCode getVersionCode()
versionName getVersionName()
ndk {

View File

@ -1,12 +1,31 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="im.status.ethereum">
<!-- non-dangerous permissions -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_PROFILE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<!-- dangerous permissions -->
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<!-- these permissions should be removed -->
<!-- react-native-orientation adds an unnecessary permission; here we remove it -->
<uses-permission android:name="android.permission.BLUETOOTH" tools:node="remove"/>
<!-- react-native-contacts -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" tools:node="remove"/>
<!-- react-native-camera -->
<uses-permission android:name="android.permission.RECORD_AUDIO" tools:node="remove"/>
<uses-permission android:name="android.permission.RECORD_VIDEO" tools:node="remove"/>
<!-- React Native unnecessary permissions (https://github.com/facebook/react-native/issues/5886) -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>
<application
android:allowBackup="true"

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- These are added by React Native for debug mode, but actually aren't needed in release mode -->
<uses-permission tools:node="remove" android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
</manifest>

View File

@ -705,6 +705,14 @@ status.response({
}
});
status.response({
name: "grant-permissions",
color: "#7099e6",
description: "Grant permissions",
icon: "lock_white",
executeImmediately: true
});
status.addListener("on-message-input-change", function (params, context) {
return jsSuggestions({code: params.message}, context);
});

View File

@ -53,7 +53,6 @@
9EE89E271E03FCB7007D3C25 /* libSplashScreen.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7F21DE718EF00D694FF /* libSplashScreen.a */; };
9EE89E2D1E03FD9F007D3C25 /* libimageCropPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20A5C9531D927137002C4965 /* libimageCropPicker.a */; };
A6AF670051B842249D520C7B /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 7ED174A34D7D42358313368B /* Foundation.ttf */; };
AD5063BC2B2A4C52ACE0A0B4 /* libUdpSockets.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A96279092BEC4C4B93914F48 /* libUdpSockets.a */; };
AE97D4B08C9F4821B8E9C50B /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 359B076A658B4FBAB5128B03 /* Ionicons.ttf */; };
B23B48FF1E76917B006D4535 /* RobotoMono-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B23B48FE1E76917B006D4535 /* RobotoMono-Medium.ttf */; };
B24FC7FD1DE7195700D694FF /* Social.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B24FC7FC1DE7195700D694FF /* Social.framework */; };
@ -143,13 +142,6 @@
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = TcpSockets;
};
201067711D477F5E00FA83B6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 134814201AA4EA6300B7C361;
remoteInfo = UdpSockets;
};
201067C31D4789F700FA83B6 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 439B6B4B407A4E2AACAFE5BE /* RCTStatus.xcodeproj */;
@ -452,7 +444,6 @@
20B6B6861D92C42600CC5C6A /* QBImagePicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = QBImagePicker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2756305FAFF144C4A6B0A039 /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = "<group>"; };
2BEE3436791D42248F853999 /* libRCTImageResizer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTImageResizer.a; sourceTree = "<group>"; };
2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = UdpSockets.xcodeproj; path = "../node_modules/react-native-udp/ios/UdpSockets.xcodeproj"; sourceTree = "<group>"; };
305F194186D848FDB07AF34C /* RNFS.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = "<group>"; };
359B076A658B4FBAB5128B03 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = "<group>"; };
38A44830EC5708E89387F641 /* Pods-StatusIm.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusIm.release.xcconfig"; path = "Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm.release.xcconfig"; sourceTree = "<group>"; };
@ -486,7 +477,6 @@
9ED2F45D1D9D52DD00B36508 /* SF-UI-Text-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Regular.otf"; sourceTree = "<group>"; };
9ED2F4601D9D577B00B36508 /* SF-UI-Text-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SF-UI-Text-Bold.otf"; sourceTree = "<group>"; };
9F1854E6D9654226B1FC8308 /* RCTCamera.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTCamera.xcodeproj; path = "../node_modules/react-native-camera/ios/RCTCamera.xcodeproj"; sourceTree = "<group>"; };
A96279092BEC4C4B93914F48 /* libUdpSockets.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libUdpSockets.a; sourceTree = "<group>"; };
ACA66A8F16CD2FE21F38738B /* Pods-StatusIm.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StatusIm.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StatusIm/Pods-StatusIm.debug.xcconfig"; sourceTree = "<group>"; };
B23B48FE1E76917B006D4535 /* RobotoMono-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "RobotoMono-Medium.ttf"; sourceTree = "<group>"; };
B24FC7FC1DE7195700D694FF /* Social.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Social.framework; path = System/Library/Frameworks/Social.framework; sourceTree = SDKROOT; };
@ -554,7 +544,6 @@
25DC9C9DC25846BD8D084888 /* libc++.tbd in Frameworks */,
BA68A2377A20496EA737000D /* libz.tbd in Frameworks */,
3E15DFEC1F6F4D7CAE088F49 /* libTcpSockets.a in Frameworks */,
AD5063BC2B2A4C52ACE0A0B4 /* libUdpSockets.a in Frameworks */,
E0AD9E8F495A4907B65104BF /* libRCTImageResizer.a in Frameworks */,
5F8585D411844E5981B94F40 /* libRNInstabug.a in Frameworks */,
8E55E6877F950B81C8D711C5 /* libPods-StatusIm.a in Frameworks */,
@ -695,14 +684,6 @@
name = Products;
sourceTree = "<group>";
};
2010676E1D477F5E00FA83B6 /* Products */ = {
isa = PBXGroup;
children = (
201067721D477F5E00FA83B6 /* libUdpSockets.a */,
);
name = Products;
sourceTree = "<group>";
};
201067BA1D4789F700FA83B6 /* Products */ = {
isa = PBXGroup;
children = (
@ -857,7 +838,6 @@
8AE71EE8751F4652B13BFE83 /* RNVectorIcons.xcodeproj */,
F090E261B9854867A728CE4F /* RealmReact.xcodeproj */,
38E1A2C8D0734EE99E2B16CE /* TcpSockets.xcodeproj */,
2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */,
439B6B4B407A4E2AACAFE5BE /* RCTStatus.xcodeproj */,
5E5A7625B76441D984EA8C0D /* RCTImageResizer.xcodeproj */,
F3548417D8DA4362B6796A54 /* RNInstabug.xcodeproj */,
@ -1181,10 +1161,6 @@
ProductGroup = 201067551D477F5E00FA83B6 /* Products */;
ProjectRef = 38E1A2C8D0734EE99E2B16CE /* TcpSockets.xcodeproj */;
},
{
ProductGroup = 2010676E1D477F5E00FA83B6 /* Products */;
ProjectRef = 2F0276A9E90843E996A0E762 /* UdpSockets.xcodeproj */;
},
);
projectRoot = "";
targets = (
@ -1258,13 +1234,6 @@
remoteRef = 2010676C1D477F5E00FA83B6 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
201067721D477F5E00FA83B6 /* libUdpSockets.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libUdpSockets.a;
remoteRef = 201067711D477F5E00FA83B6 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
201067C41D4789F700FA83B6 /* libRCTStatus.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@ -1813,7 +1782,6 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-udp/ios/**",
"$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**",
"$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer",
"$(SRCROOT)/../node_modules/instabug-reactnative/ios/RNInstabug",
@ -1863,7 +1831,6 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-udp/ios/**",
"$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**",
"$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer",
"$(SRCROOT)/../node_modules/instabug-reactnative/ios/RNInstabug",
@ -1934,7 +1901,6 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-udp/ios/**",
"$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**",
"$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**",
"$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer",
@ -1993,7 +1959,6 @@
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
"$(SRCROOT)/../node_modules/realm/src/**",
"$(SRCROOT)/../node_modules/react-native-tcp/ios/**",
"$(SRCROOT)/../node_modules/react-native-udp/ios/**",
"$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**",
"$(SRCROOT)/../modules/react-native-status/ios/RCTStatus/**",
"$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer",

View File

@ -14,6 +14,10 @@ import com.github.status_im.status_go.cmd.Statusgo;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
@ -103,10 +107,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
Activity currentActivity = getCurrentActivity();
File extStore = Environment.getExternalStorageDirectory();
String dataFolder = extStore.exists() ?
extStore.getAbsolutePath() + "/ethereum/testnet" :
currentActivity.getApplicationInfo().dataDir + "/ethereum/testnet";
String dataFolder = currentActivity.getApplicationInfo().dataDir + "/ethereum/testnet";
Log.d(TAG, "Starting Geth node in folder: " + dataFolder);
try {
@ -158,6 +159,86 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
Log.d(TAG, "Geth node started");
}
private String getOldExternalDir() {
File extStore = Environment.getExternalStorageDirectory();
return extStore.exists() ? extStore.getAbsolutePath() + "/ethereum/testnet" : getNewInternalDir();
}
private String getNewInternalDir() {
Activity currentActivity = getCurrentActivity();
return currentActivity.getApplicationInfo().dataDir + "/ethereum/testnet";
}
private void deleteDirectory(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteDirectory(f);
} else {
f.delete();
}
}
}
folder.delete();
}
private void copyDirectory(File sourceLocation, File targetLocation) throws IOException {
if (sourceLocation.isDirectory()) {
if (!targetLocation.exists() && !targetLocation.mkdirs()) {
throw new IOException("Cannot create dir " + targetLocation.getAbsolutePath());
}
String[] children = sourceLocation.list();
for (int i = 0; i < children.length; i++) {
copyDirectory(new File(sourceLocation, children[i]), new File(targetLocation, children[i]));
}
} else {
File directory = targetLocation.getParentFile();
if (directory != null && !directory.exists() && !directory.mkdirs()) {
throw new IOException("Cannot create dir " + directory.getAbsolutePath());
}
InputStream in = new FileInputStream(sourceLocation);
OutputStream out = new FileOutputStream(targetLocation);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
}
@ReactMethod
public void shouldMoveToInternalStorage(Callback callback) {
String oldDir = getOldExternalDir();
String newDir = getNewInternalDir();
File oldDirFile = new File(oldDir);
File newDirFile = new File(newDir);
callback.invoke(oldDirFile.exists() && !newDirFile.exists());
}
@ReactMethod
public void moveToInternalStorage(Callback callback) {
String oldDir = getOldExternalDir();
String newDir = getNewInternalDir();
try {
File oldDirFile = new File(oldDir);
copyDirectory(oldDirFile, new File(newDir));
deleteDirectory(oldDirFile);
} catch (IOException e) {
Log.d(TAG, "Moving error: " + e);
}
callback.invoke();
}
@ReactMethod
public void startNode(Callback callback) {
Log.d(TAG, "startNode");

View File

@ -185,6 +185,22 @@ RCT_EXPORT_METHOD(startNode:(RCTResponseSenderBlock)onResultCallback) {
}
}
////////////////////////////////////////////////////////////////////
#pragma mark - shouldMoveToInternalStorage
//////////////////////////////////////////////////////////////////// shouldMoveToInternalStorage
RCT_EXPORT_METHOD(shouldMoveToInternalStorage:(RCTResponseSenderBlock)onResultCallback) {
// Android only
onResultCallback(@[[NSNull null]]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - moveToInternalStorage
//////////////////////////////////////////////////////////////////// moveToInternalStorage
RCT_EXPORT_METHOD(moveToInternalStorage:(RCTResponseSenderBlock)onResultCallback) {
// Android only
onResultCallback(@[[NSNull null]]);
}
////////////////////////////////////////////////////////////////////
#pragma mark - StartNodeRPCServer method
//////////////////////////////////////////////////////////////////// createAccount

View File

@ -75,7 +75,7 @@
"react-native-udp": "^2.0.0",
"react-native-vector-icons": "^4.0.1",
"react-native-webview-bridge": "github:status-im/react-native-webview-bridge#0.33.12",
"readable-stream": "^1.0.33",
"readable-stream": "1.0.33",
"realm": "^0.14.3",
"stream-browserify": "^1.0.0",
"string_decoder": "^0.10.31",

View File

@ -2,10 +2,9 @@
# rn-nodeify
# temporary hack due to https://github.com/facebook/react-native/issues/4968
./node_modules/.bin/rn-nodeify --install --hack;
./node_modules/.bin/rn-nodeify --install "assert,zlib,buffer,inherits,console,constants,crypto,dns,domain,events,http,https,os,path,process,punycode,querystring,fs,stream,string_decoder,timers,tty,url,util,net,vm" --hack;
npm install --save react@16.0.0-alpha.6;
npm install --save react-native-tcp@3.2.1;
npm install --save react-native-udp@2.0.0;
# symlink for re-natal
if ! [ -f re-natal ]; then

View File

@ -37,6 +37,7 @@ Command.prototype.create = function (com) {
this["on-send"] = com.onSend;
this.fullscreen = com.fullscreen;
this.request = com.request;
this["execute-immediately?"] = com.executeImmediately;
this["sequential-params"] = com.sequentialParams;
this.addToCatalog();

View File

@ -3,7 +3,6 @@
(def command-char "/")
(def spacing-char " ")
(def arg-wrapping-char "\"")
(def masking-char "*")
(def bot-char "@")
(def input-height 56)
@ -11,6 +10,7 @@
(def input-spacing-top 16)
(def crazy-math-message-id "crazy-math-message")
(def move-to-internal-failure-message-id "move-to-internal-failure-message")
(def passphrase-message-id "passphraze-message")
(def intro-status-message-id "intro-status")
(def intro-message1-id "intro-message1")

View File

@ -132,26 +132,42 @@
(when-not (messages/get-by-id chat-consts/passphrase-message-id)
(sign-up-service/account-generation-message)))))
(register-handler :move-to-internal-failure-message
(u/side-effect!
(fn [_]
(when-not (messages/get-by-id chat-consts/move-to-internal-failure-message-id)
(sign-up-service/move-to-internal-failure-message)))))
(register-handler :show-mnemonic
(u/side-effect!
(fn [_ [_ mnemonic]]
(let [crazy-math-message? (messages/get-by-id chat-consts/crazy-math-message-id)]
(sign-up-service/passphrase-messages mnemonic crazy-math-message?)))))
(defn- handle-sms [{body :body}]
(when-let [matches (re-matches #"(\d{4})" body)]
(dispatch [:sign-up-confirm (second matches)])))
(register-handler :sign-up
(after (fn [_ [_ phone-number]]
(dispatch [:account-update {:phone phone-number}])))
(fn [db [_ phone-number message-id]]
(sign-up-service/start-listening-confirmation-code-sms)
(let [formatted (format-phone-number phone-number)]
(-> db
(assoc :user-phone-number formatted)
sign-up-service/start-listening-confirmation-code-sms
(server/sign-up formatted
message-id
sign-up-service/on-sign-up-response)))))
(register-handler :start-listening-confirmation-code-sms
(fn [db [_ listener]]
(if-not (:confirmation-code-sms-listener db)
(assoc db :confirmation-code-sms-listener listener)
db)))
(register-handler :stop-listening-confirmation-code-sms
(fn [db [_]]
(fn [db]
(if (:confirmation-code-sms-listener db)
(sign-up-service/stop-listening-confirmation-code-sms db)
db)))

View File

@ -35,3 +35,13 @@
(dispatch [:set-in [:message-data data-type message-id] result])
(when on-requested (on-requested result)))]
(status/call-jail jail-id path params callback)))))))
(handlers/register-handler :execute-command-immediately
(handlers/side-effect!
(fn [_ [_ {command-name :name :as command}]]
(case (keyword command-name)
:grant-permissions
(dispatch [:request-permissions
[:read-external-storage]
#(dispatch [:initialize-geth])])
(log/debug "ignoring command: " command)))))

View File

@ -97,7 +97,9 @@
(fn [{:keys [current-chat-id] :as db} [_ chat-id text]]
(let [chat-id (or chat-id current-chat-id)
chat-text (or text (get-in db [:chats chat-id :input-text]) "")
requests (suggestions/get-request-suggestions db chat-text)
requests (->> (suggestions/get-request-suggestions db chat-text)
(remove (fn [{:keys [type]}]
(= type :grant-permissions))))
suggestions (suggestions/get-command-suggestions db chat-text)
global-commands (suggestions/get-global-command-suggestions db chat-text)
{:keys [dapp?]} (get-in db [:contacts chat-id])]

View File

@ -42,14 +42,15 @@
:from console-chat-id
:to "me"}])))
(defn handle-sms [{body :body}]
(when-let [matches (re-matches #"(\d{4})" body)]
(dispatch [:sign-up-confirm (second matches)])))
(defn start-listening-confirmation-code-sms [db]
(if-not (:confirmation-code-sms-listener db)
(assoc db :confirmation-code-sms-listener (add-sms-listener handle-sms))
db))
(defn start-listening-confirmation-code-sms []
(dispatch [:request-permissions
[:receive-sms]
(fn []
(let [listener (add-sms-listener
(fn [{body :body}]
(when-let [matches (re-matches #"(\d{4})" body)]
(dispatch [:sign-up-confirm (second matches)]))))]
(dispatch [:start-listening-confirmation-code-sms listener])))]))
(defn stop-listening-confirmation-code-sms [db]
(when-let [listener (:confirmation-code-sms-listener db)]
@ -70,7 +71,9 @@
(defn sync-contacts []
;; TODO 'on-sync-contacts' is never called
(dispatch [:sync-contacts on-sync-contacts]))
(dispatch [:request-permissions
[:read-contacts]
#(dispatch [:sync-contacts on-sync-contacts])]))
(defn on-send-code-response [body]
(dispatch [:received-message
@ -122,6 +125,18 @@
:from console-chat-id
:to "me"}]))
(defn move-to-internal-failure-message []
(dispatch [:received-message
{:message-id const/move-to-internal-failure-message-id
:content (command-content
:grant-permissions
(label :t/move-to-internal-failure-message))
:content-type content-type-command-request
:outgoing false
:chat-id console-chat-id
:from console-chat-id
:to "me"}]))
(defn passphrase-messages [mnemonic crazy-math-message?]
(dispatch [:received-message
{:message-id const/passphrase-message-id

View File

@ -56,11 +56,13 @@
:component-will-unmount
#(reset! loop? false)
:reagent-render
(fn [message-id {command-icon :icon :as command} status-initialized?]
(fn [message-id {:keys [execute-immediately?] command-icon :icon :as command} status-initialized?]
(when command
[touchable-highlight
{:on-press (when (and (not @answered?) status-initialized?)
#(set-chat-command message-id command))
{:on-press (if execute-immediately?
#(dispatch [:execute-command-immediately command])
(when (and (not @answered?) status-initialized?)
#(set-chat-command message-id command)))
:style (st/command-request-image-touchable)
:accessibility-label (id/chat-request-message-button (:name command))}
[animated-view {:style (st/command-request-image-view command scale-anim-val)}

View File

@ -0,0 +1,24 @@
(ns status-im.components.permissions
(:require [taoensso.timbre :as log]))
(def permissions-class (.-PermissionsAndroid js/ReactNative))
(def permissions-map
{:read-external-storage "android.permission.READ_EXTERNAL_STORAGE"
:write-external-storage "android.permission.WRITE_EXTERNAL_STORAGE"
:read-contacts "android.permission.READ_CONTACTS"
:camera "android.permission.CAMERA"
:receive-sms "android.permission.RECEIVE_SMS"})
(defn all-granted? [permissions]
(let [permission-vals (distinct (vals permissions))]
(and (= (count permission-vals) 1)
(not= (first permission-vals) "denied"))))
(defn request-permissions [permissions then else]
(let [permissions (mapv #(get permissions-map %) permissions)
result (.requestMultiple permissions-class (clj->js permissions))
result (.then result #(if (all-granted? (js->clj %))
(then)
(when else (else))))
result (.catch result #(else))]))

View File

@ -61,6 +61,14 @@
(.addListener r/device-event-emitter "gethEvent"
#(dispatch [:signal-event (.-jsonEvent %)])))
(defn should-move-to-internal-storage? [on-result]
(when status
(call-module #(.shouldMoveToInternalStorage status on-result))))
(defn move-to-internal-storage [on-result]
(when status
(call-module #(.moveToInternalStorage status on-result))))
(defn start-node [on-result]
(when status
(call-module #(.startNode status on-result))))

View File

@ -6,6 +6,7 @@
[taoensso.timbre :as log]
[status-im.utils.crypt :refer [gen-random-bytes]]
[status-im.components.status :as status]
[status-im.components.permissions :as permissions]
[status-im.utils.handlers :refer [register-handler] :as u]
status-im.chat.handlers
status-im.group-settings.handlers
@ -122,11 +123,24 @@
(log/debug "Started Node")
(enet/get-network #(dispatch [:set :network %])))
(defn move-to-internal-storage [db]
(status/move-to-internal-storage
(fn []
(status/start-node
(fn [result]
(node-started db result))))))
(register-handler :initialize-geth
(u/side-effect!
(fn [db _]
(log/debug "Starting node")
(status/start-node (fn [result] (node-started db result))))))
(status/should-move-to-internal-storage?
(fn [should-move?]
(if should-move?
(dispatch [:request-permissions
[:read-external-storage]
#(move-to-internal-storage db)
#(dispatch [:move-to-internal-failure-message])])
(status/start-node (fn [result] (node-started db result)))))))))
(register-handler :signal-event
(u/side-effect!
@ -168,6 +182,14 @@
(.resetOkHttpClient webview-bridge)))
nil))))
(register-handler :request-permissions
(u/side-effect!
(fn [_ [_ permissions then else]]
(permissions/request-permissions
permissions
then
else))))
;; -- User data --------------------------------------------------------------
(register-handler :load-user-phone-number
(fn [db [_]]

View File

@ -37,8 +37,13 @@
:on-change-text #(dispatch [:set-in [:profile-edit :name] %])}]])
(def profile-icon-options
[{:text (label :t/image-source-gallery) :value #(dispatch [:open-image-picker])}
{:text (label :t/image-source-make-photo) :value #(dispatch [:navigate-to :profile-photo-capture])}])
[{:text (label :t/image-source-gallery)
:value #(dispatch [:open-image-picker])}
{:text (label :t/image-source-make-photo)
:value (fn []
(dispatch [:request-permissions
[:camera :write-external-storage]
#(dispatch [:navigate-to :profile-photo-capture])]))}])
(defn edit-profile-bage [contact]
[view st/edit-profile-bage

View File

@ -26,18 +26,6 @@
(.log js/console type error))]
(img->base64 path on-success on-error)))))))
(register-handler :open-image-source-selector
(u/side-effect!
(fn [_ [_ list-selection-fn]]
(list-selection-fn {:title (label :t/image-source-title)
:options [(label :t/image-source-make-photo) (label :t/image-source-gallery)]
:callback (fn [index]
(case index
0 (dispatch [:navigate-to :profile-photo-capture])
1 (dispatch [:open-image-picker])
:default))
:cancel-text (label :t/image-source-cancel)}))))
(register-handler :phone-number-change-requested
;; Switch user to the console issuing the !phone command automatically to let him change his phone number.
;; We allow to change phone number only from console because this requires entering SMS verification code.

View File

@ -122,6 +122,7 @@
:intro-status "Chat with me to setup your account and change your settings!"
:intro-message1 "Welcome to Status\nTap this message to set your password & get started!"
:account-generation-message "Gimmie a sec, I gotta do some crazy math to generate your account!"
:move-to-internal-failure-message "We need to move some important files from external to internal storage. To do this, we need your permission. We won't be using external storage in future versions."
:debug-enabled "Debug server has been launched! Your IP address is {{ip}}. You can now add your DApp by running *status-dev-cli add-dapp --ip {{ip}}* from your computer"
;phone types