Last active
November 14, 2016 12:32
-
-
Save samerce/0f5072a078894b159559399547553a4c to your computer and use it in GitHub Desktop.
An implementation for React Native's Settings component.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
@providesModule RNSettings | |
@flow | |
*/ | |
import { | |
DeviceEventEmitter, | |
NativeModules, | |
} from 'react-native'; | |
const {RNSettings} = NativeModules; | |
let subscriptions: {[id: string]: {keys: Array<string>, callback: ?Function}} = {}; | |
let nextId = 0; // monotonically increasing id | |
const Settings = { | |
_settings: RNSettings && RNSettings.settings, | |
get(key: string): mixed { | |
return this._settings[key]; | |
}, | |
set(settings: Object) { | |
this._settings = Object.assign(this._settings, settings); | |
RNSettings.setValues(this._settings); | |
}, | |
watchKeys(keys: string | Array<string>, callback: Function): number { | |
if (typeof keys === 'string') { | |
keys = [keys]; | |
} | |
const watchId = nextId++; | |
subscriptions[watchId] = {keys, callback}; | |
return watchId; | |
}, | |
clearWatch(watchId: number) { | |
if (subscriptions[watchId]) { | |
delete subscriptions[watchId]; | |
} | |
}, | |
_sendObservations(body: Object) { | |
Object.keys(body).forEach((key) => { | |
const newValue = body[key]; | |
const didChange = this._settings[key] !== newValue; | |
this._settings[key] = newValue; | |
if (didChange) { | |
Object.keys(subscriptions).forEach((subId) => { | |
const sub = subscriptions[subId]; | |
if (sub.keys.includes(key) && sub.callback) { | |
sub.callback(); | |
} | |
}); | |
} | |
}); | |
}, | |
}; | |
DeviceEventEmitter.addListener( | |
'settingsUpdated', | |
Settings._sendObservations.bind(Settings) | |
); | |
export default Settings; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.iodine.start; | |
import com.facebook.react.bridge.ReactApplicationContext; | |
import com.facebook.react.bridge.ReactContextBaseJavaModule; | |
import com.facebook.react.bridge.ReactMethod; | |
import com.facebook.react.bridge.ReadableMap; | |
import com.facebook.react.bridge.ReadableMapKeySetIterator; | |
import com.facebook.react.bridge.ReadableType; | |
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter; | |
import java.util.Map; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import org.json.JSONArray; | |
import org.json.JSONObject; | |
import org.json.JSONException; | |
import android.content.SharedPreferences; | |
public class RNSettingsModule extends ReactContextBaseJavaModule implements SharedPreferences.OnSharedPreferenceChangeListener { | |
private SharedPreferences _settings; | |
public RNSettingsModule(ReactApplicationContext reactContext) { | |
super(reactContext); | |
this._settings = reactContext.getSharedPreferences(YOUR_PREFS_KEY, Context.MODE_PRIVATE); | |
this._settings.registerOnSharedPreferenceChangeListener(this); | |
} | |
protected void finalize() { | |
this._settings.unregisterOnSharedPreferenceChangeListener(this); | |
} | |
@Override | |
public String getName() { | |
return "RNSettings"; | |
} | |
@Override | |
public Map<String, Object> getConstants() { | |
final Map<String, Object> constants = new HashMap<>(); | |
constants.put("settings", this._settingsToMap()); | |
return constants; | |
} | |
@ReactMethod | |
public void setValues(ReadableMap values) throws JSONException { | |
SharedPreferences.Editor editor = this._settings.edit(); | |
ReadableMapKeySetIterator iterator = values.keySetIterator(); | |
while (iterator.hasNextKey()) { | |
String key = iterator.nextKey(); | |
ReadableType type = values.getType(key); | |
switch (type) { | |
case Null: | |
editor.putString(key, null); | |
break; | |
case Boolean: | |
editor.putBoolean(key, values.getBoolean(key)); | |
break; | |
case Number: | |
// Since SharedPreferences does not support storing doubles, | |
// we store the raw long bits to preserve precision. | |
double number = values.getDouble(key); | |
editor.putLong(key, Double.doubleToRawLongBits(number)); | |
break; | |
case String: | |
editor.putString(key, values.getString(key)); | |
break; | |
case Map: | |
editor.putString(key, MapUtil.toJSONObject(values.getMap(key)).toString()); | |
break; | |
case Array: | |
editor.putString(key, ArrayUtil.toJSONArray(values.getArray(key)).toString()); | |
break; | |
} | |
editor.commit(); | |
} | |
} | |
private Map<String, Object> _settingsToMap() { | |
Iterator iterator = this._settings.getAll().entrySet().iterator(); | |
Map<String, Object> map = new HashMap<>(); | |
while (iterator.hasNext()) { | |
Map.Entry pair = (Map.Entry)iterator.next(); | |
String key = (String) pair.getKey(); | |
Object value = pair.getValue(); | |
try { | |
if (value instanceof String) { | |
if (((String) value).charAt(0) == '[') { | |
// Try parsing string as JSONArray | |
Object[] array = ArrayUtil.toArray(new JSONArray((String) value)); | |
map.put(key, array); | |
} else { | |
// Try parsing string as JSON | |
Map<String, Object> object = MapUtil.toMap(new JSONObject((String) value)); | |
map.put(key, object); | |
} | |
} else if (value instanceof Long) { | |
// Parse raw long bits as double | |
double number = Double.longBitsToDouble((long) value); | |
map.put(key, number); | |
} else { | |
// For all other data types | |
map.put(key, value); | |
} | |
} catch(JSONException e) { | |
// If string can't be parsed as JSON, we just treat it as a normal string | |
map.put(key, value); | |
} | |
iterator.remove(); | |
} | |
return map; | |
} | |
public void onSharedPreferenceChanged(SharedPreferences sharedPrefs, String key) { | |
ReactApplicationContext reactContext = this.getReactApplicationContext(); | |
reactContext.getJSModule(RCTDeviceEventEmitter.class) | |
.emit("settingsUpdated", MapUtil.toWritableMap(this._settingsToMap())); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment