Skip to content
Open
Show file tree
Hide file tree
Changes from 117 commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
de22283
feat: installed react-native-mmkv
OtavioStasiak Oct 22, 2025
fb6afe8
feat: remove old library
OtavioStasiak Oct 23, 2025
53b7c3d
fix: remove MMKV initialization
OtavioStasiak Oct 23, 2025
8a88f38
fix: mocks
OtavioStasiak Oct 23, 2025
92f79d1
fix: podfile
OtavioStasiak Oct 23, 2025
963e124
feat(ios): standalone SecureStorage module
OtavioStasiak Oct 23, 2025
96c7709
feat(ios): standalone SecureStorage module
OtavioStasiak Oct 23, 2025
87d14e1
feat: SSLPinning use new MMKV
OtavioStasiak Oct 23, 2025
99590a0
fix: disable modules to migrate
OtavioStasiak Oct 23, 2025
593e337
fix: lint
OtavioStasiak Oct 23, 2025
49a7173
feat: refactor userPreferences
OtavioStasiak Oct 23, 2025
c18acac
fix: lib name type
OtavioStasiak Oct 23, 2025
665a35c
feat: add MMKVBridge wrapper
OtavioStasiak Oct 28, 2025
c3cdd8a
chore: xcode project cleanup
OtavioStasiak Oct 28, 2025
dc06dac
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak Oct 28, 2025
da2129e
test: migration
OtavioStasiak Oct 28, 2025
ce619e8
fix: migration mmkv
OtavioStasiak Oct 28, 2025
8ca8642
feat: migrate data from react-native-mmkv-storage to react-native-mmkv
OtavioStasiak Oct 28, 2025
109469c
fix: patch package
OtavioStasiak Oct 28, 2025
124e45f
fix: patch package
OtavioStasiak Oct 28, 2025
dcb4b3c
fix: sslPinning
OtavioStasiak Oct 29, 2025
d7c2c11
feat: start android migration
OtavioStasiak Oct 29, 2025
328434e
feat: start android migration
OtavioStasiak Oct 29, 2025
a3c635e
chore:remove logs
OtavioStasiak Oct 29, 2025
7ea8b02
fix: android migration
OtavioStasiak Oct 29, 2025
2146743
chore: code improvements
OtavioStasiak Oct 29, 2025
52fb6eb
test: poc read old android data
OtavioStasiak Oct 30, 2025
b844781
feat: migrate from react-native-mmkv-storage data to react-native-mmkv
OtavioStasiak Oct 30, 2025
5d9c9c4
fix: patch package
OtavioStasiak Oct 30, 2025
097cdc8
fix: iOS build
OtavioStasiak Nov 3, 2025
29fe1f1
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak Nov 3, 2025
334f562
fix: ios build timeout
OtavioStasiak Nov 4, 2025
efcfcb3
fix: mmkvReader breaking on iOS
OtavioStasiak Nov 4, 2025
a4cbd8d
fix: build
OtavioStasiak Nov 4, 2025
854e977
test: ios migration
OtavioStasiak Nov 4, 2025
6a8b7d1
fix: use MMKVReader to migrate data on iOS
OtavioStasiak Nov 4, 2025
8ef218b
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak Nov 5, 2025
7c36d87
fix: iOS migration on native side
OtavioStasiak Nov 5, 2025
5201d9c
test
OtavioStasiak Nov 5, 2025
91067fb
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak Nov 5, 2025
5fb2870
fix: migration timing
OtavioStasiak Nov 6, 2025
d2ed633
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 6, 2025
b8abc6e
test
OtavioStasiak Nov 6, 2025
f9f4565
fix: unit test
OtavioStasiak Nov 6, 2025
ee4c809
fix: initialize mmkv storage on init
OtavioStasiak Nov 6, 2025
a899337
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 6, 2025
faa6033
feat: improve logs
OtavioStasiak Nov 6, 2025
100a077
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 6, 2025
30d1974
more logs
OtavioStasiak Nov 6, 2025
9d329e8
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak Nov 6, 2025
0667d5e
feat: improve debug on testflight
OtavioStasiak Nov 6, 2025
dee1164
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 6, 2025
eb90469
error handling for debug
OtavioStasiak Nov 6, 2025
133fb39
fix: encryption key diff
OtavioStasiak Nov 7, 2025
8b324a9
fix: esport secureStorage key
OtavioStasiak Nov 7, 2025
2c36c52
more logs
OtavioStasiak Nov 7, 2025
7160e35
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 7, 2025
25466bc
test
OtavioStasiak Nov 7, 2025
271c52d
chore: start cleanup
OtavioStasiak Nov 10, 2025
5036e74
fix: remove logs on MMKVMigration
OtavioStasiak Nov 10, 2025
e6ff968
fix: remove MMKV logs on iOS
OtavioStasiak Nov 10, 2025
fd618e1
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 10, 2025
794dffe
fix: multiple servers not showing
OtavioStasiak Nov 10, 2025
e4c5de9
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 10, 2025
b3d9e0a
fix: lint
OtavioStasiak Nov 11, 2025
81e52da
fix: brinding header
OtavioStasiak Nov 12, 2025
2abca4c
remove logs
OtavioStasiak Nov 12, 2025
10db8cb
feat: increase timeout
OtavioStasiak Nov 12, 2025
2715246
cleanup
OtavioStasiak Nov 12, 2025
acd1a51
cleanup
OtavioStasiak Nov 12, 2025
6367507
testing to remove mmmkv reader
OtavioStasiak Nov 12, 2025
01a4f6e
fix: lint
OtavioStasiak Nov 12, 2025
5131262
chore: remove MMKVReader on iOS
OtavioStasiak Nov 13, 2025
febca2d
feat(android): enable setCertificate on webview
OtavioStasiak Nov 13, 2025
afe004a
chore: code improvements
OtavioStasiak Nov 13, 2025
2070200
chore: code improvements
OtavioStasiak Nov 14, 2025
b309ff5
feat: code improvements
OtavioStasiak Nov 14, 2025
6e51f84
feat: code improvements
OtavioStasiak Nov 14, 2025
4b44b0d
chore: remove unused secureStorage methods on iOS
OtavioStasiak Nov 14, 2025
44a7b95
feat: error handling MMKV
OtavioStasiak Nov 14, 2025
e032b8b
chore: remove unused methos on SecureKeystore.java
OtavioStasiak Nov 14, 2025
32fcbf0
chore: remove logs on MMKVReader
OtavioStasiak Nov 14, 2025
a624eb7
chore: code improvements
OtavioStasiak Nov 14, 2025
a34f085
remove unused comments
OtavioStasiak Nov 14, 2025
f24ab27
remove unused methos on Storage.java
OtavioStasiak Nov 14, 2025
b9dea76
test: improve MMKV start logic on iOS
OtavioStasiak Nov 14, 2025
4061c98
start mmkv on iOS
OtavioStasiak Nov 14, 2025
c7cca85
code improvements
OtavioStasiak Nov 14, 2025
7357884
code improvements
OtavioStasiak Nov 17, 2025
c309a64
fix: lint
OtavioStasiak Nov 17, 2025
6079c64
fix: add ARM ABI filters for Apple Silicon emulator compatibility
OtavioStasiak Nov 17, 2025
136c0a3
e2e android build for all archictectures
OtavioStasiak Nov 17, 2025
ccd2fd6
fix: e2e build on android
OtavioStasiak Nov 17, 2025
74b7866
fix: build
OtavioStasiak Nov 17, 2025
229a823
fix: mmkv mock
OtavioStasiak Nov 17, 2025
0125682
fix: initialize MMKV in MainApplication onCreate
OtavioStasiak Nov 17, 2025
ae37199
rollback e2e
OtavioStasiak Nov 17, 2025
b43a28a
fix: android build on e2e
OtavioStasiak Nov 17, 2025
90f3f94
fix: android build on e2e
OtavioStasiak Nov 18, 2025
ef368e0
clear gradlew cache
OtavioStasiak Nov 18, 2025
76501d5
clean cache build
OtavioStasiak Nov 18, 2025
f19cd64
fix: mock
OtavioStasiak Nov 18, 2025
7f55e51
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 18, 2025
1ea269e
test: metroconfig
OtavioStasiak Nov 18, 2025
0676560
metro fix
OtavioStasiak Nov 18, 2025
7852730
rollback e2e build
OtavioStasiak Nov 18, 2025
65bc60e
fix: e2e build
OtavioStasiak Nov 18, 2025
9ff61d9
fix: metro config
OtavioStasiak Nov 19, 2025
f240ead
fix: build
OtavioStasiak Nov 19, 2025
18560f7
fix: migrate old mmkv
OtavioStasiak Nov 24, 2025
6207096
code improvements
OtavioStasiak Nov 24, 2025
38fdd2c
code improvements
OtavioStasiak Nov 24, 2025
f299326
chore: avoid unecessary server validation
OtavioStasiak Nov 24, 2025
434241d
code improvements
OtavioStasiak Nov 24, 2025
5323218
improve comments
OtavioStasiak Nov 24, 2025
3498353
rollback buid timeout
OtavioStasiak Nov 24, 2025
5653229
fix: migration logic
OtavioStasiak Nov 24, 2025
6312933
code improvements
OtavioStasiak Nov 24, 2025
72d079c
fix: zero-pad bytes and handle signed values
OtavioStasiak Nov 24, 2025
9a426e8
code improvements
OtavioStasiak Nov 24, 2025
fd47be8
fix: package.json and remove react-native-mmkv-storage of header sear…
OtavioStasiak Nov 24, 2025
7b0ca79
code improvements
OtavioStasiak Nov 24, 2025
d6e8c59
fix: userPreferences
OtavioStasiak Nov 25, 2025
3b72d61
code improvements
OtavioStasiak Nov 25, 2025
32769fd
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak Nov 25, 2025
4631acc
fix: mock
OtavioStasiak Nov 25, 2025
545a0ed
chore: format code and fix lint issues [skip ci]
OtavioStasiak Nov 25, 2025
f521f76
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
OtavioStasiak Nov 26, 2025
3b5bf88
chore: code improvements
OtavioStasiak Nov 26, 2025
527195c
remove path on SSLPinning
OtavioStasiak Nov 26, 2025
7305451
fix: lint
OtavioStasiak Nov 26, 2025
1b9a4c6
fix: patch package
OtavioStasiak Nov 27, 2025
57d83d3
Merge branch 'develop' into feat.migrate-react-native-mmkv-storage
diegolmello Nov 27, 2025
242ce9b
feat(migration): Implement MMKV data migration from encrypted to unen…
diegolmello Nov 27, 2025
d7f18e3
refactor(migration): Remove legacy MMKV migration code and update com…
diegolmello Nov 27, 2025
5899ca1
refactor(migration): Remove MMKVReader module and related files
diegolmello Nov 27, 2025
71d3e4c
refactor(storage): Rename logging tags in SecureKeystore and Constants
diegolmello Nov 27, 2025
3af7c56
fix mmkv write
diegolmello Nov 28, 2025
e16c64d
reKey android
diegolmello Nov 28, 2025
d66e5ff
refactor(migration): Simplify MMKV migration process and enhance logging
diegolmello Nov 28, 2025
81d2f9e
Improve logging on iOS
diegolmello Dec 1, 2025
bd12cd1
Fix apple watch
diegolmello Dec 1, 2025
5bc2139
Remove ensureServersInDatabase
diegolmello Dec 1, 2025
3940c33
Move SecureStorage for consistency
diegolmello Dec 1, 2025
02cba7a
Fix migration key iOS
diegolmello Dec 2, 2025
4235ed5
Fix missing apple watch pieces
diegolmello Dec 2, 2025
d9a5c4d
cleanup
diegolmello Dec 2, 2025
5355937
refactor(migration): Enhance MMKV encryption removal process and add …
diegolmello Dec 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions __mocks__/react-native-mmkv-storage.js

