package org.ebookdroid.common.touch;
import org.sufficientlysecure.viewer.R;
import org.ebookdroid.common.settings.AppSettings;
import android.graphics.Rect;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.emdev.common.log.LogContext;
import org.emdev.common.log.LogManager;
import org.emdev.ui.actions.ActionEx;
import org.emdev.utils.LengthUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class TouchManager {
private static final LogContext LCTX = LogManager.root().lctx("Actions");
public static final String DEFAULT_PROFILE = "DocumentView.Default";
private static final Map<String, TouchProfile> profiles = new HashMap<String, TouchManager.TouchProfile>();
private static final LinkedList<TouchProfile> stack = new LinkedList<TouchProfile>();
public static void loadFromSettings(final AppSettings newSettings) {
profiles.clear();
stack.clear();
boolean fromJSON = false;
final String str = newSettings.tapProfiles;
if (LengthUtils.isNotEmpty(str)) {
try {
final List<TouchProfile> list = fromJSON(str);
for (final TouchProfile p : list) {
profiles.put(p.name, p);
}
} catch (final Throwable ex) {
LCTX.e("Error on tap configuration load: ", ex);
}
fromJSON = profiles.containsKey(DEFAULT_PROFILE);
}
if (!fromJSON) {
if (LCTX.isDebugEnabled()) {
LCTX.d("Creating default tap configuration...");
}
final TouchProfile def = addProfile(DEFAULT_PROFILE);
{
final Region r = def.addRegion(0, 0, 100, 100);
r.setAction(Touch.DoubleTap, R.id.actions_openOptionsMenu, true);
}
{
final Region r = def.addRegion(80, 0, 100, 20);
r.setAction(Touch.DoubleTap, R.id.mainmenu_close, true);
}
{
final Region r = def.addRegion(0, 0, 100, 10);
r.setAction(Touch.SingleTap, R.id.actions_verticalConfigScrollUp, true);
}
{
final Region r = def.addRegion(0, 90, 100, 100);
r.setAction(Touch.SingleTap, R.id.actions_verticalConfigScrollDown, true);
}
{
final Region r = def.addRegion(0, 0, 10, 100); // left edge
r.setAction(Touch.SingleTap, R.id.actions_verticalConfigScrollUp, true);
}
{
final Region r = def.addRegion(90, 0, 100, 100); // right edge
r.setAction(Touch.SingleTap, R.id.actions_verticalConfigScrollDown, true);
}
persist();
}
stack.addFirst(profiles.get(DEFAULT_PROFILE));
}
public static void persist() {
try {
final JSONObject json = toJSON();
AppSettings.updateTapProfiles(json.toString());
} catch (final JSONException ex) {
ex.printStackTrace();
}
}
public static Integer getAction(final Touch type, final float x, final float y, final float width,
final float height) {
return AppSettings.current().tapsEnabled ? stack.peek().getAction(type, x, y, width, height) : null;
}
public static TouchProfile addProfile(final String name) {
final TouchProfile tp = new TouchProfile(name);
profiles.put(tp.name, tp);
return tp;
}
public static TouchProfile topProfile() {
return stack.isEmpty() ? null : stack.peek();
}
private static JSONObject toJSON() throws JSONException {
final JSONObject object = new JSONObject();
final JSONArray array = new JSONArray();
for (final TouchProfile p : profiles.values()) {
array.put(p.toJSON());
}
object.put("profiles", array);
return object;
}
private static List<TouchProfile> fromJSON(final String str) throws JSONException {
final List<TouchProfile> list = new ArrayList<TouchProfile>();
final JSONObject root = new JSONObject(str);
final JSONArray profiles = root.getJSONArray("profiles");
for (int pIndex = 0; pIndex < profiles.length(); pIndex++) {
final JSONObject p = profiles.getJSONObject(pIndex);
final TouchProfile profile = TouchProfile.fromJSON(p);
list.add(profile);
}
return list;
}
public static class TouchProfile {
public final String name;
final LinkedList<Region> regions = new LinkedList<Region>();
public TouchProfile(final String name) {
super();
this.name = name;
}
public ListIterator<Region> regions(boolean forward) {
return forward ? regions.listIterator() : regions.listIterator(regions.size());
}
public void clear() {
regions.clear();
}
public Integer getAction(final Touch type, final float x, final float y, final float width, final float height) {
LCTX.d("getAction(" + type + ", " + x + ", " + y + ", " + width + ", " + height + ")");
for (final Region r : regions) {
final RectF rect = r.getActualRect(width, height);
LCTX.d("Region: " + rect);
if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
final ActionRef action = r.getAction(type);
LCTX.d("Action: " + action);
if (action != null && action.enabled) {
return action.id;
}
}
}
return null;
}
public Region getRegion(final float x, final float y, final float width, final float height) {
LCTX.d("getRegion(" + x + ", " + y + ", " + width + ", " + height + ")");
for (final Region r : regions) {
final RectF rect = r.getActualRect(width, height);
LCTX.d("Region: " + rect);
if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
return r;
}
}
return null;
}
public Region addRegion(final int left, final int top, final int right, final int bottom) {
final Region r = new Region(new Rect(left, top, right, bottom));
return addRegion(r);
}
public Region addRegion(final Region r) {
regions.addFirst(r);
return r;
}
public void removeRegion(final Region r) {
regions.remove(r);
}
@Override
public String toString() {
final StringBuilder buf = new StringBuilder(this.getClass().getSimpleName());
buf.append("[");
buf.append("name").append("=").append(name);
buf.append(", ");
buf.append("regions").append("=").append(regions);
buf.append("]");
return buf.toString();
}
public JSONObject toJSON() throws JSONException {
final JSONObject object = new JSONObject();
object.put("name", this.name);
final JSONArray array = new JSONArray();
for (final Region r : regions) {
array.put(r.toJSON());
}
object.put("regions", array);
return object;
}
public static TouchProfile fromJSON(final JSONObject json) throws JSONException {
final TouchProfile profile = new TouchProfile(json.getString("name"));
final JSONArray regions = json.getJSONArray("regions");
for (int rIndex = 0; rIndex < regions.length(); rIndex++) {
final JSONObject r = regions.getJSONObject(rIndex);
final Region region = Region.fromJSON(r);
profile.regions.add(region);
}
return profile;
}
}
public static enum Touch {
SingleTap, DoubleTap, LongTap, TwoFingerTap;
}
public static class Region {
private final Rect rect;
private final ActionRef[] actions = new ActionRef[Touch.values().length];
public Region(final Rect r) {
rect = r;
}
public Region(final Region r) {
this.rect = new Rect(r.rect);
for (int i = 0; i < actions.length; i++) {
this.actions[i] = r.actions[i];
}
}
public Rect getRect() {
return rect;
}
public ActionRef getAction(final Touch type) {
return actions[type.ordinal()];
}
public ActionRef setAction(final Touch type, final int id, final boolean enabled) {
final ActionRef a = new ActionRef(type, id, enabled);
actions[type.ordinal()] = a;
return a;
}
public RectF getActualRect(final float width, final float height) {
return new RectF(width * rect.left / 100.0f, height * rect.top / 100.0f, width * rect.right / 100.0f,
height * rect.bottom / 100.0f);
}
public void clear(Touch type) {
this.actions[type.ordinal()] = null;
}
public void clear() {
for (int i = 0; i < actions.length; i++) {
this.actions[i] = null;
}
}
@Override
public String toString() {
final StringBuilder buf = new StringBuilder(this.getClass().getSimpleName());
buf.append("[");
buf.append("rect").append("=").append(rect);
buf.append(", ");
buf.append("actions").append("=").append(Arrays.toString(actions));
buf.append("]");
return buf.toString();
}
public JSONObject toJSON() throws JSONException {
final JSONObject object = new JSONObject();
final JSONObject r = new JSONObject();
r.put("left", rect.left);
r.put("top", rect.top);
r.put("right", rect.right);
r.put("bottom", rect.bottom);
object.put("rect", r);
final JSONArray a = new JSONArray();
for (final ActionRef action : actions) {
if (action != null) {
a.put(action.toJSON());
}
}
object.put("actions", a);
return object;
}
public static Region fromJSON(final JSONObject json) throws JSONException {
final JSONObject r = json.getJSONObject("rect");
final Rect rect = new Rect(r.getInt("left"), r.getInt("top"), r.getInt("right"), r.getInt("bottom"));
final Region region = new Region(rect);
final JSONArray actions = json.getJSONArray("actions");
for (int aIndex = 0; aIndex < actions.length(); aIndex++) {
try {
final JSONObject a = actions.getJSONObject(aIndex);
final Touch type = Touch.valueOf(a.getString("type"));
final String name = a.getString("name");
final Integer id = ActionEx.getActionId(name);
if (id != null) {
region.setAction(type, id, a.getBoolean("enabled"));
} else {
LCTX.e("Unknown action name: " + name);
}
} catch (final JSONException ex) {
throw new JSONException("Old perssitent format found. Touch action are returned to default ones: "
+ ex.getMessage());
}
}
return region;
}
}
public static class ActionRef {
public final Touch type;
public final int id;
public final String name;
public boolean enabled;
public ActionRef(final Touch type, final int id, final boolean enabled) {
this.type = type;
this.id = id;
this.name = ActionEx.getActionName(id);
this.enabled = enabled;
}
public JSONObject toJSON() throws JSONException {
final JSONObject object = new JSONObject();
object.put("type", type.name());
object.put("name", name);
object.put("enabled", enabled);
return object;
}
@Override
public String toString() {
return "(" + type + ", " + name + ", " + enabled + ")";
}
}
}