diff --git a/android/app/src/main/java/im/status/ethereum/MainActivity.java b/android/app/src/main/java/im/status/ethereum/MainActivity.java index 54b9ea8d70..f42ef68e20 100644 --- a/android/app/src/main/java/im/status/ethereum/MainActivity.java +++ b/android/app/src/main/java/im/status/ethereum/MainActivity.java @@ -12,8 +12,10 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Looper; +import android.preference.PreferenceManager; import android.support.v4.app.ActivityCompat; import android.util.Log; +import android.view.WindowManager; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; @@ -109,7 +111,7 @@ public class MainActivity extends ReactActivity final ActivityManager activityManager = getActivityManager(); Log.v("RNBootstrap", "Available system memory "+getAvailableMemory(activityManager).availMem + ", maximum usable application memory " + activityManager.getLargeMemoryClass()+"M"); - + setSecureFlag(); SplashScreen.show(this, true); super.onCreate(savedInstanceState); @@ -202,6 +204,16 @@ public class MainActivity extends ReactActivity editor.commit(); } + private void setSecureFlag() { + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); + final boolean setSecure = sharedPrefs.getBoolean("BLANK_PREVIEW", false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && setSecure) { + getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } else { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + } + @TargetApi(Build.VERSION_CODES.M) public void requestPermissions(String[] permissions, int requestCode, PermissionListener listener) { mPermissionListener = listener; diff --git a/ios/StatusIm/AppDelegate.m b/ios/StatusIm/AppDelegate.m index 2dee3efbfb..8dbf607fe7 100644 --- a/ios/StatusIm/AppDelegate.m +++ b/ios/StatusIm/AppDelegate.m @@ -20,7 +20,10 @@ #import "RNFirebaseNotifications.h" #import "RNFirebaseMessaging.h" -@implementation AppDelegate +@implementation AppDelegate +{ + UIView *_blankView; +} - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { @@ -47,6 +50,10 @@ RCTSetLogThreshold(RCTLogLevelTrace); } + NSDictionary *appDefaults = [NSDictionary + dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:@"BLANK_PREVIEW"]; + [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; + RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"StatusIm" @@ -54,6 +61,10 @@ rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + _blankView = [[UIView alloc]initWithFrame:self.window.frame]; + _blankView.backgroundColor = [UIColor whiteColor]; + _blankView.alpha = 0; + UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; @@ -101,4 +112,25 @@ #endif } +- (void)applicationWillResignActive:(UIApplication *)application { + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"BLANK_PREVIEW"]) { + [self.window addSubview:_blankView]; + [self.window bringSubviewToFront:_blankView]; + + [UIView animateWithDuration:0.5 animations:^{ + _blankView.alpha = 1; + }]; + } +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"BLANK_PREVIEW"]) { + [UIView animateWithDuration:0.5 animations:^{ + _blankView.alpha = 0; + } completion:^(BOOL finished) { + [_blankView removeFromSuperview]; + }]; + } +} + @end diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java index aec93250df..ab49ff5199 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java @@ -9,8 +9,10 @@ import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Environment; +import android.preference.PreferenceManager; import android.support.v4.content.FileProvider; import android.util.Log; +import android.view.Window; import android.view.WindowManager; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; @@ -943,6 +945,32 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL callback.invoke(uniqueID); } + @ReactMethod + public void setBlankPreviewFlag(final Boolean blankPreview) { + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.reactContext); + sharedPrefs.edit().putBoolean("BLANK_PREVIEW", blankPreview).commit(); + setSecureFlag(); + } + + private void setSecureFlag() { + final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this.reactContext); + final boolean setSecure = sharedPrefs.getBoolean("BLANK_PREVIEW", true); + final Activity activity = this.reactContext.getCurrentActivity(); + if (activity != null) { + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + final Window window = activity.getWindow(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH && setSecure) { + window.addFlags(WindowManager.LayoutParams.FLAG_SECURE); + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE); + } + } + }); + } + } + private Boolean is24Hour() { return android.text.format.DateFormat.is24HourFormat(this.reactContext.getApplicationContext()); } diff --git a/modules/react-native-status/ios/RCTStatus/RCTStatus.m b/modules/react-native-status/ios/RCTStatus/RCTStatus.m index d0d1237324..207abc6203 100644 --- a/modules/react-native-status/ios/RCTStatus/RCTStatus.m +++ b/modules/react-native-status/ios/RCTStatus/RCTStatus.m @@ -539,6 +539,15 @@ RCT_EXPORT_METHOD(getDeviceUUID:(RCTResponseSenderBlock)callback) { callback(@[Identifier]); } +RCT_EXPORT_METHOD(setBlankPreviewFlag:(BOOL *)newValue) +{ + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + [userDefaults setBool:newValue forKey:@"BLANK_PREVIEW"]; + + [userDefaults synchronize]; +} + - (bool) is24Hour { NSString *format = [NSDateFormatter dateFormatFromTemplate:@"j" options:0 locale:[NSLocale currentLocale]]; diff --git a/src/status_im/accounts/core.cljs b/src/status_im/accounts/core.cljs index b6ff55db21..d21ea66aeb 100644 --- a/src/status_im/accounts/core.cljs +++ b/src/status_im/accounts/core.cljs @@ -16,6 +16,11 @@ (fn [on] (native-module/chaos-mode-update on (constantly nil)))) +(re-frame/reg-fx + ::blank-preview-flag-changed + (fn [flag] + (native-module/set-blank-preview-flag flag))) + (fx/defn show-mainnet-is-default-alert [{:keys [db]}] (let [shown-version (get-in db [:account/account :mainnet-warning-shown-version]) current-version build/version] @@ -98,6 +103,14 @@ (assoc settings :web3-opt-in? opt-in) {}))) +(fx/defn switch-preview-privacy-mode [{:keys [db] :as cofx} private?] + (let [settings (get-in db [:account/account :settings])] + (fx/merge cofx + {::blank-preview-flag-changed private?} + (accounts.update/update-settings + (assoc settings :preview-privacy? private?) + {})))) + (fx/defn update-recent-stickers [cofx stickers] (accounts.update/account-update cofx {:recent-stickers stickers} diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 7618c6b438..732ef73de6 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -109,6 +109,7 @@ (defn default-account-settings [] {:web3-opt-in? true + :preview-privacy? false :wallet {:visible-tokens {:testnet #{:STT :HND} :mainnet #{:SNT} :rinkeby #{:MOKSHA :KDO} diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 9e2fcf9829..c4d33977c5 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -214,6 +214,11 @@ (fn [cofx [_ opt-in]] (accounts/switch-web3-opt-in-mode cofx opt-in))) +(handlers/register-handler-fx + :accounts.ui/preview-privacy-mode-switched + (fn [cofx [_ private?]] + (accounts/switch-preview-privacy-mode cofx private?))) + (handlers/register-handler-fx :accounts.ui/wallet-set-up-confirmed (fn [cofx [_ modal?]] diff --git a/src/status_im/native_module/core.cljs b/src/status_im/native_module/core.cljs index 8e4d3c78ea..81e76c3aad 100644 --- a/src/status_im/native_module/core.cljs +++ b/src/status_im/native_module/core.cljs @@ -88,6 +88,9 @@ (defn get-device-UUID [callback] (native-module/get-device-UUID callback)) +(defn set-blank-preview-flag [flag] + (native-module/set-blank-preview-flag flag)) + (defn is24Hour [] (native-module/is24Hour)) diff --git a/src/status_im/native_module/impl/module.cljs b/src/status_im/native_module/impl/module.cljs index 184807bae3..be17b21280 100644 --- a/src/status_im/native_module/impl/module.cljs +++ b/src/status_im/native_module/impl/module.cljs @@ -133,6 +133,9 @@ (fn [UUID] (callback (string/upper-case UUID))))) +(defn set-blank-preview-flag [flag] + (.setBlankPreviewFlag status flag)) + (defn extract-group-membership-signatures [signature-pairs callback] (when status (.extractGroupMembershipSignatures status signature-pairs callback))) diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index 9e3c4c3693..18e8b7cf79 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -101,7 +101,7 @@ :value value}])) (defn- my-profile-settings [{:keys [seed-backed-up? mnemonic]} - account + {:keys [settings] :as account} currency logged-in? extensions] @@ -144,6 +144,11 @@ :action-fn #(re-frame/dispatch [:navigate-to :installations]) :accessibility-label :pairing-settings-button}] [profile.components/settings-item-separator] + [profile.components/settings-switch-item + {:label-kw :t/preview-privacy + :value (boolean (:preview-privacy? settings)) + :action-fn #(re-frame/dispatch [:accounts.ui/preview-privacy-mode-switched %])}] + [profile.components/settings-item-separator] [profile.components/settings-item {:label-kw :t/dapps-permissions :accessibility-label :dapps-permissions-button diff --git a/test/cljs/status_im/test/sign_in/data.cljs b/test/cljs/status_im/test/sign_in/data.cljs index c1c30f4dca..0a1275b752 100644 --- a/test/cljs/status_im/test/sign_in/data.cljs +++ b/test/cljs/status_im/test/sign_in/data.cljs @@ -104,6 +104,7 @@ :last-request nil :desktop-notifications? false :settings {:web3-opt-in? true + :preview-privacy? true :fleet :eth.beta :wallet {:visible-tokens {:testnet #{:STT :HND} diff --git a/translations/en.json b/translations/en.json index 40a27e43ce..1f92044d41 100644 --- a/translations/en.json +++ b/translations/en.json @@ -784,6 +784,7 @@ "currency-display-name-usd": "United States Dollar", "currency-display-name-uah": "Ukraine Hryvnia", "web3-opt-in": "Browser privacy mode", + "preview-privacy": "Preview privacy mode", "recover-password-invalid": "This account already exists but passwords do not match", "recover-keycard-account-not-supported": "Recovering keycard account with password is not supported", "members-active-none": "no members",