This file was deleted.

97 changes: 97 additions & 0 deletions __mocks__/react-native-mmkv.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Mock for react-native-mmkv

// Shared storage between instances with the same id
const storageInstances = new Map();

export const Mode = {
SINGLE_PROCESS: 1,
MULTI_PROCESS: 2
};

export class MMKV {
constructor(config = {}) {
const { id = 'default', mode, path } = config;
this.id = id;
this.mode = mode;
this.path = path;

// Share storage between instances with the same id
if (!storageInstances.has(this.id)) {
storageInstances.set(this.id, {
storage: new Map(),
listeners: []
});
}

const instance = storageInstances.get(this.id);
this.storage = instance.storage;
this.listeners = instance.listeners;
}

set(key, value) {
this.storage.set(key, value);
this.notifyListeners(key);
}

getString(key) {
const value = this.storage.get(key);
return typeof value === 'string' ? value : undefined;
}

getNumber(key) {
const value = this.storage.get(key);
return typeof value === 'number' ? value : undefined;
}

getBoolean(key) {
const value = this.storage.get(key);
return typeof value === 'boolean' ? value : undefined;
}

contains(key) {
return this.storage.has(key);
}

delete(key) {
const deleted = this.storage.delete(key);
if (deleted) {
this.notifyListeners(key);
}
return deleted;
}

getAllKeys() {
return Array.from(this.storage.keys());
}

clearAll() {
this.storage.clear();
// Notify about clear (pass undefined to indicate clear all)
this.notifyListeners(undefined);
}

addOnValueChangedListener(callback) {
this.listeners.push(callback);
return {
remove: () => {
const index = this.listeners.indexOf(callback);
if (index > -1) {
this.listeners.splice(index, 1);
}
}
};
}

notifyListeners(key) {
this.listeners.forEach((listener) => {
try {
listener(key);
} catch (error) {
console.error('Error in MMKV listener:', error);
}
});
}
}

// Export Configuration type for TypeScript
export const Configuration = {};
3 changes: 3 additions & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ dependencies {
implementation "com.google.code.gson:gson:2.8.9"
implementation "com.tencent:mmkv-static:1.2.10"
implementation 'com.facebook.soloader:soloader:0.10.4'

// For SecureKeystore (EncryptedSharedPreferences)
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
}

apply plugin: 'com.google.gms.google-services'
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ import com.wix.reactnativenotifications.core.notification.IPushNotification
import com.bugsnag.android.Bugsnag
import expo.modules.ApplicationLifecycleDispatcher
import chat.rocket.reactnative.networking.SSLPinningTurboPackage;
import chat.rocket.reactnative.storage.MMKVReaderTurboPackage;
import chat.rocket.reactnative.notification.CustomPushNotification;
import com.tencent.mmkv.MMKV;

open class MainApplication : Application(), ReactApplication, INotificationsApplication {

Expand All @@ -35,7 +37,9 @@ open class MainApplication : Application(), ReactApplication, INotificationsAppl
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
add(SSLPinningTurboPackage())
add(MMKVReaderTurboPackage())
add(WatermelonDBJSIPackage())
add(SecureStoragePackage())
}

override fun getJSMainModuleName(): String = "index"
Expand All @@ -53,6 +57,9 @@ open class MainApplication : Application(), ReactApplication, INotificationsAppl
super.onCreate()
SoLoader.init(this, OpenSourceMergedSoMapping)
Bugsnag.start(this)

