implement saving setting with json for site settings

refactor-toolbar
Floens 8 years ago
parent 2d291bf9f0
commit b0271587d7
  1. 41
      Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java
  2. 33
      Clover/app/src/main/java/org/floens/chan/core/model/orm/SiteModel.java
  3. 10
      Clover/app/src/main/java/org/floens/chan/core/settings/BooleanSetting.java
  4. 3
      Clover/app/src/main/java/org/floens/chan/core/settings/ChanSettings.java
  5. 6
      Clover/app/src/main/java/org/floens/chan/core/settings/CounterSetting.java
  6. 10
      Clover/app/src/main/java/org/floens/chan/core/settings/IntegerSetting.java
  7. 10
      Clover/app/src/main/java/org/floens/chan/core/settings/LongSetting.java
  8. 10
      Clover/app/src/main/java/org/floens/chan/core/settings/OptionsSetting.java
  9. 8
      Clover/app/src/main/java/org/floens/chan/core/settings/Setting.java
  10. 36
      Clover/app/src/main/java/org/floens/chan/core/settings/SettingProvider.java
  11. 68
      Clover/app/src/main/java/org/floens/chan/core/settings/SharedPreferencesSettingProvider.java
  12. 10
      Clover/app/src/main/java/org/floens/chan/core/settings/StringSetting.java
  13. 25
      Clover/app/src/main/java/org/floens/chan/core/settings/json/BooleanJsonSetting.java
  14. 25
      Clover/app/src/main/java/org/floens/chan/core/settings/json/IntegerJsonSetting.java
  15. 7
      Clover/app/src/main/java/org/floens/chan/core/settings/json/JsonSetting.java
  16. 29
      Clover/app/src/main/java/org/floens/chan/core/settings/json/JsonSettings.java
  17. 152
      Clover/app/src/main/java/org/floens/chan/core/settings/json/JsonSettingsProvider.java
  18. 25
      Clover/app/src/main/java/org/floens/chan/core/settings/json/LongJsonSetting.java
  19. 240
      Clover/app/src/main/java/org/floens/chan/core/settings/json/RuntimeTypeAdapterFactory.java
  20. 25
      Clover/app/src/main/java/org/floens/chan/core/settings/json/StringJsonSetting.java
  21. 6
      Clover/app/src/main/java/org/floens/chan/core/site/Site.java
  22. 15
      Clover/app/src/main/java/org/floens/chan/core/site/SiteBase.java
  23. 17
      Clover/app/src/main/java/org/floens/chan/core/site/SiteManager.java
  24. 18
      Clover/app/src/main/java/org/floens/chan/core/site/SiteRepository.java
  25. 6
      Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java

