From 9258c72daa09ea00825bc10a67b9c516215e3820 Mon Sep 17 00:00:00 2001 From: VanitasVitae Date: Thu, 27 Aug 2015 01:48:33 +0200 Subject: [PATCH] Update 0.1.5 Added Enigma Models I,M3,M4, fixed ring settings --- CHANGELOG.txt | 14 +- app/app.iml | 8 +- app/build.gradle | 10 +- .../enigmandroid/ApplicationTest.java | 13 - app/src/main/AndroidManifest.xml | 1 + .../de/vanitasvitae/enigmandroid/Enigma.java | 242 ---------- .../enigmandroid/MainActivity.java | 428 +++++++----------- .../enigmandroid/enigma/Enigma.java | 168 +++++++ .../enigmandroid/enigma/Enigma_I.java | 171 +++++++ .../enigmandroid/enigma/Enigma_M3.java | 75 +++ .../enigmandroid/enigma/Enigma_M4.java | 214 +++++++++ .../enigmandroid/{ => enigma}/Plugboard.java | 5 +- .../inputPreparer/InputPreparer.java} | 57 ++- .../{ => enigma}/rotors/Reflector.java | 79 ++-- .../{ => enigma}/rotors/Rotor.java | 244 ++++++---- .../enigmandroid/enigma/util/RotorMaker.java | 107 +++++ .../enigmandroid/layout/LayoutContainer.java | 78 ++++ .../layout/LayoutContainer_I.java | 224 +++++++++ .../layout/LayoutContainer_M3.java | 115 +++++ .../layout/LayoutContainer_M4.java | 254 +++++++++++ ...tivity_main.xml => activity_main_i_m3.xml} | 0 .../main/res/layout-land/activity_main_m4.xml | 202 +++++++++ ...tivity_main.xml => activity_main_i_m3.xml} | 2 +- app/src/main/res/layout/activity_main_m4.xml | 196 ++++++++ ...tings.xml => dialog_ringsettings_i_m3.xml} | 0 .../res/layout/dialog_ringsettings_m4.xml | 67 +++ app/src/main/res/values-de/strings.xml | 18 +- .../values-de/strings_activity_settings.xml | 13 +- app/src/main/res/values/strings.xml | 27 +- .../res/values/strings_activity_settings.xml | 12 + app/src/main/res/xml/pref_page.xml | 31 +- 31 files changed, 2388 insertions(+), 687 deletions(-) delete mode 100644 app/src/androidTest/java/de/vanitasvitae/enigmandroid/ApplicationTest.java delete mode 100755 app/src/main/java/de/vanitasvitae/enigmandroid/Enigma.java create mode 100755 app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma.java create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_I.java create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M3.java create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M4.java rename app/src/main/java/de/vanitasvitae/enigmandroid/{ => enigma}/Plugboard.java (97%) rename app/src/main/java/de/vanitasvitae/enigmandroid/{InputPreparator.java => enigma/inputPreparer/InputPreparer.java} (67%) rename app/src/main/java/de/vanitasvitae/enigmandroid/{ => enigma}/rotors/Reflector.java (67%) rename app/src/main/java/de/vanitasvitae/enigmandroid/{ => enigma}/rotors/Rotor.java (56%) create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/enigma/util/RotorMaker.java create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer.java create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_I.java create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M3.java create mode 100644 app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M4.java rename app/src/main/res/layout-land/{activity_main.xml => activity_main_i_m3.xml} (100%) create mode 100755 app/src/main/res/layout-land/activity_main_m4.xml rename app/src/main/res/layout/{activity_main.xml => activity_main_i_m3.xml} (99%) create mode 100755 app/src/main/res/layout/activity_main_m4.xml rename app/src/main/res/layout/{dialog_ringsettings.xml => dialog_ringsettings_i_m3.xml} (100%) create mode 100755 app/src/main/res/layout/dialog_ringsettings_m4.xml diff --git a/CHANGELOG.txt b/CHANGELOG.txt index fad2ec9..f37101e 100755 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,10 +1,12 @@ CHANGELOG ENIGMANDROID v0.1.5-not.yet.released< -*added missing licenses to class files -*Added proper documentation -*Extended input interpretation (number spelling in different languages, any unknown special -character now becomes 'X' +*Added french number spelling +*Added Enigma Models M3, M4 +*Added option to select different enigma models into options menu +*Added developer class for simple rotor creation (not a feature in the app) +*Fixed broken ring settings +*Fixed false reset of ring settings when switching from/to landscape mode v0.1.4-15.08.2015< @@ -12,6 +14,10 @@ v0.1.4-15.08.2015< *Fixed some layout issues *Fixed anomaly for step by step inputs *Added send/receive text functionality +*added missing licenses to class files +*Added proper documentation +*Extended input interpretation (number spelling in different languages, any unknown special +character now becomes 'X' v0.1.3-14.03.2015< *Added About Dialog with ChangeLog-Button diff --git a/app/app.iml b/app/app.iml index c5beb38..e002617 100644 --- a/app/app.iml +++ b/app/app.iml @@ -71,7 +71,7 @@ - + @@ -87,9 +87,9 @@ - + - - + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 467b24a..5032d0f 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,15 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 22 + compileSdkVersion 23 buildToolsVersion "22.0.1" defaultConfig { applicationId "de.vanitasvitae.enigmandroid" minSdkVersion 15 - targetSdkVersion 22 - versionCode 10 - versionName "0.1.4-15.08.2015-beta" + targetSdkVersion 23 + versionCode 11 + versionName "0.1.5-27.08.2015-beta" } buildTypes { release { @@ -19,5 +19,5 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:support-v4:22.2.1' + compile 'com.android.support:support-v4:23.0.0' } diff --git a/app/src/androidTest/java/de/vanitasvitae/enigmandroid/ApplicationTest.java b/app/src/androidTest/java/de/vanitasvitae/enigmandroid/ApplicationTest.java deleted file mode 100644 index 5e12da5..0000000 --- a/app/src/androidTest/java/de/vanitasvitae/enigmandroid/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.vanitasvitae.enigmandroid; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 568284b..e24a93d 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ android:theme="@style/AppTheme" > diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/Enigma.java b/app/src/main/java/de/vanitasvitae/enigmandroid/Enigma.java deleted file mode 100755 index a1dce31..0000000 --- a/app/src/main/java/de/vanitasvitae/enigmandroid/Enigma.java +++ /dev/null @@ -1,242 +0,0 @@ -package de.vanitasvitae.enigmandroid; - -import de.vanitasvitae.enigmandroid.rotors.Reflector; -import de.vanitasvitae.enigmandroid.rotors.Rotor; - -/** - * Main component of the Enigma machine - * Copyright (C) 2015 Paul Schaub - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * @author vanitasvitae - */ -public class Enigma -{ - private Plugboard plugboard; - - //Slots for the rotors - private Rotor r1; - private Rotor r2; - private Rotor r3; - - //Slot for the reflector - private Reflector reflector; - - private boolean doAnomaly = false; //Has the time come to handle an anomaly? - - private boolean prefAnomaly; //Do you WANT to simulate the anomaly? - private InputPreparator inputPreparator; - - /** - * Create new Enigma with standard configuration. - * Empty Plugboard, reflector B, rotors I,II,III, Positions 0,0,0 - */ - public Enigma() - { - initialize(); - } - - private void initialize() - { - this.r1 = Rotor.createRotorI(0, 0); - this.r2 = Rotor.createRotorII(0, 0); - this.r3 = Rotor.createRotorIII(0, 0); - this.reflector = Reflector.createReflectorB(); - plugboard = new Plugboard(); - prefAnomaly = true; //TODO: Is this necessary? - } - - /** - * Encrypt / Decrypt a given String w. - * w must be prepared using prepare(w) beforehand. - * Doing so changes the state of the rotors but not the state of the plugboard and the - * ringSettings - * - * @param w Text to decrypt/encrypt - * @return encrypted/decrypted string - */ - public String encrypt(String w) - { - //output string - String output = ""; - //for each char x in k - for (char x : w.toCharArray()) - { - output = output + this.encryptChar(x); - } - //return en-/decrypted string - return output; - } - - /** - * Substitute char k by sending the signal through the enigma. - * This rotates the first rotor and eventually also the second/third beforehand. - * Also this method handles the anomaly in case it should happen - * - * @param k input char - * @return substituted output char - */ - public char encryptChar(char k) - { - //Rotate rotors - r1.rotate(); - //Eventually turn next rotor (usual turnOver or anomaly) - if (r1.isAtTurnoverPosition() || (this.doAnomaly && prefAnomaly)) - { - r2.rotate(); - //Set doAnomaly for next call of encryptChar - this.doAnomaly = r2.doubleTurnAnomaly(); - //Eventually rotate next rotor - if (r2.isAtTurnoverPosition()) - { - r3.rotate(); - } - } - int x = ((int) k)-65; //Cast to int and remove Unicode Offset (A=65 in Unicode.) - - //Encryption - //forward direction - x = plugboard.encrypt(x); - x = normalize(x + r1.getRotation()); - x = r1.encryptForward(x); - x = normalize(x + r2.getRotation() - r1.getRotation()); - x = r2.encryptForward(x); - x = normalize(x + r3.getRotation() - r2.getRotation()); - x = r3.encryptForward(x); - x = normalize(x - r3.getRotation()); - //backward direction - x = reflector.encrypt(x); - x = normalize(x + r3.getRotation()); - x = r3.encryptBackward(x); - x = normalize(x - r3.getRotation() + r2.getRotation()); - x = r2.encryptBackward(x); - x = normalize(x - r2.getRotation() + r1.getRotation()); - x = r1.encryptBackward(x); - x = normalize(x - r1.getRotation()); - x = plugboard.encrypt(x); - - return (char) (x + 65); //Add Offset again and cast back to char - } - - /** - * Normalize the input. - * Normalizing means keeping the input via modulo in the range from 0 to n-1, where n is equal - * to the size of the rotors (in this case fixed to 26, TODO?). - * This is necessary since java allows negative modulo values, - * which can break this implementation - * @param input input signal - * @return "normalized" input signal - */ - public int normalize(int input) - { - return (26+input)%26; - } - - /** - * Set config of the enigma - * - * @param conf configuration - */ - public void setConfiguration(int[] conf) - { - if (conf == null || conf.length != 10) - { - initialize(); - } - else - { - int ro1 = conf[0]; - int ro2 = conf[1]; - int ro3 = conf[2]; - int ref = conf[3]; - int ro1rot = normalize(conf[4] - 1); - int ro2rot = normalize(conf[5] - 1); - int ro3rot = normalize(conf[6] - 1); - int ro1Ring = conf[7]; - int ro2Ring = conf[8]; - int ro3Ring = conf[9]; - - //Set rotors - r1 = Rotor.createRotor(ro1, ro1Ring, ro1rot); - r2 = Rotor.createRotor(ro2, ro2Ring, ro2rot); - r3 = Rotor.createRotor(ro3, ro3Ring, ro3rot); - - //Set reflector - reflector = Reflector.createReflector(ref); - - //catch double turn anomaly on step by step basis - this.doAnomaly = r2.doubleTurnAnomaly(); - } - } - - /** - * Set the plugboard - * @param p Plugboard - */ - public void setPlugboard(Plugboard p) - { - this.plugboard = p; - } - - /** - * Return the configuration, the enigma machine is in right NOW - * - * @return array containing configuration - */ - public int[] getConfiguration() - { - int[] configuration = new int[10]; - { - configuration[0] = r1.getType(); - configuration[1] = r2.getType(); - configuration[2] = r3.getType(); - configuration[3] = reflector.getType(); - configuration[4] = r1.getRotation(); - configuration[5] = r2.getRotation(); - configuration[6] = r3.getRotation(); - configuration[7] = r1.getRingSetting(); - configuration[8] = r2.getRingSetting(); - configuration[9] = r3.getRingSetting(); - } - return configuration; - } - - /** - * Set the inputPreparator - * @param preparator concrete InputPreparator - */ - public void setInputPreparator(InputPreparator preparator) - { - this.inputPreparator = preparator; - } - - /** - * Return the inputPreparator - * @return inputPreparator - */ - public InputPreparator getInputPreparator() - { - return this.inputPreparator; - } - - /** - * set prefAnomaly variable - * @param b boolean - */ - public void setPrefAnomaly(boolean b) - { - this.prefAnomaly = b; - } -} diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/MainActivity.java b/app/src/main/java/de/vanitasvitae/enigmandroid/MainActivity.java index 4f1525b..9f029a1 100755 --- a/app/src/main/java/de/vanitasvitae/enigmandroid/MainActivity.java +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/MainActivity.java @@ -5,16 +5,19 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; import android.preference.PreferenceManager; -import android.view.View; import android.view.Menu; import android.view.MenuItem; -import android.widget.EditText; -import android.widget.Spinner; -import android.widget.ArrayAdapter; +import android.view.View; import android.widget.Toast; + +import de.vanitasvitae.enigmandroid.enigma.Plugboard; +import de.vanitasvitae.enigmandroid.enigma.inputPreparer.InputPreparer; +import de.vanitasvitae.enigmandroid.layout.LayoutContainer; + /** * Main Android Activity of the app * Copyright (C) 2015 Paul Schaub @@ -36,41 +39,27 @@ import android.widget.Toast; */ public class MainActivity extends Activity { - private Spinner rotor1View; - private Spinner rotor2View; - private Spinner rotor3View; - private Spinner reflectorView; - private Spinner rotor1PositionView; - private Spinner rotor2PositionView; - private Spinner rotor3PositionView; - - private EditText plugboardView; - private EditText inputView; - private EditText outputView; - private static final int RESULT_SETTINGS = 1; - private static final String URI_CHANGELOG = "https://github.com/vanitasvitae/EnigmAndroid/blob/master/CHANGELOG.txt"; + private static final String URI_CHANGELOG = + "https://github.com/vanitasvitae/EnigmAndroid/blob/master/CHANGELOG.txt"; - private Enigma enigma; - //memory for the ringSettings - private int[] ringSettings = {0,0,0}; - private boolean prefAnomaly; - private String prefNumericLanguage; + LayoutContainer layoutContainer; + protected String prefMachineType; + protected boolean prefAnomaly; + protected String prefNumericLanguage; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - this.setContentView(R.layout.activity_main); - this.initLayout(); SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - this.prefAnomaly = sharedPreferences.getBoolean("prefAnomaly", true); - this.prefNumericLanguage = sharedPreferences.getString("prefNumericLanguage",getResources(). - getStringArray(R.array.pref_alias_numeric_spelling_language)[0]); - this.resetLayout(); + this.prefMachineType = sharedPreferences.getString("prefMachineType", getResources(). + getStringArray(R.array.pref_list_machine_type)[0]); ActivitySingleton singleton = ActivitySingleton.getInstance(); singleton.setActivity(this); - + updateContentView(); + layoutContainer = LayoutContainer.createLayoutContainer(prefMachineType); + updatePreferenceValues(); //Handle shared text Intent intent = getIntent(); String action = intent.getAction(); @@ -82,12 +71,102 @@ public class MainActivity extends Activity String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); if (sharedText != null) { - inputView.setText(sharedText); + layoutContainer.getInputView().setText(sharedText); } } } } + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + } + + private void updateContentView() + { + switch (prefMachineType) + { + case "M4": + this.setContentView(R.layout.activity_main_m4); + break; + case "M3": + this.setContentView(R.layout.activity_main_i_m3); + break; + default: + this.setContentView(R.layout.activity_main_i_m3); + break; + } + } + + private void updatePreferenceValues() + { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); + this.setPrefMachineType(sharedPreferences.getString("prefMachineType", getResources(). + getStringArray(R.array.pref_list_machine_type)[0])); + this.setPrefAnomaly(sharedPreferences.getBoolean("prefAnomaly", true)); + this.setPrefNumericLanguage(sharedPreferences.getString("prefNumericLanguage", getResources(). + getStringArray(R.array.pref_alias_numeric_spelling_language)[0])); + } + + public void setPrefMachineType(String type) + { + if(prefMachineType == null || !prefMachineType.equals(type)) + { + prefMachineType = type; + String savedInput = ""; + if(layoutContainer != null) + { + savedInput = layoutContainer.getInputView().getText().toString(); + } + updateContentView(); + layoutContainer = LayoutContainer.createLayoutContainer(prefMachineType); + layoutContainer.getInputView().setText(savedInput); + } + } + + public String getPrefMachineType() + { + if(prefMachineType != null) return prefMachineType; + else + { + updatePreferenceValues(); + return prefMachineType; + } + } + + public void setPrefAnomaly(boolean anomaly) + { + if(prefAnomaly !=anomaly) + { + prefAnomaly = anomaly; + layoutContainer.getEnigma().setPrefAnomaly(anomaly); + } + } + + public boolean getPrefAnomaly() + { + return prefAnomaly; + } + + public void setPrefNumericLanguage(String lang) + { + if(prefNumericLanguage == null || !prefNumericLanguage.equals(lang)) + { + prefNumericLanguage = lang; + layoutContainer.getEnigma().setInputPreparer(InputPreparer.createInputPreparer(lang)); + } + } + + public String getPrefNumericLanguage() + { + if(prefNumericLanguage != null) return prefNumericLanguage; + else + { + updatePreferenceValues(); + return prefNumericLanguage; + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { @@ -104,14 +183,14 @@ public class MainActivity extends Activity int id = item.getItemId(); if (id == R.id.action_reset) { - this.resetLayout(); + layoutContainer.reset(); Toast.makeText(getApplicationContext(), R.string.message_reset, Toast.LENGTH_SHORT).show(); return true; } else if (id == R.id.action_choose_ringstellung) { - showRingSettingsDialog(); + layoutContainer.showRingSettingsDialog(); return true; } else if (id == R.id.action_settings) @@ -126,7 +205,7 @@ public class MainActivity extends Activity } else if (id == R.id.action_send) { - if(outputView.getText().length() == 0) + if(layoutContainer.getOutputView().getText().length() == 0) { Toast.makeText(this, R.string.error_no_text_to_send, Toast.LENGTH_SHORT).show(); } @@ -134,7 +213,7 @@ public class MainActivity extends Activity { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, outputView.getText().toString()); + sendIntent.putExtra(Intent.EXTRA_TEXT, layoutContainer.getOutputView().getText().toString()); sendIntent.setType("text/plain"); startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to))); } @@ -142,36 +221,6 @@ public class MainActivity extends Activity return super.onOptionsItemSelected(item); } - /** - * Updates the enigma to the chosen rotors and plugboard - * - * @param v View - */ - public void updateEnigma(View v) - { - int[] conf = new int[10]; - conf[0] = rotor1View.getSelectedItemPosition() + 1; - conf[1] = rotor2View.getSelectedItemPosition() + 1; - conf[2] = rotor3View.getSelectedItemPosition() + 1; - conf[3] = reflectorView.getSelectedItemPosition() + 1; - conf[4] = rotor1PositionView.getSelectedItemPosition() + 1; - conf[5] = rotor2PositionView.getSelectedItemPosition() + 1; - conf[6] = rotor3PositionView.getSelectedItemPosition() + 1; - conf[7] = ringSettings[0]; - conf[8] = ringSettings[1]; - conf[9] = ringSettings[2]; - - enigma = new Enigma(); - - int[][] plugboardConfiguration; - plugboardView.setText(plugboardView.getText().toString().toUpperCase()); - plugboardConfiguration = Plugboard.parseConfigurationString(plugboardView.getText().toString()); - enigma.setConfiguration(conf); - enigma.setPlugboard(new Plugboard(plugboardConfiguration)); - enigma.setPrefAnomaly(prefAnomaly); - enigma.setInputPreparator(InputPreparator.createInputPreparator(prefNumericLanguage)); - } - /** * Set the chosen Configuration to the enigma, get the input string from the input textbox and * prepare it, set the input to the prepared text, encrypt the prepared input and set the @@ -180,174 +229,21 @@ public class MainActivity extends Activity */ public void doCrypto(View v) { - if(inputView.getText().length()!=0) { - updateEnigma(null); - String m = inputView.getText().toString(); - m = enigma.getInputPreparator().prepareString(m); - inputView.setText(m); - outputView.setText(enigma.encrypt(m)); - updateSpinner(enigma.getConfiguration()); + if(layoutContainer.getInputView().getText().length()!=0) { + layoutContainer.getEnigma().setConfiguration(layoutContainer.createConfiguration()); + layoutContainer.getEnigma().setPlugboard(new Plugboard(layoutContainer.createPlugboardConfiguration())); + String m = layoutContainer.getInputView().getText().toString(); + m = layoutContainer.getEnigma().getInputPreparer().prepareString(m); + layoutContainer.getInputView().setText(m); + layoutContainer.getOutputView().setText(layoutContainer.getEnigma().encrypt(m)); + layoutContainer.updateLayout(); } } - /** - * Reset all the spinners and textboxes and the ringsettings memory + * Show a Dialog containing information about the app, license, usage, author and a link + * to the changelog */ - private void resetLayout() - { - rotor1View.setSelection(0); - rotor2View.setSelection(1); - rotor3View.setSelection(2); - reflectorView.setSelection(1); - rotor1PositionView.setSelection(0); - rotor2PositionView.setSelection(0); - rotor3PositionView.setSelection(0); - ringSettings = new int[]{0,0,0}; - plugboardView.setText(""); - inputView.setText(""); - outputView.setText(""); - } - - /** - * Initialize the Layout - */ - private void initLayout() - { - Character[] charArray = new Character[26]; - for(int i=0; i<26; i++) {charArray[i] = (char) (65+i);} - - rotor1View = (Spinner) findViewById(R.id.rotor1); - ArrayAdapter rotor1Adapter = ArrayAdapter.createFromResource(this, - R.array.enigma_rotors, android.R.layout.simple_spinner_item); - rotor1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - rotor1View.setAdapter(rotor1Adapter); - - - rotor2View = (Spinner) findViewById(R.id.rotor2); - ArrayAdapter rotor2Adapter = ArrayAdapter.createFromResource(this, - R.array.enigma_rotors, android.R.layout.simple_spinner_item); - rotor2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - rotor2View.setAdapter(rotor2Adapter); - - rotor3View = (Spinner) findViewById(R.id.rotor3); - ArrayAdapter rotor3Adapter = ArrayAdapter.createFromResource(this, - R.array.enigma_rotors, android.R.layout.simple_spinner_item); - rotor3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - rotor3View.setAdapter(rotor3Adapter); - - reflectorView = (Spinner) findViewById(R.id.reflector); - ArrayAdapter relfectorAdapter = ArrayAdapter.createFromResource(this, - R.array.enigma_reflectors, android.R.layout.simple_spinner_item); - relfectorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - reflectorView.setAdapter(relfectorAdapter); - - rotor1PositionView = (Spinner) findViewById(R.id.rotor1position); - ArrayAdapter rotor1PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(), - android.R.layout.simple_spinner_item,charArray); - rotor1PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - rotor1PositionView.setAdapter(rotor1PositionAdapter); - - rotor2PositionView = (Spinner) findViewById(R.id.rotor2position); - ArrayAdapter rotor2PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(), - android.R.layout.simple_spinner_item,charArray); - rotor2PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - rotor2PositionView.setAdapter(rotor2PositionAdapter); - - rotor3PositionView = (Spinner) findViewById(R.id.rotor3position); - ArrayAdapter rotor3PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(), - android.R.layout.simple_spinner_item,charArray); - rotor3PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - rotor3PositionView.setAdapter(rotor3PositionAdapter); - - plugboardView = (EditText) findViewById(R.id.plugboard); - plugboardView.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (!hasFocus) { - plugboardView.setText(plugboardView.getText().toString().toUpperCase()); - } - } - }); - inputView = (EditText) findViewById(R.id.input); - outputView = (EditText) findViewById(R.id.output); - - inputView.requestFocus(); - } - - /** - * Update the Spinners to their new Positions - * @param c Configuration - */ - public void updateSpinner(int[] c) - { - rotor1View.setSelection(c[0] - 1); - rotor2View.setSelection(c[1] - 1); - rotor3View.setSelection(c[2] - 1); - rotor1PositionView.setSelection(c[4]); - rotor2PositionView.setSelection(c[5]); - rotor3PositionView.setSelection(c[6]); - } - - /** - * Show the dialog where the user can pick the ringsettings and set them if the user doesn't - * abort. - */ - public void showRingSettingsDialog() - { - View ringSettingsView = View.inflate(this, R.layout.dialog_ringsettings, null); - - Integer[] ringArray = new Integer[26]; - for(int i=1; i<=26; i++) {ringArray[i-1] = i;} - - final Spinner ring1 = (Spinner) ringSettingsView.findViewById(R.id.rotor1ring); - ArrayAdapter ring1Adapter = new ArrayAdapter<>(this.getApplicationContext(), - android.R.layout.simple_spinner_item,ringArray); - ring1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - ring1.setAdapter(ring1Adapter); - ring1.setSelection(ringSettings[0]); - - final Spinner ring2 = (Spinner) ringSettingsView.findViewById(R.id.rotor2ring); - ArrayAdapter ring2Adapter = new ArrayAdapter<>(this.getApplicationContext(), - android.R.layout.simple_spinner_item,ringArray); - ring2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - ring2.setAdapter(ring2Adapter); - ring2.setSelection(ringSettings[1]); - - final Spinner ring3 = (Spinner) ringSettingsView.findViewById(R.id.rotor3ring); - ArrayAdapter ring3Adapter = new ArrayAdapter<>(this.getApplicationContext(), - android.R.layout.simple_spinner_item,ringArray); - ring3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - ring3.setAdapter(ring3Adapter); - ring3.setSelection(ringSettings[2]); - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.title_ringsetting); - builder.setView(ringSettingsView) - .setCancelable(true) - .setPositiveButton(R.string.dialog_positiv, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int id) - { - ringSettings = new int[]{ring1.getSelectedItemPosition(), - ring2.getSelectedItemPosition(), ring3.getSelectedItemPosition()}; - String message = getResources().getString(R.string.dialog_ringsettings_success) + " " + (ringSettings[2]+1) + ", " + - (ringSettings[1]+1) + ", " + (ringSettings[0]+1) + "."; - Toast.makeText(getApplicationContext(), message, - Toast.LENGTH_LONG).show(); - } - }) - .setNegativeButton(R.string.dialog_negativ, new DialogInterface.OnClickListener() - { - public void onClick(DialogInterface dialog, int id) - { - dialog.cancel(); - Toast.makeText(getApplicationContext(), R.string.dialog_ringsettings_abort, - Toast.LENGTH_SHORT).show(); - } - }).show(); - } - public void showAboutDialog() { final View aboutView = View.inflate(this, R.layout.dialog_about, null); @@ -373,6 +269,12 @@ public class MainActivity extends Activity }).show(); } + /** + * Handle preference changes + * @param requestCode requestCode + * @param resultCode resultCode (RESULT_SETTINGS is defined at the top) + * @param data data (not important here) + */ protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -380,45 +282,63 @@ public class MainActivity extends Activity case RESULT_SETTINGS: { SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - this.prefAnomaly = sharedPrefs.getBoolean("prefAnomaly", true); - this.prefNumericLanguage = sharedPrefs.getString("prefNumericLanguage",getResources(). - getStringArray(R.array.pref_alias_numeric_spelling_language)[0]); - ; + this.setPrefMachineType(sharedPrefs.getString("prefMachineType", getResources() + .getStringArray(R.array.pref_list_machine_type)[0])); + this.setPrefAnomaly(sharedPrefs.getBoolean("prefAnomaly", true)); + this.setPrefNumericLanguage(this.prefNumericLanguage = sharedPrefs.getString("prefNumericLanguage", getResources(). + getStringArray(R.array.pref_alias_numeric_spelling_language)[0])); break; } } } + /** + * Open the web page with the URL url + * @param url URL of the website + */ public void openWebPage(String url) { - Uri webpage = Uri.parse(url); - Intent intent = new Intent(Intent.ACTION_VIEW, webpage); + Uri webPage = Uri.parse(url); + Intent intent = new Intent(Intent.ACTION_VIEW, webPage); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } } - public static class ActivitySingleton +/** + * Singleton that grants access to an Activity from anywhere within the app + */ +public static class ActivitySingleton +{ + private static ActivitySingleton instance = null; + private Activity activity; + + //private constructor + private ActivitySingleton(){} + //Singleton method + public static ActivitySingleton getInstance() { - private static ActivitySingleton instance = null; - private Activity activity; - - private ActivitySingleton(){} - public static ActivitySingleton getInstance() - { - if(instance == null) instance = new ActivitySingleton(); - return instance; - } - - public void setActivity(Activity activity) - { - this.activity = activity; - } - - public Activity getActivity() - { - return activity; - } - + if(instance == null) instance = new ActivitySingleton(); + return instance; } + + /** + * Set an Activity that the Singleton returns + * @param activity activity that's stored + */ + public void setActivity(Activity activity) + { + this.activity = activity; + } + + /** + * Returns the stored Activity + * @return stored Activity + */ + public Activity getActivity() + { + return activity; + } + +} } diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma.java new file mode 100755 index 0000000..d14652a --- /dev/null +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma.java @@ -0,0 +1,168 @@ +package de.vanitasvitae.enigmandroid.enigma; + +import de.vanitasvitae.enigmandroid.MainActivity; +import de.vanitasvitae.enigmandroid.enigma.inputPreparer.InputPreparer; + +/** + * Main component of the Enigma machine + * This is the mostly abstract base of any enigma machine. + * Copyright (C) 2015 Paul Schaub + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * @author vanitasvitae + */ +public abstract class Enigma +{ + protected static String machineType; + protected boolean doAnomaly = false; //Has the time come to handle an anomaly? + protected boolean prefAnomaly; //Do you WANT to simulate the anomaly? + protected InputPreparer inputPreparer; + + /** + * Set the enigma to an initial state + */ + public abstract void initialize(); + + /** + * Set the reflector of the enigma machine to one of type type. + * @param type type indicator of the reflector + * @return success (not every reflector fits in every machine) + */ + public abstract boolean setReflector(int type); + + /** + * Set the rotor on position pos to a rotor of type type with ring-setting ringSetting and + * rotation rotation. + * @param pos position of the rotor + * @param type type indicator + * @param ringSetting ringSetting + * @param rotation rotation + * @return success + */ + public abstract boolean setRotor(int pos, int type, int ringSetting, int rotation); + + /** + * Encrypt / Decrypt a given String w. + * w must be prepared using prepare(w) beforehand. + * Doing so changes the state of the rotors but not the state of the plugboard and the + * ringSettings + * + * @param w Text to decrypt/encrypt + * @return encrypted/decrypted string + */ + public String encrypt(String w) + { + //output string + String output = ""; + //for each char x in k + for (char x : w.toCharArray()) + { + output = output + this.encryptChar(x); + } + //return en-/decrypted string + return output; + } + + /** + * Set the enigma into the next mechanical state. + * This rotates the first rotor and eventually also the second/third. + * Also this method handles the anomaly in case it should happen. + */ + public abstract void nextState(); + + /** + * Substitute char k by sending the signal through the enigma. + * The signal passes the plugboard, the rotors and returns back after going through the + * reflector wheel. + * + * @param k input char + * @return substituted output char + */ + public abstract char encryptChar(char k); + + /** + * Normalize the input. + * Normalizing means keeping the input via modulo in the range from 0 to n-1, where n is equal + * to the size of the rotors. + * This is necessary since java allows negative modulo values, + * which can break this implementation + * @param input input signal + * @return "normalized" input signal + */ + public abstract int normalize(int input); + + /** + * Set config of the enigma + * + * @param conf configuration + */ + public abstract void setConfiguration(int[] conf); + + /** + * Set the plugboard + * @param p Plugboard + */ + public abstract void setPlugboard(Plugboard p); + + /** + * Return the configuration, the enigma machine is in right NOW + * + * @return array containing configuration + */ + public abstract int[] getConfiguration(); + + /** + * Set the inputPreparer + * @param preparer concrete InputPreparer + */ + public void setInputPreparer(InputPreparer preparer) + { + this.inputPreparer = preparer; + } + + + + /** + * Return the inputPreparer + * @return inputPreparer + */ + public InputPreparer getInputPreparer() + { + if(inputPreparer == null) + { + MainActivity main = (MainActivity) MainActivity.ActivitySingleton.getInstance().getActivity(); + inputPreparer = InputPreparer.createInputPreparer(main.getPrefNumericLanguage()); + } + return this.inputPreparer; + } + + /** + * set prefAnomaly variable + * @param b boolean + */ + public void setPrefAnomaly(boolean b) + { + this.prefAnomaly = b; + } + + /** + * Return the type indicator of the enigma machine + * @return type + */ + public String getMachineType() + { + return machineType; + } +} diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_I.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_I.java new file mode 100644 index 0000000..969f950 --- /dev/null +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_I.java @@ -0,0 +1,171 @@ +package de.vanitasvitae.enigmandroid.enigma; + +import android.util.Log; + +import de.vanitasvitae.enigmandroid.enigma.rotors.Reflector; +import de.vanitasvitae.enigmandroid.enigma.rotors.Rotor; + +/** + * Concrete implementation of an enigma machine of type I + * Copyright (C) 2015 Paul Schaub + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * @author vanitasvitae + */ +public class Enigma_I extends Enigma { + protected Rotor rotor1; + protected Rotor rotor2; + protected Rotor rotor3; + + protected Reflector reflector; + + protected Plugboard plugboard; + + public Enigma_I() { + machineType = "I"; + } + + @Override + public void initialize() + { + this.setPlugboard(new Plugboard()); + //I,II,III, A, 0,0,0, 0,0,0 + this.setConfiguration(new int[]{1,2,3,1,0,0,0,0,0,0}); + } + + @Override + public void nextState() + { + rotor1.rotate(); + if (rotor1.isAtTurnoverPosition() || (this.doAnomaly && prefAnomaly)) + { + rotor2.rotate(); + this.doAnomaly = rotor2.doubleTurnAnomaly(); + if (rotor2.isAtTurnoverPosition()) + { + rotor3.rotate(); + } + } + } + + @Override + public char encryptChar(char k) + { + nextState(); + int x = ((int) k)-65; //Cast to int and remove Unicode Offset (A=65 in Unicode.) + //Encryption + //forward direction + x = plugboard.encrypt(x); + x = normalize(x + rotor1.getRotation() - rotor1.getRingSetting()); + x = rotor1.encryptForward(x); + x = normalize(x - rotor1.getRotation() + rotor1.getRingSetting() + rotor2.getRotation() - rotor2.getRingSetting()); + x = rotor2.encryptForward(x); + x = normalize(x - rotor2.getRotation() + rotor2.getRingSetting() + rotor3.getRotation() - rotor3.getRingSetting()); + x = rotor3.encryptForward(x); + x = normalize(x - rotor3.getRotation() + rotor3.getRingSetting()); + //TODO: CHECK + //backward direction + x = reflector.encrypt(x); + x = normalize(x + rotor3.getRotation() - rotor3.getRingSetting()); + x = rotor3.encryptBackward(x); + x = normalize(x + rotor2.getRotation() - rotor2.getRingSetting() - rotor3.getRotation() + rotor3.getRingSetting()); + x = rotor2.encryptBackward(x); + x = normalize(x + rotor1.getRotation() - rotor1.getRingSetting() - rotor2.getRotation() + rotor2.getRingSetting()); + x = rotor1.encryptBackward(x); + x = normalize(x - rotor1.getRotation() + rotor1.getRingSetting()); + x = plugboard.encrypt(x); + return (char) (x + 65); //Add Offset again, cast back to char and return + } + + @Override + public int normalize(int input) { + return (input+26) % 26; + } + + @Override + public void setPlugboard(Plugboard p) + { + this.plugboard = p; + } + + @Override + public int[] getConfiguration() + { + int[] conf = new int[10]; + conf[0] = rotor1.getType(); + conf[1] = rotor2.getType(); + conf[2] = rotor3.getType(); + conf[3] = reflector.getType(); + conf[4] = rotor1.getRotation(); + conf[5] = rotor2.getRotation(); + conf[6] = rotor3.getRotation(); + conf[7] = rotor1.getRingSetting(); + conf[8] = rotor2.getRingSetting(); + conf[9] = rotor3.getRingSetting(); + return conf; + } + + @Override + /** + * conf: + * 0..2 -> rotor1..rotor3 type + * 3 -> reflector type + * 4..6 -> rotor1..rotor3 rotation + * 7..9 -> rotor1..rotor3 ringSetting + */ + public void setConfiguration(int[] conf) + { + this.setRotor(1,conf[0],conf[7],conf[4]); + this.setRotor(2,conf[1],conf[8],conf[5]); + this.setRotor(3,conf[2],conf[9],conf[6]); + this.setReflector(conf[3]); + } + + @Override + public boolean setRotor(int pos, int type, int ringSetting, int rotation) + { + if(pos >= 1 && pos <= 3) { + if (type >= 1 && type <= 5) { + Rotor rotor = Rotor.createRotor(type, ringSetting, rotation); + switch (pos) { + case 1: + rotor1 = rotor; + break; + case 2: + rotor2 = rotor; + break; + default: + rotor3 = rotor; + break; + } + return true; + } + } + Log.d("EnigmAndroid/M3/setRot", "Error: Type " + type + " at position " + pos); + return false; + } + + @Override + public boolean setReflector(int type) + { + if(type >= 1 && type <= 3) + { + reflector = Reflector.createReflector(type); + return true; + } + Log.d("EnigmAndroid/I/setRef", "Error: Can't set type "+type); + return false; + } +} diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M3.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M3.java new file mode 100644 index 0000000..d016838 --- /dev/null +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M3.java @@ -0,0 +1,75 @@ +package de.vanitasvitae.enigmandroid.enigma; + +import android.util.Log; + +import de.vanitasvitae.enigmandroid.enigma.rotors.Reflector; +import de.vanitasvitae.enigmandroid.enigma.rotors.Rotor; + +/** + * Concrete implementation of an enigma machine model M3 + * Copyright (C) 2015 Paul Schaub + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * @author vanitasvitae + */ +public class Enigma_M3 extends Enigma_I +{ + public Enigma_M3() + { + machineType = "M3"; + } + @Override + public void initialize() + { + this.setPlugboard(new Plugboard()); + //I,II,III, B, 0,0,0, 0,0,0 + this.setConfiguration(new int[]{1,2,3,2,0,0,0,0,0,0}); + } + @Override + public boolean setRotor(int pos, int type, int ringSetting, int rotation) + { + if(pos >= 1 && pos <= 3) { + if (type >= 1 && type <= 8) { + Rotor rotor = Rotor.createRotor(type, ringSetting, rotation); + switch (pos) { + case 1: + rotor1 = rotor; + break; + case 2: + rotor2 = rotor; + break; + default: + rotor3 = rotor; + break; + } + return true; + } + } + Log.d("EnigmAndroid/M3/setRot", "Error: Type " + type + " at position " + pos); + return false; + } + + @Override + public boolean setReflector(int type) + { + if(type >= 2 && type <= 3) + { + reflector = Reflector.createReflector(type); + return true; + } + Log.d("EnigmAndroid/M3/setRef", "Error: Can't set type "+type); + return false; + } +} diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M4.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M4.java new file mode 100644 index 0000000..444faab --- /dev/null +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Enigma_M4.java @@ -0,0 +1,214 @@ +package de.vanitasvitae.enigmandroid.enigma; + +import android.util.Log; + +import de.vanitasvitae.enigmandroid.enigma.rotors.Reflector; +import de.vanitasvitae.enigmandroid.enigma.rotors.Rotor; + +/** + * Concrete Implementation of the Enigma Machine type M4 of the german Kriegsmarine + * Copyright (C) 2015 Paul Schaub + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * @author vanitasvitae + */ +public class Enigma_M4 extends Enigma +{ + private Rotor rotor1; + private Rotor rotor2; + private Rotor rotor3; + + private Rotor rotor4; + private Reflector thinReflector; + + private Plugboard plugboard; + + public Enigma_M4() + { + machineType = "M4"; + } + + @Override + public boolean setRotor(int pos, int type, int ringSetting, int rotation) + { + if(pos >= 1 && pos <= 3) + { + if(type >= 1 && type <= 8) + { + Rotor rotor = Rotor.createRotor(type, ringSetting, rotation); + switch (pos) + { + case 1: rotor1 = rotor; + break; + case 2: rotor2 = rotor; + break; + default: rotor3 = rotor; + } + return true; + } + else + { + Log.d("EnigmAndroid/M4/setRot", "Error: Type " + type + " at position " + pos); + return false; + } + } + //Thin rotor + else if(pos == 4) + { + if(type >=9 && type <=10) { + rotor4 = Rotor.createRotor(type, ringSetting, rotation); + return true; + } + } + Log.d("EnigmAndroid/M3/setRot", "Error: Type " + type + " at position " + pos); + return false; + } + + @Override + public boolean setReflector(int type) + { + if(type >= 4 && type <=5) + { + this.thinReflector = Reflector.createReflector(type); + return true; + } + Log.d("EnigmAndroid/M4/setRef","Error: Can't set type "+type); + return false; + } + + @Override + public void initialize() + { + this.setPlugboard(new Plugboard()); + this.setConfiguration(new int[]{1,2,3,9,4,0,0,0,0,0,0,0,0}); + this.prefAnomaly = true; + } + + @Override + /** + * Set the enigma into the next mechanical state. + * This rotates the first rotor and eventually also the second/third. + * Also this method handles the anomaly in case it should happen. + */ + public void nextState() + { + //Rotate rotors + rotor1.rotate(); + //Eventually turn next rotor (usual turnOver or anomaly) + if (rotor1.isAtTurnoverPosition() || (this.doAnomaly && prefAnomaly)) + { + rotor2.rotate(); + //Set doAnomaly for next call of encryptChar + this.doAnomaly = rotor2.doubleTurnAnomaly(); + //Eventually rotate next rotor + if (rotor2.isAtTurnoverPosition()) + { + rotor3.rotate(); + } + } + } + + @Override + /** + * Substitute char k by sending the signal through the enigma. + * The signal passes the plugboard, the rotors and returns back after going through the + * reflector wheel. + * + * @param k input char + * @return substituted output char + */ + public char encryptChar(char k) + { + nextState(); //Rotate rotors + int x = ((int) k)-65; //Cast to int and remove Unicode Offset (A=65 in Unicode.) + //Encryption + //forward direction + x = plugboard.encrypt(x); + x = normalize(x + rotor1.getRotation() - rotor1.getRingSetting()); + x = rotor1.encryptForward(x); + x = normalize(x - rotor1.getRotation() + rotor1.getRingSetting() + rotor2.getRotation() - rotor2.getRingSetting()); + x = rotor2.encryptForward(x); + x = normalize(x - rotor2.getRotation() + rotor2.getRingSetting() + rotor3.getRotation() - rotor3.getRingSetting()); + x = rotor3.encryptForward(x); + x = normalize(x - rotor3.getRotation() + rotor3.getRingSetting() + rotor4.getRotation() - rotor4.getRingSetting()); + x = rotor4.encryptForward(x); + x = normalize(x - rotor4.getRotation() + rotor4.getRingSetting()); + //backward direction + x = thinReflector.encrypt(x); + x = normalize(x + rotor4.getRotation() - rotor4.getRingSetting()); + x = rotor4.encryptBackward(x); + x = normalize(x + rotor3.getRotation() - rotor3.getRingSetting() - rotor4.getRotation() + rotor4.getRingSetting()); + x = rotor3.encryptBackward(x); + x = normalize(x + rotor2.getRotation() - rotor2.getRingSetting() - rotor3.getRotation() + rotor3.getRingSetting()); + x = rotor2.encryptBackward(x); + x = normalize(x + rotor1.getRotation() - rotor1.getRingSetting() - rotor2.getRotation() + rotor2.getRingSetting()); + x = rotor1.encryptBackward(x); + x = normalize(x - rotor1.getRotation() + rotor1.getRingSetting()); + x = plugboard.encrypt(x); + return (char) (x + 65); //Add Offset again and cast back to char + } + + @Override + public int normalize(int input) { + return (input + 26) % 26; + } + + @Override + public void setPlugboard(Plugboard p) + { + this.plugboard = p; + } + + @Override + public int[] getConfiguration() { + int[] configuration = new int[13]; + configuration[0] = rotor1.getType(); + configuration[1] = rotor2.getType(); + configuration[2] = rotor3.getType(); + configuration[3] = rotor4.getType(); + + configuration[4] = thinReflector.getType(); + + configuration[5] = rotor1.getRotation(); + configuration[6] = rotor2.getRotation(); + configuration[7] = rotor3.getRotation(); + configuration[8] = rotor4.getRotation(); + + configuration[9] = rotor1.getRingSetting(); + configuration[10] = rotor2.getRingSetting(); + configuration[11] = rotor3.getRingSetting(); + + configuration[12] = rotor4.getRingSetting(); + return configuration; + } + + @Override + public void setConfiguration(int[] conf) + { + if(conf.length != 13) + { + Log.d("EnigmAndroid/M4/setCon", "Invalid conf array length. conf array length " + + "should be 13 (is "+conf.length+")"); + } + else + { + setRotor(1, conf[0], conf[9], conf[5]); + setRotor(2, conf[1], conf[10], conf[6]); + setRotor(3, conf[2], conf[11], conf[7]); + setRotor(4, conf[3], conf[12], conf[8]); + setReflector(conf[4]); + } + } +} diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/Plugboard.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Plugboard.java similarity index 97% rename from app/src/main/java/de/vanitasvitae/enigmandroid/Plugboard.java rename to app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Plugboard.java index f182320..3d3350d 100644 --- a/app/src/main/java/de/vanitasvitae/enigmandroid/Plugboard.java +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/Plugboard.java @@ -1,10 +1,13 @@ -package de.vanitasvitae.enigmandroid; +package de.vanitasvitae.enigmandroid.enigma; import android.app.Activity; import android.widget.Toast; import java.util.ArrayList; +import de.vanitasvitae.enigmandroid.MainActivity; +import de.vanitasvitae.enigmandroid.R; + /** * Plugboard of the enigma * Copyright (C) 2015 Paul Schaub diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/InputPreparator.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/inputPreparer/InputPreparer.java similarity index 67% rename from app/src/main/java/de/vanitasvitae/enigmandroid/InputPreparator.java rename to app/src/main/java/de/vanitasvitae/enigmandroid/enigma/inputPreparer/InputPreparer.java index 81eb0f8..64e6561 100644 --- a/app/src/main/java/de/vanitasvitae/enigmandroid/InputPreparator.java +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/inputPreparer/InputPreparer.java @@ -1,9 +1,7 @@ -package de.vanitasvitae.enigmandroid; - -import java.text.Normalizer; +package de.vanitasvitae.enigmandroid.enigma.inputPreparer; /** - * Preparator class that prepares input text to only consist of [A..Z] + * Preparer class that prepares input text to only consist of [A..Z] * Copyright (C) 2015 Paul Schaub This program is free software; you can redistribute it and/or modify @@ -21,11 +19,11 @@ import java.text.Normalizer; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * @author vanitasvitae */ -public abstract class InputPreparator +public abstract class InputPreparer { /** * Prepare the input String in a way that it only contains letters from [A..Z]. - * Replace special characters and spell numbers. + * Replace special characters, remove spaces and spell numbers. * @param input String * @return prepared String */ @@ -46,7 +44,7 @@ public abstract class InputPreparator output = output + replaceNumber(x); } //x is special symbol - else + else if (x != ' ') { output = output + 'X'; } @@ -62,24 +60,25 @@ public abstract class InputPreparator public abstract String replaceNumber(char input); /** - * Factory method that creates a specific InputPreparator - * @param language language alias that specifies the language (de,en) - * @return concrete InputPreparator + * Factory method that creates a specific InputPreparer + * @param language language alias that specifies the language (de,fr,en) + * @return concrete InputPreparer */ - public static InputPreparator createInputPreparator(String language) + public static InputPreparer createInputPreparer(String language) { switch (language) { - case "de": return new InputPreparatorGerman(); - default: return new InputPreparatorEnglish(); + case "de": return new InputPreparerGerman(); + case "fr": return new InputPreparerFrench(); + default: return new InputPreparerEnglish(); } } } /** - * Concrete implementation of a german InputPreparator + * Concrete implementation of a german InputPreparer */ -class InputPreparatorGerman extends InputPreparator +class InputPreparerGerman extends InputPreparer { @Override public String replaceNumber(char input) { @@ -100,9 +99,9 @@ class InputPreparatorGerman extends InputPreparator } /** - * Concrete implementation of an english InputPreparator + * Concrete implementation of an english InputPreparer */ -class InputPreparatorEnglish extends InputPreparator +class InputPreparerEnglish extends InputPreparer { @Override public String replaceNumber(char input) @@ -120,4 +119,28 @@ class InputPreparatorEnglish extends InputPreparator default: return "NINE"; } } +} + +/** + * Concrete implementation of a french InputPreparer + */ +class InputPreparerFrench extends InputPreparer +{ + + @Override + public String replaceNumber(char input) + { + switch (input) { + case '0': return "ZERO"; + case '1': return "UN"; + case '2': return "DEUX"; + case '3': return "TROIS"; + case '4': return "QUATRE"; + case '5': return "CINQ"; + case '6': return "SIX"; + case '7': return "SEPT"; + case '8': return "HUIT"; + default: return "NEUF"; + } + } } \ No newline at end of file diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/rotors/Reflector.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/rotors/Reflector.java similarity index 67% rename from app/src/main/java/de/vanitasvitae/enigmandroid/rotors/Reflector.java rename to app/src/main/java/de/vanitasvitae/enigmandroid/enigma/rotors/Reflector.java index 9917095..c7d782d 100644 --- a/app/src/main/java/de/vanitasvitae/enigmandroid/rotors/Reflector.java +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/rotors/Reflector.java @@ -1,4 +1,4 @@ -package de.vanitasvitae.enigmandroid.rotors; +package de.vanitasvitae.enigmandroid.enigma.rotors; /** * Reflector of the enigma machine. @@ -42,33 +42,6 @@ public class Reflector this.connections = connections; } - /** - * Creates a reflector of type A - * @return ReflectorA - */ - public static Reflector createReflectorA() - { - return new ReflectorA(); - } - - /** - * Creates a reflector of type B - * @return ReflectorB - */ - public static Reflector createReflectorB() - { - return new ReflectorB(); - } - - /** - * Creates a reflector of type C - * @return ReflectorC - */ - public static Reflector createReflectorC() - { - return new ReflectorC(); - } - /** * Factory method to create reflectors. * @param type type of the created reflector @@ -81,9 +54,11 @@ public class Reflector { switch (type) { - case 1: return createReflectorA(); - case 2: return createReflectorB(); - default: return createReflectorC(); + case 1: return new ReflectorA(); + case 2: return new ReflectorB(); + case 3: return new ReflectorC(); + case 4: return new ReflectorThinB(); + default: return new ReflectorThinC(); } } @@ -141,34 +116,70 @@ public class Reflector /** * Concrete implementation of ReflectorA + * Used in Enigma I + * AE BJ CM DZ FL GY HX IV KW NR OQ PU ST */ private static class ReflectorA extends Reflector { public ReflectorA() { - super("A", 1, new Integer[]{4, 9, 12, 25, 0, 11, 24, 23, 21, 1, 22, 5, 2, 17, 16, 20, 14, 13, 19, 18, 15, 8, 10, 7, 6, 3}); + super("A", 1, new Integer[]{4,9,12,25,0,11,24,23,21,1,22,5,2,17,16,20,14,13,19,18,15,8,10,7,6,3}); } } /** * Concrete implementation of ReflectorB + * Used in Enigma I, M3 + * AY BR CU DH EQ FS GL IP JX KN MO TZ VW */ private static class ReflectorB extends Reflector { public ReflectorB() { - super("B", 2, new Integer[]{24, 17, 20, 7, 16, 18, 11, 3, 15, 23, 13, 6, 14, 10, 12, 8, 4, 1, 5, 25, 2, 22, 21, 9, 0, 19}); + super("B", 2, new Integer[]{24,17,20,7,16,18,11,3,15,23,13,6,14,10,12,8,4,1,5,25,2,22,21,9,0,19}); } } /** * Concrete implementation of ReflectorC + * Used in Enigma I, M3 + * AF BV CP DJ EI GO HY KR LZ MX NW QT SU */ private static class ReflectorC extends Reflector { public ReflectorC() { - super("C", 3, new Integer[]{5, 21, 15, 9, 8, 0, 14, 24, 4, 3, 17, 25, 23, 22, 6, 2, 19, 10, 20, 16, 18, 1, 13, 12, 7, 11}); + super("C", 3, new Integer[]{5,21,15,9,8,0,14,24,4,3,17,25,23,22,6,2,19,10,20,16,18,1,13,12,7,11}); + } + } + + /** + * Concrete implementation of thin reflector type b (not equal to normal type b!) + * When used with Rotor Beta on rotation 0, the pair was equivalent to normal reflector B + * S->Beta->ThinB->Beta'->X == X->UKWB->S + * Used in Enigma M4 + * E N K Q A U Y W J I C O P B L M D X Z V F T H R G S + */ + private static class ReflectorThinB extends Reflector + { + public ReflectorThinB() + { + super("B", 4, new Integer[]{4,13,10,16,0,20,24,22,9,8,2,14,15,1,11,12,3,23,25,21,5,19,7,17,6,18}); + } + } + + /** + * Concrete implementation of thin reflector type c (not equal to normal type c!) + * When used with Rotor Gamma on rotation 0, the pair was equivalent to normal reflector C + * S->Gamma->ThinC->Gamma'->X == X->UKWC->S + * Used in Enigma M4 + * R D O B J N T K V E H M L F C W Z A X G Y I P S U Q + */ + private static class ReflectorThinC extends Reflector + { + public ReflectorThinC() + { + super("B", 5, new Integer[]{17,3,14,1,9,13,19,10,21,4,7,12,11,5,2,22,25,0,23,6,24,8,15,18,20,16}); } } diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/rotors/Rotor.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/rotors/Rotor.java similarity index 56% rename from app/src/main/java/de/vanitasvitae/enigmandroid/rotors/Rotor.java rename to app/src/main/java/de/vanitasvitae/enigmandroid/enigma/rotors/Rotor.java index 105b766..b45fcb2 100644 --- a/app/src/main/java/de/vanitasvitae/enigmandroid/rotors/Rotor.java +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/rotors/Rotor.java @@ -1,4 +1,4 @@ -package de.vanitasvitae.enigmandroid.rotors; +package de.vanitasvitae.enigmandroid.enigma.rotors; /** * Rotor super class and inner concrete implementations @@ -30,7 +30,7 @@ public class Rotor protected int type; protected Integer[] connections; protected Integer[] reversedConnections; - protected int turnOverNotch; + protected Integer[] turnOverNotches; protected int ringSetting; protected int rotation; @@ -47,18 +47,18 @@ public class Rotor * @param reversedConnections inverse wiring used to encrypt in the opposite direction * (connections[reversedConnections[i]] = i * for all i in 0..getRotorSize()-1. - * @param turnOverNotch Position of the turnover notch + * @param turnOverNotches Position(s) of the turnover notch(es) * @param ringSetting setting of the ring that holds the letters * @param rotation rotation of the rotor */ protected Rotor(String name, int type, Integer[] connections, Integer[] reversedConnections, - int turnOverNotch, int ringSetting, int rotation) + Integer[] turnOverNotches, int ringSetting, int rotation) { this.name = name; this.type = type; this.connections = connections; this.reversedConnections = reversedConnections; - this.turnOverNotch = turnOverNotch; + this.turnOverNotches = turnOverNotches; this.ringSetting = ringSetting; this.rotation = rotation; } @@ -66,7 +66,9 @@ public class Rotor /** * Factory method that creates a rotor accordingly to the type. * Also initialize the rotor with ringSetting and rotation. - * @param type type indicator (1..5) + * @param type type indicator (1..10) + * 1..8 -> I..VIII + * 9,10 -> Beta, Gamma * @param ringSetting setting of the outer ring (0..25) * @param rotation rotation of the rotor * @return Concrete rotor @@ -75,77 +77,28 @@ public class Rotor { switch (type) { - case 1: return createRotorI(ringSetting, rotation); - case 2: return createRotorII(ringSetting, rotation); - case 3: return createRotorIII(ringSetting, rotation); - case 4: return createRotorIV(ringSetting, rotation); - default: return createRotorV(ringSetting, rotation); + case 1: return new RotorI(ringSetting, rotation); + case 2: return new RotorII(ringSetting, rotation); + case 3: return new RotorIII(ringSetting, rotation); + case 4: return new RotorIV(ringSetting, rotation); + case 5: return new RotorV(ringSetting, rotation); + case 6: return new RotorVI(ringSetting, rotation); + case 7: return new RotorVII(ringSetting, rotation); + case 8: return new RotorVIII(ringSetting, rotation); + case 9: return new RotorBeta(ringSetting, rotation); + default: return new RotorGamma(ringSetting, rotation); } } /** - * Create concrete Rotor of type 1 (I) initialized with ringSetting and rotation - * @param ringSetting setting of the outer ring - * @param rotation rotation of the rotor - * @return RotorI - */ - public static Rotor createRotorI(int ringSetting, int rotation) - { - return new RotorI(ringSetting, rotation); - } - - /** - * Create concrete Rotor of type 2 (II) initialized with ringSetting and rotation - * @param ringSetting setting of the outer ring - * @param rotation rotation of the rotor - * @return RotorI - */ - public static Rotor createRotorII(int ringSetting, int rotation) - { - return new RotorII(ringSetting, rotation); - } - - /** - * Create concrete Rotor of type 3 (III) initialized with ringSetting and rotation - * @param ringSetting setting of the outer ring - * @param rotation rotation of the rotor - * @return RotorI - */ - public static Rotor createRotorIII(int ringSetting, int rotation) - { - return new RotorIII(ringSetting, rotation); - } - - /** - * Create concrete Rotor of type 4 (IV) initialized with ringSetting and rotation - * @param ringSetting setting of the outer ring - * @param rotation rotation of the rotor - * @return RotorI - */ - public static Rotor createRotorIV(int ringSetting, int rotation) - { - return new RotorIV(ringSetting, rotation); - } - - /** - * Create concrete Rotor of type 5 (V) initialized with ringSetting and rotation - * @param ringSetting setting of the outer ring - * @param rotation rotation of the rotor - * @return RotorI - */ - public static Rotor createRotorV(int ringSetting, int rotation) - { - return new RotorV(ringSetting, rotation); - } - - /** - * Encrypt an input signal via the internal wiring in "forward" direction (using connections) + * Encrypt an input signal via the internal wiring in "forward" direction towards the reflector + * (using connections) * @param input signal * @return encrypted signal */ public int encryptForward(int input) { - return this.connections[normalize(input)]; + return this.connections[normalize(input)];// - this.ringSetting)]; } /** @@ -156,7 +109,7 @@ public class Rotor */ public int encryptBackward(int input) { - return this.reversedConnections[normalize(input)]; + return this.reversedConnections[normalize(input)];// + this.ringSetting)]; } /** @@ -175,7 +128,7 @@ public class Rotor */ public int getRotation() { - return this.rotation - this.getRingSetting(); + return this.rotation; } /** @@ -192,7 +145,12 @@ public class Rotor */ public boolean isAtTurnoverPosition() { - return this.rotation == this.turnOverNotch; + for(int x : getTurnOverNotches()) + { + //if(x == this.rotation + this.ringSetting) return true; + if(x == this.rotation) return true; + } + return false; } /** @@ -206,16 +164,20 @@ public class Rotor */ public boolean doubleTurnAnomaly() { - return this.rotation == this.getTurnOver() - 1; + for(int x : getTurnOverNotches()) + { + if(this.rotation == x-1) return true; + } + return false; } /** - * Returns the position of the turnover notch - * @return turnOverNotch + * Returns the positions of the turnover notches in a array + * @return turnOverNotches */ - public int getTurnOver() + public Integer[] getTurnOverNotches() { - return this.turnOverNotch; + return this.turnOverNotches; } /** @@ -261,6 +223,8 @@ public class Rotor /** * Concrete implementation of Rotor of type 1 (I) + * Used in Enigma I, M3, M4 + * E K M F L G D Q V Z N T O W Y H X U S P A I B R C J */ private static class RotorI extends Rotor { @@ -269,12 +233,14 @@ public class Rotor super("I", 1, new Integer[]{4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9}, new Integer[]{20, 22, 24, 6, 0, 3, 5, 15, 21, 25, 1, 4, 2, 10, 12, 19, 7, 23, 18, 11, 17, 8, 13, 16, 14, 9}, - 17, ringSetting, rotation); + new Integer[]{17}, ringSetting, rotation); } } /** * Concrete implementation of Rotor of type 2 (II) + * Used in Enigma I, M3, M4 + * A J D K S I R U X B L H W T M C Q G Z N P Y F V O E */ private static class RotorII extends Rotor { @@ -283,12 +249,14 @@ public class Rotor super("II", 2, new Integer[]{0, 9, 3, 10, 18, 8, 17, 20, 23, 1, 11, 7, 22, 19, 12, 2, 16, 6, 25, 13, 15, 24, 5, 21, 14, 4}, new Integer[]{0, 9, 15, 2, 25, 22, 17, 11, 5, 1, 3, 10, 14, 19, 24, 20, 16, 6, 4, 13, 7, 23, 12, 8, 21, 18}, - 5, ringSetting, rotation); + new Integer[]{5}, ringSetting, rotation); } } /** * Concrete implementation of Rotor of type 3 (III) + * Used in Enigma I, M3, M4 + * B D F H J L C P R T X V Z N Y E I W G A K M U S Q O */ private static class RotorIII extends Rotor { @@ -297,12 +265,14 @@ public class Rotor super("III", 3, new Integer[]{1, 3, 5, 7, 9, 11, 2, 15, 17, 19, 23, 21, 25, 13, 24, 4, 8, 22, 6, 0, 10, 12, 20, 18, 16, 14}, new Integer[]{19, 0, 6, 1, 15, 2, 18, 3, 16, 4, 20, 5, 21, 13, 25, 7, 24, 8, 23, 9, 22, 11, 17, 10, 14, 12}, - 22, ringSetting, rotation); + new Integer[]{22}, ringSetting, rotation); } } /** * Concrete implementation of Rotor of type 4 (IV) + * Used in Enigma M3, M4 + * E S O V P Z J A Y Q U I R H X L N F T G K D C M W B */ private static class RotorIV extends Rotor { @@ -311,12 +281,14 @@ public class Rotor super("IV", 4, new Integer[]{4, 18, 14, 21, 15, 25, 9, 0, 24, 16, 20, 8, 17, 7, 23, 11, 13, 5, 19, 6, 10, 3, 2, 12, 22, 1}, new Integer[]{7, 25, 22, 21, 0, 17, 19, 13, 11, 6, 20, 15, 23, 16, 2, 4, 9, 12, 1, 18, 10, 3, 24, 14, 8, 5}, - 10, ringSetting, rotation); + new Integer[]{10}, ringSetting, rotation); } } /** * Concrete implementation of Rotor of type 5 (V) + * Used in Enigma M3, M4 + * V Z B R G I T Y U P S D N H L X A W M J Q O F E C K */ private static class RotorV extends Rotor { @@ -325,7 +297,117 @@ public class Rotor super("V", 5, new Integer[]{21, 25, 1, 17, 6, 8, 19, 24, 20, 15, 18, 3, 13, 7, 11, 23, 0, 22, 12, 9, 16, 14, 5, 4, 2, 10}, new Integer[]{16, 2, 24, 11, 23, 22, 4, 13, 5, 19, 25, 14, 18, 12, 21, 9, 20, 3, 10, 6, 8, 0, 17, 15, 7, 1}, - 0, ringSetting, rotation); + new Integer[]{0}, ringSetting, rotation); + } + } + + /** + * Concrete implementation of Rotor of type 6 (VI) + * Used in Enigma M3, M4 + * J P G V O U M F Y Q B E N H Z R D K A S X L I C T W + */ + private static class RotorVI extends Rotor + { + public RotorVI(int ringSetting, int rotation) + { + super("VI", 6, + new Integer[]{9,15,6,21,14,20,12,5,24,16,1,4,13,7,25,17,3,10,0,18,23,11,8,2,19,22}, + new Integer[]{18,10,23,16,11,7,2,13,22,0,17,21,6,12,4,1,9,15,19,24,5,3,25,20,8,14}, + new Integer[]{0,13}, ringSetting, rotation); + } + } + + /** + * Concrete implementation of Rotor of type 7 (VII) + * Used in Enigma M3, M4 + * N Z J H G R C X M Y S W B O U F A I V L P E K Q D T + */ + private static class RotorVII extends Rotor + { + public RotorVII(int ringSetting, int rotation) + { + super("VII", 7, + new Integer[]{13,25,9,7,6,17,2,23,12,24,18,22,1,14,20,5,0,8,21,11,15,4,10,16,3,19}, + new Integer[]{16,12,6,24,21,15,4,3,17,2,22,19,8,0,13,20,23,5,10,25,14,18,11,7,9,1}, + new Integer[]{0,13}, ringSetting, rotation); + } + } + + /** + * Concrete implementation of Rotor of type 8 (VIII) + * Used in Enigma M3, M4 + * F K Q H T L X O C B J S P D Z R A M E W N I U Y G V + */ + private static class RotorVIII extends Rotor + { + public RotorVIII(int ringSetting, int rotation) + { + super("VIII", 8, + new Integer[]{5,10,16,7,19,11,23,14,2,1,9,18,15,3,25,17,0,12,4,22,13,8,20,24,6,21}, + new Integer[]{16,9,8,13,18,0,24,3,21,10,1,5,17,20,7,12,2,15,11,4,22,25,19,6,23,14}, + new Integer[]{0,13}, ringSetting, rotation); + } + } + + /** + * Concrete implementation of Rotor of type beta (Griechenwalze Beta) + * Beta was used as a "thin" rotor in the M4. It was thinner than a "normal" rotor, so it + * could be used together with one of the two thin reflectors as one rotor. + * When used together with ReflectorThinB, Beta was equivalent to Reflector B (if rotation == 0) + * That way the M4 was backwards compatible to the M3 + * Used in M4 + */ + private static class RotorBeta extends Rotor + { + public RotorBeta(int ringSetting, int rotation) + { + super("Beta",9, + new Integer[]{11,4,24,9,21,2,13,8,23,22,15,1,16,12,3,17,19,0,10,25,6,5,20,7,14,18}, + new Integer[]{17,11,5,14,1,21,20,23,7,3,18,0,13,6,24,10,12,15,25,16,22,4,9,8,2,19}, + new Integer[]{}, ringSetting, rotation); + } + @Override + public void rotate() + { + //Thin rotors are fixed in position, so they dont rotate + } + + @Override + public boolean doubleTurnAnomaly() + { + //Nope, no anomaly + return false; + } + } + + /** + * Concrete implementation of Rotor of type gamma (Griechenwalze Gamma) + * Gamma was used as a "thin" rotor in the M4. It was thinner than a "normal" rotor, so it + * could be used together with one of the two thin reflectors as one rotor. + * When used together with ReflectorThinC, Gamma is equivalent to Reflector C + * (if rotation == 0). That way the M4 was backwards compatible to the M3 + * Used in M4 + */ + private static class RotorGamma extends Rotor + { + public RotorGamma(int ringSetting, int rotation) + { + super("Gamma",10, + new Integer[]{5,18,14,10,0,13,20,4,17,7,12,1,19,8,24,2,22,11,16,15,25,23,21,6,9,3}, + new Integer[]{4,11,15,25,7,0,23,9,13,24,3,17,10,5,2,19,18,8,1,12,6,22,16,21,14,20}, + new Integer[]{}, ringSetting, rotation); + } + @Override + public void rotate() + { + //Thin rotors are fixed in position, so they don't rotate + } + + @Override + public boolean doubleTurnAnomaly() + { + //Thin rotors don't do such weird stuff, they're normal just like you and me. + return false; } } } diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/util/RotorMaker.java b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/util/RotorMaker.java new file mode 100644 index 0000000..858e578 --- /dev/null +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/enigma/util/RotorMaker.java @@ -0,0 +1,107 @@ +package de.vanitasvitae.enigmandroid.enigma.util; + +/** + * Used to create wiring arrays from strings + * Use strings like "E K M F L G D Q V Z N T O W Y H X U S P A I B R C J" as input + * Copyright (C) 2015 Paul Schaub + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * @author vanitasvitae + */ +public class RotorMaker +{ + public static final int l = 26; + public static void main(String[] args) + { + if(args.length == l) makeRotor(args); + else if(args.length == 1) makeRotor(prepare(args[0])); + else System.out.println("wrong input format!"); + } + + public static void makeRotor(String input) + { + makeRotor(prepare(input)); + } + + /** + * Prepare the string (add spaces) + * @param in input string + * @return prepared string + */ + public static String[] prepare(String in) + { + String[] out = new String[l]; + int pos = 0; + for(char x : in.toCharArray()) + { + if(x != ' ') + { + try + { + out[pos] = ""+x; + } + catch (ArrayIndexOutOfBoundsException e) + { + System.out.println("String too long!"); + return null; + } + pos++; + } + } + if(pos!=l) + { + System.out.println("String too short!"); + return null; + } + return out; + } + + /** + * Generate Array initializer for the given rotor configuration + * @param input String describing the rotor + */ + public static void makeRotor(String[] input) + { + if(input.length != l) System.out.println("Wrong length! Input must have length "+l+"!"); + else + { + Integer[] out1 = new Integer[l]; + for(int i=0; i rotor1Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_5, + android.R.layout.simple_spinner_item); + rotor1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor1View.setAdapter(rotor1Adapter); + ArrayAdapter rotor2Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_5, + android.R.layout.simple_spinner_item); + rotor2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor2View.setAdapter(rotor2Adapter); + ArrayAdapter rotor3Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_5, + android.R.layout.simple_spinner_item); + rotor3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor3View.setAdapter(rotor3Adapter); + + ArrayAdapter reflectorAdapter = ArrayAdapter.createFromResource(main, R.array.reflectors_a_c, + android.R.layout.simple_spinner_item); + reflectorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + reflectorView.setAdapter(reflectorAdapter); + + ArrayAdapter rotor1PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor1PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor1PositionView.setAdapter(rotor1PositionAdapter); + ArrayAdapter rotor2PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor2PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor2PositionView.setAdapter(rotor2PositionAdapter); + ArrayAdapter rotor3PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor3PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor3PositionView.setAdapter(rotor3PositionAdapter); + } + + @Override + public void reset() + { + rotor1View.setSelection(0); //I + rotor2View.setSelection(1); //II + rotor3View.setSelection(2); //III + reflectorView.setSelection(0); //A + rotor1PositionView.setSelection(0); //0 + rotor2PositionView.setSelection(0); //0 + rotor3PositionView.setSelection(0); //0 + this.ringSettings = new int[]{0,0,0}; + inputView.setText(""); + outputView.setText(""); + plugboardView.setText(""); + enigma.setConfiguration(createConfiguration()); + enigma.setPlugboard(new Plugboard()); + } + + @Override + public void updateLayout() + { + int[] conf = enigma.getConfiguration(); + rotor1View.setSelection(conf[0]-1); + rotor2View.setSelection(conf[1]-1); + rotor3View.setSelection(conf[2]-1); + reflectorView.setSelection(conf[3]-1); + rotor1PositionView.setSelection(conf[4]); + rotor2PositionView.setSelection(conf[5]); + rotor3PositionView.setSelection(conf[6]); + ringSettings[0] = conf[7]; + ringSettings[1] = conf[8]; + ringSettings[2] = conf[9]; + } + + @Override + public int[] createConfiguration() + { + int[] conf = new int[10]; + //Rotors 1..3 + conf[0] = rotor1View.getSelectedItemPosition() + 1; + conf[1] = rotor2View.getSelectedItemPosition() + 1; + conf[2] = rotor3View.getSelectedItemPosition() + 1; + //Reflector + conf[3] = reflectorView.getSelectedItemPosition() + 1; + + conf[4] = rotor1PositionView.getSelectedItemPosition(); + conf[5] = rotor2PositionView.getSelectedItemPosition(); + conf[6] = rotor3PositionView.getSelectedItemPosition(); + + conf[7] = ringSettings[0]; + conf[8] = ringSettings[1]; + conf[9] = ringSettings[2]; + + return conf; + } + + @Override + public int[][] createPlugboardConfiguration() { + return Plugboard.parseConfigurationString(plugboardView.getText().toString()); + } + + @Override + public void showRingSettingsDialog() + { + Integer[] ringArray = new Integer[26]; + for(int i=1; i<=26; i++) {ringArray[i-1] = i;} + View ringSettingsView = View.inflate(main, R.layout.dialog_ringsettings_i_m3, null); + final Spinner ring1 = (Spinner) ringSettingsView.findViewById(R.id.rotor1ring); + ArrayAdapter ring1Adapter = new ArrayAdapter<>(main, + android.R.layout.simple_spinner_item,ringArray); + ring1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ring1.setAdapter(ring1Adapter); + ring1.setSelection(ringSettings[0]); + + final Spinner ring2 = (Spinner) ringSettingsView.findViewById(R.id.rotor2ring); + ArrayAdapter ring2Adapter = new ArrayAdapter<>(main, + android.R.layout.simple_spinner_item,ringArray); + ring2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ring2.setAdapter(ring2Adapter); + ring2.setSelection(ringSettings[1]); + + final Spinner ring3 = (Spinner) ringSettingsView.findViewById(R.id.rotor3ring); + ArrayAdapter ring3Adapter = new ArrayAdapter<>(main, + android.R.layout.simple_spinner_item,ringArray); + ring3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ring3.setAdapter(ring3Adapter); + ring3.setSelection(ringSettings[2]); + + AlertDialog.Builder builder = new AlertDialog.Builder(main); + builder.setTitle(R.string.title_ringsetting); + builder.setView(ringSettingsView) + .setCancelable(true) + .setPositiveButton(R.string.dialog_positiv, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int id) + { + ringSettings[0] = ring1.getSelectedItemPosition(); + ringSettings[1] = ring2.getSelectedItemPosition(); + ringSettings[2] = ring3.getSelectedItemPosition(); + String message = main.getResources().getString( + R.string.dialog_ringsettings_success) + " " + + (ringSettings[2]+1) + ", " + + (ringSettings[1]+1) + ", " + + (ringSettings[0]+1) + "."; + Toast.makeText(main, message, Toast.LENGTH_LONG).show(); + } + }) + .setNegativeButton(R.string.dialog_negativ, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + Toast.makeText(main, R.string.dialog_ringsettings_abort, + Toast.LENGTH_SHORT).show(); + } + }).show(); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M3.java b/app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M3.java new file mode 100644 index 0000000..d4b7cc1 --- /dev/null +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M3.java @@ -0,0 +1,115 @@ +package de.vanitasvitae.enigmandroid.layout; + +import android.widget.ArrayAdapter; + +import de.vanitasvitae.enigmandroid.R; +import de.vanitasvitae.enigmandroid.enigma.Enigma_M3; + +/** + * Concrete LayoutContainer for the M3 layout. + * This class contains the layout and controls the layout elements such as spinners and stuff + * Copyright (C) 2015 Paul Schaub + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * @author vanitasvitae + */ +public class LayoutContainer_M3 extends LayoutContainer_I +{ + public LayoutContainer_M3() + { + super(); + main.setTitle("M3 - EnigmAndroid"); + this.enigma = new Enigma_M3(); + } + + @Override + void initialize() + { + Character[] rotorPositionArray = new Character[26]; + for(int i=0; i<26; i++) {rotorPositionArray[i] = (char) (65+i); /**Fill with A..Z*/} + + ArrayAdapter rotor1Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_8, + android.R.layout.simple_spinner_item); + rotor1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor1View.setAdapter(rotor1Adapter); + ArrayAdapter rotor2Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_8, + android.R.layout.simple_spinner_item); + rotor2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor2View.setAdapter(rotor2Adapter); + ArrayAdapter rotor3Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_8, + android.R.layout.simple_spinner_item); + rotor3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor3View.setAdapter(rotor3Adapter); + + ArrayAdapter reflectorAdapter = ArrayAdapter.createFromResource(main, R.array.reflectors_b_c, + android.R.layout.simple_spinner_item); + reflectorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + reflectorView.setAdapter(reflectorAdapter); + + ArrayAdapter rotor1PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor1PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor1PositionView.setAdapter(rotor1PositionAdapter); + ArrayAdapter rotor2PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor2PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor2PositionView.setAdapter(rotor2PositionAdapter); + ArrayAdapter rotor3PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor3PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor3PositionView.setAdapter(rotor3PositionAdapter); + } + + @Override + public void updateLayout() + { + int[] conf = enigma.getConfiguration(); + rotor1View.setSelection(conf[0]-1); + rotor2View.setSelection(conf[1]-1); + rotor3View.setSelection(conf[2]-1); + reflectorView.setSelection(conf[3]-2); //B=2 -> 0 + rotor1PositionView.setSelection(conf[4]); + rotor2PositionView.setSelection(conf[5]); + rotor3PositionView.setSelection(conf[6]); + ringSettings[0] = conf[7]; + ringSettings[1] = conf[8]; + ringSettings[2] = conf[9]; + } + + @Override + public int[] createConfiguration() + { + int[] conf = new int[10]; + //Rotors 1..3 + conf[0] = rotor1View.getSelectedItemPosition() + 1; + conf[1] = rotor2View.getSelectedItemPosition() + 1; + conf[2] = rotor3View.getSelectedItemPosition() + 1; + //Reflector + conf[3] = reflectorView.getSelectedItemPosition() + 2; //M3 has no B + + conf[4] = rotor1PositionView.getSelectedItemPosition(); + conf[5] = rotor2PositionView.getSelectedItemPosition(); + conf[6] = rotor3PositionView.getSelectedItemPosition(); + + conf[7] = ringSettings[0]; + conf[8] = ringSettings[1]; + conf[9] = ringSettings[2]; + + return conf; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M4.java b/app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M4.java new file mode 100644 index 0000000..cea28c4 --- /dev/null +++ b/app/src/main/java/de/vanitasvitae/enigmandroid/layout/LayoutContainer_M4.java @@ -0,0 +1,254 @@ +package de.vanitasvitae.enigmandroid.layout; + +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.Spinner; +import android.widget.Toast; + +import de.vanitasvitae.enigmandroid.R; +import de.vanitasvitae.enigmandroid.enigma.Enigma_M4; +import de.vanitasvitae.enigmandroid.enigma.Plugboard; + +/** + * Concrete LayoutContainer for the M4 layout. + * This class contains the layout and controls the layout elements such as spinners and stuff + * Copyright (C) 2015 Paul Schaub + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * @author vanitasvitae + */ +public class LayoutContainer_M4 extends LayoutContainer +{ + private Spinner rotor1View; + private Spinner rotor2View; + private Spinner rotor3View; + private Spinner rotor4View; + private Spinner reflectorView; + private Spinner rotor1PositionView; + private Spinner rotor2PositionView; + private Spinner rotor3PositionView; + private Spinner rotor4PositionView; + + private EditText plugboardView; + + public LayoutContainer_M4() + { + super(); + main.setTitle("M4 - EnigmAndroid"); + this.enigma = new Enigma_M4(); + this.ringSettings = new int[]{0,0,0,0}; + this.rotor1View = (Spinner) main.findViewById(R.id.rotor1); + this.rotor2View = (Spinner) main.findViewById(R.id.rotor2); + this.rotor3View = (Spinner) main.findViewById(R.id.rotor3); + this.rotor4View = (Spinner) main.findViewById(R.id.thin_rotor); + this.reflectorView = (Spinner) main.findViewById(R.id.reflector); + this.rotor1PositionView = (Spinner) main.findViewById(R.id.rotor1position); + this.rotor2PositionView = (Spinner) main.findViewById(R.id.rotor2position); + this.rotor3PositionView = (Spinner) main.findViewById(R.id.rotor3position); + this.rotor4PositionView = (Spinner) main.findViewById(R.id.thin_rotor_position); + this.plugboardView = (EditText) main.findViewById(R.id.plugboard); + this.inputView = (EditText) main.findViewById(R.id.input); + this.outputView = (EditText) main.findViewById(R.id.output); + + initialize(); + reset(); + } + + @Override + void initialize() + { + Character[] rotorPositionArray = new Character[26]; + for(int i=0; i<26; i++) {rotorPositionArray[i] = (char) (65+i); /**Fill with A..Z*/} + + ArrayAdapter rotor1Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_8, + android.R.layout.simple_spinner_item); + rotor1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor1View.setAdapter(rotor1Adapter); + ArrayAdapter rotor2Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_8, + android.R.layout.simple_spinner_item); + rotor2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor2View.setAdapter(rotor2Adapter); + ArrayAdapter rotor3Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_1_8, + android.R.layout.simple_spinner_item); + rotor3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor3View.setAdapter(rotor3Adapter); + ArrayAdapter rotor4Adapter = ArrayAdapter.createFromResource(main, R.array.rotors_beta_gamma, + android.R.layout.simple_spinner_item); + rotor4Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + rotor4View.setAdapter(rotor4Adapter); + + ArrayAdapter reflectorAdapter = ArrayAdapter.createFromResource(main, R.array.reflectors_b_c, + android.R.layout.simple_spinner_item); + reflectorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + reflectorView.setAdapter(reflectorAdapter); + + ArrayAdapter rotor1PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor1PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor1PositionView.setAdapter(rotor1PositionAdapter); + ArrayAdapter rotor2PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor2PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor2PositionView.setAdapter(rotor2PositionAdapter); + ArrayAdapter rotor3PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor3PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor3PositionView.setAdapter(rotor3PositionAdapter); + ArrayAdapter rotor4PositionAdapter = new ArrayAdapter<>(main.getApplicationContext(), + android.R.layout.simple_spinner_item,rotorPositionArray); + rotor4PositionAdapter.setDropDownViewResource( + android.R.layout.simple_spinner_dropdown_item); + rotor4PositionView.setAdapter(rotor4PositionAdapter); + } + + @Override + public void reset() + { + rotor1View.setSelection(0); //I + rotor2View.setSelection(1); //II + rotor3View.setSelection(2); //III + rotor4View.setSelection(0); //Beta + reflectorView.setSelection(0); //B + rotor1PositionView.setSelection(0); //0 + rotor2PositionView.setSelection(0); //0 + rotor3PositionView.setSelection(0); //0 + rotor4PositionView.setSelection(0); //0 + ringSettings = new int[]{0,0,0,0}; + inputView.setText(""); + outputView.setText(""); + plugboardView.setText(""); + enigma.setConfiguration(createConfiguration()); + enigma.setPlugboard(new Plugboard()); + } + + @Override + public void updateLayout() + { + int[] conf = enigma.getConfiguration(); + rotor1View.setSelection(conf[0]-1); + rotor2View.setSelection(conf[1]-1); + rotor3View.setSelection(conf[2]-1); + rotor4View.setSelection(conf[3]-9); + reflectorView.setSelection(conf[4]-4); + rotor1PositionView.setSelection(conf[5]); + rotor2PositionView.setSelection(conf[6]); + rotor3PositionView.setSelection(conf[7]); + rotor4PositionView.setSelection(conf[8]); + ringSettings[0] = conf[9]; + ringSettings[1] = conf[10]; + ringSettings[2] = conf[11]; + ringSettings[3] = conf[12]; + } + + @Override + public int[] createConfiguration() + { + int[] conf = new int[13]; + //Rotors 1..4 + conf[0] = rotor1View.getSelectedItemPosition() + 1; + conf[1] = rotor2View.getSelectedItemPosition() + 1; + conf[2] = rotor3View.getSelectedItemPosition() + 1; + conf[3] = rotor4View.getSelectedItemPosition() + 9; //Beta is rotor #9 + //Reflector + conf[4] = reflectorView.getSelectedItemPosition() + 4; //thinB is reflector #4 + + conf[5] = rotor1PositionView.getSelectedItemPosition(); + conf[6] = rotor2PositionView.getSelectedItemPosition(); + conf[7] = rotor3PositionView.getSelectedItemPosition(); + conf[8] = rotor4PositionView.getSelectedItemPosition(); + + conf[9] = ringSettings[0]; + conf[10] = ringSettings[1]; + conf[11] = ringSettings[2]; + conf[12] = ringSettings[3]; + + return conf; + } + + @Override + public int[][] createPlugboardConfiguration() { + return Plugboard.parseConfigurationString(plugboardView.getText().toString()); + } + + @Override + public void showRingSettingsDialog() + { + Integer[] ringArray = new Integer[26]; + for(int i=1; i<=26; i++) {ringArray[i-1] = i;} + View ringSettingsView = View.inflate(main, R.layout.dialog_ringsettings_m4, null); + final Spinner ring1 = (Spinner) ringSettingsView.findViewById(R.id.rotor1ring); + ArrayAdapter ring1Adapter = new ArrayAdapter<>(main, + android.R.layout.simple_spinner_item,ringArray); + ring1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ring1.setAdapter(ring1Adapter); + ring1.setSelection(ringSettings[0]); + + final Spinner ring2 = (Spinner) ringSettingsView.findViewById(R.id.rotor2ring); + ArrayAdapter ring2Adapter = new ArrayAdapter<>(main, + android.R.layout.simple_spinner_item,ringArray); + ring2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ring2.setAdapter(ring2Adapter); + ring2.setSelection(ringSettings[1]); + + final Spinner ring3 = (Spinner) ringSettingsView.findViewById(R.id.rotor3ring); + ArrayAdapter ring3Adapter = new ArrayAdapter<>(main, + android.R.layout.simple_spinner_item,ringArray); + ring3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ring3.setAdapter(ring3Adapter); + ring3.setSelection(ringSettings[2]); + + final Spinner ring4 = (Spinner) ringSettingsView.findViewById(R.id.rotor4ring); + ArrayAdapter ring4Adapter = new ArrayAdapter<>(main, + android.R.layout.simple_spinner_item, ringArray); + ring4Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + ring4.setAdapter(ring4Adapter); + ring4.setSelection(ringSettings[3]); + + AlertDialog.Builder builder = new AlertDialog.Builder(main); + builder.setTitle(R.string.title_ringsetting); + builder.setView(ringSettingsView) + .setCancelable(true) + .setPositiveButton(R.string.dialog_positiv, new DialogInterface.OnClickListener() + { + public void onClick(DialogInterface dialog, int id) + { + ringSettings[0] = ring1.getSelectedItemPosition(); + ringSettings[1] = ring2.getSelectedItemPosition(); + ringSettings[2] = ring3.getSelectedItemPosition(); + ringSettings[3] = ring4.getSelectedItemPosition(); + String message = main.getResources().getString( + R.string.dialog_ringsettings_success) + " " + + (ringSettings[3]+1) + ", " + + (ringSettings[2]+1) + ", " + + (ringSettings[1]+1) + ", " + + (ringSettings[0]+1) + "."; + Toast.makeText(main, message, Toast.LENGTH_LONG).show(); + } + }) + .setNegativeButton(R.string.dialog_negativ, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + Toast.makeText(main, R.string.dialog_ringsettings_abort, + Toast.LENGTH_SHORT).show(); + } + }).show(); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main_i_m3.xml similarity index 100% rename from app/src/main/res/layout-land/activity_main.xml rename to app/src/main/res/layout-land/activity_main_i_m3.xml diff --git a/app/src/main/res/layout-land/activity_main_m4.xml b/app/src/main/res/layout-land/activity_main_m4.xml new file mode 100755 index 0000000..f40f043 --- /dev/null +++ b/app/src/main/res/layout-land/activity_main_m4.xml @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +