This commit does many things : - Upgrade `react-native ` to `0.72.5` - Upgrade `react-native-reanimated` to `3.5.4` - Upgrade `react-native-navigation` to `7.37.0` - `ndkVersion` has been bumped to `25.2.9519653` - `cmakeVersion` has been bumped to `3.22.1` - `kotlinVersion` has been bumped to `1.7.22` - `AGP` has been bumped to `7.4.2` - `Gradle` has been upgraded to `8.0.1` - Android `CompileSDK` and `TargetSDK` have been bumped to 33 - `@react-native-async-storage/async-storage` has been upgraded to `1.19.3` - `@walletconnect/client` has been nuked - some of the old `react-native-reanimated` code has been nuked - `react-native-keychain` fork has been replaced with `8.1.2` - On Android we are currently relying on `Hermes` Engine. - On iOS we are currently relying on `JSC` - We are not enabling new architecture for now (I have plans for that in the future) ref: https://github.com/status-im/status-mobile/issues/18138 IOS only PR : https://github.com/status-im/status-mobile/pull/16721 Android only PR : https://github.com/status-im/status-mobile/pull/17062 - `make run-metro` now has a target of `android` which was `clojure` earlier, this will increase the time it takes to start metro terminal but this is needed otherwise you will get a nasty error while developing for android locally.
269 lines
11 KiB
Java
269 lines
11 KiB
Java
package im.status.ethereum;
|
|
|
|
import android.content.Context;
|
|
import android.annotation.TargetApi;
|
|
import androidx.annotation.Nullable;
|
|
import android.app.AlertDialog;
|
|
import android.app.ActivityManager;
|
|
import android.content.DialogInterface;
|
|
import android.content.DialogInterface.OnCancelListener;
|
|
import android.content.DialogInterface.OnClickListener;
|
|
import android.content.pm.PackageManager;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Looper;
|
|
import android.preference.PreferenceManager;
|
|
import androidx.core.app.ActivityCompat;
|
|
import android.util.Log;
|
|
import android.view.WindowManager;
|
|
import android.content.Intent;
|
|
import android.content.SharedPreferences;
|
|
import android.content.res.Configuration;
|
|
import android.provider.Settings;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import com.facebook.react.ReactActivityDelegate;
|
|
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
|
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
import com.facebook.react.bridge.Arguments;
|
|
import com.facebook.react.bridge.WritableMap;
|
|
import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView;
|
|
|
|
import com.reactnativenavigation.NavigationActivity;
|
|
import com.facebook.react.modules.core.PermissionListener;
|
|
import androidx.core.splashscreen.SplashScreen;
|
|
|
|
import java.util.Properties;
|
|
import im.status.ethereum.module.StatusThreadPoolExecutor;
|
|
import im.status.ethereum.MainApplication;
|
|
|
|
public class MainActivity extends NavigationActivity
|
|
implements ActivityCompat.OnRequestPermissionsResultCallback{
|
|
|
|
|
|
@Nullable private PermissionListener mPermissionListener;
|
|
private boolean keepSplash = true;
|
|
private final int SPLASH_DELAY = 3200;
|
|
|
|
/**
|
|
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
|
* rendering of the component.
|
|
*/
|
|
protected String getMainComponentName() {
|
|
return "StatusIm";
|
|
}
|
|
|
|
private static void registerUncaughtExceptionHandler(final Context context) {
|
|
final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
|
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
|
@Override
|
|
public void uncaughtException(final Thread thread, final Throwable t) {
|
|
// High priority, so don't use StatusThreadPoolExecutor
|
|
new Thread() {
|
|
@Override
|
|
public void run() {
|
|
Looper.prepare();
|
|
|
|
new AlertDialog.Builder(context)
|
|
.setTitle("Error")
|
|
.setMessage(t.toString())
|
|
.setNegativeButton("Exit", new DialogInterface.OnClickListener() {
|
|
public void onClick(final DialogInterface dialog, final int id) {
|
|
dialog.dismiss();
|
|
defaultUncaughtExceptionHandler.uncaughtException(thread, t);
|
|
}
|
|
}).show();
|
|
|
|
Looper.loop();
|
|
}
|
|
}.start();
|
|
}
|
|
});
|
|
}
|
|
|
|
private ActivityManager getActivityManager() {
|
|
return (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
|
|
}
|
|
|
|
private ActivityManager.MemoryInfo getAvailableMemory(final ActivityManager activityManager) {
|
|
final ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
|
|
activityManager.getMemoryInfo(memoryInfo);
|
|
return memoryInfo;
|
|
}
|
|
|
|
protected void configureStatus() {
|
|
// Required because of crazy APN settings redirecting localhost (found in GB)
|
|
Properties properties = System.getProperties();
|
|
properties.setProperty("http.nonProxyHosts", "localhost|127.0.0.1");
|
|
properties.setProperty("https.nonProxyHosts", "localhost|127.0.0.1");
|
|
}
|
|
|
|
private Intent createNotificationSettingsIntent() {
|
|
final Intent intent = new Intent();
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
|
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
|
|
} else {
|
|
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
|
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
|
intent.setData(Uri.parse("package:" + getPackageName()));
|
|
}
|
|
return intent;
|
|
}
|
|
|
|
private void tryToEmit(String eventName, WritableMap event) {
|
|
try {
|
|
((MainApplication) getApplication()).getReactNativeHost()
|
|
.getReactInstanceManager()
|
|
.getCurrentReactContext()
|
|
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
.emit("url", event);
|
|
} catch(Exception e) {/* we expect NPE on first start, which is OK because we have a fallback */}
|
|
}
|
|
|
|
@Override
|
|
public void onNewIntent(final Intent intent) {
|
|
super.onNewIntent(intent);
|
|
if (intent.getDataString() != null && intent.getData().getScheme().startsWith("app-settings")) {
|
|
startActivity(createNotificationSettingsIntent());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
SplashScreen splashScreen = SplashScreen.installSplashScreen(this);
|
|
setTheme(R.style.DarkTheme);
|
|
// Make sure we get an Alert for every uncaught exceptions
|
|
registerUncaughtExceptionHandler(MainActivity.this);
|
|
|
|
// Report memory details for this application
|
|
final ActivityManager activityManager = getActivityManager();
|
|
Log.v("RNBootstrap", "Available system memory "+getAvailableMemory(activityManager).availMem + ", maximum usable application memory " + activityManager.getLargeMemoryClass()+"M");
|
|
|
|
setSecureFlag();
|
|
|
|
// NOTE: Try to not restore the state https://github.com/software-mansion/react-native-screens/issues/17
|
|
super.onCreate(null);
|
|
|
|
if (!shouldShowRootedNotification()) {
|
|
configureStatus();
|
|
} else {
|
|
AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
|
|
.setMessage(getResources().getString(R.string.root_warning))
|
|
.setPositiveButton(getResources().getString(R.string.root_okay), new OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
rejectRootedNotification();
|
|
dialog.dismiss();
|
|
configureStatus();
|
|
}
|
|
})
|
|
.setNegativeButton(getResources().getString(R.string.root_cancel), new OnClickListener() {
|
|
@Override
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
dialog.dismiss();
|
|
MainActivity.this.finishAffinity();
|
|
}
|
|
})
|
|
.setOnCancelListener(new OnCancelListener() {
|
|
@Override
|
|
public void onCancel(DialogInterface dialog) {
|
|
dialog.dismiss();
|
|
MainActivity.this.finishAffinity();
|
|
}
|
|
})
|
|
.create();
|
|
|
|
dialog.show();
|
|
}
|
|
|
|
Runnable r = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
System.loadLibrary("status-logs");
|
|
|
|
// when app is started but the Activity has been destroyed, the deep linking url event is
|
|
// not emitted when coming back to foreground. This is a workaround. If the problem is
|
|
// resolved in react-native this code should be removed
|
|
if (getIntent().getData() != null) {
|
|
WritableMap event = Arguments.createMap();
|
|
event.putString("url", getIntent().getDataString());
|
|
// on first start emit will (silently) fail, but the regular deep linking handler will work
|
|
tryToEmit("url", event);
|
|
}
|
|
}
|
|
};
|
|
|
|
splashScreen.setKeepOnScreenCondition(() -> keepSplash);
|
|
|
|
Handler handler = new Handler();
|
|
handler.postDelayed(() -> keepSplash = false, SPLASH_DELAY);
|
|
|
|
StatusThreadPoolExecutor.getInstance().execute(r);
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
super.onDestroy();
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
public void onConfigurationChanged(Configuration newConfig) {
|
|
super.onConfigurationChanged(newConfig);
|
|
Intent intent = new Intent("onConfigurationChanged");
|
|
intent.putExtra("newConfig", newConfig);
|
|
this.sendBroadcast(intent);
|
|
}
|
|
|
|
private static final String REJECTED_ROOTED_NOTIFICATION = "rejectedRootedNotification";
|
|
private static final Integer FREQUENCY_OF_REMINDER_IN_PERCENT = 5;
|
|
|
|
private boolean shouldShowRootedNotification() {
|
|
if (RootUtil.isDeviceRooted() && BuildConfig.ENABLE_ROOT_ALERT == "1") {
|
|
if (userRejectedRootedNotification()) {
|
|
return ((Math.random() * 100) < FREQUENCY_OF_REMINDER_IN_PERCENT);
|
|
} else return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private boolean userRejectedRootedNotification() {
|
|
SharedPreferences preferences = getPreferences(0);
|
|
return preferences.getBoolean(REJECTED_ROOTED_NOTIFICATION, false);
|
|
}
|
|
|
|
private void rejectRootedNotification() {
|
|
SharedPreferences preferences = getPreferences(0);
|
|
SharedPreferences.Editor editor = preferences.edit();
|
|
editor.putBoolean(REJECTED_ROOTED_NOTIFICATION, true);
|
|
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;
|
|
requestPermissions(permissions, requestCode);
|
|
}
|
|
|
|
@Override
|
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
|
if (mPermissionListener != null && mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
|
|
mPermissionListener = null;
|
|
}
|
|
}
|
|
}
|