From b21eab7cf206d4a6656ebdbf359f1e3191f5e2a6 Mon Sep 17 00:00:00 2001 From: pelya Date: Mon, 24 Jun 2013 20:39:47 +0300 Subject: [PATCH] Split Settings.java into 4 files --- changeAppSettings.sh | 41 +- project/java/DataDownloader.java | 2 +- project/java/Globals.java | 4 +- project/java/MainActivity.java | 20 +- project/java/Settings.java | 2233 +- project/java/SettingsMenu.java | 257 + project/java/SettingsMenuKeyboard.java | 801 + project/java/SettingsMenuMisc.java | 701 + project/java/SettingsMenuMouse.java | 771 + .../commandergenius/commandergenius | 2 +- .../openarena/AndroidAppSettings.cfg | 8 +- .../teeworlds/AndroidAppSettings.cfg | 16 +- project/res/raw/simpletheme.raw | Bin 26666 -> 26666 bytes project/res/raw/sun.raw | Bin 431886 -> 431886 bytes project/res/raw/ultimatedroid.raw | Bin 7391 -> 7391 bytes project/themes/{converter => }/Makefile | 0 project/themes/convert.sh | 141 + project/themes/{converter => }/converter.cpp | 0 project/themes/converter/convert.sh | 152 - project/themes/converter/converter | Bin 117982 -> 0 bytes project/themes/readme.txt | 1 + project/themes/touchscreentheme.h | 29129 ---------------- 22 files changed, 2733 insertions(+), 31546 deletions(-) create mode 100644 project/java/SettingsMenu.java create mode 100644 project/java/SettingsMenuKeyboard.java create mode 100644 project/java/SettingsMenuMisc.java create mode 100644 project/java/SettingsMenuMouse.java rename project/themes/{converter => }/Makefile (100%) create mode 100755 project/themes/convert.sh rename project/themes/{converter => }/converter.cpp (100%) delete mode 100755 project/themes/converter/convert.sh delete mode 100755 project/themes/converter/converter create mode 100644 project/themes/readme.txt delete mode 100644 project/themes/touchscreentheme.h diff --git a/changeAppSettings.sh b/changeAppSettings.sh index 59ff1d1de..863684820 100755 --- a/changeAppSettings.sh +++ b/changeAppSettings.sh @@ -1,6 +1,6 @@ #!/bin/sh -CHANGE_APP_SETTINGS_VERSION=18 +CHANGE_APP_SETTINGS_VERSION=19 AUTO= CHANGED= @@ -485,10 +485,15 @@ if [ -n "$var" ] ; then fi fi +MenuOptionsAvailable= +for FF in Menu MenuMisc MenuMouse MenuKeyboard ; do + MenuOptionsAvailable1=`grep 'extends Menu' project/java/Settings$FF.java | sed "s/.* class \(.*\) extends .*/Settings$FF.\1/" | tr '\n' ' '` + MenuOptionsAvailable="$MenuOptionsAvailable $MenuOptionsAvailable1" +done if [ -z "$AUTO" ]; then echo echo "Menu items to hide from startup menu, available menu items:" -echo "`grep 'extends Menu' project/java/Settings.java | sed 's/.* class \(.*\) extends .*/\1/'`" +echo "$MenuOptionsAvailable" echo "($HiddenMenuOptions)" echo -n ": " read var @@ -498,13 +503,13 @@ if [ -n "$var" ] ; then fi fi -FirstStartMenuOptionsDefault='new Settings.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new Settings.DisplaySizeConfig(true) : new Settings.DummyMenu()), new Settings.OptionalDownloadConfig(true), new Settings.GyroscopeCalibration()' +FirstStartMenuOptionsDefault='new SettingsMenuMisc.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new SettingsMenuMouse.DisplaySizeConfig(true) : new SettingsMenu.DummyMenu()), new SettingsMenuMisc.OptionalDownloadConfig(true), new SettingsMenuMisc.GyroscopeCalibration()' if [ -z "$AUTO" ]; then echo -echo "Menu items to show at startup - this is Java code snippet, leave empty for default" -echo $FirstStartMenuOptionsDefault +echo "Menu items to show at startup - this is Java code snippet, leave empty for default:" +echo "$FirstStartMenuOptionsDefault" echo "Available menu items:" -echo "`grep 'extends Menu' project/java/Settings.java | sed 's/.* class \(.*\) extends .*/new Settings.\1(), /'`" +echo "$MenuOptionsAvailable" echo "Current value: " "$FirstStartMenuOptions" echo -n ": " read var @@ -863,13 +868,13 @@ echo "# How long to show startup menu button, in msec, 0 to disable startup menu echo StartupMenuButtonTimeout=$StartupMenuButtonTimeout >> AndroidAppSettings.cfg echo >> AndroidAppSettings.cfg echo "# Menu items to hide from startup menu, available menu items:" >> AndroidAppSettings.cfg -echo "# `grep 'extends Menu' project/java/Settings.java | sed 's/.* class \(.*\) extends .*/\1/' | tr '\n' ' '`" >> AndroidAppSettings.cfg +echo "# $MenuOptionsAvailable" >> AndroidAppSettings.cfg echo HiddenMenuOptions=\'$HiddenMenuOptions\' >> AndroidAppSettings.cfg echo >> AndroidAppSettings.cfg -echo "# Menu items to show at startup - this is Java code snippet, leave empty for default:" >> AndroidAppSettings.cfg +echo "# Menu items to show at startup - this is Java code snippet, leave empty for default" >> AndroidAppSettings.cfg echo "# $FirstStartMenuOptionsDefault" >> AndroidAppSettings.cfg echo "# Available menu items:" >> AndroidAppSettings.cfg -echo "# `grep 'extends Menu' project/java/Settings.java | sed 's/.* class \(.*\) extends .*/new Settings.\1(), /' | tr -d '\n'`" >> AndroidAppSettings.cfg +echo "# $MenuOptionsAvailable" >> AndroidAppSettings.cfg echo FirstStartMenuOptions=\'$FirstStartMenuOptions\' >> AndroidAppSettings.cfg echo >> AndroidAppSettings.cfg echo "# Enable multi-ABI binary, with hardware FPU support - it will also work on old devices," >> AndroidAppSettings.cfg @@ -1157,12 +1162,17 @@ fi HiddenMenuOptions1="" for F in $HiddenMenuOptions; do - HiddenMenuOptions1="$HiddenMenuOptions1 new Settings.$F()," + HiddenMenuOptions1="$HiddenMenuOptions1 new $F()," done -if [ -z "$FirstStartMenuOptions" ]; then - FirstStartMenuOptions="$FirstStartMenuOptionsDefault" -fi +FirstStartMenuOptions1="" +for F in $FirstStartMenuOptions; do + FirstStartMenuOptions1="$FirstStartMenuOptions1 new $F()," +done + +#if [ -z "$FirstStartMenuOptions" ]; then +# FirstStartMenuOptions="$FirstStartMenuOptionsDefault" +#fi ReadmeText="`echo $ReadmeText | sed 's/\"/\\\\\\\\\"/g' | sed 's/[&%]//g'`" @@ -1259,8 +1269,8 @@ $SEDI "s/public static int AppTouchscreenKeyboardKeysAmountAutoFire = .*;/public $SEDI "s@public static String\\[\\] AppTouchscreenKeyboardKeysNames = .*;@public static String[] AppTouchscreenKeyboardKeysNames = \"$RedefinedKeysScreenKbNames\".split(\" \");@" project/src/Globals.java $SEDI "s/public static int StartupMenuButtonTimeout = .*;/public static int StartupMenuButtonTimeout = $StartupMenuButtonTimeout;/" project/src/Globals.java $SEDI "s/public static int AppMinimumRAM = .*;/public static int AppMinimumRAM = $AppMinimumRAM;/" project/src/Globals.java -$SEDI "s/public static Settings.Menu HiddenMenuOptions .*;/public static Settings.Menu HiddenMenuOptions [] = { $HiddenMenuOptions1 };/" project/src/Globals.java -$SEDI "s@public static Settings.Menu FirstStartMenuOptions .*;@public static Settings.Menu FirstStartMenuOptions [] = { $FirstStartMenuOptions };@" project/src/Globals.java +$SEDI "s/public static SettingsMenu.Menu HiddenMenuOptions .*;/public static SettingsMenu.Menu HiddenMenuOptions [] = { $HiddenMenuOptions1 };/" project/src/Globals.java +[ -n "$FirstStartMenuOptions1" ] && $SEDI "s@public static SettingsMenu.Menu FirstStartMenuOptions .*;@public static SettingsMenu.Menu FirstStartMenuOptions [] = { $FirstStartMenuOptions1 };@" project/src/Globals.java $SEDI "s%public static String ReadmeText = .*%public static String ReadmeText = \"$ReadmeText\";%" project/src/Globals.java $SEDI "s%public static String CommandLine = .*%public static String CommandLine = \"$AppCmdline\";%" project/src/Globals.java $SEDI "s/public static String AdmobPublisherId = .*/public static String AdmobPublisherId = \"$AdmobPublisherId\";/" project/src/Globals.java @@ -1268,6 +1278,7 @@ $SEDI "s/public static String AdmobTestDeviceId = .*/public static String AdmobT $SEDI "s/public static String AdmobBannerSize = .*/public static String AdmobBannerSize = \"$AdmobBannerSize\";/" project/src/Globals.java $SEDI "s/public static String AppLibraries.*/public static String AppLibraries[] = { $LibrariesToLoad };/" project/src/Globals.java + echo Patching project/jni/Settings.mk echo '# DO NOT EDIT THIS FILE - it is automatically generated, edit file SettingsTemplate.mk' > project/jni/Settings.mk cat project/jni/SettingsTemplate.mk | \ diff --git a/project/java/DataDownloader.java b/project/java/DataDownloader.java index 54b9d05a2..99b786b3a 100644 --- a/project/java/DataDownloader.java +++ b/project/java/DataDownloader.java @@ -686,7 +686,7 @@ class DataDownloader extends Thread } - public class BackKeyListener implements Settings.KeyEventsListener + public class BackKeyListener implements MainActivity.KeyEventsListener { MainActivity p; public BackKeyListener(MainActivity _p) diff --git a/project/java/Globals.java b/project/java/Globals.java index 135527fc4..135dd5aff 100644 --- a/project/java/Globals.java +++ b/project/java/Globals.java @@ -65,8 +65,8 @@ class Globals public static String[] AppTouchscreenKeyboardKeysNames = "Fire Shoot Switch_weapon Jump Run Hide/Seek".split(" "); public static int StartupMenuButtonTimeout = 3000; public static int AppMinimumRAM = 0; - public static Settings.Menu HiddenMenuOptions [] = {}; - public static Settings.Menu FirstStartMenuOptions [] = { (AppUsesMouse && ! ForceRelativeMouseMode ? new Settings.DisplaySizeConfig(true) : new Settings.DummyMenu()), new Settings.OptionalDownloadConfig(true), new Settings.GyroscopeCalibration() }; + public static SettingsMenu.Menu HiddenMenuOptions [] = {}; + public static SettingsMenu.Menu FirstStartMenuOptions [] = { new SettingsMenuMisc.ShowReadme(), (AppUsesMouse && ! ForceRelativeMouseMode ? new SettingsMenuMouse.DisplaySizeConfig() : new SettingsMenu.DummyMenu()), new SettingsMenuMisc.OptionalDownloadConfig(), new SettingsMenuMisc.GyroscopeCalibration() }; public static String AdmobPublisherId = ""; public static String AdmobTestDeviceId = ""; public static String AdmobBannerSize = ""; diff --git a/project/java/MainActivity.java b/project/java/MainActivity.java index 8ec2aa4cf..425a9454e 100644 --- a/project/java/MainActivity.java +++ b/project/java/MainActivity.java @@ -117,7 +117,7 @@ public class MainActivity extends Activity setUpStatusLabel(); Log.i("SDL", "libSDL: User clicked change phone config button"); loadedLibraries.acquireUninterruptibly(); - Settings.showConfig(p, false); + SettingsMenu.showConfig(p, false); } }; _btn.setOnClickListener(new onClickListener(this)); @@ -394,6 +394,9 @@ public class MainActivity extends Activity if( mGLView != null ) mGLView.exitApp(); super.onDestroy(); + try{ + Thread.sleep(2000); // The event is sent asynchronously, allow app to save it's state, and call exit() itself. + } catch (InterruptedException e) {} System.exit(0); } @@ -1059,8 +1062,19 @@ public class MainActivity extends Activity private EditText _screenKeyboard = null; private String _screenKeyboardHintMessage = null; private boolean sdlInited = false; - public Settings.TouchEventsListener touchListener = null; - public Settings.KeyEventsListener keyListener = null; + + public interface TouchEventsListener + { + public void onTouchEvent(final MotionEvent ev); + } + + public interface KeyEventsListener + { + public void onKeyEvent(final int keyCode); + } + + public TouchEventsListener touchListener = null; + public KeyEventsListener keyListener = null; boolean _isPaused = false; private InputMethodManager _inputManager = null; diff --git a/project/java/Settings.java b/project/java/Settings.java index 8ac1dabcd..fad46d0b5 100644 --- a/project/java/Settings.java +++ b/project/java/Settings.java @@ -402,2237 +402,9 @@ class Settings Log.i("SDL", "libSDL: Settings.Load(): loading settings failed, running config dialog"); p.setUpStatusLabel(); if( checkRamSize(p) ) - showConfig(p, true); + SettingsMenu.showConfig(p, true); } - // =============================================================================================== - - public static abstract class Menu - { - // Should be overridden by children - abstract void run(final MainActivity p); - abstract String title(final MainActivity p); - boolean enabled() - { - return true; - } - // Should not be overridden - boolean enabledOrHidden() - { - for( Menu m: Globals.HiddenMenuOptions ) - { - if( m.getClass().getName().equals( this.getClass().getName() ) ) - return false; - } - return enabled(); - } - void showMenuOptionsList(final MainActivity p, final Menu[] list) - { - menuStack.add(this); - ArrayList items = new ArrayList (); - for( Menu m: list ) - { - if(m.enabledOrHidden()) - items.add(m.title(p)); - } - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(title(p)); - builder.setItems(items.toArray(new CharSequence[0]), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - int selected = 0; - - for( Menu m: list ) - { - if(m.enabledOrHidden()) - { - if( selected == item ) - { - m.run(p); - return; - } - selected ++; - } - } - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBackOuterMenu(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static ArrayList menuStack = new ArrayList (); - - public static void showConfig(final MainActivity p, final boolean firstStart) - { - settingsChanged = true; - if( Globals.OptionalDataDownload == null ) - { - String downloads[] = Globals.DataDownloadUrl; - Globals.OptionalDataDownload = new boolean[downloads.length]; - boolean oldFormat = true; - for( int i = 0; i < downloads.length; i++ ) - { - if( downloads[i].indexOf("!") == 0 ) - { - Globals.OptionalDataDownload[i] = true; - oldFormat = false; - } - } - if( oldFormat ) - Globals.OptionalDataDownload[0] = true; - } - - if(!firstStart) - new MainMenu().run(p); - else - { - if( Globals.StartupMenuButtonTimeout > 0 ) // If we did not disable startup menu altogether - { - for( Menu m: Globals.FirstStartMenuOptions ) - { - boolean hidden = false; - for( Menu m1: Globals.HiddenMenuOptions ) - { - if( m1.getClass().getName().equals( m.getClass().getName() ) ) - hidden = true; - } - if( ! hidden ) - menuStack.add(0, m); - } - } - goBack(p); - } - } - - static void goBack(final MainActivity p) - { - if(menuStack.isEmpty()) - { - Save(p); - p.startDownloader(); - } - else - { - Menu c = menuStack.remove(menuStack.size() - 1); - c.run(p); - } - } - - static void goBackOuterMenu(final MainActivity p) - { - if(!menuStack.isEmpty()) - menuStack.remove(menuStack.size() - 1); - goBack(p); - } - - static class OkButton extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.ok); - } - void run (final MainActivity p) - { - goBackOuterMenu(p); - } - } - - static class DummyMenu extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.ok); - } - void run (final MainActivity p) - { - goBack(p); - } - } - - static class MainMenu extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.device_config); - } - void run (final MainActivity p) - { - Menu options[] = - { - new DownloadConfig(), - new OptionalDownloadConfig(false), - new KeyboardConfigMainMenu(), - new MouseConfigMainMenu(), - new GyroscopeCalibration(), - new AudioConfig(), - new RemapHwKeysConfig(), - new ScreenGesturesConfig(), - new VideoSettingsConfig(), - new ResetToDefaultsConfig(), - new OkButton(), - }; - showMenuOptionsList(p, options); - } - } - - static class MouseConfigMainMenu extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.mouse_emulation); - } - boolean enabled() - { - return Globals.AppUsesMouse; - } - void run (final MainActivity p) - { - Menu options[] = - { - new DisplaySizeConfig(false), - new LeftClickConfig(), - new RightClickConfig(), - new AdditionalMouseConfig(), - new JoystickMouseConfig(), - new TouchPressureMeasurementTool(), - new CalibrateTouchscreenMenu(), - new OkButton(), - }; - showMenuOptionsList(p, options); - } - } - - static class KeyboardConfigMainMenu extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.controls_screenkb); - } - boolean enabled() - { - return Globals.UseTouchscreenKeyboard; - } - void run (final MainActivity p) - { - Menu options[] = - { - new ScreenKeyboardThemeConfig(), - new ScreenKeyboardSizeConfig(), - new ScreenKeyboardDrawSizeConfig(), - new ScreenKeyboardTransparencyConfig(), - new RemapScreenKbConfig(), - new CustomizeScreenKbLayout(), - new OkButton(), - }; - showMenuOptionsList(p, options); - } - } - - static class DownloadConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.storage_question); - } - void run (final MainActivity p) - { - long freeSdcard = 0; - long freePhone = 0; - try - { - StatFs sdcard = new StatFs(Environment.getExternalStorageDirectory().getPath()); - StatFs phone = new StatFs(Environment.getDataDirectory().getPath()); - freeSdcard = (long)sdcard.getAvailableBlocks() * sdcard.getBlockSize() / 1024 / 1024; - freePhone = (long)phone.getAvailableBlocks() * phone.getBlockSize() / 1024 / 1024; - } - catch(Exception e) {} - - final CharSequence[] items = { p.getResources().getString(R.string.storage_phone, freePhone), - p.getResources().getString(R.string.storage_sd, freeSdcard), - p.getResources().getString(R.string.storage_custom) }; - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.storage_question)); - builder.setSingleChoiceItems(items, Globals.DownloadToSdcard ? 1 : 0, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - - if( item == 2 ) - showCustomDownloadDirConfig(p); - else - { - Globals.DownloadToSdcard = (item != 0); - Globals.DataDir = Globals.DownloadToSdcard ? - SdcardAppPath.getPath(p) : - p.getFilesDir().getAbsolutePath(); - goBack(p); - } - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - static void showCustomDownloadDirConfig(final MainActivity p) - { - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.storage_custom)); - - final EditText edit = new EditText(p); - edit.setFocusableInTouchMode(true); - edit.setFocusable(true); - edit.setText(Globals.DataDir); - builder.setView(edit); - - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.DataDir = edit.getText().toString(); - dialog.dismiss(); - showCommandLineConfig(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - static void showCommandLineConfig(final MainActivity p) - { - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.storage_commandline)); - - final EditText edit = new EditText(p); - edit.setFocusableInTouchMode(true); - edit.setFocusable(true); - edit.setText(Globals.CommandLine); - builder.setView(edit); - - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.CommandLine = edit.getText().toString(); - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class OptionalDownloadConfig extends Menu - { - boolean firstStart = false; - OptionalDownloadConfig() - { - firstStart = false; - } - OptionalDownloadConfig(boolean firstStart) - { - this.firstStart = firstStart; - } - String title(final MainActivity p) - { - return p.getResources().getString(R.string.downloads); - } - void run (final MainActivity p) - { - String [] downloadFiles = Globals.DataDownloadUrl; - final boolean [] mandatory = new boolean[downloadFiles.length]; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.downloads)); - - CharSequence[] items = new CharSequence[downloadFiles.length]; - for(int i = 0; i < downloadFiles.length; i++ ) - { - items[i] = new String(downloadFiles[i].split("[|]")[0]); - if( items[i].toString().indexOf("!") == 0 ) - items[i] = items[i].toString().substring(1); - if( items[i].toString().indexOf("!") == 0 ) - { - items[i] = items[i].toString().substring(1); - mandatory[i] = true; - } - } - - if( Globals.OptionalDataDownload == null || Globals.OptionalDataDownload.length != items.length ) - { - Globals.OptionalDataDownload = new boolean[downloadFiles.length]; - boolean oldFormat = true; - for( int i = 0; i < downloadFiles.length; i++ ) - { - if( downloadFiles[i].indexOf("!") == 0 ) - { - Globals.OptionalDataDownload[i] = true; - oldFormat = false; - } - } - if( oldFormat ) - { - Globals.OptionalDataDownload[0] = true; - mandatory[0] = true; - } - } - - builder.setMultiChoiceItems(items, Globals.OptionalDataDownload, new DialogInterface.OnMultiChoiceClickListener() - { - public void onClick(DialogInterface dialog, int item, boolean isChecked) - { - Globals.OptionalDataDownload[item] = isChecked; - if( mandatory[item] && !isChecked ) - { - Globals.OptionalDataDownload[item] = true; - ((AlertDialog)dialog).getListView().setItemChecked(item, true); - } - } - }); - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - goBack(p); - } - }); - if( firstStart ) - { - builder.setNegativeButton(p.getResources().getString(R.string.show_more_options), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - menuStack.clear(); - new MainMenu().run(p); - } - }); - } - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class ScreenKeyboardSizeConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.controls_screenkb_size); - } - void run (final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_large), - p.getResources().getString(R.string.controls_screenkb_medium), - p.getResources().getString(R.string.controls_screenkb_small), - p.getResources().getString(R.string.controls_screenkb_tiny), - p.getResources().getString(R.string.controls_screenkb_custom) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.controls_screenkb_size)); - builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardSize, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.TouchscreenKeyboardSize = item; - dialog.dismiss(); - if( Globals.TouchscreenKeyboardSize == Globals.TOUCHSCREEN_KEYBOARD_CUSTOM ) - new CustomizeScreenKbLayout().run(p); - else - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class ScreenKeyboardDrawSizeConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.controls_screenkb_drawsize); - } - void run (final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_large), - p.getResources().getString(R.string.controls_screenkb_medium), - p.getResources().getString(R.string.controls_screenkb_small), - p.getResources().getString(R.string.controls_screenkb_tiny) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.controls_screenkb_drawsize)); - builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardDrawSize, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.TouchscreenKeyboardDrawSize = item; - - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class ScreenKeyboardThemeConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.controls_screenkb_theme); - } - void run (final MainActivity p) - { - final CharSequence[] items = { - p.getResources().getString(R.string.controls_screenkb_by, "Ultimate Droid", "Sean Stieber"), - p.getResources().getString(R.string.controls_screenkb_by, "Simple Theme", "Beholder"), - p.getResources().getString(R.string.controls_screenkb_by, "Sun", "Sirea") - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.controls_screenkb_theme)); - builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardTheme, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.TouchscreenKeyboardTheme = item; - - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class ScreenKeyboardTransparencyConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.controls_screenkb_transparency); - } - void run (final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_trans_0), - p.getResources().getString(R.string.controls_screenkb_trans_1), - p.getResources().getString(R.string.controls_screenkb_trans_2), - p.getResources().getString(R.string.controls_screenkb_trans_3), - p.getResources().getString(R.string.controls_screenkb_trans_4) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.controls_screenkb_transparency)); - builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardTransparency, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.TouchscreenKeyboardTransparency = item; - - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class AudioConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.audiobuf_question); - } - void run (final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.audiobuf_verysmall), - p.getResources().getString(R.string.audiobuf_small), - p.getResources().getString(R.string.audiobuf_medium), - p.getResources().getString(R.string.audiobuf_large) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.audiobuf_question); - builder.setSingleChoiceItems(items, Globals.AudioBufferConfig, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.AudioBufferConfig = item; - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class DisplaySizeConfig extends Menu - { - boolean firstStart = false; - DisplaySizeConfig() - { - this.firstStart = false; - } - DisplaySizeConfig(boolean firstStart) - { - this.firstStart = firstStart; - } - String title(final MainActivity p) - { - return p.getResources().getString(R.string.display_size_mouse); - } - void run (final MainActivity p) - { - CharSequence[] items = { - p.getResources().getString(R.string.display_size_tiny_touchpad), - p.getResources().getString(R.string.display_size_tiny), - p.getResources().getString(R.string.display_size_small), - p.getResources().getString(R.string.display_size_small_touchpad), - p.getResources().getString(R.string.display_size_large), - }; - int _size_tiny_touchpad = 0; - int _size_tiny = 1; - int _size_small = 2; - int _size_small_touchpad = 3; - int _size_large = 4; - int _more_options = 5; - - if( ! Globals.SwVideoMode ) - { - CharSequence[] items2 = { - p.getResources().getString(R.string.display_size_small_touchpad), - p.getResources().getString(R.string.display_size_large), - }; - items = items2; - _size_small_touchpad = 0; - _size_large = 1; - _size_tiny_touchpad = _size_tiny = _size_small = 1000; - - } - if( firstStart ) - { - CharSequence[] items2 = { - p.getResources().getString(R.string.display_size_tiny_touchpad), - p.getResources().getString(R.string.display_size_tiny), - p.getResources().getString(R.string.display_size_small), - p.getResources().getString(R.string.display_size_small_touchpad), - p.getResources().getString(R.string.display_size_large), - p.getResources().getString(R.string.show_more_options), - }; - items = items2; - if( ! Globals.SwVideoMode ) - { - CharSequence[] items3 = { - p.getResources().getString(R.string.display_size_small_touchpad), - p.getResources().getString(R.string.display_size_large), - p.getResources().getString(R.string.show_more_options), - }; - items = items3; - _more_options = 3; - } - } - // Java is so damn worse than C++11 - final int size_tiny_touchpad = _size_tiny_touchpad; - final int size_tiny = _size_tiny; - final int size_small = _size_small; - final int size_small_touchpad = _size_small_touchpad; - final int size_large = _size_large; - final int more_options = _more_options; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.display_size); - class ClickListener implements DialogInterface.OnClickListener - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - if( item == size_large ) - { - Globals.LeftClickMethod = Mouse.LEFT_CLICK_NORMAL; - Globals.RelativeMouseMovement = false; - Globals.ShowScreenUnderFinger = Mouse.ZOOM_NONE; - } - if( item == size_small ) - { - Globals.LeftClickMethod = Mouse.LEFT_CLICK_NEAR_CURSOR; - Globals.RelativeMouseMovement = false; - Globals.ShowScreenUnderFinger = Mouse.ZOOM_MAGNIFIER; - } - if( item == size_small_touchpad ) - { - Globals.LeftClickMethod = Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT; - Globals.RelativeMouseMovement = true; - Globals.ShowScreenUnderFinger = Mouse.ZOOM_NONE; - } - if( item == size_tiny ) - { - Globals.LeftClickMethod = Mouse.LEFT_CLICK_NEAR_CURSOR; - Globals.RelativeMouseMovement = false; - Globals.ShowScreenUnderFinger = Mouse.ZOOM_SCREEN_TRANSFORM; - } - if( item == size_tiny_touchpad ) - { - Globals.LeftClickMethod = Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT; - Globals.RelativeMouseMovement = true; - Globals.ShowScreenUnderFinger = Mouse.ZOOM_FULLSCREEN_MAGNIFIER; - } - if( item == more_options ) - { - menuStack.clear(); - new MainMenu().run(p); - return; - } - goBack(p); - } - } - builder.setItems(items, new ClickListener()); - /* - else - builder.setSingleChoiceItems(items, - Globals.ShowScreenUnderFinger == Mouse.ZOOM_NONE ? - ( Globals.RelativeMouseMovement ? Globals.SwVideoMode ? 2 : 1 : 0 ) : - ( Globals.ShowScreenUnderFinger == Mouse.ZOOM_MAGNIFIER && Globals.SwVideoMode ) ? 1 : - Globals.ShowScreenUnderFinger + 1, - new ClickListener()); - */ - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class LeftClickConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.leftclick_question); - } - void run (final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.leftclick_normal), - p.getResources().getString(R.string.leftclick_near_cursor), - p.getResources().getString(R.string.leftclick_multitouch), - p.getResources().getString(R.string.leftclick_pressure), - p.getResources().getString(R.string.rightclick_key), - p.getResources().getString(R.string.leftclick_timeout), - p.getResources().getString(R.string.leftclick_tap), - p.getResources().getString(R.string.leftclick_tap_or_timeout) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.leftclick_question); - builder.setSingleChoiceItems(items, Globals.LeftClickMethod, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - Globals.LeftClickMethod = item; - if( item == Mouse.LEFT_CLICK_WITH_KEY ) - p.keyListener = new KeyRemapToolMouseClick(p, true); - else if( item == Mouse.LEFT_CLICK_WITH_TIMEOUT || item == Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT ) - showLeftClickTimeoutConfig(p); - else - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - static void showLeftClickTimeoutConfig(final MainActivity p) { - final CharSequence[] items = { p.getResources().getString(R.string.leftclick_timeout_time_0), - p.getResources().getString(R.string.leftclick_timeout_time_1), - p.getResources().getString(R.string.leftclick_timeout_time_2), - p.getResources().getString(R.string.leftclick_timeout_time_3), - p.getResources().getString(R.string.leftclick_timeout_time_4) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.leftclick_timeout_time); - builder.setSingleChoiceItems(items, Globals.LeftClickTimeout, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.LeftClickTimeout = item; - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class RightClickConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.rightclick_question); - } - boolean enabled() - { - return Globals.AppNeedsTwoButtonMouse; - } - void run (final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.rightclick_none), - p.getResources().getString(R.string.rightclick_multitouch), - p.getResources().getString(R.string.rightclick_pressure), - p.getResources().getString(R.string.rightclick_key), - p.getResources().getString(R.string.leftclick_timeout) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.rightclick_question); - builder.setSingleChoiceItems(items, Globals.RightClickMethod, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.RightClickMethod = item; - dialog.dismiss(); - if( item == Mouse.RIGHT_CLICK_WITH_KEY ) - p.keyListener = new KeyRemapToolMouseClick(p, false); - else if( item == Mouse.RIGHT_CLICK_WITH_TIMEOUT ) - showRightClickTimeoutConfig(p); - else - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - static void showRightClickTimeoutConfig(final MainActivity p) { - final CharSequence[] items = { p.getResources().getString(R.string.leftclick_timeout_time_0), - p.getResources().getString(R.string.leftclick_timeout_time_1), - p.getResources().getString(R.string.leftclick_timeout_time_2), - p.getResources().getString(R.string.leftclick_timeout_time_3), - p.getResources().getString(R.string.leftclick_timeout_time_4) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.leftclick_timeout_time); - builder.setSingleChoiceItems(items, Globals.RightClickTimeout, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.RightClickTimeout = item; - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - public static class KeyRemapToolMouseClick implements KeyEventsListener - { - MainActivity p; - boolean leftClick; - public KeyRemapToolMouseClick(MainActivity _p, boolean leftClick) - { - p = _p; - p.setText(p.getResources().getString(R.string.remap_hwkeys_press)); - this.leftClick = leftClick; - } - - public void onKeyEvent(final int keyCode) - { - p.keyListener = null; - int keyIndex = keyCode; - if( keyIndex < 0 ) - keyIndex = 0; - if( keyIndex > SDL_Keys.JAVA_KEYCODE_LAST ) - keyIndex = 0; - - if( leftClick ) - Globals.LeftClickKey = keyIndex; - else - Globals.RightClickKey = keyIndex; - - goBack(p); - } - } - - static class AdditionalMouseConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.pointandclick_question); - } - void run (final MainActivity p) - { - CharSequence[] items = { - p.getResources().getString(R.string.pointandclick_joystickmouse), - p.getResources().getString(R.string.click_with_dpadcenter), - p.getResources().getString(R.string.pointandclick_relative) - }; - - boolean defaults[] = { - Globals.MoveMouseWithJoystick, - Globals.ClickMouseWithDpad, - Globals.RelativeMouseMovement - }; - - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.pointandclick_question)); - builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() - { - public void onClick(DialogInterface dialog, int item, boolean isChecked) - { - if( item == 0 ) - Globals.MoveMouseWithJoystick = isChecked; - if( item == 1 ) - Globals.ClickMouseWithDpad = isChecked; - if( item == 2 ) - Globals.RelativeMouseMovement = isChecked; - } - }); - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - if( Globals.RelativeMouseMovement ) - showRelativeMouseMovementConfig(p); - else - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - static void showRelativeMouseMovementConfig(final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.accel_veryslow), - p.getResources().getString(R.string.accel_slow), - p.getResources().getString(R.string.accel_medium), - p.getResources().getString(R.string.accel_fast), - p.getResources().getString(R.string.accel_veryfast) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.pointandclick_relative_speed); - builder.setSingleChoiceItems(items, Globals.RelativeMouseMovementSpeed, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.RelativeMouseMovementSpeed = item; - - dialog.dismiss(); - showRelativeMouseMovementConfig1(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - static void showRelativeMouseMovementConfig1(final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.none), - p.getResources().getString(R.string.accel_slow), - p.getResources().getString(R.string.accel_medium), - p.getResources().getString(R.string.accel_fast) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.pointandclick_relative_accel); - builder.setSingleChoiceItems(items, Globals.RelativeMouseMovementAccel, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.RelativeMouseMovementAccel = item; - - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class JoystickMouseConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.pointandclick_joystickmousespeed); - } - boolean enabled() - { - return Globals.MoveMouseWithJoystick; - }; - void run (final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.accel_slow), - p.getResources().getString(R.string.accel_medium), - p.getResources().getString(R.string.accel_fast) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.pointandclick_joystickmousespeed); - builder.setSingleChoiceItems(items, Globals.MoveMouseWithJoystickSpeed, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.MoveMouseWithJoystickSpeed = item; - - dialog.dismiss(); - showJoystickMouseAccelConfig(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - static void showJoystickMouseAccelConfig(final MainActivity p) - { - final CharSequence[] items = { p.getResources().getString(R.string.none), - p.getResources().getString(R.string.accel_slow), - p.getResources().getString(R.string.accel_medium), - p.getResources().getString(R.string.accel_fast) }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.pointandclick_joystickmouseaccel); - builder.setSingleChoiceItems(items, Globals.MoveMouseWithJoystickAccel, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.MoveMouseWithJoystickAccel = item; - - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - public interface TouchEventsListener - { - public void onTouchEvent(final MotionEvent ev); - } - - public interface KeyEventsListener - { - public void onKeyEvent(final int keyCode); - } - - static class TouchPressureMeasurementTool extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.measurepressure); - } - boolean enabled() - { - return Globals.RightClickMethod == Mouse.RIGHT_CLICK_WITH_PRESSURE || - Globals.LeftClickMethod == Mouse.LEFT_CLICK_WITH_PRESSURE; - }; - void run (final MainActivity p) - { - p.setText(p.getResources().getString(R.string.measurepressure_touchplease)); - p.touchListener = new TouchMeasurementTool(p); - } - - public static class TouchMeasurementTool implements TouchEventsListener - { - MainActivity p; - ArrayList force = new ArrayList(); - ArrayList radius = new ArrayList(); - static final int maxEventAmount = 100; - - public TouchMeasurementTool(MainActivity _p) - { - p = _p; - } - - public void onTouchEvent(final MotionEvent ev) - { - force.add(new Integer((int)(ev.getPressure() * 1000.0))); - radius.add(new Integer((int)(ev.getSize() * 1000.0))); - p.setText(p.getResources().getString(R.string.measurepressure_response, force.get(force.size()-1), radius.get(radius.size()-1))); - try { - Thread.sleep(10L); - } catch (InterruptedException e) { } - - if( force.size() >= maxEventAmount ) - { - p.touchListener = null; - Globals.ClickScreenPressure = getAverageForce(); - Globals.ClickScreenTouchspotSize = getAverageRadius(); - Log.i("SDL", "SDL: measured average force " + Globals.ClickScreenPressure + " radius " + Globals.ClickScreenTouchspotSize); - goBack(p); - } - } - - int getAverageForce() - { - int avg = 0; - for(Integer f: force) - { - avg += f; - } - return avg / force.size(); - } - int getAverageRadius() - { - int avg = 0; - for(Integer r: radius) - { - avg += r; - } - return avg / radius.size(); - } - } - } - - static class RemapHwKeysConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.remap_hwkeys); - } - //boolean enabled() { return true; }; - void run (final MainActivity p) - { - p.setText(p.getResources().getString(R.string.remap_hwkeys_press)); - p.keyListener = new KeyRemapTool(p); - } - - public static class KeyRemapTool implements KeyEventsListener - { - MainActivity p; - public KeyRemapTool(MainActivity _p) - { - p = _p; - } - - public void onKeyEvent(final int keyCode) - { - p.keyListener = null; - int keyIndex = keyCode; - if( keyIndex < 0 ) - keyIndex = 0; - if( keyIndex > SDL_Keys.JAVA_KEYCODE_LAST ) - keyIndex = 0; - - final int KeyIndexFinal = keyIndex; - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.remap_hwkeys_select); - builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapHwKeycode[keyIndex]], new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.RemapHwKeycode[KeyIndexFinal] = SDL_Keys.namesSortedIdx[item]; - - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - } - - static class RemapScreenKbConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.remap_screenkb); - } - //boolean enabled() { return true; }; - void run (final MainActivity p) - { - CharSequence[] items = { - p.getResources().getString(R.string.remap_screenkb_joystick), - p.getResources().getString(R.string.remap_screenkb_button_text), - p.getResources().getString(R.string.remap_screenkb_button) + " 1", - p.getResources().getString(R.string.remap_screenkb_button) + " 2", - p.getResources().getString(R.string.remap_screenkb_button) + " 3", - p.getResources().getString(R.string.remap_screenkb_button) + " 4", - p.getResources().getString(R.string.remap_screenkb_button) + " 5", - p.getResources().getString(R.string.remap_screenkb_button) + " 6", - }; - - boolean defaults[] = Arrays.copyOf(Globals.ScreenKbControlsShown, Globals.ScreenKbControlsShown.length); - if( Globals.AppUsesSecondJoystick ) - { - items = Arrays.copyOf(items, items.length + 1); - items[items.length - 1] = p.getResources().getString(R.string.remap_screenkb_joystick) + " 2"; - defaults = Arrays.copyOf(defaults, defaults.length + 1); - defaults[defaults.length - 1] = true; - } - - for( int i = 0; i < Math.min(6, Globals.AppTouchscreenKeyboardKeysNames.length); i++ ) - items[i+2] = items[i+2] + " - " + Globals.AppTouchscreenKeyboardKeysNames[i]; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.remap_screenkb)); - builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() - { - public void onClick(DialogInterface dialog, int item, boolean isChecked) - { - Globals.ScreenKbControlsShown[item] = isChecked; - } - }); - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - showRemapScreenKbConfig2(p, 0); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - static void showRemapScreenKbConfig2(final MainActivity p, final int currentButton) - { - CharSequence[] items = { - p.getResources().getString(R.string.remap_screenkb_button) + " 1", - p.getResources().getString(R.string.remap_screenkb_button) + " 2", - p.getResources().getString(R.string.remap_screenkb_button) + " 3", - p.getResources().getString(R.string.remap_screenkb_button) + " 4", - p.getResources().getString(R.string.remap_screenkb_button) + " 5", - p.getResources().getString(R.string.remap_screenkb_button) + " 6", - }; - - for( int i = 0; i < Math.min(6, Globals.AppTouchscreenKeyboardKeysNames.length); i++ ) - items[i] = items[i] + " - " + Globals.AppTouchscreenKeyboardKeysNames[i]; - - if( currentButton >= Globals.RemapScreenKbKeycode.length ) - { - goBack(p); - return; - } - if( ! Globals.ScreenKbControlsShown[currentButton + 2] ) - { - showRemapScreenKbConfig2(p, currentButton + 1); - return; - } - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(items[currentButton]); - builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapScreenKbKeycode[currentButton]], new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.RemapScreenKbKeycode[currentButton] = SDL_Keys.namesSortedIdx[item]; - - dialog.dismiss(); - showRemapScreenKbConfig2(p, currentButton + 1); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class ScreenGesturesConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.remap_screenkb_button_gestures); - } - //boolean enabled() { return true; }; - void run (final MainActivity p) - { - CharSequence[] items = { - p.getResources().getString(R.string.remap_screenkb_button_zoomin), - p.getResources().getString(R.string.remap_screenkb_button_zoomout), - p.getResources().getString(R.string.remap_screenkb_button_rotateleft), - p.getResources().getString(R.string.remap_screenkb_button_rotateright), - }; - - boolean defaults[] = { - Globals.MultitouchGesturesUsed[0], - Globals.MultitouchGesturesUsed[1], - Globals.MultitouchGesturesUsed[2], - Globals.MultitouchGesturesUsed[3], - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.remap_screenkb_button_gestures)); - builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() - { - public void onClick(DialogInterface dialog, int item, boolean isChecked) - { - Globals.MultitouchGesturesUsed[item] = isChecked; - } - }); - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - showScreenGesturesConfig2(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - static void showScreenGesturesConfig2(final MainActivity p) - { - final CharSequence[] items = { - p.getResources().getString(R.string.accel_slow), - p.getResources().getString(R.string.accel_medium), - p.getResources().getString(R.string.accel_fast), - p.getResources().getString(R.string.accel_veryfast) - }; - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(R.string.remap_screenkb_button_gestures_sensitivity); - builder.setSingleChoiceItems(items, Globals.MultitouchGestureSensitivity, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.MultitouchGestureSensitivity = item; - - dialog.dismiss(); - showScreenGesturesConfig3(p, 0); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - static void showScreenGesturesConfig3(final MainActivity p, final int currentButton) - { - CharSequence[] items = { - p.getResources().getString(R.string.remap_screenkb_button_zoomin), - p.getResources().getString(R.string.remap_screenkb_button_zoomout), - p.getResources().getString(R.string.remap_screenkb_button_rotateleft), - p.getResources().getString(R.string.remap_screenkb_button_rotateright), - }; - - if( currentButton >= Globals.RemapMultitouchGestureKeycode.length ) - { - goBack(p); - return; - } - if( ! Globals.MultitouchGesturesUsed[currentButton] ) - { - showScreenGesturesConfig3(p, currentButton + 1); - return; - } - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(items[currentButton]); - builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapMultitouchGestureKeycode[currentButton]], new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - Globals.RemapMultitouchGestureKeycode[currentButton] = SDL_Keys.namesSortedIdx[item]; - - dialog.dismiss(); - showScreenGesturesConfig3(p, currentButton + 1); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class CalibrateTouchscreenMenu extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.calibrate_touchscreen); - } - //boolean enabled() { return true; }; - void run (final MainActivity p) - { - p.setText(p.getResources().getString(R.string.calibrate_touchscreen_touch)); - Globals.TouchscreenCalibration[0] = 0; - Globals.TouchscreenCalibration[1] = 0; - Globals.TouchscreenCalibration[2] = 0; - Globals.TouchscreenCalibration[3] = 0; - ScreenEdgesCalibrationTool tool = new ScreenEdgesCalibrationTool(p); - p.touchListener = tool; - p.keyListener = tool; - } - - static class ScreenEdgesCalibrationTool implements TouchEventsListener, KeyEventsListener - { - MainActivity p; - ImageView img; - Bitmap bmp; - - public ScreenEdgesCalibrationTool(MainActivity _p) - { - p = _p; - img = new ImageView(p); - img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); - img.setScaleType(ImageView.ScaleType.MATRIX); - bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate ); - img.setImageBitmap(bmp); - Matrix m = new Matrix(); - RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); - RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], - Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - img.setImageMatrix(m); - p.getVideoLayout().addView(img); - } - - public void onTouchEvent(final MotionEvent ev) - { - if( Globals.TouchscreenCalibration[0] == Globals.TouchscreenCalibration[1] && - Globals.TouchscreenCalibration[1] == Globals.TouchscreenCalibration[2] && - Globals.TouchscreenCalibration[2] == Globals.TouchscreenCalibration[3] ) - { - Globals.TouchscreenCalibration[0] = (int)ev.getX(); - Globals.TouchscreenCalibration[1] = (int)ev.getY(); - Globals.TouchscreenCalibration[2] = (int)ev.getX(); - Globals.TouchscreenCalibration[3] = (int)ev.getY(); - } - if( ev.getX() < Globals.TouchscreenCalibration[0] ) - Globals.TouchscreenCalibration[0] = (int)ev.getX(); - if( ev.getY() < Globals.TouchscreenCalibration[1] ) - Globals.TouchscreenCalibration[1] = (int)ev.getY(); - if( ev.getX() > Globals.TouchscreenCalibration[2] ) - Globals.TouchscreenCalibration[2] = (int)ev.getX(); - if( ev.getY() > Globals.TouchscreenCalibration[3] ) - Globals.TouchscreenCalibration[3] = (int)ev.getY(); - Matrix m = new Matrix(); - RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); - RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], - Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - img.setImageMatrix(m); - } - - public void onKeyEvent(final int keyCode) - { - p.touchListener = null; - p.keyListener = null; - p.getVideoLayout().removeView(img); - goBack(p); - } - } - } - - static class CustomizeScreenKbLayout extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.screenkb_custom_layout); - } - //boolean enabled() { return true; }; - void run (final MainActivity p) - { - p.setText(p.getResources().getString(R.string.screenkb_custom_layout_help)); - CustomizeScreenKbLayoutTool tool = new CustomizeScreenKbLayoutTool(p); - p.touchListener = tool; - p.keyListener = tool; - Globals.TouchscreenKeyboardSize = Globals.TOUCHSCREEN_KEYBOARD_CUSTOM; - } - - static class CustomizeScreenKbLayoutTool implements TouchEventsListener, KeyEventsListener - { - MainActivity p; - FrameLayout layout = null; - ImageView imgs[] = new ImageView[Globals.ScreenKbControlsLayout.length]; - Bitmap bmps[] = new Bitmap[Globals.ScreenKbControlsLayout.length]; - ImageView boundary = null; - Bitmap boundaryBmp = null; - int currentButton = 0; - int buttons[] = { - R.drawable.dpad, - R.drawable.keyboard, - R.drawable.b1, - R.drawable.b2, - R.drawable.b3, - R.drawable.b4, - R.drawable.b5, - R.drawable.b6, - R.drawable.dpad - }; - int oldX = 0, oldY = 0; - boolean resizing = false; - - public CustomizeScreenKbLayoutTool(MainActivity _p) - { - p = _p; - layout = new FrameLayout(p); - p.getVideoLayout().addView(layout); - boundary = new ImageView(p); - boundary.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); - boundary.setScaleType(ImageView.ScaleType.MATRIX); - boundaryBmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.rectangle ); - boundary.setImageBitmap(boundaryBmp); - layout.addView(boundary); - currentButton = -1; - if( Globals.TouchscreenKeyboardTheme == 2 ) - { - buttons = new int[] { - R.drawable.sun_dpad, - R.drawable.sun_keyboard, - R.drawable.sun_b1, - R.drawable.sun_b2, - R.drawable.sun_b3, - R.drawable.sun_b4, - R.drawable.sun_b5, - R.drawable.sun_b6, - R.drawable.sun_dpad - }; - } - - int displayX = 800; - int displayY = 480; - try { - DisplayMetrics dm = new DisplayMetrics(); - p.getWindowManager().getDefaultDisplay().getMetrics(dm); - displayX = dm.widthPixels; - displayY = dm.heightPixels; - } catch (Exception eeeee) {} - - for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) - { - if( ! Globals.ScreenKbControlsShown[i] ) - continue; - if( currentButton == -1 ) - currentButton = i; - //Log.i("SDL", "Screen kb button " + i + " coords " + Globals.ScreenKbControlsLayout[i][0] + ":" + Globals.ScreenKbControlsLayout[i][1] + ":" + Globals.ScreenKbControlsLayout[i][2] + ":" + Globals.ScreenKbControlsLayout[i][3] ); - // Check if the button is off screen edge or shrunk to zero - if( Globals.ScreenKbControlsLayout[i][0] > Globals.ScreenKbControlsLayout[i][2] - displayY/12 ) - Globals.ScreenKbControlsLayout[i][0] = Globals.ScreenKbControlsLayout[i][2] - displayY/12; - if( Globals.ScreenKbControlsLayout[i][1] > Globals.ScreenKbControlsLayout[i][3] - displayY/12 ) - Globals.ScreenKbControlsLayout[i][1] = Globals.ScreenKbControlsLayout[i][3] - displayY/12; - if( Globals.ScreenKbControlsLayout[i][0] < Globals.ScreenKbControlsLayout[i][2] - displayY*2/3 ) - Globals.ScreenKbControlsLayout[i][0] = Globals.ScreenKbControlsLayout[i][2] - displayY*2/3; - if( Globals.ScreenKbControlsLayout[i][1] < Globals.ScreenKbControlsLayout[i][3] - displayY*2/3 ) - Globals.ScreenKbControlsLayout[i][1] = Globals.ScreenKbControlsLayout[i][3] - displayY*2/3; - if( Globals.ScreenKbControlsLayout[i][0] < 0 ) - { - Globals.ScreenKbControlsLayout[i][2] += -Globals.ScreenKbControlsLayout[i][0]; - Globals.ScreenKbControlsLayout[i][0] = 0; - } - if( Globals.ScreenKbControlsLayout[i][2] > displayX ) - { - Globals.ScreenKbControlsLayout[i][0] -= Globals.ScreenKbControlsLayout[i][2] - displayX; - Globals.ScreenKbControlsLayout[i][2] = displayX; - } - if( Globals.ScreenKbControlsLayout[i][1] < 0 ) - { - Globals.ScreenKbControlsLayout[i][3] += -Globals.ScreenKbControlsLayout[i][1]; - Globals.ScreenKbControlsLayout[i][1] = 0; - } - if( Globals.ScreenKbControlsLayout[i][3] > displayY ) - { - Globals.ScreenKbControlsLayout[i][1] -= Globals.ScreenKbControlsLayout[i][3] - displayY; - Globals.ScreenKbControlsLayout[i][3] = displayY; - } - //Log.i("SDL", "After bounds check coords " + Globals.ScreenKbControlsLayout[i][0] + ":" + Globals.ScreenKbControlsLayout[i][1] + ":" + Globals.ScreenKbControlsLayout[i][2] + ":" + Globals.ScreenKbControlsLayout[i][3] ); - - imgs[i] = new ImageView(p); - imgs[i].setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); - imgs[i].setScaleType(ImageView.ScaleType.MATRIX); - bmps[i] = BitmapFactory.decodeResource( p.getResources(), buttons[i] ); - imgs[i].setImageBitmap(bmps[i]); - imgs[i].setAlpha(128); - layout.addView(imgs[i]); - Matrix m = new Matrix(); - RectF src = new RectF(0, 0, bmps[i].getWidth(), bmps[i].getHeight()); - RectF dst = new RectF(Globals.ScreenKbControlsLayout[i][0], Globals.ScreenKbControlsLayout[i][1], - Globals.ScreenKbControlsLayout[i][2], Globals.ScreenKbControlsLayout[i][3]); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - imgs[i].setImageMatrix(m); - } - boundary.bringToFront(); - if( currentButton == -1 ) - onKeyEvent( KeyEvent.KEYCODE_BACK ); // All buttons disabled - do not show anything - else - setupButton(currentButton); - } - - void setupButton(int i) - { - Matrix m = new Matrix(); - RectF src = new RectF(0, 0, bmps[i].getWidth(), bmps[i].getHeight()); - RectF dst = new RectF(Globals.ScreenKbControlsLayout[i][0], Globals.ScreenKbControlsLayout[i][1], - Globals.ScreenKbControlsLayout[i][2], Globals.ScreenKbControlsLayout[i][3]); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - imgs[i].setImageMatrix(m); - m = new Matrix(); - src = new RectF(0, 0, boundaryBmp.getWidth(), boundaryBmp.getHeight()); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - boundary.setImageMatrix(m); - String buttonText = ""; - if( i >= 2 && i <= 7 ) - buttonText = p.getResources().getString(R.string.remap_screenkb_button) + (i - 2); - if( i >= 2 && i - 2 < Globals.AppTouchscreenKeyboardKeysNames.length ) - buttonText = Globals.AppTouchscreenKeyboardKeysNames[i - 2]; - if( i == 0 ) - buttonText = "Joystick"; - if( i == 1 ) - buttonText = "Text input"; - if( i == 8 ) - buttonText = "Joystick 2"; - p.setText(p.getResources().getString(R.string.screenkb_custom_layout_help) + "\n" + buttonText.replace("_", " ")); - } - - public void onTouchEvent(final MotionEvent ev) - { - if( ev.getAction() == MotionEvent.ACTION_DOWN ) - { - oldX = (int)ev.getX(); - oldY = (int)ev.getY(); - resizing = true; - for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) - { - if( ! Globals.ScreenKbControlsShown[i] ) - continue; - if( Globals.ScreenKbControlsLayout[i][0] <= oldX && - Globals.ScreenKbControlsLayout[i][2] >= oldX && - Globals.ScreenKbControlsLayout[i][1] <= oldY && - Globals.ScreenKbControlsLayout[i][3] >= oldY ) - { - currentButton = i; - setupButton(currentButton); - resizing = false; - break; - } - } - } - if( ev.getAction() == MotionEvent.ACTION_MOVE ) - { - int dx = (int)ev.getX() - oldX; - int dy = (int)ev.getY() - oldY; - if( resizing ) - { - // Resize slowly, with 1/3 of movement speed - dx /= 6; - dy /= 6; - if( Globals.ScreenKbControlsLayout[currentButton][0] <= Globals.ScreenKbControlsLayout[currentButton][2] + dx*2 ) - { - Globals.ScreenKbControlsLayout[currentButton][0] -= dx; - Globals.ScreenKbControlsLayout[currentButton][2] += dx; - } - if( Globals.ScreenKbControlsLayout[currentButton][1] <= Globals.ScreenKbControlsLayout[currentButton][3] + dy*2 ) - { - Globals.ScreenKbControlsLayout[currentButton][1] += dy; - Globals.ScreenKbControlsLayout[currentButton][3] -= dy; - } - dx *= 6; - dy *= 6; - } - else - { - Globals.ScreenKbControlsLayout[currentButton][0] += dx; - Globals.ScreenKbControlsLayout[currentButton][2] += dx; - Globals.ScreenKbControlsLayout[currentButton][1] += dy; - Globals.ScreenKbControlsLayout[currentButton][3] += dy; - } - oldX += dx; - oldY += dy; - Matrix m = new Matrix(); - RectF src = new RectF(0, 0, bmps[currentButton].getWidth(), bmps[currentButton].getHeight()); - RectF dst = new RectF(Globals.ScreenKbControlsLayout[currentButton][0], Globals.ScreenKbControlsLayout[currentButton][1], - Globals.ScreenKbControlsLayout[currentButton][2], Globals.ScreenKbControlsLayout[currentButton][3]); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - imgs[currentButton].setImageMatrix(m); - m = new Matrix(); - src = new RectF(0, 0, boundaryBmp.getWidth(), boundaryBmp.getHeight()); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - boundary.setImageMatrix(m); - } - } - - public void onKeyEvent(final int keyCode) - { - if( keyCode == KeyEvent.KEYCODE_BACK ) - { - p.getVideoLayout().removeView(layout); - layout = null; - p.touchListener = null; - p.keyListener = null; - goBack(p); - } - } - } - } - - static class VideoSettingsConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.video); - } - //boolean enabled() { return true; }; - void run (final MainActivity p) - { - CharSequence[] items = { - p.getResources().getString(R.string.pointandclick_keepaspectratio), - p.getResources().getString(R.string.video_smooth) - }; - boolean defaults[] = { - Globals.KeepAspectRatio, - Globals.VideoLinearFilter - }; - - if(Globals.SwVideoMode && !Globals.CompatibilityHacksVideo) - { - CharSequence[] items2 = { - p.getResources().getString(R.string.pointandclick_keepaspectratio), - p.getResources().getString(R.string.video_smooth), - p.getResources().getString(R.string.video_separatethread), - }; - boolean defaults2[] = { - Globals.KeepAspectRatio, - Globals.VideoLinearFilter, - Globals.MultiThreadedVideo - }; - items = items2; - defaults = defaults2; - } - - if(Globals.Using_SDL_1_3) - { - CharSequence[] items2 = { - p.getResources().getString(R.string.pointandclick_keepaspectratio), - }; - boolean defaults2[] = { - Globals.KeepAspectRatio, - }; - items = items2; - defaults = defaults2; - } - - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.video)); - builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() - { - public void onClick(DialogInterface dialog, int item, boolean isChecked) - { - if( item == 0 ) - Globals.KeepAspectRatio = isChecked; - if( item == 1 ) - Globals.VideoLinearFilter = isChecked; - if( item == 2 ) - Globals.MultiThreadedVideo = isChecked; - } - }); - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class ShowReadme extends Menu - { - String title(final MainActivity p) - { - return "Readme"; - } - boolean enabled() - { - return true; - } - void run (final MainActivity p) - { - String readmes[] = Globals.ReadmeText.split("\\^"); - String lang = new String(Locale.getDefault().getLanguage()) + ":"; - String readme = readmes[0]; - String buttonName = "", buttonUrl = ""; - for( String r: readmes ) - { - if( r.startsWith(lang) ) - readme = r.substring(lang.length()); - if( r.startsWith("button:") ) - { - buttonName = r.substring("button:".length()); - if( buttonName.indexOf(":") != -1 ) - { - buttonUrl = buttonName.substring(buttonName.indexOf(":") + 1); - buttonName = buttonName.substring(0, buttonName.indexOf(":")); - } - } - } - readme = readme.trim(); - if( readme.length() <= 2 ) - { - goBack(p); - return; - } - TextView text = new TextView(p); - text.setMaxLines(1000); - text.setText(readme); - text.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.FILL_PARENT)); - AlertDialog.Builder builder = new AlertDialog.Builder(p); - ScrollView scroll = new ScrollView(p); - scroll.addView(text); - Button ok = new Button(p); - final AlertDialog alertDismiss[] = new AlertDialog[1]; - ok.setOnClickListener(new View.OnClickListener() - { - public void onClick(View v) - { - alertDismiss[0].cancel(); - } - }); - ok.setText(R.string.ok); - LinearLayout layout = new LinearLayout(p); - layout.setOrientation(LinearLayout.VERTICAL); - layout.addView(scroll); - layout.addView(ok); - if( buttonName.length() > 0 ) - { - Button cancel = new Button(p); - cancel.setText(buttonName); - final String url = buttonUrl; - cancel.setOnClickListener(new View.OnClickListener() - { - public void onClick(View v) - { - if( url.length() > 0 ) - { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - p.startActivity(i); - } - alertDismiss[0].cancel(); - System.exit(0); - } - }); - layout.addView(cancel); - } - builder.setView(layout); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alertDismiss[0] = alert; - alert.setOwnerActivity(p); - alert.show(); - } - } - - static class GyroscopeCalibration extends Menu implements SensorEventListener - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.calibrate_gyroscope); - } - boolean enabled() - { - return Globals.AppUsesGyroscope; - } - void run (final MainActivity p) - { - if( !Globals.AppUsesGyroscope || !AccelerometerReader.gyro.available(p) ) - { - if( Globals.AppUsesGyroscope ) - { - Toast toast = Toast.makeText(p, p.getResources().getString(R.string.calibrate_gyroscope_not_supported), Toast.LENGTH_LONG); - toast.show(); - } - goBack(p); - return; - } - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.calibrate_gyroscope)); - builder.setMessage(p.getResources().getString(R.string.calibrate_gyroscope_text)); - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - startCalibration(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - - ImageView img; - Bitmap bmp; - int numEvents; - MainActivity p; - - void startCalibration(final MainActivity _p) - { - p = _p; - img = new ImageView(p); - img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); - img.setScaleType(ImageView.ScaleType.MATRIX); - bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate ); - img.setImageBitmap(bmp); - Matrix m = new Matrix(); - RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); - RectF dst = new RectF( p.getVideoLayout().getWidth()/2 - 50, p.getVideoLayout().getHeight()/2 - 50, - p.getVideoLayout().getWidth()/2 + 50, p.getVideoLayout().getHeight()/2 + 50); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - img.setImageMatrix(m); - p.getVideoLayout().addView(img); - numEvents = 0; - AccelerometerReader.gyro.x1 = 100; - AccelerometerReader.gyro.x2 = -100; - AccelerometerReader.gyro.xc = 0; - AccelerometerReader.gyro.y1 = 100; - AccelerometerReader.gyro.y2 = -100; - AccelerometerReader.gyro.yc = 0; - AccelerometerReader.gyro.z1 = 100; - AccelerometerReader.gyro.z2 = -100; - AccelerometerReader.gyro.zc = 0; - AccelerometerReader.gyro.registerListener(p, this); - (new Thread(new Runnable() - { - public void run() - { - for(int count = 1; count < 10; count++) - { - p.setText("" + count + "0% ..."); - try { - Thread.sleep(500); - } catch( Exception e ) {} - } - finishCalibration(p); - } - } - )).start(); - } - - public void onSensorChanged(SensorEvent event) - { - gyroscopeEvent(event.values[0], event.values[1], event.values[2]); - } - public void onAccuracyChanged(Sensor s, int a) - { - } - void gyroscopeEvent(float x, float y, float z) - { - numEvents++; - AccelerometerReader.gyro.xc += x; - AccelerometerReader.gyro.yc += y; - AccelerometerReader.gyro.zc += z; - AccelerometerReader.gyro.x1 = Math.min(AccelerometerReader.gyro.x1, x * 1.1f); // Small safety bound coefficient - AccelerometerReader.gyro.x2 = Math.max(AccelerometerReader.gyro.x2, x * 1.1f); - AccelerometerReader.gyro.y1 = Math.min(AccelerometerReader.gyro.y1, y * 1.1f); - AccelerometerReader.gyro.y2 = Math.max(AccelerometerReader.gyro.y2, y * 1.1f); - AccelerometerReader.gyro.z1 = Math.min(AccelerometerReader.gyro.z1, z * 1.1f); - AccelerometerReader.gyro.z2 = Math.max(AccelerometerReader.gyro.z2, z * 1.1f); - final Matrix m = new Matrix(); - RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); - RectF dst = new RectF( x * 5000 + p.getVideoLayout().getWidth()/2 - 50, y * 5000 + p.getVideoLayout().getHeight()/2 - 50, - x * 5000 + p.getVideoLayout().getWidth()/2 + 50, y * 5000 + p.getVideoLayout().getHeight()/2 + 50); - m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); - p.runOnUiThread(new Runnable() - { - public void run() - { - img.setImageMatrix(m); - } - }); - } - void finishCalibration(final MainActivity p) - { - AccelerometerReader.gyro.unregisterListener(p, this); - try { - Thread.sleep(200); // Just in case we have pending events - } catch( Exception e ) {} - if( numEvents > 5 ) - { - AccelerometerReader.gyro.xc /= (float)numEvents; - AccelerometerReader.gyro.yc /= (float)numEvents; - AccelerometerReader.gyro.zc /= (float)numEvents; - } - p.runOnUiThread(new Runnable() - { - public void run() - { - p.getVideoLayout().removeView(img); - goBack(p); - } - }); - } - } - - static class ResetToDefaultsConfig extends Menu - { - String title(final MainActivity p) - { - return p.getResources().getString(R.string.reset_config); - } - boolean enabled() - { - return true; - } - void run (final MainActivity p) - { - AlertDialog.Builder builder = new AlertDialog.Builder(p); - builder.setTitle(p.getResources().getString(R.string.reset_config_ask)); - builder.setMessage(p.getResources().getString(R.string.reset_config_ask)); - - builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - DeleteSdlConfigOnUpgradeAndRestart(p); // Never returns - dialog.dismiss(); - goBack(p); - } - }); - builder.setNegativeButton(p.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int item) - { - dialog.dismiss(); - goBack(p); - } - }); - builder.setOnCancelListener(new DialogInterface.OnCancelListener() - { - public void onCancel(DialogInterface dialog) - { - goBack(p); - } - }); - AlertDialog alert = builder.create(); - alert.setOwnerActivity(p); - alert.show(); - } - } - - // =============================================================================================== public static boolean deleteRecursively(File dir) @@ -2690,7 +462,6 @@ class Settings System.exit(0); } - // =============================================================================================== static void Apply(Activity p) @@ -2932,7 +703,7 @@ class Settings { public void onClick(DialogInterface dialog, int item) { - showConfig(p, true); + SettingsMenu.showConfig(p, true); return; } }); diff --git a/project/java/SettingsMenu.java b/project/java/SettingsMenu.java new file mode 100644 index 000000000..544eb0977 --- /dev/null +++ b/project/java/SettingsMenu.java @@ -0,0 +1,257 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenu +{ + public static abstract class Menu + { + // Should be overridden by children + abstract void run(final MainActivity p); + abstract String title(final MainActivity p); + boolean enabled() + { + return true; + } + // Should not be overridden + boolean enabledOrHidden() + { + for( Menu m: Globals.HiddenMenuOptions ) + { + if( m.getClass().getName().equals( this.getClass().getName() ) ) + return false; + } + return enabled(); + } + void showMenuOptionsList(final MainActivity p, final Menu[] list) + { + menuStack.add(this); + ArrayList items = new ArrayList (); + for( Menu m: list ) + { + if(m.enabledOrHidden()) + items.add(m.title(p)); + } + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(title(p)); + builder.setItems(items.toArray(new CharSequence[0]), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + int selected = 0; + + for( Menu m: list ) + { + if(m.enabledOrHidden()) + { + if( selected == item ) + { + m.run(p); + return; + } + selected ++; + } + } + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBackOuterMenu(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static ArrayList menuStack = new ArrayList (); + + public static void showConfig(final MainActivity p, final boolean firstStart) + { + Settings.settingsChanged = true; + if( Globals.OptionalDataDownload == null ) + { + String downloads[] = Globals.DataDownloadUrl; + Globals.OptionalDataDownload = new boolean[downloads.length]; + boolean oldFormat = true; + for( int i = 0; i < downloads.length; i++ ) + { + if( downloads[i].indexOf("!") == 0 ) + { + Globals.OptionalDataDownload[i] = true; + oldFormat = false; + } + } + if( oldFormat ) + Globals.OptionalDataDownload[0] = true; + } + + if(!firstStart) + new MainMenu().run(p); + else + { + if( Globals.StartupMenuButtonTimeout > 0 ) // If we did not disable startup menu altogether + { + for( Menu m: Globals.FirstStartMenuOptions ) + { + boolean hidden = false; + for( Menu m1: Globals.HiddenMenuOptions ) + { + if( m1.getClass().getName().equals( m.getClass().getName() ) ) + hidden = true; + } + if( ! hidden ) + menuStack.add(0, m); + } + } + goBack(p); + } + } + + static void goBack(final MainActivity p) + { + if(menuStack.isEmpty()) + { + Settings.Save(p); + p.startDownloader(); + } + else + { + Menu c = menuStack.remove(menuStack.size() - 1); + c.run(p); + } + } + + static void goBackOuterMenu(final MainActivity p) + { + if(!menuStack.isEmpty()) + menuStack.remove(menuStack.size() - 1); + goBack(p); + } + + static class OkButton extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.ok); + } + void run (final MainActivity p) + { + goBackOuterMenu(p); + } + } + + static class DummyMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.ok); + } + void run (final MainActivity p) + { + goBack(p); + } + } + + static class MainMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.device_config); + } + void run (final MainActivity p) + { + Menu options[] = + { + new SettingsMenuMisc.DownloadConfig(), + new SettingsMenuMisc.OptionalDownloadConfig(false), + new SettingsMenuKeyboard.KeyboardConfigMainMenu(), + new SettingsMenuMouse.MouseConfigMainMenu(), + new SettingsMenuMisc.GyroscopeCalibration(), + new SettingsMenuMisc.AudioConfig(), + new SettingsMenuKeyboard.RemapHwKeysConfig(), + new SettingsMenuKeyboard.ScreenGesturesConfig(), + new SettingsMenuMisc.VideoSettingsConfig(), + new SettingsMenuMisc.ResetToDefaultsConfig(), + new OkButton(), + }; + showMenuOptionsList(p, options); + } + } +} diff --git a/project/java/SettingsMenuKeyboard.java b/project/java/SettingsMenuKeyboard.java new file mode 100644 index 000000000..179d24c88 --- /dev/null +++ b/project/java/SettingsMenuKeyboard.java @@ -0,0 +1,801 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenuKeyboard extends SettingsMenu +{ + static class KeyboardConfigMainMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb); + } + boolean enabled() + { + return Globals.UseTouchscreenKeyboard; + } + void run (final MainActivity p) + { + Menu options[] = + { + new ScreenKeyboardThemeConfig(), + new ScreenKeyboardSizeConfig(), + new ScreenKeyboardDrawSizeConfig(), + new ScreenKeyboardTransparencyConfig(), + new RemapScreenKbConfig(), + new CustomizeScreenKbLayout(), + new OkButton(), + }; + showMenuOptionsList(p, options); + } + } + + static class ScreenKeyboardSizeConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_size); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_large), + p.getResources().getString(R.string.controls_screenkb_medium), + p.getResources().getString(R.string.controls_screenkb_small), + p.getResources().getString(R.string.controls_screenkb_tiny), + p.getResources().getString(R.string.controls_screenkb_custom) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_size)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardSize, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardSize = item; + dialog.dismiss(); + if( Globals.TouchscreenKeyboardSize == Globals.TOUCHSCREEN_KEYBOARD_CUSTOM ) + new CustomizeScreenKbLayout().run(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenKeyboardDrawSizeConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_drawsize); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_large), + p.getResources().getString(R.string.controls_screenkb_medium), + p.getResources().getString(R.string.controls_screenkb_small), + p.getResources().getString(R.string.controls_screenkb_tiny) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_drawsize)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardDrawSize, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardDrawSize = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenKeyboardThemeConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_theme); + } + void run (final MainActivity p) + { + final CharSequence[] items = { + p.getResources().getString(R.string.controls_screenkb_by, "Ultimate Droid", "Sean Stieber"), + p.getResources().getString(R.string.controls_screenkb_by, "Simple Theme", "Beholder"), + p.getResources().getString(R.string.controls_screenkb_by, "Sun", "Sirea") + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_theme)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardTheme, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardTheme = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenKeyboardTransparencyConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.controls_screenkb_transparency); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.controls_screenkb_trans_0), + p.getResources().getString(R.string.controls_screenkb_trans_1), + p.getResources().getString(R.string.controls_screenkb_trans_2), + p.getResources().getString(R.string.controls_screenkb_trans_3), + p.getResources().getString(R.string.controls_screenkb_trans_4) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.controls_screenkb_transparency)); + builder.setSingleChoiceItems(items, Globals.TouchscreenKeyboardTransparency, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.TouchscreenKeyboardTransparency = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class RemapHwKeysConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.remap_hwkeys); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.remap_hwkeys_press)); + p.keyListener = new KeyRemapTool(p); + } + + public static class KeyRemapTool implements MainActivity.KeyEventsListener + { + MainActivity p; + public KeyRemapTool(MainActivity _p) + { + p = _p; + } + + public void onKeyEvent(final int keyCode) + { + p.keyListener = null; + int keyIndex = keyCode; + if( keyIndex < 0 ) + keyIndex = 0; + if( keyIndex > SDL_Keys.JAVA_KEYCODE_LAST ) + keyIndex = 0; + + final int KeyIndexFinal = keyIndex; + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.remap_hwkeys_select); + builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapHwKeycode[keyIndex]], new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RemapHwKeycode[KeyIndexFinal] = SDL_Keys.namesSortedIdx[item]; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + } + + static class RemapScreenKbConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.remap_screenkb); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_joystick), + p.getResources().getString(R.string.remap_screenkb_button_text), + p.getResources().getString(R.string.remap_screenkb_button) + " 1", + p.getResources().getString(R.string.remap_screenkb_button) + " 2", + p.getResources().getString(R.string.remap_screenkb_button) + " 3", + p.getResources().getString(R.string.remap_screenkb_button) + " 4", + p.getResources().getString(R.string.remap_screenkb_button) + " 5", + p.getResources().getString(R.string.remap_screenkb_button) + " 6", + }; + + boolean defaults[] = Arrays.copyOf(Globals.ScreenKbControlsShown, Globals.ScreenKbControlsShown.length); + if( Globals.AppUsesSecondJoystick ) + { + items = Arrays.copyOf(items, items.length + 1); + items[items.length - 1] = p.getResources().getString(R.string.remap_screenkb_joystick) + " 2"; + defaults = Arrays.copyOf(defaults, defaults.length + 1); + defaults[defaults.length - 1] = true; + } + + for( int i = 0; i < Math.min(6, Globals.AppTouchscreenKeyboardKeysNames.length); i++ ) + items[i+2] = items[i+2] + " - " + Globals.AppTouchscreenKeyboardKeysNames[i]; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.remap_screenkb)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + Globals.ScreenKbControlsShown[item] = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + showRemapScreenKbConfig2(p, 0); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRemapScreenKbConfig2(final MainActivity p, final int currentButton) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_button) + " 1", + p.getResources().getString(R.string.remap_screenkb_button) + " 2", + p.getResources().getString(R.string.remap_screenkb_button) + " 3", + p.getResources().getString(R.string.remap_screenkb_button) + " 4", + p.getResources().getString(R.string.remap_screenkb_button) + " 5", + p.getResources().getString(R.string.remap_screenkb_button) + " 6", + }; + + for( int i = 0; i < Math.min(6, Globals.AppTouchscreenKeyboardKeysNames.length); i++ ) + items[i] = items[i] + " - " + Globals.AppTouchscreenKeyboardKeysNames[i]; + + if( currentButton >= Globals.RemapScreenKbKeycode.length ) + { + goBack(p); + return; + } + if( ! Globals.ScreenKbControlsShown[currentButton + 2] ) + { + showRemapScreenKbConfig2(p, currentButton + 1); + return; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(items[currentButton]); + builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapScreenKbKeycode[currentButton]], new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RemapScreenKbKeycode[currentButton] = SDL_Keys.namesSortedIdx[item]; + + dialog.dismiss(); + showRemapScreenKbConfig2(p, currentButton + 1); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ScreenGesturesConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.remap_screenkb_button_gestures); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_button_zoomin), + p.getResources().getString(R.string.remap_screenkb_button_zoomout), + p.getResources().getString(R.string.remap_screenkb_button_rotateleft), + p.getResources().getString(R.string.remap_screenkb_button_rotateright), + }; + + boolean defaults[] = { + Globals.MultitouchGesturesUsed[0], + Globals.MultitouchGesturesUsed[1], + Globals.MultitouchGesturesUsed[2], + Globals.MultitouchGesturesUsed[3], + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.remap_screenkb_button_gestures)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + Globals.MultitouchGesturesUsed[item] = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + showScreenGesturesConfig2(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showScreenGesturesConfig2(final MainActivity p) + { + final CharSequence[] items = { + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast), + p.getResources().getString(R.string.accel_veryfast) + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.remap_screenkb_button_gestures_sensitivity); + builder.setSingleChoiceItems(items, Globals.MultitouchGestureSensitivity, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.MultitouchGestureSensitivity = item; + + dialog.dismiss(); + showScreenGesturesConfig3(p, 0); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showScreenGesturesConfig3(final MainActivity p, final int currentButton) + { + CharSequence[] items = { + p.getResources().getString(R.string.remap_screenkb_button_zoomin), + p.getResources().getString(R.string.remap_screenkb_button_zoomout), + p.getResources().getString(R.string.remap_screenkb_button_rotateleft), + p.getResources().getString(R.string.remap_screenkb_button_rotateright), + }; + + if( currentButton >= Globals.RemapMultitouchGestureKeycode.length ) + { + goBack(p); + return; + } + if( ! Globals.MultitouchGesturesUsed[currentButton] ) + { + showScreenGesturesConfig3(p, currentButton + 1); + return; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(items[currentButton]); + builder.setSingleChoiceItems(SDL_Keys.namesSorted, SDL_Keys.namesSortedBackIdx[Globals.RemapMultitouchGestureKeycode[currentButton]], new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RemapMultitouchGestureKeycode[currentButton] = SDL_Keys.namesSortedIdx[item]; + + dialog.dismiss(); + showScreenGesturesConfig3(p, currentButton + 1); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class CustomizeScreenKbLayout extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.screenkb_custom_layout); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.screenkb_custom_layout_help)); + CustomizeScreenKbLayoutTool tool = new CustomizeScreenKbLayoutTool(p); + p.touchListener = tool; + p.keyListener = tool; + Globals.TouchscreenKeyboardSize = Globals.TOUCHSCREEN_KEYBOARD_CUSTOM; + } + + static class CustomizeScreenKbLayoutTool implements MainActivity.TouchEventsListener, MainActivity.KeyEventsListener + { + MainActivity p; + FrameLayout layout = null; + ImageView imgs[] = new ImageView[Globals.ScreenKbControlsLayout.length]; + Bitmap bmps[] = new Bitmap[Globals.ScreenKbControlsLayout.length]; + ImageView boundary = null; + Bitmap boundaryBmp = null; + int currentButton = 0; + int buttons[] = { + R.drawable.dpad, + R.drawable.keyboard, + R.drawable.b1, + R.drawable.b2, + R.drawable.b3, + R.drawable.b4, + R.drawable.b5, + R.drawable.b6, + R.drawable.dpad + }; + int oldX = 0, oldY = 0; + boolean resizing = false; + + public CustomizeScreenKbLayoutTool(MainActivity _p) + { + p = _p; + layout = new FrameLayout(p); + p.getVideoLayout().addView(layout); + boundary = new ImageView(p); + boundary.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + boundary.setScaleType(ImageView.ScaleType.MATRIX); + boundaryBmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.rectangle ); + boundary.setImageBitmap(boundaryBmp); + layout.addView(boundary); + currentButton = -1; + if( Globals.TouchscreenKeyboardTheme == 2 ) + { + buttons = new int[] { + R.drawable.sun_dpad, + R.drawable.sun_keyboard, + R.drawable.sun_b1, + R.drawable.sun_b2, + R.drawable.sun_b3, + R.drawable.sun_b4, + R.drawable.sun_b5, + R.drawable.sun_b6, + R.drawable.sun_dpad + }; + } + + int displayX = 800; + int displayY = 480; + try { + DisplayMetrics dm = new DisplayMetrics(); + p.getWindowManager().getDefaultDisplay().getMetrics(dm); + displayX = dm.widthPixels; + displayY = dm.heightPixels; + } catch (Exception eeeee) {} + + for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) + { + if( ! Globals.ScreenKbControlsShown[i] ) + continue; + if( currentButton == -1 ) + currentButton = i; + //Log.i("SDL", "Screen kb button " + i + " coords " + Globals.ScreenKbControlsLayout[i][0] + ":" + Globals.ScreenKbControlsLayout[i][1] + ":" + Globals.ScreenKbControlsLayout[i][2] + ":" + Globals.ScreenKbControlsLayout[i][3] ); + // Check if the button is off screen edge or shrunk to zero + if( Globals.ScreenKbControlsLayout[i][0] > Globals.ScreenKbControlsLayout[i][2] - displayY/12 ) + Globals.ScreenKbControlsLayout[i][0] = Globals.ScreenKbControlsLayout[i][2] - displayY/12; + if( Globals.ScreenKbControlsLayout[i][1] > Globals.ScreenKbControlsLayout[i][3] - displayY/12 ) + Globals.ScreenKbControlsLayout[i][1] = Globals.ScreenKbControlsLayout[i][3] - displayY/12; + if( Globals.ScreenKbControlsLayout[i][0] < Globals.ScreenKbControlsLayout[i][2] - displayY*2/3 ) + Globals.ScreenKbControlsLayout[i][0] = Globals.ScreenKbControlsLayout[i][2] - displayY*2/3; + if( Globals.ScreenKbControlsLayout[i][1] < Globals.ScreenKbControlsLayout[i][3] - displayY*2/3 ) + Globals.ScreenKbControlsLayout[i][1] = Globals.ScreenKbControlsLayout[i][3] - displayY*2/3; + if( Globals.ScreenKbControlsLayout[i][0] < 0 ) + { + Globals.ScreenKbControlsLayout[i][2] += -Globals.ScreenKbControlsLayout[i][0]; + Globals.ScreenKbControlsLayout[i][0] = 0; + } + if( Globals.ScreenKbControlsLayout[i][2] > displayX ) + { + Globals.ScreenKbControlsLayout[i][0] -= Globals.ScreenKbControlsLayout[i][2] - displayX; + Globals.ScreenKbControlsLayout[i][2] = displayX; + } + if( Globals.ScreenKbControlsLayout[i][1] < 0 ) + { + Globals.ScreenKbControlsLayout[i][3] += -Globals.ScreenKbControlsLayout[i][1]; + Globals.ScreenKbControlsLayout[i][1] = 0; + } + if( Globals.ScreenKbControlsLayout[i][3] > displayY ) + { + Globals.ScreenKbControlsLayout[i][1] -= Globals.ScreenKbControlsLayout[i][3] - displayY; + Globals.ScreenKbControlsLayout[i][3] = displayY; + } + //Log.i("SDL", "After bounds check coords " + Globals.ScreenKbControlsLayout[i][0] + ":" + Globals.ScreenKbControlsLayout[i][1] + ":" + Globals.ScreenKbControlsLayout[i][2] + ":" + Globals.ScreenKbControlsLayout[i][3] ); + + imgs[i] = new ImageView(p); + imgs[i].setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + imgs[i].setScaleType(ImageView.ScaleType.MATRIX); + bmps[i] = BitmapFactory.decodeResource( p.getResources(), buttons[i] ); + imgs[i].setImageBitmap(bmps[i]); + imgs[i].setAlpha(128); + layout.addView(imgs[i]); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmps[i].getWidth(), bmps[i].getHeight()); + RectF dst = new RectF(Globals.ScreenKbControlsLayout[i][0], Globals.ScreenKbControlsLayout[i][1], + Globals.ScreenKbControlsLayout[i][2], Globals.ScreenKbControlsLayout[i][3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + imgs[i].setImageMatrix(m); + } + boundary.bringToFront(); + if( currentButton == -1 ) + onKeyEvent( KeyEvent.KEYCODE_BACK ); // All buttons disabled - do not show anything + else + setupButton(currentButton); + } + + void setupButton(int i) + { + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmps[i].getWidth(), bmps[i].getHeight()); + RectF dst = new RectF(Globals.ScreenKbControlsLayout[i][0], Globals.ScreenKbControlsLayout[i][1], + Globals.ScreenKbControlsLayout[i][2], Globals.ScreenKbControlsLayout[i][3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + imgs[i].setImageMatrix(m); + m = new Matrix(); + src = new RectF(0, 0, boundaryBmp.getWidth(), boundaryBmp.getHeight()); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + boundary.setImageMatrix(m); + String buttonText = ""; + if( i >= 2 && i <= 7 ) + buttonText = p.getResources().getString(R.string.remap_screenkb_button) + (i - 2); + if( i >= 2 && i - 2 < Globals.AppTouchscreenKeyboardKeysNames.length ) + buttonText = Globals.AppTouchscreenKeyboardKeysNames[i - 2]; + if( i == 0 ) + buttonText = "Joystick"; + if( i == 1 ) + buttonText = "Text input"; + if( i == 8 ) + buttonText = "Joystick 2"; + p.setText(p.getResources().getString(R.string.screenkb_custom_layout_help) + "\n" + buttonText.replace("_", " ")); + } + + public void onTouchEvent(final MotionEvent ev) + { + if( ev.getAction() == MotionEvent.ACTION_DOWN ) + { + oldX = (int)ev.getX(); + oldY = (int)ev.getY(); + resizing = true; + for( int i = 0; i < Globals.ScreenKbControlsLayout.length; i++ ) + { + if( ! Globals.ScreenKbControlsShown[i] ) + continue; + if( Globals.ScreenKbControlsLayout[i][0] <= oldX && + Globals.ScreenKbControlsLayout[i][2] >= oldX && + Globals.ScreenKbControlsLayout[i][1] <= oldY && + Globals.ScreenKbControlsLayout[i][3] >= oldY ) + { + currentButton = i; + setupButton(currentButton); + resizing = false; + break; + } + } + } + if( ev.getAction() == MotionEvent.ACTION_MOVE ) + { + int dx = (int)ev.getX() - oldX; + int dy = (int)ev.getY() - oldY; + if( resizing ) + { + // Resize slowly, with 1/3 of movement speed + dx /= 6; + dy /= 6; + if( Globals.ScreenKbControlsLayout[currentButton][0] <= Globals.ScreenKbControlsLayout[currentButton][2] + dx*2 ) + { + Globals.ScreenKbControlsLayout[currentButton][0] -= dx; + Globals.ScreenKbControlsLayout[currentButton][2] += dx; + } + if( Globals.ScreenKbControlsLayout[currentButton][1] <= Globals.ScreenKbControlsLayout[currentButton][3] + dy*2 ) + { + Globals.ScreenKbControlsLayout[currentButton][1] += dy; + Globals.ScreenKbControlsLayout[currentButton][3] -= dy; + } + dx *= 6; + dy *= 6; + } + else + { + Globals.ScreenKbControlsLayout[currentButton][0] += dx; + Globals.ScreenKbControlsLayout[currentButton][2] += dx; + Globals.ScreenKbControlsLayout[currentButton][1] += dy; + Globals.ScreenKbControlsLayout[currentButton][3] += dy; + } + oldX += dx; + oldY += dy; + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmps[currentButton].getWidth(), bmps[currentButton].getHeight()); + RectF dst = new RectF(Globals.ScreenKbControlsLayout[currentButton][0], Globals.ScreenKbControlsLayout[currentButton][1], + Globals.ScreenKbControlsLayout[currentButton][2], Globals.ScreenKbControlsLayout[currentButton][3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + imgs[currentButton].setImageMatrix(m); + m = new Matrix(); + src = new RectF(0, 0, boundaryBmp.getWidth(), boundaryBmp.getHeight()); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + boundary.setImageMatrix(m); + } + } + + public void onKeyEvent(final int keyCode) + { + if( keyCode == KeyEvent.KEYCODE_BACK ) + { + p.getVideoLayout().removeView(layout); + layout = null; + p.touchListener = null; + p.keyListener = null; + goBack(p); + } + } + } + } +} + diff --git a/project/java/SettingsMenuMisc.java b/project/java/SettingsMenuMisc.java new file mode 100644 index 000000000..1cfae7263 --- /dev/null +++ b/project/java/SettingsMenuMisc.java @@ -0,0 +1,701 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenuMisc extends SettingsMenu +{ + static class DownloadConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.storage_question); + } + void run (final MainActivity p) + { + long freeSdcard = 0; + long freePhone = 0; + try + { + StatFs sdcard = new StatFs(Environment.getExternalStorageDirectory().getPath()); + StatFs phone = new StatFs(Environment.getDataDirectory().getPath()); + freeSdcard = (long)sdcard.getAvailableBlocks() * sdcard.getBlockSize() / 1024 / 1024; + freePhone = (long)phone.getAvailableBlocks() * phone.getBlockSize() / 1024 / 1024; + } + catch(Exception e) {} + + final CharSequence[] items = { p.getResources().getString(R.string.storage_phone, freePhone), + p.getResources().getString(R.string.storage_sd, freeSdcard), + p.getResources().getString(R.string.storage_custom) }; + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.storage_question)); + builder.setSingleChoiceItems(items, Globals.DownloadToSdcard ? 1 : 0, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + + if( item == 2 ) + showCustomDownloadDirConfig(p); + else + { + Globals.DownloadToSdcard = (item != 0); + Globals.DataDir = Globals.DownloadToSdcard ? + Settings.SdcardAppPath.getPath(p) : + p.getFilesDir().getAbsolutePath(); + goBack(p); + } + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + static void showCustomDownloadDirConfig(final MainActivity p) + { + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.storage_custom)); + + final EditText edit = new EditText(p); + edit.setFocusableInTouchMode(true); + edit.setFocusable(true); + edit.setText(Globals.DataDir); + builder.setView(edit); + + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.DataDir = edit.getText().toString(); + dialog.dismiss(); + showCommandLineConfig(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + static void showCommandLineConfig(final MainActivity p) + { + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.storage_commandline)); + + final EditText edit = new EditText(p); + edit.setFocusableInTouchMode(true); + edit.setFocusable(true); + edit.setText(Globals.CommandLine); + builder.setView(edit); + + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.CommandLine = edit.getText().toString(); + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class OptionalDownloadConfig extends Menu + { + boolean firstStart = false; + OptionalDownloadConfig() + { + firstStart = true; + } + OptionalDownloadConfig(boolean firstStart) + { + this.firstStart = firstStart; + } + String title(final MainActivity p) + { + return p.getResources().getString(R.string.downloads); + } + void run (final MainActivity p) + { + String [] downloadFiles = Globals.DataDownloadUrl; + final boolean [] mandatory = new boolean[downloadFiles.length]; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.downloads)); + + CharSequence[] items = new CharSequence[downloadFiles.length]; + for(int i = 0; i < downloadFiles.length; i++ ) + { + items[i] = new String(downloadFiles[i].split("[|]")[0]); + if( items[i].toString().indexOf("!") == 0 ) + items[i] = items[i].toString().substring(1); + if( items[i].toString().indexOf("!") == 0 ) + { + items[i] = items[i].toString().substring(1); + mandatory[i] = true; + } + } + + if( Globals.OptionalDataDownload == null || Globals.OptionalDataDownload.length != items.length ) + { + Globals.OptionalDataDownload = new boolean[downloadFiles.length]; + boolean oldFormat = true; + for( int i = 0; i < downloadFiles.length; i++ ) + { + if( downloadFiles[i].indexOf("!") == 0 ) + { + Globals.OptionalDataDownload[i] = true; + oldFormat = false; + } + } + if( oldFormat ) + { + Globals.OptionalDataDownload[0] = true; + mandatory[0] = true; + } + } + + builder.setMultiChoiceItems(items, Globals.OptionalDataDownload, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + Globals.OptionalDataDownload[item] = isChecked; + if( mandatory[item] && !isChecked ) + { + Globals.OptionalDataDownload[item] = true; + ((AlertDialog)dialog).getListView().setItemChecked(item, true); + } + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + goBack(p); + } + }); + if( firstStart ) + { + builder.setNegativeButton(p.getResources().getString(R.string.show_more_options), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + menuStack.clear(); + new MainMenu().run(p); + } + }); + } + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class AudioConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.audiobuf_question); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.audiobuf_verysmall), + p.getResources().getString(R.string.audiobuf_small), + p.getResources().getString(R.string.audiobuf_medium), + p.getResources().getString(R.string.audiobuf_large) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.audiobuf_question); + builder.setSingleChoiceItems(items, Globals.AudioBufferConfig, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.AudioBufferConfig = item; + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class VideoSettingsConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.video); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.pointandclick_keepaspectratio), + p.getResources().getString(R.string.video_smooth) + }; + boolean defaults[] = { + Globals.KeepAspectRatio, + Globals.VideoLinearFilter + }; + + if(Globals.SwVideoMode && !Globals.CompatibilityHacksVideo) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.pointandclick_keepaspectratio), + p.getResources().getString(R.string.video_smooth), + p.getResources().getString(R.string.video_separatethread), + }; + boolean defaults2[] = { + Globals.KeepAspectRatio, + Globals.VideoLinearFilter, + Globals.MultiThreadedVideo + }; + items = items2; + defaults = defaults2; + } + + if(Globals.Using_SDL_1_3) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.pointandclick_keepaspectratio), + }; + boolean defaults2[] = { + Globals.KeepAspectRatio, + }; + items = items2; + defaults = defaults2; + } + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.video)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + if( item == 0 ) + Globals.KeepAspectRatio = isChecked; + if( item == 1 ) + Globals.VideoLinearFilter = isChecked; + if( item == 2 ) + Globals.MultiThreadedVideo = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class ShowReadme extends Menu + { + String title(final MainActivity p) + { + return "Readme"; + } + boolean enabled() + { + return true; + } + void run (final MainActivity p) + { + String readmes[] = Globals.ReadmeText.split("\\^"); + String lang = new String(Locale.getDefault().getLanguage()) + ":"; + String readme = readmes[0]; + String buttonName = "", buttonUrl = ""; + for( String r: readmes ) + { + if( r.startsWith(lang) ) + readme = r.substring(lang.length()); + if( r.startsWith("button:") ) + { + buttonName = r.substring("button:".length()); + if( buttonName.indexOf(":") != -1 ) + { + buttonUrl = buttonName.substring(buttonName.indexOf(":") + 1); + buttonName = buttonName.substring(0, buttonName.indexOf(":")); + } + } + } + readme = readme.trim(); + if( readme.length() <= 2 ) + { + goBack(p); + return; + } + TextView text = new TextView(p); + text.setMaxLines(1000); + text.setText(readme); + text.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.FILL_PARENT)); + AlertDialog.Builder builder = new AlertDialog.Builder(p); + ScrollView scroll = new ScrollView(p); + scroll.addView(text); + Button ok = new Button(p); + final AlertDialog alertDismiss[] = new AlertDialog[1]; + ok.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + alertDismiss[0].cancel(); + } + }); + ok.setText(R.string.ok); + LinearLayout layout = new LinearLayout(p); + layout.setOrientation(LinearLayout.VERTICAL); + layout.addView(scroll); + layout.addView(ok); + if( buttonName.length() > 0 ) + { + Button cancel = new Button(p); + cancel.setText(buttonName); + final String url = buttonUrl; + cancel.setOnClickListener(new View.OnClickListener() + { + public void onClick(View v) + { + if( url.length() > 0 ) + { + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(url)); + p.startActivity(i); + } + alertDismiss[0].cancel(); + System.exit(0); + } + }); + layout.addView(cancel); + } + builder.setView(layout); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alertDismiss[0] = alert; + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class GyroscopeCalibration extends Menu implements SensorEventListener + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.calibrate_gyroscope); + } + boolean enabled() + { + return Globals.AppUsesGyroscope; + } + void run (final MainActivity p) + { + if( !Globals.AppUsesGyroscope || !AccelerometerReader.gyro.available(p) ) + { + if( Globals.AppUsesGyroscope ) + { + Toast toast = Toast.makeText(p, p.getResources().getString(R.string.calibrate_gyroscope_not_supported), Toast.LENGTH_LONG); + toast.show(); + } + goBack(p); + return; + } + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.calibrate_gyroscope)); + builder.setMessage(p.getResources().getString(R.string.calibrate_gyroscope_text)); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + startCalibration(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + ImageView img; + Bitmap bmp; + int numEvents; + MainActivity p; + + void startCalibration(final MainActivity _p) + { + p = _p; + img = new ImageView(p); + img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + img.setScaleType(ImageView.ScaleType.MATRIX); + bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate ); + img.setImageBitmap(bmp); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF( p.getVideoLayout().getWidth()/2 - 50, p.getVideoLayout().getHeight()/2 - 50, + p.getVideoLayout().getWidth()/2 + 50, p.getVideoLayout().getHeight()/2 + 50); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + p.getVideoLayout().addView(img); + numEvents = 0; + AccelerometerReader.gyro.x1 = 100; + AccelerometerReader.gyro.x2 = -100; + AccelerometerReader.gyro.xc = 0; + AccelerometerReader.gyro.y1 = 100; + AccelerometerReader.gyro.y2 = -100; + AccelerometerReader.gyro.yc = 0; + AccelerometerReader.gyro.z1 = 100; + AccelerometerReader.gyro.z2 = -100; + AccelerometerReader.gyro.zc = 0; + AccelerometerReader.gyro.registerListener(p, this); + (new Thread(new Runnable() + { + public void run() + { + for(int count = 1; count < 10; count++) + { + p.setText("" + count + "0% ..."); + try { + Thread.sleep(500); + } catch( Exception e ) {} + } + finishCalibration(p); + } + } + )).start(); + } + + public void onSensorChanged(SensorEvent event) + { + gyroscopeEvent(event.values[0], event.values[1], event.values[2]); + } + public void onAccuracyChanged(Sensor s, int a) + { + } + void gyroscopeEvent(float x, float y, float z) + { + numEvents++; + AccelerometerReader.gyro.xc += x; + AccelerometerReader.gyro.yc += y; + AccelerometerReader.gyro.zc += z; + AccelerometerReader.gyro.x1 = Math.min(AccelerometerReader.gyro.x1, x * 1.1f); // Small safety bound coefficient + AccelerometerReader.gyro.x2 = Math.max(AccelerometerReader.gyro.x2, x * 1.1f); + AccelerometerReader.gyro.y1 = Math.min(AccelerometerReader.gyro.y1, y * 1.1f); + AccelerometerReader.gyro.y2 = Math.max(AccelerometerReader.gyro.y2, y * 1.1f); + AccelerometerReader.gyro.z1 = Math.min(AccelerometerReader.gyro.z1, z * 1.1f); + AccelerometerReader.gyro.z2 = Math.max(AccelerometerReader.gyro.z2, z * 1.1f); + final Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF( x * 5000 + p.getVideoLayout().getWidth()/2 - 50, y * 5000 + p.getVideoLayout().getHeight()/2 - 50, + x * 5000 + p.getVideoLayout().getWidth()/2 + 50, y * 5000 + p.getVideoLayout().getHeight()/2 + 50); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + p.runOnUiThread(new Runnable() + { + public void run() + { + img.setImageMatrix(m); + } + }); + } + void finishCalibration(final MainActivity p) + { + AccelerometerReader.gyro.unregisterListener(p, this); + try { + Thread.sleep(200); // Just in case we have pending events + } catch( Exception e ) {} + if( numEvents > 5 ) + { + AccelerometerReader.gyro.xc /= (float)numEvents; + AccelerometerReader.gyro.yc /= (float)numEvents; + AccelerometerReader.gyro.zc /= (float)numEvents; + } + p.runOnUiThread(new Runnable() + { + public void run() + { + p.getVideoLayout().removeView(img); + goBack(p); + } + }); + } + } + + static class ResetToDefaultsConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.reset_config); + } + boolean enabled() + { + return true; + } + void run (final MainActivity p) + { + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.reset_config_ask)); + builder.setMessage(p.getResources().getString(R.string.reset_config_ask)); + + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Settings.DeleteSdlConfigOnUpgradeAndRestart(p); // Never returns + dialog.dismiss(); + goBack(p); + } + }); + builder.setNegativeButton(p.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } +} + diff --git a/project/java/SettingsMenuMouse.java b/project/java/SettingsMenuMouse.java new file mode 100644 index 000000000..c4495874d --- /dev/null +++ b/project/java/SettingsMenuMouse.java @@ -0,0 +1,771 @@ +/* +Simple DirectMedia Layer +Java source code (C) 2009-2012 Sergii Pylypenko + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +package net.sourceforge.clonekeenplus; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.view.MotionEvent; +import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; +import android.widget.TextView; +import android.util.Log; +import java.io.*; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Environment; +import android.os.StatFs; +import java.util.Locale; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.zip.GZIPInputStream; +import java.util.Collections; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import java.lang.String; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.BitmapFactory; +import android.graphics.Bitmap; +import android.widget.TextView; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.Button; +import android.view.View; +import android.widget.LinearLayout; +import android.text.Editable; +import android.text.SpannedString; +import android.content.Intent; +import android.app.PendingIntent; +import android.app.AlarmManager; +import android.util.DisplayMetrics; +import android.net.Uri; +import java.util.concurrent.Semaphore; +import java.util.Arrays; +import android.graphics.Color; +import android.hardware.SensorEventListener; +import android.hardware.SensorEvent; +import android.hardware.Sensor; +import android.widget.Toast; + + +class SettingsMenuMouse extends SettingsMenu +{ + static class MouseConfigMainMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.mouse_emulation); + } + boolean enabled() + { + return Globals.AppUsesMouse; + } + void run (final MainActivity p) + { + Menu options[] = + { + new DisplaySizeConfig(false), + new LeftClickConfig(), + new RightClickConfig(), + new AdditionalMouseConfig(), + new JoystickMouseConfig(), + new TouchPressureMeasurementTool(), + new CalibrateTouchscreenMenu(), + new OkButton(), + }; + showMenuOptionsList(p, options); + } + } + + static class DisplaySizeConfig extends Menu + { + boolean firstStart = false; + DisplaySizeConfig() + { + this.firstStart = true; + } + DisplaySizeConfig(boolean firstStart) + { + this.firstStart = firstStart; + } + String title(final MainActivity p) + { + return p.getResources().getString(R.string.display_size_mouse); + } + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.display_size_tiny_touchpad), + p.getResources().getString(R.string.display_size_tiny), + p.getResources().getString(R.string.display_size_small), + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + }; + int _size_tiny_touchpad = 0; + int _size_tiny = 1; + int _size_small = 2; + int _size_small_touchpad = 3; + int _size_large = 4; + int _more_options = 5; + + if( ! Globals.SwVideoMode ) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + }; + items = items2; + _size_small_touchpad = 0; + _size_large = 1; + _size_tiny_touchpad = _size_tiny = _size_small = 1000; + + } + if( firstStart ) + { + CharSequence[] items2 = { + p.getResources().getString(R.string.display_size_tiny_touchpad), + p.getResources().getString(R.string.display_size_tiny), + p.getResources().getString(R.string.display_size_small), + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + p.getResources().getString(R.string.show_more_options), + }; + items = items2; + if( ! Globals.SwVideoMode ) + { + CharSequence[] items3 = { + p.getResources().getString(R.string.display_size_small_touchpad), + p.getResources().getString(R.string.display_size_large), + p.getResources().getString(R.string.show_more_options), + }; + items = items3; + _more_options = 3; + } + } + // Java is so damn worse than C++11 + final int size_tiny_touchpad = _size_tiny_touchpad; + final int size_tiny = _size_tiny; + final int size_small = _size_small; + final int size_small_touchpad = _size_small_touchpad; + final int size_large = _size_large; + final int more_options = _more_options; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.display_size); + class ClickListener implements DialogInterface.OnClickListener + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + if( item == size_large ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_NORMAL; + Globals.RelativeMouseMovement = false; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_NONE; + } + if( item == size_small ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_NEAR_CURSOR; + Globals.RelativeMouseMovement = false; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_MAGNIFIER; + } + if( item == size_small_touchpad ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT; + Globals.RelativeMouseMovement = true; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_NONE; + } + if( item == size_tiny ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_NEAR_CURSOR; + Globals.RelativeMouseMovement = false; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_SCREEN_TRANSFORM; + } + if( item == size_tiny_touchpad ) + { + Globals.LeftClickMethod = Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT; + Globals.RelativeMouseMovement = true; + Globals.ShowScreenUnderFinger = Mouse.ZOOM_FULLSCREEN_MAGNIFIER; + } + if( item == more_options ) + { + menuStack.clear(); + new MainMenu().run(p); + return; + } + goBack(p); + } + } + builder.setItems(items, new ClickListener()); + /* + else + builder.setSingleChoiceItems(items, + Globals.ShowScreenUnderFinger == Mouse.ZOOM_NONE ? + ( Globals.RelativeMouseMovement ? Globals.SwVideoMode ? 2 : 1 : 0 ) : + ( Globals.ShowScreenUnderFinger == Mouse.ZOOM_MAGNIFIER && Globals.SwVideoMode ) ? 1 : + Globals.ShowScreenUnderFinger + 1, + new ClickListener()); + */ + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class LeftClickConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.leftclick_question); + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.leftclick_normal), + p.getResources().getString(R.string.leftclick_near_cursor), + p.getResources().getString(R.string.leftclick_multitouch), + p.getResources().getString(R.string.leftclick_pressure), + p.getResources().getString(R.string.rightclick_key), + p.getResources().getString(R.string.leftclick_timeout), + p.getResources().getString(R.string.leftclick_tap), + p.getResources().getString(R.string.leftclick_tap_or_timeout) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.leftclick_question); + builder.setSingleChoiceItems(items, Globals.LeftClickMethod, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + Globals.LeftClickMethod = item; + if( item == Mouse.LEFT_CLICK_WITH_KEY ) + p.keyListener = new KeyRemapToolMouseClick(p, true); + else if( item == Mouse.LEFT_CLICK_WITH_TIMEOUT || item == Mouse.LEFT_CLICK_WITH_TAP_OR_TIMEOUT ) + showLeftClickTimeoutConfig(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + static void showLeftClickTimeoutConfig(final MainActivity p) { + final CharSequence[] items = { p.getResources().getString(R.string.leftclick_timeout_time_0), + p.getResources().getString(R.string.leftclick_timeout_time_1), + p.getResources().getString(R.string.leftclick_timeout_time_2), + p.getResources().getString(R.string.leftclick_timeout_time_3), + p.getResources().getString(R.string.leftclick_timeout_time_4) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.leftclick_timeout_time); + builder.setSingleChoiceItems(items, Globals.LeftClickTimeout, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.LeftClickTimeout = item; + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class RightClickConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.rightclick_question); + } + boolean enabled() + { + return Globals.AppNeedsTwoButtonMouse; + } + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.rightclick_none), + p.getResources().getString(R.string.rightclick_multitouch), + p.getResources().getString(R.string.rightclick_pressure), + p.getResources().getString(R.string.rightclick_key), + p.getResources().getString(R.string.leftclick_timeout) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.rightclick_question); + builder.setSingleChoiceItems(items, Globals.RightClickMethod, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RightClickMethod = item; + dialog.dismiss(); + if( item == Mouse.RIGHT_CLICK_WITH_KEY ) + p.keyListener = new KeyRemapToolMouseClick(p, false); + else if( item == Mouse.RIGHT_CLICK_WITH_TIMEOUT ) + showRightClickTimeoutConfig(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRightClickTimeoutConfig(final MainActivity p) { + final CharSequence[] items = { p.getResources().getString(R.string.leftclick_timeout_time_0), + p.getResources().getString(R.string.leftclick_timeout_time_1), + p.getResources().getString(R.string.leftclick_timeout_time_2), + p.getResources().getString(R.string.leftclick_timeout_time_3), + p.getResources().getString(R.string.leftclick_timeout_time_4) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.leftclick_timeout_time); + builder.setSingleChoiceItems(items, Globals.RightClickTimeout, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RightClickTimeout = item; + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + public static class KeyRemapToolMouseClick implements MainActivity.KeyEventsListener + { + MainActivity p; + boolean leftClick; + public KeyRemapToolMouseClick(MainActivity _p, boolean leftClick) + { + p = _p; + p.setText(p.getResources().getString(R.string.remap_hwkeys_press)); + this.leftClick = leftClick; + } + + public void onKeyEvent(final int keyCode) + { + p.keyListener = null; + int keyIndex = keyCode; + if( keyIndex < 0 ) + keyIndex = 0; + if( keyIndex > SDL_Keys.JAVA_KEYCODE_LAST ) + keyIndex = 0; + + if( leftClick ) + Globals.LeftClickKey = keyIndex; + else + Globals.RightClickKey = keyIndex; + + goBack(p); + } + } + + static class AdditionalMouseConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.pointandclick_question); + } + void run (final MainActivity p) + { + CharSequence[] items = { + p.getResources().getString(R.string.pointandclick_joystickmouse), + p.getResources().getString(R.string.click_with_dpadcenter), + p.getResources().getString(R.string.pointandclick_relative) + }; + + boolean defaults[] = { + Globals.MoveMouseWithJoystick, + Globals.ClickMouseWithDpad, + Globals.RelativeMouseMovement + }; + + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(p.getResources().getString(R.string.pointandclick_question)); + builder.setMultiChoiceItems(items, defaults, new DialogInterface.OnMultiChoiceClickListener() + { + public void onClick(DialogInterface dialog, int item, boolean isChecked) + { + if( item == 0 ) + Globals.MoveMouseWithJoystick = isChecked; + if( item == 1 ) + Globals.ClickMouseWithDpad = isChecked; + if( item == 2 ) + Globals.RelativeMouseMovement = isChecked; + } + }); + builder.setPositiveButton(p.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + dialog.dismiss(); + if( Globals.RelativeMouseMovement ) + showRelativeMouseMovementConfig(p); + else + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRelativeMouseMovementConfig(final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.accel_veryslow), + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast), + p.getResources().getString(R.string.accel_veryfast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_relative_speed); + builder.setSingleChoiceItems(items, Globals.RelativeMouseMovementSpeed, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RelativeMouseMovementSpeed = item; + + dialog.dismiss(); + showRelativeMouseMovementConfig1(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showRelativeMouseMovementConfig1(final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.none), + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_relative_accel); + builder.setSingleChoiceItems(items, Globals.RelativeMouseMovementAccel, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.RelativeMouseMovementAccel = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class JoystickMouseConfig extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.pointandclick_joystickmousespeed); + } + boolean enabled() + { + return Globals.MoveMouseWithJoystick; + }; + void run (final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_joystickmousespeed); + builder.setSingleChoiceItems(items, Globals.MoveMouseWithJoystickSpeed, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.MoveMouseWithJoystickSpeed = item; + + dialog.dismiss(); + showJoystickMouseAccelConfig(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + + static void showJoystickMouseAccelConfig(final MainActivity p) + { + final CharSequence[] items = { p.getResources().getString(R.string.none), + p.getResources().getString(R.string.accel_slow), + p.getResources().getString(R.string.accel_medium), + p.getResources().getString(R.string.accel_fast) }; + + AlertDialog.Builder builder = new AlertDialog.Builder(p); + builder.setTitle(R.string.pointandclick_joystickmouseaccel); + builder.setSingleChoiceItems(items, Globals.MoveMouseWithJoystickAccel, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int item) + { + Globals.MoveMouseWithJoystickAccel = item; + + dialog.dismiss(); + goBack(p); + } + }); + builder.setOnCancelListener(new DialogInterface.OnCancelListener() + { + public void onCancel(DialogInterface dialog) + { + goBack(p); + } + }); + AlertDialog alert = builder.create(); + alert.setOwnerActivity(p); + alert.show(); + } + } + + static class TouchPressureMeasurementTool extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.measurepressure); + } + boolean enabled() + { + return Globals.RightClickMethod == Mouse.RIGHT_CLICK_WITH_PRESSURE || + Globals.LeftClickMethod == Mouse.LEFT_CLICK_WITH_PRESSURE; + }; + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.measurepressure_touchplease)); + p.touchListener = new TouchMeasurementTool(p); + } + + public static class TouchMeasurementTool implements MainActivity.TouchEventsListener + { + MainActivity p; + ArrayList force = new ArrayList(); + ArrayList radius = new ArrayList(); + static final int maxEventAmount = 100; + + public TouchMeasurementTool(MainActivity _p) + { + p = _p; + } + + public void onTouchEvent(final MotionEvent ev) + { + force.add(new Integer((int)(ev.getPressure() * 1000.0))); + radius.add(new Integer((int)(ev.getSize() * 1000.0))); + p.setText(p.getResources().getString(R.string.measurepressure_response, force.get(force.size()-1), radius.get(radius.size()-1))); + try { + Thread.sleep(10L); + } catch (InterruptedException e) { } + + if( force.size() >= maxEventAmount ) + { + p.touchListener = null; + Globals.ClickScreenPressure = getAverageForce(); + Globals.ClickScreenTouchspotSize = getAverageRadius(); + Log.i("SDL", "SDL: measured average force " + Globals.ClickScreenPressure + " radius " + Globals.ClickScreenTouchspotSize); + goBack(p); + } + } + + int getAverageForce() + { + int avg = 0; + for(Integer f: force) + { + avg += f; + } + return avg / force.size(); + } + int getAverageRadius() + { + int avg = 0; + for(Integer r: radius) + { + avg += r; + } + return avg / radius.size(); + } + } + } + + static class CalibrateTouchscreenMenu extends Menu + { + String title(final MainActivity p) + { + return p.getResources().getString(R.string.calibrate_touchscreen); + } + //boolean enabled() { return true; }; + void run (final MainActivity p) + { + p.setText(p.getResources().getString(R.string.calibrate_touchscreen_touch)); + Globals.TouchscreenCalibration[0] = 0; + Globals.TouchscreenCalibration[1] = 0; + Globals.TouchscreenCalibration[2] = 0; + Globals.TouchscreenCalibration[3] = 0; + ScreenEdgesCalibrationTool tool = new ScreenEdgesCalibrationTool(p); + p.touchListener = tool; + p.keyListener = tool; + } + + static class ScreenEdgesCalibrationTool implements MainActivity.TouchEventsListener, MainActivity.KeyEventsListener + { + MainActivity p; + ImageView img; + Bitmap bmp; + + public ScreenEdgesCalibrationTool(MainActivity _p) + { + p = _p; + img = new ImageView(p); + img.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT)); + img.setScaleType(ImageView.ScaleType.MATRIX); + bmp = BitmapFactory.decodeResource( p.getResources(), R.drawable.calibrate ); + img.setImageBitmap(bmp); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], + Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + p.getVideoLayout().addView(img); + } + + public void onTouchEvent(final MotionEvent ev) + { + if( Globals.TouchscreenCalibration[0] == Globals.TouchscreenCalibration[1] && + Globals.TouchscreenCalibration[1] == Globals.TouchscreenCalibration[2] && + Globals.TouchscreenCalibration[2] == Globals.TouchscreenCalibration[3] ) + { + Globals.TouchscreenCalibration[0] = (int)ev.getX(); + Globals.TouchscreenCalibration[1] = (int)ev.getY(); + Globals.TouchscreenCalibration[2] = (int)ev.getX(); + Globals.TouchscreenCalibration[3] = (int)ev.getY(); + } + if( ev.getX() < Globals.TouchscreenCalibration[0] ) + Globals.TouchscreenCalibration[0] = (int)ev.getX(); + if( ev.getY() < Globals.TouchscreenCalibration[1] ) + Globals.TouchscreenCalibration[1] = (int)ev.getY(); + if( ev.getX() > Globals.TouchscreenCalibration[2] ) + Globals.TouchscreenCalibration[2] = (int)ev.getX(); + if( ev.getY() > Globals.TouchscreenCalibration[3] ) + Globals.TouchscreenCalibration[3] = (int)ev.getY(); + Matrix m = new Matrix(); + RectF src = new RectF(0, 0, bmp.getWidth(), bmp.getHeight()); + RectF dst = new RectF(Globals.TouchscreenCalibration[0], Globals.TouchscreenCalibration[1], + Globals.TouchscreenCalibration[2], Globals.TouchscreenCalibration[3]); + m.setRectToRect(src, dst, Matrix.ScaleToFit.FILL); + img.setImageMatrix(m); + } + + public void onKeyEvent(final int keyCode) + { + p.touchListener = null; + p.keyListener = null; + p.getVideoLayout().removeView(img); + goBack(p); + } + } + } +} + diff --git a/project/jni/application/commandergenius/commandergenius b/project/jni/application/commandergenius/commandergenius index cdd8564eb..6f28771d5 160000 --- a/project/jni/application/commandergenius/commandergenius +++ b/project/jni/application/commandergenius/commandergenius @@ -1 +1 @@ -Subproject commit cdd8564ebdd26b6c733bad120a8df44e11edc041 +Subproject commit 6f28771d57ca7d878e3cfee08014651b854b9c7f diff --git a/project/jni/application/openarena/AndroidAppSettings.cfg b/project/jni/application/openarena/AndroidAppSettings.cfg index a5c4e3cd0..26cdeda37 100644 --- a/project/jni/application/openarena/AndroidAppSettings.cfg +++ b/project/jni/application/openarena/AndroidAppSettings.cfg @@ -142,13 +142,13 @@ RedefinedKeysScreenKbNames="Change_weapon Sniper_view Show_scores Center_view Fi StartupMenuButtonTimeout=3000 # Menu items to hide from startup menu, available menu items: -# OkButton DummyMenu MainMenu MouseConfigMainMenu KeyboardConfigMainMenu DownloadConfig OptionalDownloadConfig ScreenKeyboardSizeConfig ScreenKeyboardDrawSizeConfig ScreenKeyboardThemeConfig ScreenKeyboardTransparencyConfig AudioConfig DisplaySizeConfig LeftClickConfig RightClickConfig AdditionalMouseConfig JoystickMouseConfig TouchPressureMeasurementTool RemapHwKeysConfig RemapScreenKbConfig ScreenGesturesConfig CalibrateTouchscreenMenu CustomizeScreenKbLayout VideoSettingsConfig ShowReadme GyroscopeCalibration ResetToDefaultsConfig +# HiddenMenuOptions='OptionalDownloadConfig DisplaySizeConfig' -# Menu items to show at startup - this is Java code snippet, leave empty for default: -# new Settings.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new Settings.DisplaySizeConfig(true) : new Settings.DummyMenu()), new Settings.OptionalDownloadConfig(true), new Settings.GyroscopeCalibration() +# Menu items to show at startup - this is Java code snippet, leave empty for default +# new SettingsMenuMisc.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new SettingsMenuMouse.DisplaySizeConfig(true) : new SettingsMenu.DummyMenu()), new SettingsMenuMisc.OptionalDownloadConfig(true), new SettingsMenuMisc.GyroscopeCalibration() # Available menu items: -# new Settings.OkButton(), new Settings.DummyMenu(), new Settings.MainMenu(), new Settings.MouseConfigMainMenu(), new Settings.KeyboardConfigMainMenu(), new Settings.DownloadConfig(), new Settings.OptionalDownloadConfig(), new Settings.ScreenKeyboardSizeConfig(), new Settings.ScreenKeyboardDrawSizeConfig(), new Settings.ScreenKeyboardThemeConfig(), new Settings.ScreenKeyboardTransparencyConfig(), new Settings.AudioConfig(), new Settings.DisplaySizeConfig(), new Settings.LeftClickConfig(), new Settings.RightClickConfig(), new Settings.AdditionalMouseConfig(), new Settings.JoystickMouseConfig(), new Settings.TouchPressureMeasurementTool(), new Settings.RemapHwKeysConfig(), new Settings.RemapScreenKbConfig(), new Settings.ScreenGesturesConfig(), new Settings.CalibrateTouchscreenMenu(), new Settings.CustomizeScreenKbLayout(), new Settings.VideoSettingsConfig(), new Settings.ShowReadme(), new Settings.GyroscopeCalibration(), new Settings.ResetToDefaultsConfig(), +# new SettingsMenu.OkButton(), new SettingsMenu.DummyMenu(), new SettingsMenu.MainMenu(), new SettingsMenuMisc.DownloadConfig(), new SettingsMenuMisc.OptionalDownloadConfig(), new SettingsMenuMisc.AudioConfig(), new SettingsMenuMisc.VideoSettingsConfig(), new SettingsMenuMisc.ShowReadme(), new SettingsMenuMisc.GyroscopeCalibration(), new SettingsMenuMisc.ResetToDefaultsConfig(), new SettingsMenuMouse.MouseConfigMainMenu(), new SettingsMenuMouse.DisplaySizeConfig(), new SettingsMenuMouse.LeftClickConfig(), new SettingsMenuMouse.RightClickConfig(), new SettingsMenuMouse.AdditionalMouseConfig(), new SettingsMenuMouse.JoystickMouseConfig(), new SettingsMenuMouse.TouchPressureMeasurementTool(), new SettingsMenuMouse.CalibrateTouchscreenMenu(), new SettingsMenuKeyboard.KeyboardConfigMainMenu(), new SettingsMenuKeyboard.ScreenKeyboardSizeConfig(), new SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig(), new SettingsMenuKeyboard.ScreenKeyboardThemeConfig(), new SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig(), new SettingsMenuKeyboard.RemapHwKeysConfig(), new SettingsMenuKeyboard.RemapScreenKbConfig(), new SettingsMenuKeyboard.ScreenGesturesConfig(), new SettingsMenuKeyboard.CustomizeScreenKbLayout(), FirstStartMenuOptions='' # Enable multi-ABI binary, with hardware FPU support - it will also work on old devices, diff --git a/project/jni/application/teeworlds/AndroidAppSettings.cfg b/project/jni/application/teeworlds/AndroidAppSettings.cfg index 87573c813..d885c858b 100644 --- a/project/jni/application/teeworlds/AndroidAppSettings.cfg +++ b/project/jni/application/teeworlds/AndroidAppSettings.cfg @@ -1,6 +1,6 @@ # The application settings for Android libSDL port -AppSettingVersion=18 +AppSettingVersion=19 # libSDL version to use (1.2 or 1.3, specify 1.3 for SDL2) LibSdlVersion=1.2 @@ -142,13 +142,13 @@ RedefinedKeysScreenKbNames="Jump Next_weapon Previous_weapon Show_scores Fire Ho StartupMenuButtonTimeout=3000 # Menu items to hide from startup menu, available menu items: -# OkButton DummyMenu MainMenu MouseConfigMainMenu KeyboardConfigMainMenu DownloadConfig OptionalDownloadConfig ScreenKeyboardSizeConfig ScreenKeyboardDrawSizeConfig ScreenKeyboardThemeConfig ScreenKeyboardTransparencyConfig AudioConfig DisplaySizeConfig LeftClickConfig RightClickConfig AdditionalMouseConfig JoystickMouseConfig TouchPressureMeasurementTool RemapHwKeysConfig RemapScreenKbConfig ScreenGesturesConfig CalibrateTouchscreenMenu CustomizeScreenKbLayout VideoSettingsConfig ShowReadme GyroscopeCalibration ResetToDefaultsConfig -HiddenMenuOptions='OptionalDownloadConfig DisplaySizeConfig' +# SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout +HiddenMenuOptions='SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMouse.DisplaySizeConfig' -# Menu items to show at startup - this is Java code snippet, leave empty for default: -# new Settings.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new Settings.DisplaySizeConfig(true) : new Settings.DummyMenu()), new Settings.OptionalDownloadConfig(true), new Settings.GyroscopeCalibration() +# Menu items to show at startup - this is Java code snippet, leave empty for default +# new SettingsMenuMisc.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new SettingsMenuMouse.DisplaySizeConfig(true) : new SettingsMenu.DummyMenu()), new SettingsMenuMisc.OptionalDownloadConfig(true), new SettingsMenuMisc.GyroscopeCalibration() # Available menu items: -# new Settings.OkButton(), new Settings.DummyMenu(), new Settings.MainMenu(), new Settings.MouseConfigMainMenu(), new Settings.KeyboardConfigMainMenu(), new Settings.DownloadConfig(), new Settings.OptionalDownloadConfig(), new Settings.ScreenKeyboardSizeConfig(), new Settings.ScreenKeyboardDrawSizeConfig(), new Settings.ScreenKeyboardThemeConfig(), new Settings.ScreenKeyboardTransparencyConfig(), new Settings.AudioConfig(), new Settings.DisplaySizeConfig(), new Settings.LeftClickConfig(), new Settings.RightClickConfig(), new Settings.AdditionalMouseConfig(), new Settings.JoystickMouseConfig(), new Settings.TouchPressureMeasurementTool(), new Settings.RemapHwKeysConfig(), new Settings.RemapScreenKbConfig(), new Settings.ScreenGesturesConfig(), new Settings.CalibrateTouchscreenMenu(), new Settings.CustomizeScreenKbLayout(), new Settings.VideoSettingsConfig(), new Settings.ShowReadme(), new Settings.GyroscopeCalibration(), new Settings.ResetToDefaultsConfig(), +# SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout FirstStartMenuOptions='' # Enable multi-ABI binary, with hardware FPU support - it will also work on old devices, @@ -159,10 +159,10 @@ MultiABI=y AppMinimumRAM=0 # Application version code (integer) -AppVersionCode=06211 +AppVersionCode=06212 # Application user-visible version name (string) -AppVersionName="0.6.2.11" +AppVersionName="0.6.2.12" # Reset SDL config when updating application to the new version (y) / (n) ResetSdlConfigForThisVersion=y diff --git a/project/res/raw/simpletheme.raw b/project/res/raw/simpletheme.raw index 2ce9ee07ec9c3f3049c8ef3f2354949a6293d12e..9b3129e07cd6a28c04ac52a8a95acb6dd6d1b1a7 100644 GIT binary patch delta 19 acmZ2=fpOIZMh^LI4u-Po6M-8!lrjKIKL-8) delta 19 bcmZ2=fpOIZMh^LI4u;vMFZyrfP|5%RPQwR! diff --git a/project/res/raw/sun.raw b/project/res/raw/sun.raw index 1e6da6ae587946118bdee85575dbfc2e70a69c87..e28d4830a878d3775e47b70993521e452921ba8a 100644 GIT binary patch delta 37 scmeBMC)Kx3ibKAegQ2YYL|`LFD+gmM2U9Btb1Mf+D+lXV4mP&w0NauZDgXcg delta 37 tcmeBMC)Kx3ibKAegJI6;i~fxqtsIQ4989eo%&i delta 17 Zcmca_dEb&lzMF$#_UViM8#%7a002Nh2OR(a diff --git a/project/themes/converter/Makefile b/project/themes/Makefile similarity index 100% rename from project/themes/converter/Makefile rename to project/themes/Makefile diff --git a/project/themes/convert.sh b/project/themes/convert.sh new file mode 100755 index 000000000..040cc8b69 --- /dev/null +++ b/project/themes/convert.sh @@ -0,0 +1,141 @@ +#!/bin/sh + +# Ultimate Droid by Sean Stieber + +[ -x ./converter ] || make || exit 1 + +for f in UltimateDroid/*.png; do + newname=`echo $f | sed 's@.*/@@' | tr '[A-Z]' '[a-z]'`.raw + ./converter $f ../res/raw/$newname 16 +done + +rm -f ../res/raw/ultimatedroid.raw +printf '\000\000\000\030' > ../res/raw/ultimatedroid.raw # size, 030 = 24 + +for F in \ +dpadbutton \ +leftbuttonpressed \ +rightbuttonpressed \ +upbuttonpressed \ +downbuttonpressed \ +\ +button1auto \ +button1autoanim \ +button2auto \ +button2autoanim \ +\ +button1 \ +button1pressed \ +button2 \ +button2pressed \ +button3 \ +button3pressed \ +button4 \ +button4pressed \ +button5 \ +button5pressed \ +button6 \ +button6pressed \ +button7 \ +button7 \ +mouse_pointer \ +; do + if [ \! -e ../res/raw/ultimatedroid$F.png.raw ]; then + echo Cannot find ../res/raw/ultimatedroid$F.png.raw - check if all files are in place + exit 1 + fi + cat ../res/raw/ultimatedroid$F.png.raw >> ../res/raw/ultimatedroid.raw +done + +rm ../res/raw/ultimatedroid*.png.raw + +gzip -9 < ../res/raw/ultimatedroid.raw > ../res/raw/ultimatedroid.raw.gz +mv -f ../res/raw/ultimatedroid.raw.gz ../res/raw/ultimatedroid.raw + +# Simple Theme by Dmitry Matveev + +for f in SimpleTheme/*.png; do + newname=simpletheme`echo $f | sed 's@.*/@@' | tr '[A-Z]' '[a-z]'`.raw + ./converter $f ../res/raw/$newname 16 +done + +rm -f ../res/raw/simpletheme.raw +printf '\000\000\000\030' > ../res/raw/simpletheme.raw # size, 030 = 24 + +for F in \ +dpad \ +dpad_left \ +dpad_right \ +dpad_up \ +dpad_down \ +\ +1auto_pressed \ +1auto \ +2auto_pressed \ +2auto \ +\ +1 \ +1pressed \ +2 \ +2pressed \ +3 \ +3pressed \ +4 \ +4pressed \ +5 \ +5pressed \ +6 \ +6pressed \ +keyboard \ +keyboard \ +mouse_pointer \ +; do + if [ \! -e ../res/raw/simpletheme$F.png.raw ]; then + echo Cannot find ../res/raw/simpletheme$F.png.raw - check if all files are in place + exit 1 + fi + cat ../res/raw/simpletheme$F.png.raw >> ../res/raw/simpletheme.raw +done + +rm ../res/raw/simpletheme*.png.raw + +gzip -9 < ../res/raw/simpletheme.raw > ../res/raw/simpletheme.raw.gz +mv -f ../res/raw/simpletheme.raw.gz ../res/raw/simpletheme.raw + + +# Abstract Sun Icon Set by Sirea (Martina Šmejkalová) +for f in Sun/*.png; do + newname=`echo $f | sed 's@.*/@@' | tr '[A-Z]' '[a-z]'`.raw + ./converter $f ../res/raw/$newname 32 +done + +rm -f ../res/raw/sun.raw +printf '\000\000\000\012' > ../res/raw/sun.raw # size, 012 = 10 + +for F in \ +sun-2.ico-10 \ +\ +sun-4.ico-10 \ +sun-5.ico-10 \ +sun-6.ico-10 \ +sun-7.ico-10 \ +\ +sun-3.ico-10 \ +sun-9.ico-10 \ +sun-8.ico-10 \ +sun-1.ico-10 \ +\ +sun-mouse_pointer \ +; do + if [ \! -e ../res/raw/$F.png.raw ]; then + echo Cannot find ../res/raw/$F.png.raw - check if all files are in place + exit 1 + fi + + cat ../res/raw/$F.png.raw >> ../res/raw/sun.raw +done + +rm ../res/raw/sun*.png.raw + +gzip -9 < ../res/raw/sun.raw > ../res/raw/sun.raw.gz +mv -f ../res/raw/sun.raw.gz ../res/raw/sun.raw diff --git a/project/themes/converter/converter.cpp b/project/themes/converter.cpp similarity index 100% rename from project/themes/converter/converter.cpp rename to project/themes/converter.cpp diff --git a/project/themes/converter/convert.sh b/project/themes/converter/convert.sh deleted file mode 100755 index c611a20e6..000000000 --- a/project/themes/converter/convert.sh +++ /dev/null @@ -1,152 +0,0 @@ -#!/bin/sh - -# Ultimate Droid by Sean Stieber - -for f in ../UltimateDroid/*.png; do - newname=`echo $f | sed 's@.*/@@' | tr '[A-Z]' '[a-z]'`.raw - ./converter $f ../../res/raw/$newname 16 -done - -rm -f ../../res/raw/ultimatedroid.raw -printf '\000\000\000\030' > ../../res/raw/ultimatedroid.raw # size, 030 = 24 - -for F in \ -dpadbutton \ -leftbuttonpressed \ -rightbuttonpressed \ -upbuttonpressed \ -downbuttonpressed \ -\ -button1auto \ -button1autoanim \ -button2auto \ -button2autoanim \ -\ -button1 \ -button1pressed \ -button2 \ -button2pressed \ -button3 \ -button3pressed \ -button4 \ -button4pressed \ -button5 \ -button5pressed \ -button6 \ -button6pressed \ -button7 \ -button7 \ -mouse_pointer \ -; do - if [ \! -e ../../res/raw/ultimatedroid$F.png.raw ]; then - echo Cannot find ../../res/raw/ultimatedroid$F.png.raw - check if all files are in place - exit 1 - fi - cat ../../res/raw/ultimatedroid$F.png.raw >> ../../res/raw/ultimatedroid.raw -done - -rm ../../res/raw/ultimatedroid*.png.raw - -gzip -9 < ../../res/raw/ultimatedroid.raw > ../../res/raw/ultimatedroid.raw.gz -mv -f ../../res/raw/ultimatedroid.raw.gz ../../res/raw/ultimatedroid.raw -echo "// Touchscreen theme to be included directly into the code witohut Java resources, not used yet"> ../touchscreentheme.h -echo "unsigned char * UltimateDroidTheme[] = { 00 // Dummy byte, skip it" >> ../touchscreentheme.h -cat ../../res/raw/ultimatedroid.raw | od -t x1 -v -A n | tr " " "," >> ../touchscreentheme.h -echo "};" >> ../touchscreentheme.h - - -# Simple Theme by Dmitry Matveev - - -for f in ../SimpleTheme/*.png; do - newname=simpletheme`echo $f | sed 's@.*/@@' | tr '[A-Z]' '[a-z]'`.raw - ./converter $f ../../res/raw/$newname 16 -done - -rm -f ../../res/raw/simpletheme.raw -printf '\000\000\000\030' > ../../res/raw/simpletheme.raw # size, 030 = 24 - -for F in \ -dpad \ -dpad_left \ -dpad_right \ -dpad_up \ -dpad_down \ -\ -1auto_pressed \ -1auto \ -2auto_pressed \ -2auto \ -\ -1 \ -1pressed \ -2 \ -2pressed \ -3 \ -3pressed \ -4 \ -4pressed \ -5 \ -5pressed \ -6 \ -6pressed \ -keyboard \ -keyboard \ -mouse_pointer \ -; do - if [ \! -e ../../res/raw/simpletheme$F.png.raw ]; then - echo Cannot find ../../res/raw/simpletheme$F.png.raw - check if all files are in place - exit 1 - fi - cat ../../res/raw/simpletheme$F.png.raw >> ../../res/raw/simpletheme.raw -done - -rm ../../res/raw/simpletheme*.png.raw - -gzip -9 < ../../res/raw/simpletheme.raw > ../../res/raw/simpletheme.raw.gz -mv -f ../../res/raw/simpletheme.raw.gz ../../res/raw/simpletheme.raw -#echo "// Touchscreen theme to be included directly into the code witohut Java resources, not used yet"> ../touchscreentheme.h -echo "unsigned char * SimpleTheme[] = { 00 // Dummy byte, skip it" >> ../touchscreentheme.h -cat ../../res/raw/simpletheme.raw | od -t x1 -v -A n | tr " " "," >> ../touchscreentheme.h -echo "};" >> ../touchscreentheme.h - - -# Abstract Sun Icon Set by Sirea (Martina Šmejkalová) -for f in ../Sun/*.png; do - newname=`echo $f | sed 's@.*/@@' | tr '[A-Z]' '[a-z]'`.raw - ./converter $f ../../res/raw/$newname 32 -done - -rm -f ../../res/raw/sun.raw -printf '\000\000\000\012' > ../../res/raw/sun.raw # size, 012 = 10 - -for F in \ -sun-2.ico-10 \ -\ -sun-4.ico-10 \ -sun-5.ico-10 \ -sun-6.ico-10 \ -sun-7.ico-10 \ -\ -sun-3.ico-10 \ -sun-9.ico-10 \ -sun-8.ico-10 \ -sun-1.ico-10 \ -\ -sun-mouse_pointer \ -; do - if [ \! -e ../../res/raw/$F.png.raw ]; then - echo Cannot find ../../res/raw/$F.png.raw - check if all files are in place - exit 1 - fi - - cat ../../res/raw/$F.png.raw >> ../../res/raw/sun.raw -done - -rm ../../res/raw/sun*.png.raw - -gzip -9 < ../../res/raw/sun.raw > ../../res/raw/sun.raw.gz -mv -f ../../res/raw/sun.raw.gz ../../res/raw/sun.raw -echo "unsigned char * SunTheme[] = { 00 // Dummy byte, skip it" >> ../touchscreentheme.h -cat ../../res/raw/sun.raw | od -t x1 -v -A n | tr " " "," >> ../touchscreentheme.h -echo "};" >> ../touchscreentheme.h diff --git a/project/themes/converter/converter b/project/themes/converter/converter deleted file mode 100755 index 89d8a6e17feedf08a336275d07d66a0014bbe478..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117982 zcmb<-^>JfjWMqH=CI&kO5bpxJ16T+`GB7mk1c`!$7#tWZ7&sXm7*rXQ7}yvX7+4t? z7+~rgpez{u1F8*1b3lw>U}j)oU}0cjuwa4+STI3MfYCA#VFnltH3sZ9kbO{T6q`X} zF?=ABfdNJ{FercpK>9&$<&Xn&893w;zzi6j0Ch(Jl!oa8aY6bFp!y7;`e5`1kiiTL z3@{qzKak%*I0R$>0|P?{)PFGA1#CM51B?c#1qlT_ElB~fJ9t4n28Iq^NVvdg9f&Xk zj0UL%2?ahaNddVN#3lyA+!+M54_COr;tfVaz007VlbK{@qMwtZo0FMWTA^EEVWw+l zqF0=+X9SK{0R{$eT)F#&f}H~j29WzCpz$ukzyMAIAbHCbt~1-t2w$qH_BQl8raAYf z*f~j1`T#{G$Ucz1jspse3@i?uATf~HLJSNHyB<%fady4>=0;(J%+B7Ae6KIR2T6nN zLnjt7Fff4hpo1RESVfYGO%hkh>F1Fg`6aFB57_d}eN9dMd&)PhaG)I5fQ(vo6^f}+g4k~D_2;S0+VFe^{kR2c~5Y|8vhm}1baRVfAPLKc;TOf&ZLB&9n z1Cls5NC1jGki>bQVjwC2NgP_Ff`l17n%{7|_|3q;@S=;6f#JncRt5%-<|7=3|C`>D zP+<74dPhQmfnVN%;lC<~p8?YI^1=WA|NpCAl2BmC02NCwFM#={KzvXTzdQiu9|G|~ z#n;OXVE!%;9~7i77l8SjKzvY8zMKH&uLAKwLHM!(%wGiJgM#j50hm7v#0Lf0%LFig z5{M59s+R#^eiw)j3Zj<||Ns9teCyeHG0vm&kw^2B4*?;b#}6#vabWN`eoz2R9pA#? z!0;mc|NsAACQJZ@fk!v%O>qSVkJbYvO#d%{gJ1$O2=(ZE8Vpj{U~3_vz`#)Iy<0&p zfx)Aj)ks2t!K3lD1c-O=H?v3cYeo;t8^tPV{PHfKz&ya{(aFjS3QUosU`5@m|3Nlc z-Y9x>jPFaadz(flT%vv!Y1XX%FK+C5D5BKsM59T;B3{`>!b2RL%nm>C#6x?MMTG#}vb z=w|TfJpN+BzyJR|I$bY#G}m5WDCL5v`^UtDa$d}e~b;a{}@WxcYtb#*Q`50m5~eoHrM|i-Jut{t3<$*mjHh@@^Jpm8NEsWq0 zKjG2s!Qs*EdSMsHnI4_zKtccY57@CcJeq57z%?{OG=QQ=02J6w(7;{+QT)K8x%L5E zaTrALjcx~l&f_mw|Nj5q>H5N>JM@7^uj>ns&WpQ1SqvOJAp5R@jOcc~(d{78dHluo zKOog_psL?ss9yf(|9@z#f->@JA#j+ogA>SWR&W$`9)B?rY|00yDIYLQN&NHw|8dtZ zpd|aU2^1BsKS1o)DNv(94t&kJ17w3ocjyg|ZZ8E8&A&ZJf`9uZ{`H|>Jd!^_)Pg

