diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/AccountManager.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/AccountManager.java index 02b78658da..1ba44a1033 100644 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/AccountManager.java +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/AccountManager.java @@ -210,18 +210,14 @@ public class AccountManager extends ReactContextBaseJavaModule { @ReactMethod public void saveAccountAndLoginWithKeycard(final String multiaccountData, final String password, final String settings, final String config, final String accountsData, final String chatKey) { - try { - Log.d(TAG, "saveAccountAndLoginWithKeycard"); - String finalConfig = prepareDirAndUpdateConfig(config, this.utils.getKeyUID(multiaccountData)); - String result = Statusgo.saveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey); - if (result.startsWith("{\"error\":\"\"")) { - Log.d(TAG, "saveAccountAndLoginWithKeycard result: " + result); - Log.d(TAG, "Geth node started"); - } else { - Log.e(TAG, "saveAccountAndLoginWithKeycard failed: " + result); - } - } catch (JSONException e) { - Log.e(TAG, "JSON conversion failed: " + e.getMessage()); + Log.d(TAG, "saveAccountAndLoginWithKeycard"); + String finalConfig = prepareDirAndUpdateConfig(config, this.utils.getKeyUID(multiaccountData)); + String result = Statusgo.saveAccountAndLoginWithKeycard(multiaccountData, password, settings, finalConfig, accountsData, chatKey); + if (result.startsWith("{\"error\":\"\"")) { + Log.d(TAG, "saveAccountAndLoginWithKeycard result: " + result); + Log.d(TAG, "Geth node started"); + } else { + Log.e(TAG, "saveAccountAndLoginWithKeycard failed: " + result); } } 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 deleted file mode 100644 index be1216fb27..0000000000 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.java +++ /dev/null @@ -1,147 +0,0 @@ -package im.status.ethereum.module; - -import android.app.Activity; -import android.util.Log; -import android.view.Window; -import android.view.WindowManager; -import android.os.Build; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.LifecycleEventListener; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.modules.core.DeviceEventManagerModule; - -import statusgo.SignalHandler; -import statusgo.Statusgo; - -import org.json.JSONException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import javax.annotation.Nullable; - -class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventListener, SignalHandler { - - private static final String TAG = "StatusModule"; - private static StatusModule module; - private ReactApplicationContext reactContext; - private boolean rootedDevice; - private boolean background; - private Utils utils; - - - StatusModule(ReactApplicationContext reactContext, boolean rootedDevice) { - super(reactContext); - this.reactContext = reactContext; - this.rootedDevice = rootedDevice; - this.utils = new Utils(reactContext); - - reactContext.addLifecycleEventListener(this); - } - - @Override - public String getName() { - return "Status"; - } - - @Override - public void onHostResume() { - module = this; - this.background = false; - Statusgo.setMobileSignalHandler(this); - } - - @Override - public void onHostPause() { - this.background = true; - } - - @Override - public void onHostDestroy() { - Log.d(TAG, "******************* ON HOST DESTROY *************************"); - } - - public void handleSignal(final String jsonEventString) { - Log.d(TAG, "Signal event"); - WritableMap params = Arguments.createMap(); - params.putString("jsonEvent", jsonEventString); - this.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("gethEvent", params); - } - - @ReactMethod - public void closeApplication() { - System.exit(0); - } - - @ReactMethod - public void connectionChange(final String type, final boolean isExpensive) { - Log.d(TAG, "ConnectionChange: " + type + ", is expensive " + isExpensive); - Statusgo.connectionChange(type, isExpensive ? 1 : 0); - } - - @ReactMethod - public void appStateChange(final String type) { - Log.d(TAG, "AppStateChange: " + type); - Statusgo.appStateChange(type); - } - - @ReactMethod - public void startLocalNotifications() { - Log.d(TAG, "startLocalNotifications"); - Statusgo.startLocalNotifications(); - } - - @ReactMethod - public void getNodeConfig(final Callback callback) throws JSONException { - this.utils.executeRunnableStatusGoMethod(() -> Statusgo.getNodeConfig(), callback); - } - - @ReactMethod - public void deleteImportedKey(final String keyUID, final String address, final String password, final Callback callback) throws JSONException { - final String keyStoreDir = this.utils.getKeyStorePath(keyUID); - this.utils.executeRunnableStatusGoMethod(() -> Statusgo.deleteImportedKey(address, password, keyStoreDir), callback); - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String fleets() { - return Statusgo.fleets(); - } - - @Override - public @Nullable - Map getConstants() { - HashMap constants = new HashMap(); - - constants.put("is24Hour", this.utils.is24Hour()); - constants.put("model", Build.MODEL); - constants.put("brand", Build.BRAND); - constants.put("buildId", Build.ID); - constants.put("deviceId", Build.BOARD); - return constants; - } - - @ReactMethod - public void isDeviceRooted(final Callback callback) { - callback.invoke(rootedDevice); - } - - @ReactMethod - public void deactivateKeepAwake() { - final Activity activity = getCurrentActivity(); - - if (activity != null) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - activity.getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - } - }); - } - } - -} - diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.kt b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.kt new file mode 100644 index 0000000000..65df036fb7 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusModule.kt @@ -0,0 +1,114 @@ +package im.status.ethereum.module + +import android.app.Activity +import android.os.Build +import android.util.Log +import com.facebook.react.bridge.* +import com.facebook.react.modules.core.DeviceEventManagerModule +import statusgo.SignalHandler +import statusgo.Statusgo +import org.json.JSONException +import android.view.WindowManager + +class StatusModule(private val reactContext: ReactApplicationContext, private val rootedDevice: Boolean) : ReactContextBaseJavaModule(reactContext), LifecycleEventListener, SignalHandler { + + companion object { + private const val TAG = "StatusModule" + private var module: StatusModule? = null + } + + private val utils: Utils = Utils(reactContext) + private var background: Boolean = false + + init { + reactContext.addLifecycleEventListener(this) + } + + override fun getName(): String { + return "Status" + } + + override fun onHostResume() { + module = this + background = false + Statusgo.setMobileSignalHandler(this) + } + + override fun onHostPause() { + background = true + } + + override fun onHostDestroy() { + Log.d(TAG, "******************* ON HOST DESTROY *************************") + } + + override fun handleSignal(jsonEventString: String) { + Log.d(TAG, "Signal event") + val params = Arguments.createMap() + params.putString("jsonEvent", jsonEventString) + reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java).emit("gethEvent", params) + } + + @ReactMethod + fun closeApplication() { + System.exit(0) + } + + @ReactMethod + fun connectionChange(type: String, isExpensive: Boolean) { + Log.d(TAG, "ConnectionChange: $type, is expensive $isExpensive") + Statusgo.connectionChange(type, if (isExpensive) 1 else 0) + } + + @ReactMethod + fun appStateChange(type: String) { + Log.d(TAG, "AppStateChange: $type") + Statusgo.appStateChange(type) + } + + @ReactMethod + fun startLocalNotifications() { + Log.d(TAG, "startLocalNotifications") + Statusgo.startLocalNotifications() + } + + @ReactMethod + fun getNodeConfig(callback: Callback) { + utils.executeRunnableStatusGoMethod({ Statusgo.getNodeConfig() }, callback) + } + + @ReactMethod + fun deleteImportedKey(keyUID: String, address: String, password: String, callback: Callback) { + val keyStoreDir = utils.getKeyStorePath(keyUID) + utils.executeRunnableStatusGoMethod({ Statusgo.deleteImportedKey(address, password, keyStoreDir) }, callback) + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun fleets(): String { + return Statusgo.fleets() + } + + override fun getConstants(): Map? { + return hashMapOf( + "is24Hour" to utils.is24Hour(), + "model" to Build.MODEL, + "brand" to Build.BRAND, + "buildId" to Build.ID, + "deviceId" to Build.BOARD + ) + } + + @ReactMethod + fun isDeviceRooted(callback: Callback) { + callback.invoke(rootedDevice) + } + + @ReactMethod + fun deactivateKeepAwake() { + val activity = currentActivity + + activity?.runOnUiThread { + activity.window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + } +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.java deleted file mode 100644 index 76874cf176..0000000000 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.java +++ /dev/null @@ -1,51 +0,0 @@ -package im.status.ethereum.module; - -import com.facebook.react.ReactPackage; -import com.facebook.react.bridge.JavaScriptModule; -import com.facebook.react.bridge.NativeModule; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.uimanager.ViewManager; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import statusgo.Statusgo; - -public class StatusPackage implements ReactPackage { - - private boolean rootedDevice; - - public static String getImageTLSCert() { - return Statusgo.imageServerTLSCert(); - } - - public StatusPackage(boolean rootedDevice) { - this.rootedDevice = rootedDevice; - } - - @Override - public List createNativeModules(ReactApplicationContext reactContext) { - List modules = new ArrayList<>(); - - modules.add(new StatusModule(reactContext, this.rootedDevice)); - modules.add(new AccountManager(reactContext)); - modules.add(new EncryptionUtils(reactContext)); - modules.add(new DatabaseManager(reactContext)); - modules.add(new UIHelper(reactContext)); - modules.add(new LogManager(reactContext)); - modules.add(new Utils(reactContext)); - modules.add(new NetworkManager(reactContext)); - modules.add(new RNSelectableTextInputModule(reactContext)); - - return modules; - } - - @Override - public List createViewManagers(ReactApplicationContext reactContext) { - return Arrays.asList( - new RNSelectableTextInputViewManager() - ); - } -} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.kt b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.kt new file mode 100644 index 0000000000..738f9f0768 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/StatusPackage.kt @@ -0,0 +1,38 @@ +package im.status.ethereum.module + +import com.facebook.react.ReactPackage +import com.facebook.react.bridge.NativeModule +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.uimanager.ViewManager +import statusgo.Statusgo + +class StatusPackage(private val rootedDevice: Boolean) : ReactPackage { + + companion object { + fun getImageTLSCert(): String = Statusgo.imageServerTLSCert() + } + + override fun createNativeModules(reactContext: ReactApplicationContext): List { + val modules = mutableListOf() + + modules.apply { + add(StatusModule(reactContext, rootedDevice)) + add(AccountManager(reactContext)) + add(EncryptionUtils(reactContext)) + add(DatabaseManager(reactContext)) + add(UIHelper(reactContext)) + add(LogManager(reactContext)) + add(Utils(reactContext)) + add(NetworkManager(reactContext)) + add(RNSelectableTextInputModule(reactContext)) + } + + return modules + } + + override fun createViewManagers(reactContext: ReactApplicationContext): List> { + return listOf( + RNSelectableTextInputViewManager() + ) + } +} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/Utils.java b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/Utils.java deleted file mode 100644 index 0cbb057e1d..0000000000 --- a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/Utils.java +++ /dev/null @@ -1,155 +0,0 @@ -package im.status.ethereum.module; - -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import android.util.Log; -import java.util.function.Supplier; -import java.io.File; -import android.content.Context; -import android.os.Environment; -import org.json.JSONObject; -import org.json.JSONException; -import statusgo.Statusgo; - -public class Utils extends ReactContextBaseJavaModule { - private static final String gethLogFileName = "geth.log"; - - private static final String TAG = "Utils"; - private ReactApplicationContext reactContext; - - public Utils(ReactApplicationContext reactContext) { - super(reactContext); - this.reactContext = reactContext; - } - - @Override - public String getName() { - return "Utils"; - } - public String getNoBackupDirectory() { - return this.getReactApplicationContext().getNoBackupFilesDir().getAbsolutePath(); - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String backupDisabledDataDir() { - return getNoBackupDirectory(); - } - - public File getPublicStorageDirectory() { - final Context context = this.getReactApplicationContext(); - // Environment.getExternalStoragePublicDirectory doesn't work as expected on Android Q - // https://developer.android.com/reference/android/os/Environment#getExternalStoragePublicDirectory(java.lang.String) - return context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); - } - - public File getLogsFile() { - final File pubDirectory = getPublicStorageDirectory(); - final File logFile = new File(pubDirectory, gethLogFileName); - - return logFile; - } - - public String getKeyUID(final String json) throws JSONException { - final JSONObject jsonObj = new JSONObject(json); - return jsonObj.getString("key-uid"); - } - - public String pathCombine(final String path1, final String path2) { - // Replace this logic with Paths.get(path1, path2) once API level 26+ becomes the minimum supported API level - final File file = new File(path1, path2); - return file.getAbsolutePath(); - } - - public String getKeyStorePath(String keyUID) { - final String commonKeydir = pathCombine(getNoBackupDirectory(), "/keystore"); - final String keydir = pathCombine(commonKeydir, keyUID); - - return keydir; - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String keystoreDir() { - final String absRootDirPath = getNoBackupDirectory(); - return pathCombine(absRootDirPath, "keystore"); - } - - public void migrateKeyStoreDir(final String accountData, final String password) { - try { - final String commonKeydir = pathCombine(getNoBackupDirectory(), "/keystore"); - final String keydir = getKeyStorePath(getKeyUID(accountData)); - Log.d(TAG, "before migrateKeyStoreDir " + keydir); - - File keydirFile = new File(keydir); - if(!keydirFile.exists() || keydirFile.list().length == 0) { - Log.d(TAG, "migrateKeyStoreDir"); - Statusgo.migrateKeyStoreDir(accountData, password, commonKeydir, keydir); - Statusgo.initKeystore(keydir); - } - } catch (JSONException e) { - Log.e(TAG, "JSON conversion failed: " + e.getMessage()); - } - } - - public boolean checkAvailability() { - // We wait at least 10s for getCurrentActivity to return a value, - // otherwise we give up - for (int attempts = 0; attempts < 100; attempts++) { - if (getCurrentActivity() != null) { - return true; - } - try { - Thread.sleep(100); - } catch (InterruptedException ex) { - if (getCurrentActivity() != null) { - return true; - } - Log.d(TAG, "Activity doesn't exist"); - return false; - } - } - - Log.d(TAG, "Activity doesn't exist"); - return false; - } - - public void executeRunnableStatusGoMethod(Supplier method, Callback callback) throws JSONException { - if (!checkAvailability()) { - callback.invoke(false); - return; - } - - Runnable runnableTask = () -> { - String res = method.get(); - callback.invoke(res); - }; - - StatusThreadPoolExecutor.getInstance().execute(runnableTask); - } - - @ReactMethod - public void validateMnemonic(final String seed, final Callback callback) throws JSONException { - executeRunnableStatusGoMethod(() -> Statusgo.validateMnemonic(seed), callback); - } - - public Boolean is24Hour() { - return android.text.format.DateFormat.is24HourFormat(this.reactContext.getApplicationContext()); - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String checkAddressChecksum(final String address) { - return Statusgo.checkAddressChecksum(address); - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String isAddress(final String address) { - return Statusgo.isAddress(address); - } - - @ReactMethod(isBlockingSynchronousMethod = true) - public String toChecksumAddress(final String address) { - return Statusgo.toChecksumAddress(address); - } - -} diff --git a/modules/react-native-status/android/src/main/java/im/status/ethereum/module/Utils.kt b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/Utils.kt new file mode 100644 index 0000000000..c7801fe5d2 --- /dev/null +++ b/modules/react-native-status/android/src/main/java/im/status/ethereum/module/Utils.kt @@ -0,0 +1,144 @@ +package im.status.ethereum.module + +import android.content.Context +import android.os.Environment +import com.facebook.react.bridge.Callback +import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReactContextBaseJavaModule +import com.facebook.react.bridge.ReactMethod +import android.util.Log +import org.json.JSONException +import org.json.JSONObject +import java.io.File +import java.util.function.Supplier +import statusgo.Statusgo + +class Utils(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { + + companion object { + private const val gethLogFileName = "geth.log" + private const val TAG = "Utils" + } + + override fun getName(): String { + return "Utils" + } + + fun getNoBackupDirectory(): String { + return reactContext.noBackupFilesDir.absolutePath + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun backupDisabledDataDir(): String { + return getNoBackupDirectory() + } + + fun getPublicStorageDirectory(): File? { + // Environment.getExternalStoragePublicDirectory doesn't work as expected on Android Q + // https://developer.android.com/reference/android/os/Environment#getExternalStoragePublicDirectory(java.lang.String) + return reactContext.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + } + + fun getLogsFile(): File { + val pubDirectory = getPublicStorageDirectory() + return File(pubDirectory, gethLogFileName) + } + + fun getKeyUID(json: String): String { + val jsonObj = JSONObject(json) + return jsonObj.getString("key-uid") + } + + fun pathCombine(path1: String, path2: String): String { + val file = File(path1, path2) + return file.absolutePath + } + + fun getKeyStorePath(keyUID: String): String { + val commonKeydir = pathCombine(getNoBackupDirectory(), "/keystore") + return pathCombine(commonKeydir, keyUID) + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun keystoreDir(): String { + val absRootDirPath = getNoBackupDirectory() + return pathCombine(absRootDirPath, "keystore") + } + + fun migrateKeyStoreDir(accountData: String, password: String) { + try { + val commonKeydir = pathCombine(getNoBackupDirectory(), "/keystore") + val keydir = getKeyStorePath(getKeyUID(accountData)) + Log.d(TAG, "before migrateKeyStoreDir $keydir") + + val keydirFile = File(keydir) + if (!keydirFile.exists() || keydirFile.list().isEmpty()) { + Log.d(TAG, "migrateKeyStoreDir") + Statusgo.migrateKeyStoreDir(accountData, password, commonKeydir, keydir) + Statusgo.initKeystore(keydir) + } + } catch (e: JSONException) { + Log.e(TAG, "JSON conversion failed: ${e.message}") + } + } + + fun checkAvailability(): Boolean { + // We wait at least 10s for getCurrentActivity to return a value, + // otherwise we give up + for (attempts in 0 until 100) { + if (currentActivity != null) { + return true + } + try { + Thread.sleep(100) + } catch (ex: InterruptedException) { + if (currentActivity != null) { + return true + } + Log.d(TAG, "Activity doesn't exist") + return false + } + } + + Log.d(TAG, "Activity doesn't exist") + return false + } + + fun executeRunnableStatusGoMethod(method: Supplier, callback: Callback) { + if (!checkAvailability()) { + callback.invoke(false) + return + } + + val runnableTask = Runnable { + val res = method.get() + callback.invoke(res) + } + + StatusThreadPoolExecutor.getInstance().execute(runnableTask) + } + + @ReactMethod + fun validateMnemonic(seed: String, callback: Callback) { + executeRunnableStatusGoMethod({ Statusgo.validateMnemonic(seed) }, callback) + } + + fun is24Hour(): Boolean { + return android.text.format.DateFormat.is24HourFormat(reactContext.applicationContext) + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun checkAddressChecksum(address: String): String { + return Statusgo.checkAddressChecksum(address) + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun isAddress(address: String): String { + return Statusgo.isAddress(address) + } + + @ReactMethod(isBlockingSynchronousMethod = true) + fun toChecksumAddress(address: String): String { + return Statusgo.toChecksumAddress(address) + } +}