@ -32,13 +32,12 @@ public class DatabaseSiteManager {
this.helper = helper;
}
public Callable<SiteModel> byId(int id) {
return () -> helper.siteDao.queryForId(id);
}
public Callable<List<SiteModel>> getAll() {
return new Callable<List<SiteModel>>() {
@Override
public List<SiteModel> call() throws Exception {
return helper.siteDao.queryForAll();
}
};
return () -> helper.siteDao.queryForAll();
}
public Callable<Long> getCount() {
@ -46,35 +45,23 @@ public class DatabaseSiteManager {
}
public Callable<SiteModel> add(final SiteModel site) {
return new Callable<SiteModel>() {
@Override
public SiteModel call() throws Exception {
helper.siteDao.create(site);
return site;
}
return () -> {
helper.siteDao.create(site);
return site;
};
}
public Callable<SiteModel> update(final SiteModel site) {
return new Callable<SiteModel>() {
@Override
public SiteModel call() throws Exception {
helper.siteDao.update(site);
return site;
}
return () -> {
helper.siteDao.update(site);
return site;
};
}
public Callable<SiteModel> updateId(final SiteModel site, final int newId) {
return new Callable<SiteModel>() {
@Override
public SiteModel call() throws Exception {
helper.siteDao.updateId(site, newId);
return site;
}
return () -> {
helper.siteDao.updateId(site, newId);
return site;
};
}
}

@ -20,15 +20,35 @@ package org.floens.chan.core.model.orm;
import android.util.Pair;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import org.floens.chan.core.settings.json.BooleanJsonSetting;
import org.floens.chan.core.settings.json.LongJsonSetting;
import org.floens.chan.core.settings.json.RuntimeTypeAdapterFactory;
import org.floens.chan.core.settings.json.IntegerJsonSetting;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.settings.json.JsonSetting;
import org.floens.chan.core.settings.json.JsonSettings;
import org.floens.chan.core.settings.json.StringJsonSetting;
@DatabaseTable(tableName = "site")
public class SiteModel {
private static final Gson gson = new Gson();
private static final Gson gson;
static {
RuntimeTypeAdapterFactory<JsonSetting> userSettingAdapter =
RuntimeTypeAdapterFactory.of(JsonSetting.class, "type")
.registerSubtype(StringJsonSetting.class, "string")
.registerSubtype(IntegerJsonSetting.class, "integer")
.registerSubtype(LongJsonSetting.class, "long")
.registerSubtype(BooleanJsonSetting.class, "boolean");
gson = new GsonBuilder()
.registerTypeAdapterFactory(userSettingAdapter)
.create();
}
@DatabaseField(generatedId = true, allowGeneratedIdInsert = true)
public int id;
@ -42,15 +62,18 @@ public class SiteModel {
public SiteModel() {
}
public void storeConfigFields(SiteConfig config, SiteUserSettings userSettings) {
public void storeConfig(SiteConfig config) {
this.configuration = gson.toJson(config);
}
public void storeUserSettings(JsonSettings userSettings) {
this.userSettings = gson.toJson(userSettings);
}
public Pair<SiteConfig, SiteUserSettings> loadConfigFields() {
public Pair<SiteConfig, JsonSettings> loadConfigFields() {
return Pair.create(
gson.fromJson(this.configuration, SiteConfig.class),
gson.fromJson(this.userSettings, SiteUserSettings.class)
gson.fromJson(this.userSettings, JsonSettings.class)
);
}
}

@ -17,14 +17,12 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
public class BooleanSetting extends Setting<Boolean> {
private boolean hasCached = false;
private boolean cached;
public BooleanSetting(SharedPreferences sharedPreferences, String key, Boolean def) {
super(sharedPreferences, key, def);
public BooleanSetting(SettingProvider settingProvider, String key, Boolean def) {
super(settingProvider, key, def);
}
@Override
@ -32,7 +30,7 @@ public class BooleanSetting extends Setting<Boolean> {
if (hasCached) {
return cached;
} else {
cached = sharedPreferences.getBoolean(key, def);
cached = settingProvider.getBoolean(key, def);
hasCached = true;
return cached;
}
@ -41,7 +39,7 @@ public class BooleanSetting extends Setting<Boolean> {
@Override
public void set(Boolean value) {
if (!value.equals(get())) {
sharedPreferences.edit().putBoolean(key, value).apply();
settingProvider.putBoolean(key, value);
cached = value;
onValueChanged();
}

@ -17,7 +17,6 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
import android.os.Environment;
import android.text.TextUtils;
@ -159,7 +158,7 @@ public class ChanSettings {
public static final BooleanSetting crashReporting;
static {
SharedPreferences p = AndroidUtils.getPreferences();
SettingProvider p = new SharedPreferencesSettingProvider(AndroidUtils.getPreferences());
theme = new StringSetting(p, "preference_theme", "yotsuba");

@ -17,11 +17,9 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
public class CounterSetting extends IntegerSetting {
public CounterSetting(SharedPreferences sharedPreferences, String key) {
super(sharedPreferences, key, 0);
public CounterSetting(SettingProvider settingProvider, String key) {
super(settingProvider, key, 0);
}
public int increase() {

@ -17,14 +17,12 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
public class IntegerSetting extends Setting<Integer> {
private boolean hasCached = false;
private Integer cached;
public IntegerSetting(SharedPreferences sharedPreferences, String key, Integer def) {
super(sharedPreferences, key, def);
public IntegerSetting(SettingProvider settingProvider, String key, Integer def) {
super(settingProvider, key, def);
}
@Override
@ -32,7 +30,7 @@ public class IntegerSetting extends Setting<Integer> {
if (hasCached) {
return cached;
} else {
cached = sharedPreferences.getInt(key, def);
cached = settingProvider.getInt(key, def);
hasCached = true;
return cached;
}
@ -41,7 +39,7 @@ public class IntegerSetting extends Setting<Integer> {
@Override
public void set(Integer value) {
if (!value.equals(get())) {
sharedPreferences.edit().putInt(key, value).apply();
settingProvider.putInt(key, value);
cached = value;
onValueChanged();
}

@ -17,14 +17,12 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
public class LongSetting extends Setting<Long> {
private boolean hasCached = false;
private Long cached;
public LongSetting(SharedPreferences sharedPreferences, String key, Long def) {
super(sharedPreferences, key, def);
public LongSetting(SettingProvider settingProvider, String key, Long def) {
super(settingProvider, key, def);
}
@Override
@ -32,7 +30,7 @@ public class LongSetting extends Setting<Long> {
if (hasCached) {
return cached;
} else {
cached = sharedPreferences.getLong(key, def);
cached = settingProvider.getLong(key, def);
hasCached = true;
return cached;
}
@ -41,7 +39,7 @@ public class LongSetting extends Setting<Long> {
@Override
public void set(Long value) {
if (!value.equals(get())) {
sharedPreferences.edit().putLong(key, value).apply();
settingProvider.putLong(key, value);
cached = value;
onValueChanged();
}

@ -17,15 +17,13 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
public class OptionsSetting<T extends OptionSettingItem> extends Setting<T> {
private boolean hasCached = false;
private T cached;
private T[] items;
public OptionsSetting(SharedPreferences sharedPreferences, String key, T[] items, T def) {
super(sharedPreferences, key, def);
public OptionsSetting(SettingProvider settingProvider, String key, T[] items, T def) {
super(settingProvider, key, def);
this.items = items;
}
@ -34,7 +32,7 @@ public class OptionsSetting<T extends OptionSettingItem> extends Setting<T> {
if (hasCached) {
return cached;
} else {
String itemName = sharedPreferences.getString(key, def.getName());
String itemName = settingProvider.getString(key, def.getName());
T selectedItem = null;
for (T item : items) {
if (item.getName().equals(itemName)) {
@ -54,7 +52,7 @@ public class OptionsSetting<T extends OptionSettingItem> extends Setting<T> {
@Override
public void set(T value) {
if (!value.equals(get())) {
sharedPreferences.edit().putString(key, value.getName()).apply();
settingProvider.putString(key, value.getName());
cached = value;
onValueChanged();
}

@ -17,19 +17,17 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
import java.util.ArrayList;
import java.util.List;
public abstract class Setting<T> {
protected final SharedPreferences sharedPreferences;
protected final SettingProvider settingProvider;
protected final String key;
protected final T def;
private List<SettingCallback<T>> callbacks = new ArrayList<>();
public Setting(SharedPreferences sharedPreferences, String key, T def) {
this.sharedPreferences = sharedPreferences;
public Setting(SettingProvider settingProvider, String key, T def) {
this.settingProvider = settingProvider;
this.key = key;
this.def = def;
}

@ -0,0 +1,36 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings;
public interface SettingProvider {
int getInt(String key, int def);
void putInt(String key, int value);
long getLong(String key, long def);
void putLong(String key, long value);
boolean getBoolean(String key, boolean def);
void putBoolean(String key, boolean value);
String getString(String key, String def);
void putString(String key, String value);
}

@ -0,0 +1,68 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
public class SharedPreferencesSettingProvider implements SettingProvider {
private SharedPreferences prefs;
public SharedPreferencesSettingProvider(SharedPreferences prefs) {
this.prefs = prefs;
}
@Override
public int getInt(String key, int def) {
return prefs.getInt(key, def);
}
@Override
public void putInt(String key, int value) {
prefs.edit().putInt(key, value).apply();
}
@Override
public long getLong(String key, long def) {
return prefs.getLong(key, def);
}
@Override
public void putLong(String key, long value) {
prefs.edit().putLong(key, value).apply();
}
@Override
public boolean getBoolean(String key, boolean def) {
return prefs.getBoolean(key, def);
}
@Override
public void putBoolean(String key, boolean value) {
prefs.edit().putBoolean(key, value).apply();
}
@Override
public String getString(String key, String def) {
return prefs.getString(key, def);
}
@Override
public void putString(String key, String value) {
prefs.edit().putString(key, value).apply();
}
}

@ -17,14 +17,12 @@
*/
package org.floens.chan.core.settings;
import android.content.SharedPreferences;
public class StringSetting extends Setting<String> {
private boolean hasCached = false;
private String cached;
public StringSetting(SharedPreferences sharedPreferences, String key, String def) {
super(sharedPreferences, key, def);
public StringSetting(SettingProvider settingProvider, String key, String def) {
super(settingProvider, key, def);
}
@Override
@ -32,7 +30,7 @@ public class StringSetting extends Setting<String> {
if (hasCached) {
return cached;
} else {
cached = sharedPreferences.getString(key, def);
cached = settingProvider.getString(key, def);
hasCached = true;
return cached;
}
@ -41,7 +39,7 @@ public class StringSetting extends Setting<String> {
@Override
public void set(String value) {
if (!value.equals(get())) {
sharedPreferences.edit().putString(key, value).apply();
settingProvider.putString(key, value);
cached = value;
onValueChanged();
}

@ -0,0 +1,25 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings.json;
import com.google.gson.annotations.SerializedName;
public class BooleanJsonSetting extends JsonSetting {
@SerializedName("value")
public boolean value;
}

@ -0,0 +1,25 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings.json;
import com.google.gson.annotations.SerializedName;
public class IntegerJsonSetting extends JsonSetting {
@SerializedName("value")
public int value;
}

@ -15,8 +15,11 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.model.json.site;
package org.floens.chan.core.settings.json;
import com.google.gson.annotations.SerializedName;
public class SiteUserSettings {
public class JsonSetting {
@SerializedName("key")
public String key;
}

@ -0,0 +1,29 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings.json;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
import java.util.List;
public class JsonSettings {
@SerializedName("settings")
List<JsonSetting> settings = new ArrayList<>();
}

@ -0,0 +1,152 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings.json;
import org.floens.chan.core.settings.SettingProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JsonSettingsProvider implements SettingProvider {
public final JsonSettings jsonSettings;
private Callback callback;
private Map<String, JsonSetting> byKey = new HashMap<>();
public JsonSettingsProvider(JsonSettings jsonSettings, Callback callback) {
this.jsonSettings = jsonSettings;
this.callback = callback;
load();
}
@Override
public int getInt(String key, int def) {
JsonSetting setting = byKey.get(key);
if (setting != null) {
return ((IntegerJsonSetting) setting).value;
} else {
return def;
}
}
@Override
public void putInt(String key, int value) {
JsonSetting jsonSetting = byKey.get(key);
if (jsonSetting == null) {
IntegerJsonSetting v = new IntegerJsonSetting();
v.value = value;
byKey.put(key, v);
} else {
((IntegerJsonSetting) jsonSetting).value = value;
}
save();
}
@Override
public long getLong(String key, long def) {
JsonSetting setting = byKey.get(key);
if (setting != null) {
return ((LongJsonSetting) setting).value;
} else {
return def;
}
}
@Override
public void putLong(String key, long value) {
JsonSetting jsonSetting = byKey.get(key);
if (jsonSetting == null) {
LongJsonSetting v = new LongJsonSetting();
v.value = value;
byKey.put(key, v);
} else {
((LongJsonSetting) jsonSetting).value = value;
}
save();
}
@Override
public boolean getBoolean(String key, boolean def) {
JsonSetting setting = byKey.get(key);
if (setting != null) {
return ((BooleanJsonSetting) setting).value;
} else {
return def;
}
}
@Override
public void putBoolean(String key, boolean value) {
JsonSetting jsonSetting = byKey.get(key);
if (jsonSetting == null) {
BooleanJsonSetting v = new BooleanJsonSetting();
v.value = value;
byKey.put(key, v);
} else {
((BooleanJsonSetting) jsonSetting).value = value;
}
save();
}
@Override
public String getString(String key, String def) {
JsonSetting setting = byKey.get(key);
if (setting != null) {
return ((StringJsonSetting) setting).value;
} else {
return def;
}
}
@Override
public void putString(String key, String value) {
JsonSetting jsonSetting = byKey.get(key);
if (jsonSetting == null) {
StringJsonSetting v = new StringJsonSetting();
v.value = value;
byKey.put(key, v);
} else {
((StringJsonSetting) jsonSetting).value = value;
}
save();
}
private void load() {
byKey.clear();
for (JsonSetting setting : jsonSettings.settings) {
byKey.put(setting.key, setting);
}
}
private void save() {
List<JsonSetting> settings = new ArrayList<>();
for (Map.Entry<String, JsonSetting> e : byKey.entrySet()) {
settings.add(e.getValue());
}
jsonSettings.settings = settings;
callback.save();
}
public interface Callback {
void save();
}
}

@ -0,0 +1,25 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings.json;
import com.google.gson.annotations.SerializedName;
public class LongJsonSetting extends JsonSetting {
@SerializedName("value")
public long value;
}

@ -0,0 +1,240 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.floens.chan.core.settings.json;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.internal.Streams;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Adapts values whose runtime type may differ from their declaration type. This
* is necessary when a field's type is not the same type that GSON should create
* when deserializing that field. For example, consider these types:
* <pre> {@code
* abstract class Shape {
* int x;
* int y;
* }
* class Circle extends Shape {
* int radius;
* }
* class Rectangle extends Shape {
* int width;
* int height;
* }
* class Diamond extends Shape {
* int width;
* int height;
* }
* class Drawing {
* Shape bottomShape;
* Shape topShape;
* }
* }</pre>
* <p>Without additional type information, the serialized JSON is ambiguous. Is
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
* {
* "bottomShape": {
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* This class addresses this problem by adding type information to the
* serialized JSON and honoring that type information when the JSON is
* deserialized: <pre> {@code
* {
* "bottomShape": {
* "type": "Diamond",
* "width": 10,
* "height": 5,
* "x": 0,
* "y": 0
* },
* "topShape": {
* "type": "Circle",
* "radius": 2,
* "x": 4,
* "y": 1
* }
* }}</pre>
* Both the type field name ({@code "type"}) and the type labels ({@code
* "Rectangle"}) are configurable.
*
* <h3>Registering Types</h3>
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
* name to the {@link #of} factory method. If you don't supply an explicit type
* field name, {@code "type"} will be used. <pre> {@code
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
* = RuntimeTypeAdapterFactory.of(Shape.class, "type");
* }</pre>
* Next register all of your subtypes. Every subtype must be explicitly
* registered. This protects your application from injection attacks. If you
* don't supply an explicit type label, the type's simple name will be used.
* <pre> {@code
* shapeAdapter.registerSubtype(Rectangle.class, "Rectangle");
* shapeAdapter.registerSubtype(Circle.class, "Circle");
* shapeAdapter.registerSubtype(Diamond.class, "Diamond");
* }</pre>
* Finally, register the type adapter factory in your application's GSON builder:
* <pre> {@code
* Gson gson = new GsonBuilder()
* .registerTypeAdapterFactory(shapeAdapterFactory)
* .create();
* }</pre>
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
* .registerSubtype(Rectangle.class)
* .registerSubtype(Circle.class)
* .registerSubtype(Diamond.class);
* }</pre>
*/
public class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
private final Class<?> baseType;
private final String typeFieldName;
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) {
if (typeFieldName == null || baseType == null) {
throw new NullPointerException();
}
this.baseType = baseType;
this.typeFieldName = typeFieldName;
}
/**
* Creates a new runtime type adapter using for {@code baseType} using {@code
* typeFieldName} as the type field name. Type field names are case sensitive.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName);
}
/**
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
* the type field name.
*/
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
return new RuntimeTypeAdapterFactory<T>(baseType, "type");
}
/**
* Registers {@code type} identified by {@code label}. Labels are case
* sensitive.
*
* @throws IllegalArgumentException if either {@code type} or {@code label}
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
if (type == null || label == null) {
throw new NullPointerException();
}
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
throw new IllegalArgumentException("types and labels must be unique");
}
labelToSubtype.put(label, type);
subtypeToLabel.put(type, label);
return this;
}
/**
* Registers {@code type} identified by its {@link Class#getSimpleName simple
* name}. Labels are case sensitive.
*
* @throws IllegalArgumentException if either {@code type} or its simple name
* have already been registered on this type adapter.
*/
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
return registerSubtype(type, type.getSimpleName());
}
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
if (type.getRawType() != baseType) {
return null;
}
final Map<String, TypeAdapter<?>> labelToDelegate
= new LinkedHashMap<String, TypeAdapter<?>>();
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate
= new LinkedHashMap<Class<?>, TypeAdapter<?>>();
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
labelToDelegate.put(entry.getKey(), delegate);
subtypeToDelegate.put(entry.getValue(), delegate);
}
return new TypeAdapter<R>() {
@Override public R read(JsonReader in) throws IOException {
JsonElement jsonElement = Streams.parse(in);
JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
if (labelJsonElement == null) {
throw new JsonParseException("cannot deserialize " + baseType
+ " because it does not define a field named " + typeFieldName);
}
String label = labelJsonElement.getAsString();
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
if (delegate == null) {
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
+ label + "; did you forget to register a subtype?");
}
return delegate.fromJsonTree(jsonElement);
}
@Override public void write(JsonWriter out, R value) throws IOException {
Class<?> srcType = value.getClass();
String label = subtypeToLabel.get(srcType);
@SuppressWarnings("unchecked") // registration requires that subtype extends T
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
if (delegate == null) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ "; did you forget to register a subtype?");
}
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
if (jsonObject.has(typeFieldName)) {
throw new JsonParseException("cannot serialize " + srcType.getName()
+ " because it already defines a field named " + typeFieldName);
}
JsonObject clone = new JsonObject();
clone.add(typeFieldName, new JsonPrimitive(label));
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
clone.add(e.getKey(), e.getValue());
}
Streams.write(clone, out);
}
}.nullSafe();
}
}

@ -0,0 +1,25 @@
/*
* Clover - 4chan browser https://github.com/Floens/Clover/
* Copyright (C) 2014 Floens
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.floens.chan.core.settings.json;
import com.google.gson.annotations.SerializedName;
public class StringJsonSetting extends JsonSetting {
@SerializedName("value")
public String value;
}

@ -21,7 +21,7 @@ import android.support.annotation.Nullable;
import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.settings.json.JsonSettings;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.site.common.ChanReader;
@ -115,13 +115,13 @@ public interface Site {
* @param config the site config
* @param userSettings the site user settings
*/
void initialize(int id, SiteConfig config, SiteUserSettings userSettings);
void initialize(int id, SiteConfig config, JsonSettings userSettings);
void postInitialize();
/**
* Global positive (>0) integer that uniquely identifies this site.<br>
* Use the id received from {@link #initialize(int, SiteConfig, SiteUserSettings)}.
* Use the id received from {@link #initialize(int, SiteConfig, JsonSettings)}.
*
* @return a positive (>0) integer that uniquely identifies this site.
*/

@ -24,8 +24,10 @@ import org.codejargon.feather.Feather;
import org.floens.chan.core.database.LoadableProvider;
import org.floens.chan.core.manager.BoardManager;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.settings.SettingProvider;
import org.floens.chan.core.settings.json.JsonSettings;
import org.floens.chan.core.settings.json.JsonSettingsProvider;
import org.floens.chan.core.site.http.HttpCallManager;
import java.util.Collections;
@ -35,15 +37,17 @@ import static org.floens.chan.Chan.injector;
public abstract class SiteBase implements Site {
protected int id;
protected SiteConfig config;
protected SiteUserSettings userSettings;
protected HttpCallManager httpCallManager;
protected RequestQueue requestQueue;
protected BoardManager boardManager;
protected LoadableProvider loadableProvider;
private JsonSettings userSettings;
protected SettingProvider settingsProvider;
@Override
public void initialize(int id, SiteConfig config, SiteUserSettings userSettings) {
public void initialize(int id, SiteConfig config, JsonSettings userSettings) {
this.id = id;
this.config = config;
this.userSettings = userSettings;
@ -56,6 +60,11 @@ public abstract class SiteBase implements Site {
requestQueue = injector.instance(RequestQueue.class);
boardManager = injector.instance(BoardManager.class);
loadableProvider = injector.instance(LoadableProvider.class);
SiteManager siteManager = injector.instance(SiteManager.class);
settingsProvider = new JsonSettingsProvider(userSettings, () -> {
siteManager.updateUserSettings(this, userSettings);
});
if (boardsType() == BoardsType.DYNAMIC) {
boards(boards -> boardManager.createAll(boards.boards));

@ -21,7 +21,7 @@ package org.floens.chan.core.site;
import android.util.Pair;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.settings.json.JsonSettings;
import org.floens.chan.core.model.orm.SiteModel;
import org.floens.chan.core.site.sites.chan4.Chan4;
@ -86,10 +86,17 @@ public class SiteManager {
callback.onSiteAdded(site);
}
public void updateUserSettings(Site site, JsonSettings jsonSettings) {
SiteModel siteModel = siteRepository.byId(site.id());
if (siteModel == null) throw new NullPointerException("siteModel == null");
siteRepository.updateSiteUserSettingsAsync(siteModel, jsonSettings);
}
public void initialize() {
if (initialized) {
throw new IllegalStateException("Already initialized");
}
initialized = true;
if (addSiteForLegacy) {
addSiteForLegacy = false;
@ -100,7 +107,7 @@ public class SiteManager {
config.classId = Sites.SITE_CLASSES.indexOfValue(site.getClass());
config.external = false;
SiteModel model = siteRepository.create(config, new SiteUserSettings());
SiteModel model = siteRepository.create(config, new JsonSettings());
siteRepository.setId(model, 0);
}
@ -129,7 +136,7 @@ public class SiteManager {
*/
private void createNewSite(Site site) {
SiteConfig config = new SiteConfig();
SiteUserSettings settings = new SiteUserSettings();
JsonSettings settings = new JsonSettings();
config.classId = Sites.SITE_CLASSES.indexOfValue(site.getClass());
config.external = false;
@ -138,9 +145,9 @@ public class SiteManager {
}
private Site fromModel(SiteModel siteModel) {
Pair<SiteConfig, SiteUserSettings> configFields = siteModel.loadConfigFields();
Pair<SiteConfig, JsonSettings> configFields = siteModel.loadConfigFields();
SiteConfig config = configFields.first;
SiteUserSettings settings = configFields.second;
JsonSettings settings = configFields.second;
Site site = instantiateSiteClass(config.classId);
site.initialize(siteModel.id, config, settings);

@ -2,7 +2,7 @@ package org.floens.chan.core.site;
import org.floens.chan.core.database.DatabaseManager;
import org.floens.chan.core.model.json.site.SiteConfig;
import org.floens.chan.core.model.json.site.SiteUserSettings;
import org.floens.chan.core.settings.json.JsonSettings;
import org.floens.chan.core.model.orm.SiteModel;
import java.util.List;
@ -21,9 +21,15 @@ public class SiteRepository {
return databaseManager.runTask(databaseManager.getDatabaseSiteManager().getAll());
}
public SiteModel create(SiteConfig config, SiteUserSettings userSettings) {
public SiteModel byId(int id) {
return databaseManager.runTask(databaseManager.getDatabaseSiteManager()
.byId(id));
}
public SiteModel create(SiteConfig config, JsonSettings userSettings) {
SiteModel siteModel = new SiteModel();
siteModel.storeConfigFields(config, userSettings);
siteModel.storeConfig(config);
siteModel.storeUserSettings(userSettings);
databaseManager.runTask(databaseManager.getDatabaseSiteManager().add(siteModel));
return siteModel;
}
@ -32,4 +38,10 @@ public class SiteRepository {
databaseManager.runTask(databaseManager.getDatabaseSiteManager()
.updateId(siteModel, id));
}
public void updateSiteUserSettingsAsync(SiteModel siteModel, JsonSettings jsonSettings) {
siteModel.storeUserSettings(jsonSettings);
databaseManager.runTaskAsync(databaseManager.getDatabaseSiteManager()
.update(siteModel));
}
}

@ -17,7 +17,6 @@
*/
package org.floens.chan.core.site.sites.chan4;
import android.content.SharedPreferences;
import android.support.annotation.Nullable;
import android.webkit.CookieManager;
import android.webkit.WebView;
@ -26,6 +25,8 @@ import org.floens.chan.core.model.Post;
import org.floens.chan.core.model.orm.Board;
import org.floens.chan.core.model.orm.Loadable;
import org.floens.chan.core.settings.ChanSettings;
import org.floens.chan.core.settings.SettingProvider;
import org.floens.chan.core.settings.SharedPreferencesSettingProvider;
import org.floens.chan.core.settings.StringSetting;
import org.floens.chan.core.site.Authentication;
import org.floens.chan.core.site.Boards;
@ -253,7 +254,8 @@ public class Chan4 extends SiteBase {
private final StringSetting passToken;
public Chan4() {
SharedPreferences p = AndroidUtils.getPreferences();
// we used these before multisite, and lets keep using them.
SettingProvider p = new SharedPreferencesSettingProvider(AndroidUtils.getPreferences());
passUser = new StringSetting(p, "preference_pass_token", "");
passPass = new StringSetting(p, "preference_pass_pin", "");
// token was renamed, before it meant the username, now it means the token returned

Loading…
Cancel
Save