{$V8*B|`z540R8 z?QVAc0u>aocKretmxZypE#GJAGjf3f)2 z|NkDXZ%bqhZ-dHDpUz)Co!?wK-#h*nx#Q7%n9;-Xb?GaQ?idvgkM0~50iW&^6$zj2 z92Eth?h+LZpY9qJ1CQn-7Ki!e8$hA=8QF0YAbB5L9(i=LdI%~ocr^d`Un031)QpFg zJFhwZU-0N=21%5DgqARnvhIcc@BjZnB8NdK6BM=#457t|>8Vx<$&dy^YLP;5erZv1 zs$M}}x~LUQuGXLNtV}kW^5hV5?wgrjTExU~Cl2U~B|(Bm)Bjw8fEHWTl{5 ztl$(7prD$fpqf&lnxasUS`?63k?NRJkdX)yg&L)r0y2$@p&Zl%VqlbJwg$E7L5ZZ} z=l}m63=9l9zyJR)U|?YA`1k++1O^5MiT}_TV_;wawVy#&GgbvLFjfdKO7pODOkiYS zP+?#I_n9ny{{Mdfq<~MrjZeahpSzr+fx%wNTFY2P2_y{aSAqH-TYmlj9|)?g9Qg#= zn4Edpn0=Ue*g3%Fq%bfrd;n!MkcPk>);6|yK8I%Zex^R=UKXYZK2W6uaz_sX14G2W z|NkK&3Q|0ZM?+vV1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz3ONU^D~fpHN30#qI}&<)~)huIhy7+`Y%pm{}*xG0D~!l0oI5cdUiJ_I&K0Ul~a8b<*Q zxP#P*g9y;L2P?#WnD`ASA2j9xQUbFNG!T!>|Mwr_pB+&3|Dk+XczuBKd!X_lz4XAa z`D%2#)S&bHW>DGsLrQ4wNG$_3cN^gVG$DlO2J?_rVRtg%SNu_xurTRXZ zd5J~&3MP7Hdd9kjrC>o*O$Z-6vki$QNLYhrEI_7#N2x#zG|Vmqn)d<;axkuAW?*0m zn=Poq>UM1``7V z)0H@o2nYLJkYX10M~n;%Y#hwVbD0?!rZO-vuzzA^U|>$M2Psl!;3!JXWnfN)@K_jF z=Yb^Cau^sG*aVof9XS{nPQvx%fS61$J-HAb2LlraYat5*1FI7U0|Rs30*Kb~ZVm

DI)^|4=7NW8JIZO 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