// Initialize MMKV before React Native starts
MMKV.initialize(this)

if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app.
Expand Down
163 changes: 163 additions & 0 deletions android/app/src/main/java/chat/rocket/reactnative/SecureStorage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package chat.rocket.reactnative;

import android.content.Context;
import android.content.SharedPreferences;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import android.util.Log;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.UUID;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;

public class SecureStorage extends ReactContextBaseJavaModule {
private static final String TAG = "SecureStorage";
private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
private static final String PREFS_NAME = "SecureStoragePrefs";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private static final int GCM_TAG_LENGTH = 128;

private final ReactApplicationContext reactContext;

public SecureStorage(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}

@Override
public String getName() {
return "SecureStorage";
}

@ReactMethod
public void getSecureKey(String alias, Promise promise) {
try {
String value = getSecureKeyInternal(alias);
promise.resolve(value);
} catch (Exception e) {
Log.e(TAG, "Error getting secure key: " + alias, e);
promise.resolve(null);
}
}

@ReactMethod
public void setSecureKey(String alias, String value, Promise promise) {
try {
setSecureKeyInternal(alias, value);
promise.resolve(true);
} catch (Exception e) {
Log.e(TAG, "Error setting secure key: " + alias, e);
promise.reject("SET_SECURE_KEY_ERROR", e);
}
}

// Internal methods (can be called from Java)
public String getSecureKeyInternal(String alias) {
try {
SharedPreferences prefs = reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
String encryptedValue = prefs.getString(alias, null);

if (encryptedValue == null) {
return null;
}

// Decrypt the value
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
keyStore.load(null);

if (!keyStore.containsAlias(alias)) {
return null;
}

SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);

// Split IV and encrypted data
byte[] combined = Base64.decode(encryptedValue, Base64.DEFAULT);
byte[] iv = new byte[12];
byte[] encrypted = new byte[combined.length - 12];
System.arraycopy(combined, 0, iv, 0, 12);
System.arraycopy(combined, 12, encrypted, 0, encrypted.length);

GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec);

byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
Log.e(TAG, "Error retrieving secure key", e);
return null;
}
}

public void setSecureKeyInternal(String alias, String value) throws Exception {
KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
keyStore.load(null);

// Create key if it doesn't exist
if (!keyStore.containsAlias(alias)) {
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
KEYSTORE_PROVIDER
);
keyGenerator.init(
new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setRandomizedEncryptionRequired(true)
.build()
);
keyGenerator.generateKey();
}

// Encrypt the value
SecretKey secretKey = (SecretKey) keyStore.getKey(alias, null);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

byte[] iv = cipher.getIV();
byte[] encrypted = cipher.doFinal(value.getBytes(StandardCharsets.UTF_8));

// Combine IV and encrypted data
byte[] combined = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);

String encryptedValue = Base64.encodeToString(combined, Base64.DEFAULT);

// Store in SharedPreferences
SharedPreferences prefs = reactContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().putString(alias, encryptedValue).apply();
}

// Generate a secure key if it doesn't exist
public String getOrCreateSecureKey(String alias) {
String key = getSecureKeyInternal(alias);
if (key == null) {
// Generate a new random key
key = UUID.randomUUID().toString();
try {
setSecureKeyInternal(alias, key);
} catch (Exception e) {
Log.e(TAG, "Error creating secure key", e);
return null;
}
}
return key;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package chat.rocket.reactnative;

import com.facebook.react.ReactPackage;
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.Collections;
import java.util.List;

public class SecureStoragePackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new SecureStorage(reactContext));
return modules;
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.Callback;

import com.ammarahmed.mmkv.SecureKeystore;
import chat.rocket.reactnative.SecureStorage;
import com.tencent.mmkv.MMKV;
import com.wix.reactnativenotifications.core.AppLifecycleFacade;
import com.wix.reactnativenotifications.core.AppLifecycleFacadeHolder;
Expand Down Expand Up @@ -59,13 +59,15 @@ public Ejson() {
try {
// Start MMKV container
MMKV.initialize(this.reactContext);
SecureKeystore secureKeystore = new SecureKeystore(this.reactContext);
SecureStorage secureStorage = new SecureStorage(this.reactContext);

// https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
// https://github.com/ammarahm-ed/react-native-mmkv-storage/blob/master/src/loader.js#L31
String alias = Utils.toHex("com.MMKV.default");

// Retrieve container password
String password = secureKeystore.getSecureKey(alias);

String password = secureStorage.getSecureKeyInternal(alias);


// Initialize MMKV (works with or without encryption)
mmkv = MMKV.mmkvWithID("default", MMKV.SINGLE_PROCESS_MODE, password);
} catch (Exception e) {
Log.e("Ejson", "Failed to initialize MMKV: " + e.getMessage());
Expand Down
Loading
Loading