diff --git a/DroidFish/res/layout-land/main.xml b/DroidFish/res/layout-land/main.xml index 1f21971..1f7ff7a 100644 --- a/DroidFish/res/layout-land/main.xml +++ b/DroidFish/res/layout-land/main.xml @@ -20,18 +20,18 @@ android:layout_width="fill_parent" android:layout_height="wrap_content"> + android:src="@drawable/custom"> + android:src="@drawable/custom"> + android:src="@drawable/custom"> Load Next Game No previous game No next game + Select Action + Flip Board + Toggle Show Thinking + Toggle Book Hints + Toggle Variations + Toggle Comments + Toggle Headers Cancel Yes No @@ -249,6 +256,17 @@ you are not actively using the program.\ Font size for move list and game information Large Buttons Use large buttons for game navigation and mode setting + Configure Buttons + Configure button size and button actions. + Main Action + Menu Action 1 + Menu Action 2 + Menu Action 3 + Menu Action 4 + Menu Action 5 + Menu Action 6 + Custom Button 1 + Custom Button 2 Color Settings Change individual colors Chess Board @@ -321,8 +339,8 @@ you are not actively using the program.\ Enable tablebase probing in engine, when supported. Takes effect next time engine is started GTB Directory Directory where Gaviota tablebases are installed. Leave blank to use default directory - Custom button - Flip board button + Custom button 1 + Custom button 2 Mode button Backward button Forward button @@ -480,4 +498,22 @@ you are not actively using the program.\ 50 1000000 + + Disabled + Flip Board + @string/toggle_show_thinking + @string/toggle_book_hints + @string/toggle_pgn_variations + @string/toggle_pgn_comments + @string/toggle_pgn_headers + + + + flipboard + showThinking + bookHints + viewVariations + viewComments + viewHeaders + diff --git a/DroidFish/res/xml/preferences.xml b/DroidFish/res/xml/preferences.xml index 3001d2a..04bbea5 100644 --- a/DroidFish/res/xml/preferences.xml +++ b/DroidFish/res/xml/preferences.xml @@ -172,12 +172,121 @@ android:entries="@array/font_size_texts" android:defaultValue="@string/font_size_default"> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + . +*/ + +package org.petero.droidfish; + +public interface ActionFactory { + + /** Create action given an action ID. */ + public UIAction getAction(String actionId); +} diff --git a/DroidFish/src/org/petero/droidfish/ButtonActions.java b/DroidFish/src/org/petero/droidfish/ButtonActions.java new file mode 100644 index 0000000..c3b8cc5 --- /dev/null +++ b/DroidFish/src/org/petero/droidfish/ButtonActions.java @@ -0,0 +1,132 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2012 Peter Österlund, peterosterlund2@gmail.com + + 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 . +*/ + +package org.petero.droidfish; + +import java.util.ArrayList; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.widget.ImageButton; + +/** + * Handle all actions connected to a button. + */ +public class ButtonActions { + private ImageButton button; + private String name; + private int longClickDialog; + private int menuTitle; + + private UIAction mainAction = null; + private ArrayList menuActions = new ArrayList(); + + private static final int maxMenuActions = 6; + + /** Constructor. */ + public ButtonActions(String buttonName, int longClickDialog, int menuTitle) { + button = null; + name = buttonName; + this.longClickDialog = longClickDialog; + this.menuTitle = menuTitle; + } + + /** Connect GUI button. */ + public void setImageButton(ImageButton button, final Activity activity) { + this.button = button; + button.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (mainAction != null) { + if (mainAction.enabled()) + mainAction.run(); + } else { + showMenu(activity); + } + } + }); + button.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + return showMenu(activity); + } + }); + } + + private boolean showMenu(Activity activity) { + boolean haveActions = false; + boolean haveEnabledActions = false; + for (UIAction a : menuActions) { + if (a != null) { + haveActions = true; + if (a.enabled()) + haveEnabledActions = true; + } + } + if (haveActions) { + if (haveEnabledActions) { + activity.removeDialog(longClickDialog); + activity.showDialog(longClickDialog); + } + return true; + } + return false; + } + + /** Get menu title resource. */ + public int getMenuTitle() { + return menuTitle; + } + + /** Get a menu action. */ + public ArrayList getMenuActions() { + return menuActions; + } + + /** Update button actions from preferences settings. */ + public void readPrefs(SharedPreferences settings, ActionFactory factory) { + boolean visible = false; + String actionId = settings.getString("button_action_" + name + "_0", ""); + mainAction = factory.getAction(actionId); + if (mainAction != null) + visible = true; + menuActions.clear(); + for (int i = 0; i < maxMenuActions; i++) { + actionId = settings.getString("button_action_" + name + "_" + (i+1), ""); + UIAction a = factory.getAction(actionId); + if (a != null) + visible = true; + menuActions.add(a); + } + if (button != null) + button.setVisibility(visible ? View.VISIBLE : View.GONE); + } + + /** Get icon resource for button. */ + public int getIcon(boolean large) { + int ret = -1; + if (mainAction != null) + ret = mainAction.getIcon(large); + if (ret == -1) + ret = large ? R.drawable.custom_large : R.drawable.custom; + return ret; + } +} diff --git a/DroidFish/src/org/petero/droidfish/DroidFish.java b/DroidFish/src/org/petero/droidfish/DroidFish.java index 48ca4f1..306e140 100644 --- a/DroidFish/src/org/petero/droidfish/DroidFish.java +++ b/DroidFish/src/org/petero/droidfish/DroidFish.java @@ -28,6 +28,7 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.TreeMap; @@ -166,7 +167,9 @@ public class DroidFish extends Activity implements GUIInterface { private ScrollView moveListScroll; private TextView moveList; private TextView thinking; - private ImageButton custButton, flipButton, modeButton, undoButton, redoButton; + private ImageButton custom1Button, custom2Button; + private ImageButton modeButton, undoButton, redoButton; + private ButtonActions custom1ButtonActions, custom2ButtonActions; private TextView whiteClock, blackClock, titleText; SharedPreferences settings; @@ -195,6 +198,88 @@ public class DroidFish extends Activity implements GUIInterface { private WakeLock wakeLock = null; private boolean useWakeLock = false; + /** Defines all configurable button actions. */ + private ActionFactory actionFactory = new ActionFactory() { + private HashMap actions; + + private void addAction(UIAction a) { + actions.put(a.getId(), a); + } + + { + actions = new HashMap(); + addAction(new UIAction() { + public String getId() { return "flipboard"; } + public int getName() { return R.string.flip_board; } + public int getIcon(boolean large) { return large ? R.drawable.flip_large : R.drawable.flip; } + public boolean enabled() { return true; } + public void run() { + boardFlipped = !cb.flipped; + setBooleanPref("boardFlipped", boardFlipped); + cb.setFlipped(boardFlipped); + } + }); + addAction(new UIAction() { + public String getId() { return "showThinking"; } + public int getName() { return R.string.toggle_show_thinking; } + public int getIcon(boolean large) { return -1; } + public boolean enabled() { return true; } + public void run() { + mShowThinking = toggleBooleanPref("showThinking"); + updateThinkingInfo(); + } + }); + addAction(new UIAction() { + public String getId() { return "bookHints"; } + public int getName() { return R.string.toggle_book_hints; } + public int getIcon(boolean large) { return -1; } + public boolean enabled() { return true; } + public void run() { + mShowBookHints = toggleBooleanPref("bookHints"); + updateThinkingInfo(); + } + }); + addAction(new UIAction() { + public String getId() { return "viewVariations"; } + public int getName() { return R.string.toggle_pgn_variations; } + public int getIcon(boolean large) { return -1; } + public boolean enabled() { return true; } + public void run() { + pgnOptions.view.variations = toggleBooleanPref("viewVariations"); + gameTextListener.clear(); + ctrl.prefsChanged(); + } + }); + addAction(new UIAction() { + public String getId() { return "viewComments"; } + public int getName() { return R.string.toggle_pgn_comments; } + public int getIcon(boolean large) { return -1; } + public boolean enabled() { return true; } + public void run() { + pgnOptions.view.comments = toggleBooleanPref("viewComments"); + gameTextListener.clear(); + ctrl.prefsChanged(); + } + }); + addAction(new UIAction() { + public String getId() { return "viewHeaders"; } + public int getName() { return R.string.toggle_pgn_headers; } + public int getIcon(boolean large) { return -1; } + public boolean enabled() { return true; } + public void run() { + pgnOptions.view.headers = toggleBooleanPref("viewHeaders"); + gameTextListener.clear(); + ctrl.prefsChanged(); + } + }); + } + + @Override + public UIAction getAction(String actionId) { + return actions.get(actionId); + } + }; + /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { @@ -204,6 +289,7 @@ public class DroidFish extends Activity implements GUIInterface { createDirectories(); + PreferenceManager.setDefaultValues(this, R.xml.preferences, false); settings = PreferenceManager.getDefaultSharedPreferences(this); settings.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener() { @Override @@ -218,6 +304,11 @@ public class DroidFish extends Activity implements GUIInterface { wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "droidfish"); wakeLock.setReferenceCounted(false); + custom1ButtonActions = new ButtonActions("custom1", CUSTOM1_BUTTON_DIALOG, + R.string.select_action); + custom2ButtonActions = new ButtonActions("custom2", CUSTOM2_BUTTON_DIALOG, + R.string.select_action); + initUI(true); gameTextListener = new PgnScreenText(pgnOptions); @@ -479,20 +570,12 @@ public class DroidFish extends Activity implements GUIInterface { } }); - custButton = (ImageButton)findViewById(R.id.customButton); - custButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - } - }); - flipButton = (ImageButton)findViewById(R.id.flipButton); - flipButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - setBoardFlipPrefs(!cb.flipped); - cb.setFlipped(boardFlipped); - } - }); + custom1Button = (ImageButton)findViewById(R.id.custom1Button); + custom1ButtonActions.setImageButton(custom1Button, this); + + custom2Button = (ImageButton)findViewById(R.id.custom2Button); + custom2ButtonActions.setImageButton(custom2Button, this); + modeButton = (ImageButton)findViewById(R.id.modeButton); modeButton.setOnClickListener(new OnClickListener() { @Override @@ -631,6 +714,9 @@ public class DroidFish extends Activity implements GUIInterface { vibrateEnabled = settings.getBoolean("vibrateEnabled", false); animateMoves = settings.getBoolean("animateMoves", true); + custom1ButtonActions.readPrefs(settings, actionFactory); + custom2ButtonActions.readPrefs(settings, actionFactory); + boolean largeButtons = settings.getBoolean("largeButtons", false); Resources r = getResources(); int bWidth = (int)Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 36, r.getDisplayMetrics())); @@ -638,26 +724,24 @@ public class DroidFish extends Activity implements GUIInterface { if (largeButtons) { bWidth = bWidth * 3 / 2; bHeight = bHeight * 3 / 2; - custButton.setImageResource(R.drawable.custom_large); - flipButton.setImageResource(R.drawable.flip_large); + custom1Button.setImageResource(custom1ButtonActions.getIcon(true)); + custom2Button.setImageResource(custom2ButtonActions.getIcon(true)); modeButton.setImageResource(R.drawable.mode_large); undoButton.setImageResource(R.drawable.left_large); redoButton.setImageResource(R.drawable.right_large); } else { - custButton.setImageResource(R.drawable.custom); - flipButton.setImageResource(R.drawable.flip); + custom1Button.setImageResource(custom1ButtonActions.getIcon(false)); + custom2Button.setImageResource(custom2ButtonActions.getIcon(false)); modeButton.setImageResource(R.drawable.mode); undoButton.setImageResource(R.drawable.left); redoButton.setImageResource(R.drawable.right); } - custButton.setLayoutParams(new LinearLayout.LayoutParams(bWidth, bHeight)); - flipButton.setLayoutParams(new LinearLayout.LayoutParams(bWidth, bHeight)); + custom1Button.setLayoutParams(new LinearLayout.LayoutParams(bWidth, bHeight)); + custom2Button.setLayoutParams(new LinearLayout.LayoutParams(bWidth, bHeight)); modeButton.setLayoutParams(new LinearLayout.LayoutParams(bWidth, bHeight)); undoButton.setLayoutParams(new LinearLayout.LayoutParams(bWidth, bHeight)); redoButton.setLayoutParams(new LinearLayout.LayoutParams(bWidth, bHeight)); - custButton.setVisibility(true ? View.GONE : View.VISIBLE); - bookOptions.filename = settings.getString("bookFile", ""); bookOptions.maxLength = getIntSetting("bookMaxLength", 1000000); bookOptions.preferMainLines = settings.getBoolean("bookPreferMainLines", false); @@ -954,13 +1038,20 @@ public class DroidFish extends Activity implements GUIInterface { setBoardFlip(false); } - private final void setBoardFlipPrefs(boolean flipped) { - boardFlipped = flipped; + /** Set a boolean preference setting. */ + private final void setBooleanPref(String name, boolean value) { Editor editor = settings.edit(); - editor.putBoolean("boardFlipped", boardFlipped); + editor.putBoolean(name, value); editor.commit(); } + /** Toggle a boolean preference setting. Return new value. */ + private final boolean toggleBooleanPref(String name) { + boolean value = !settings.getBoolean(name, false); + setBooleanPref(name, value); + return value; + } + private final void setBoardFlip(boolean matchPlayerNames) { boolean flipped = boardFlipped; if (playerNameFlip && matchPlayerNames && (ctrl != null)) { @@ -971,7 +1062,8 @@ public class DroidFish extends Activity implements GUIInterface { if (( flipped && (whiteMatch > blackMatch)) || (!flipped && (whiteMatch < blackMatch))) { flipped = !flipped; - setBoardFlipPrefs(flipped); + boardFlipped = flipped; + setBooleanPref("boardFlipped", flipped); } } if (autoSwapSides) { @@ -1222,6 +1314,8 @@ public class DroidFish extends Activity implements GUIInterface { static private final int GO_FORWARD_MENU_DIALOG = 14; static private final int FILE_MENU_DIALOG = 15; static private final int NEW_GAME_DIALOG = 16; + static private final int CUSTOM1_BUTTON_DIALOG = 17; + static private final int CUSTOM2_BUTTON_DIALOG = 18; @Override protected Dialog onCreateDialog(int id) { @@ -1973,10 +2067,37 @@ public class DroidFish extends Activity implements GUIInterface { AlertDialog alert = builder.create(); return alert; } + case CUSTOM1_BUTTON_DIALOG: + return makeButtonDialog(custom1ButtonActions); + case CUSTOM2_BUTTON_DIALOG: + return makeButtonDialog(custom2ButtonActions); } return null; } + private Dialog makeButtonDialog(ButtonActions buttonActions) { + List names = new ArrayList(); + final List actions = new ArrayList(); + + HashSet used = new HashSet(); + for (UIAction a : buttonActions.getMenuActions()) { + if ((a != null) && a.enabled() && !used.contains(a.getId())) { + names.add(getString(a.getName())); + actions.add(a); + used.add(a.getId()); + } + } + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(buttonActions.getMenuTitle()); + builder.setItems(names.toArray(new CharSequence[names.size()]), new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + UIAction a = actions.get(item); + a.run(); + } + }); + return builder.create(); + } + /** Open a load/save file dialog. Uses OI file manager if available. */ private void selectPgnFile(boolean save) { String action = "org.openintents.action.PICK_FILE"; diff --git a/DroidFish/src/org/petero/droidfish/UIAction.java b/DroidFish/src/org/petero/droidfish/UIAction.java new file mode 100644 index 0000000..5ab9d6d --- /dev/null +++ b/DroidFish/src/org/petero/droidfish/UIAction.java @@ -0,0 +1,34 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2012 Peter Österlund, peterosterlund2@gmail.com + + 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 . +*/ + +package org.petero.droidfish; + +/** Interface for user interface actions. */ +public interface UIAction extends Runnable { + /** Get a unique identifier for this action. */ + public String getId(); + + /** Get name resource for the action. */ + public int getName(); + + /** Get icon resource or -1 for no icon. */ + public int getIcon(boolean large); + + /** Return true if the action is currently enabled. */ + public boolean enabled(); +}