package kc.spark.pixels.android.app;
import static org.solemnsilence.util.Py.list;
import static org.solemnsilence.util.Py.map;
import static org.solemnsilence.util.Py.set;
import static org.solemnsilence.util.Py.truthy;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import kc.get.pixel.list.android.R;
import kc.spark.pixels.android.cloud.WebHelpers;
import kc.spark.pixels.android.cloud.api.Device;
import kc.spark.pixels.android.storage.Prefs;
import org.solemnsilence.util.TLog;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.AsyncTask;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.gson.reflect.TypeToken;
/**
* Common access to {@link Device}s.
*
* Uses an in-memory cache, backed by on-disk storage using SharedPreferences.
*
*/
public class DeviceState {
static final TLog log = new TLog(DeviceState.class);
private static final Map<String, Device> deviceMap = map();
private static final Random random = new Random();
private static Context appContext;
public static synchronized void initialize(Context ctx) {
appContext = ctx.getApplicationContext();
String coresJsonArray = Prefs.getInstance().getCoresJsonArray();
Type listType = new TypeToken<List<Device>>() {
}.getType();
List<Device> devices = WebHelpers.getGson().fromJson(coresJsonArray, listType);
updateAllKnownDevices(devices);
}
public synchronized static List<Device> getKnownDevices() {
return Lists.newArrayList(deviceMap.values());
}
public synchronized static Device getDeviceById(String deviceId) {
return (deviceId == null) ? null : deviceMap.get(deviceId);
}
public synchronized static void updateAllKnownDevices(List<Device> updatedDevices) {
log.d("Updating known devices with: " + updatedDevices);
Set<String> updatedDeviceIds = set();
for (Device updatedDevice : updatedDevices) {
String updatedDeviceId = updateSingleDevice(updatedDevice, false);
updatedDeviceIds.add(updatedDeviceId);
}
// now remove the devices which weren't in the update.
for (String missingDeviceId : set(deviceMap.keySet()).getDifference(updatedDeviceIds)) {
log.d("Removing device from local device store: " + missingDeviceId);
deviceMap.remove(missingDeviceId);
}
saveDevices();
}
// returns the ID of the updated device.
public synchronized static String updateSingleDevice(Device updatedDevice, boolean save) {
Device oldDevice = deviceMap.get(updatedDevice.id);
Device.Builder toInsert = updatedDevice.toBuilder();
// ensure we never have a device with a
if (oldDevice != null) {
toInsert.fillInFalseyValuesFromOther(oldDevice);
}
if (!truthy(toInsert.getColor())) {
toInsert.setColor(getRandomCoreColor());
}
if (!truthy(toInsert.getName())) {
// don't allow null or empty string names
toInsert.setName(appContext.getString(R.string._unnamed_core_));
}
Device built = toInsert.build();
deviceMap.put(built.id, built);
if (save) {
saveDevices();
}
return updatedDevice.id;
}
public synchronized static void renameDevice(String coreId, String newName) {
// Create a device with default values and let 'updateSingleDevice' do
// the work. Kinda cheesy, but it works.
Device device = deviceMap.get(coreId);
if (device == null) {
log.e("Cannot rename, no device found with ID: " + coreId);
return;
}
updateSingleDevice(device.toBuilder()
.setName(newName)
.build(),
true);
}
public synchronized static Set<String> getExistingCoreNames() {
return set(Collections2.transform(deviceMap.values(),
new Function<Device, String>() {
@Override
public String apply(Device device) {
return device.name;
}
}));
}
private static int getRandomCoreColor() {
TypedArray colors = appContext.getResources().obtainTypedArray(R.array.core_colors);
int max = colors.length() - 1;
int min = 0;
int randomIdx = random.nextInt((max - min) + 1) + min;
int color = colors.getColor(randomIdx, 0);
colors.recycle();
return color;
}
private synchronized static void saveDevices() {
new DevicesSaver(list(deviceMap.values()))
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private static class DevicesSaver extends AsyncTask<Void, Void, Void> {
final Prefs prefs = Prefs.getInstance();
final List<Device> devices;
public DevicesSaver(List<Device> devices) {
this.devices = devices;
}
@Override
protected Void doInBackground(Void... params) {
Type listType = new TypeToken<List<Device>>() {
}.getType();
String asJson = WebHelpers.getGson().toJson(devices, listType);
prefs.saveCoresJsonArray(asJson);
return null;
}
}
}