Added missing license headers, extended documentation, extended input interpretation and added numeric spelling in different languages (TODO: improve implementation, this is experimental)

This commit is contained in:
VanitasVitae 2015-08-16 02:59:56 +02:00
parent 980b7e8c7d
commit 2e405429cf
29 changed files with 563 additions and 303 deletions

View file

@ -1 +0,0 @@
EnigmAndroid

View file

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac" />
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View file

@ -1,3 +0,0 @@
<component name="CopyrightManager">
<settings default="" />
</component>

View file

@ -1,9 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="vanitas">
<words>
<w>plugboard</w>
<w>ringsetting</w>
<w>ringsettings</w>
</words>
</dictionary>
</component>

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
</project>

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="distributionType" value="LOCAL" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$APPLICATION_HOME_DIR$/gradle/gradle-2.2.1" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View file

@ -1,11 +0,0 @@
<component name="libraryTable">
<library name="support-annotations-21.0.3">
<CLASSES>
<root url="jar:///media/Daten/AndroidSDK/android-sdk-linux/extras/android/m2repository/com/android/support/support-annotations/21.0.3/support-annotations-21.0.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar:///media/Daten/AndroidSDK/android-sdk-linux/extras/android/m2repository/com/android/support/support-annotations/21.0.3/support-annotations-21.0.3-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -1,13 +0,0 @@
<component name="libraryTable">
<library name="support-v4-21.0.3">
<CLASSES>
<root url="jar://$PROJECT_DIR$/app/build/intermediates/exploded-aar/com.android.support/support-v4/21.0.3/libs/internal_impl-21.0.3.jar!/" />
<root url="jar://$PROJECT_DIR$/app/build/intermediates/exploded-aar/com.android.support/support-v4/21.0.3/classes.jar!/" />
<root url="file://$PROJECT_DIR$/app/build/intermediates/exploded-aar/com.android.support/support-v4/21.0.3/res" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar:///media/Daten/AndroidSDK/android-sdk-linux/extras/android/m2repository/com/android/support/support-v4/21.0.3/support-v4-21.0.3-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="masterDetails">
<states>
<state key="ProjectJDKs.UI">
<settings>
<last-edited>Android API 19 Platform</last-edited>
<splitter-proportions>
<option name="proportions">
<list>
<option value="0.2" />
</list>
</option>
</splitter-proportions>
</settings>
</state>
</states>
</component>
</project>

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/EnigmAndroid.iml" filepath="$PROJECT_DIR$/EnigmAndroid.iml" />
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
</modules>
</component>
</project>

View file

@ -1,5 +0,0 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View file

@ -1,12 +1,18 @@
CHANGELOG ENIGMANDROID 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'
v0.1.4-15.08.2015< v0.1.4-15.08.2015<
*Rewrite of the core implementation to follow some principals of Software Engineering *Rewrite of the core implementation to follow some principals of Software Engineering
*Fixed some layout issues *Fixed some layout issues
*Fixed anomaly for step by step inputs *Fixed anomaly for step by step inputs
*Added send/receive text functionality *Added send/receive text functionality
v0.1.3-14.03.2015< v0.1.3-14.03.2015<
*Added About Dialog with ChangeLog-Button *Added About Dialog with ChangeLog-Button
*Moved Version Info into About Dialog *Moved Version Info into About Dialog

1
app/.gitignore vendored
View file

@ -1 +0,0 @@
/build

Binary file not shown.

12
app/build.gradle Normal file → Executable file
View file

@ -1,15 +1,15 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
android { android {
compileSdkVersion 21 compileSdkVersion 22
buildToolsVersion "20.0.0" buildToolsVersion "22.0.1"
defaultConfig { defaultConfig {
applicationId "de.vanitasvitae.enigmandroid" applicationId "de.vanitasvitae.enigmandroid"
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 21 targetSdkVersion 22
versionCode 9 versionCode 10
versionName "0.1.3-14.03.2015-beta" versionName "0.1.4-15.08.2015-beta"
} }
buildTypes { buildTypes {
release { release {
@ -19,5 +19,5 @@ android {
dependencies { dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:support-v4:21.+' compile 'com.android.support:support-v4:22.2.1'
} }

0
app/proguard-rules.pro vendored Normal file → Executable file
View file

5
app/src/main/AndroidManifest.xml Normal file → Executable file
View file

@ -15,6 +15,11 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity> </activity>
<activity <activity
android:name=".SettingsActivity" android:name=".SettingsActivity"

View file

@ -1,12 +1,11 @@
package de.vanitasvitae.enigmandroid; package de.vanitasvitae.enigmandroid;
import de.vanitasvitae.enigmandroid.rotors.Reflector; import de.vanitasvitae.enigmandroid.rotors.Reflector;
import de.vanitasvitae.enigmandroid.rotors.Rotor; import de.vanitasvitae.enigmandroid.rotors.Rotor;
/** /**
* Enigma-machine * Main component of the Enigma machine
*Copyright (C) 2015 Paul Schaub * Copyright (C) 2015 Paul Schaub
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -35,13 +34,14 @@ public class Enigma
//Slot for the reflector //Slot for the reflector
private Reflector reflector; private Reflector reflector;
private boolean doAnomaly = false; private boolean doAnomaly = false; //Has the time come to handle an anomaly?
private boolean prefAnomaly; //Do you want to simulate the anomaly? private boolean prefAnomaly; //Do you WANT to simulate the anomaly?
private InputPreparator inputPreparator;
/** /**
* Create new Enigma with standard configuration. * Create new Enigma with standard configuration.
* Empty Plugboard, rotors I,II,III, Positions 0,0,0 * Empty Plugboard, reflector B, rotors I,II,III, Positions 0,0,0
*/ */
public Enigma() public Enigma()
{ {
@ -55,11 +55,14 @@ public class Enigma
this.r3 = Rotor.createRotorIII(0, 0); this.r3 = Rotor.createRotorIII(0, 0);
this.reflector = Reflector.createReflectorB(); this.reflector = Reflector.createReflectorB();
plugboard = new Plugboard(); plugboard = new Plugboard();
prefAnomaly = true; prefAnomaly = true; //TODO: Is this necessary?
} }
/** /**
* Encrypt / Decrypt a given String * 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 * @param w Text to decrypt/encrypt
* @return encrypted/decrypted string * @return encrypted/decrypted string
@ -78,28 +81,30 @@ public class Enigma
} }
/** /**
* Perform crypto on char. * Substitute char k by sending the signal through the enigma.
* Beforehand rotate rotors. Also implement the rotor anomaly. * 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 * @param k input char
* @return output char * @return substituted output char
*/ */
public char encryptChar(char k) public char encryptChar(char k)
{ {
//Rotate rotors //Rotate rotors
r1.rotate(); r1.rotate();
//Eventually turn next rotor (usual turnOver or anomaly)
if (r1.isAtTurnoverPosition() || (this.doAnomaly && prefAnomaly)) if (r1.isAtTurnoverPosition() || (this.doAnomaly && prefAnomaly))
{ {
r2.rotate(); r2.rotate();
//Handle Anomaly //Set doAnomaly for next call of encryptChar
this.doAnomaly = r2.doubleTurnAnomaly(); this.doAnomaly = r2.doubleTurnAnomaly();
//Eventually rotate next rotor
if (r2.isAtTurnoverPosition()) if (r2.isAtTurnoverPosition())
{ {
r3.rotate(); r3.rotate();
} }
} }
int x = (int) k; int x = ((int) k)-65; //Cast to int and remove Unicode Offset (A=65 in Unicode.)
x = x - 65; //Remove Unicode Offset (A=65 in Unicode.)
//Encryption //Encryption
//forward direction //forward direction
@ -122,43 +127,21 @@ public class Enigma
x = normalize(x - r1.getRotation()); x = normalize(x - r1.getRotation());
x = plugboard.encrypt(x); x = plugboard.encrypt(x);
return (char) (x + 65); //Add Offset return (char) (x + 65); //Add Offset again and cast back to char
}
public int normalize(int input)
{
return (26+input)%26;
} }
/** /**
* Prepare String for encryption via enigma * Normalize the input.
* Replace . , ! ? : with X * Normalizing means keeping the input via modulo in the range from 0 to n-1, where n is equal
* Remove all other chars that are not A..Z * to the size of the rotors (in this case fixed to 26, TODO?).
* * This is necessary since java allows negative modulo values,
* @param word string * which can break this implementation
* @return prepared string * @param input input signal
* @return "normalized" input signal
*/ */
public static String prepare(String word) public int normalize(int input)
{ {
String input = word.toUpperCase(); return (26+input)%26;
String output = "";
for (char x : input.toCharArray())
{
if (x >= 65 && x <= 90) //If x in [A..Z]
{
output = output + x; //Append to String
}
//if x is special symbol
else
{
if (x == '.' || x == ',' || x == '!' || x == '?' || x == ':')
{
//replace x with X and encrypt
output = output + 'X';
}
}
}
return output;
} }
/** /**
@ -198,6 +181,10 @@ public class Enigma
} }
} }
/**
* Set the plugboard
* @param p Plugboard
*/
public void setPlugboard(Plugboard p) public void setPlugboard(Plugboard p)
{ {
this.plugboard = p; this.plugboard = p;
@ -226,6 +213,28 @@ public class Enigma
return configuration; 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) public void setPrefAnomaly(boolean b)
{ {
this.prefAnomaly = b; this.prefAnomaly = b;

View file

@ -0,0 +1,123 @@
package de.vanitasvitae.enigmandroid;
import java.text.Normalizer;
/**
* Preparator 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
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 InputPreparator
{
/**
* Prepare the input String in a way that it only contains letters from [A..Z].
* Replace special characters and spell numbers.
* @param input String
* @return prepared String
*/
public String prepareString(String input)
{
input = input.toUpperCase();
input = input.replace("Ä","AE").replace("Ö","OE").replace("Ü","UE").replace("ß","SS");
String output = "";
for (char x : input.toCharArray())
{
if (x >= 65 && x <= 90) //x in [A..Z]
{
output = output + x;
}
else if (x >= 48 && x <= 57) //x in [0..9]
{
output = output + replaceNumber(x);
}
//x is special symbol
else
{
output = output + 'X';
}
}
return output;
}
/**
* Abstract method that spells numbers in a certain language specified by the implementation
* @param input character
* @return spelled number
*/
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
*/
public static InputPreparator createInputPreparator(String language)
{
switch (language)
{
case "de": return new InputPreparatorGerman();
default: return new InputPreparatorEnglish();
}
}
}
/**
* Concrete implementation of a german InputPreparator
*/
class InputPreparatorGerman extends InputPreparator
{
@Override
public String replaceNumber(char input) {
switch (input)
{
case '0': return "NULL";
case '1': return "EINS";
case '2': return "ZWEI";
case '3': return "DREI";
case '4': return "VIER";
case '5': return "FUENF";
case '6': return "SECHS";
case '7': return "SIEBEN";
case '8': return "ACHT";
default: return "NEUN";
}
}
}
/**
* Concrete implementation of an english InputPreparator
*/
class InputPreparatorEnglish extends InputPreparator
{
@Override
public String replaceNumber(char input)
{
switch (input) {
case '0': return "ZERO";
case '1': return "ONE";
case '2': return "TWO";
case '3': return "THREE";
case '4': return "FOUR";
case '5': return "FIVE";
case '6': return "SIX";
case '7': return "SEVEN";
case '8': return "EIGHT";
default: return "NINE";
}
}
}

View file

@ -1,21 +1,3 @@
/**
* 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.
*/
package de.vanitasvitae.enigmandroid; package de.vanitasvitae.enigmandroid;
import android.app.Activity; import android.app.Activity;
@ -33,21 +15,38 @@ import android.widget.EditText;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Toast; import android.widget.Toast;
/**
* Main Android Activity of the app
* 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 MainActivity extends Activity public class MainActivity extends Activity
{ {
private Menu menu; private Spinner rotor1View;
private Spinner rotor1; private Spinner rotor2View;
private Spinner rotor2; private Spinner rotor3View;
private Spinner rotor3; private Spinner reflectorView;
private Spinner reflector; private Spinner rotor1PositionView;
private Spinner rotor1Position; private Spinner rotor2PositionView;
private Spinner rotor2Position; private Spinner rotor3PositionView;
private Spinner rotor3Position;
private EditText plugboard; private EditText plugboardView;
private EditText input; private EditText inputView;
private EditText output; private EditText outputView;
private static final int RESULT_SETTINGS = 1; 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";
@ -56,6 +55,7 @@ public class MainActivity extends Activity
//memory for the ringSettings //memory for the ringSettings
private int[] ringSettings = {0,0,0}; private int[] ringSettings = {0,0,0};
private boolean prefAnomaly; private boolean prefAnomaly;
private String prefNumericLanguage;
@Override @Override
public void onCreate(Bundle savedInstanceState) public void onCreate(Bundle savedInstanceState)
@ -63,7 +63,10 @@ public class MainActivity extends Activity
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main); this.setContentView(R.layout.activity_main);
this.initLayout(); this.initLayout();
this.prefAnomaly = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("prefAnomaly", true); 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.resetLayout();
ActivitySingleton singleton = ActivitySingleton.getInstance(); ActivitySingleton singleton = ActivitySingleton.getInstance();
singleton.setActivity(this); singleton.setActivity(this);
@ -79,7 +82,7 @@ public class MainActivity extends Activity
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) if (sharedText != null)
{ {
input.setText(sharedText); inputView.setText(sharedText);
} }
} }
} }
@ -88,7 +91,6 @@ public class MainActivity extends Activity
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) public boolean onCreateOptionsMenu(Menu menu)
{ {
this.menu = menu;
this.getMenuInflater().inflate(R.menu.main, menu); this.getMenuInflater().inflate(R.menu.main, menu);
return true; return true;
} }
@ -109,7 +111,7 @@ public class MainActivity extends Activity
} }
else if (id == R.id.action_choose_ringstellung) else if (id == R.id.action_choose_ringstellung)
{ {
showRingsettingsDialog(); showRingSettingsDialog();
return true; return true;
} }
else if (id == R.id.action_settings) else if (id == R.id.action_settings)
@ -124,15 +126,15 @@ public class MainActivity extends Activity
} }
else if (id == R.id.action_send) else if (id == R.id.action_send)
{ {
if(output.getText().length() == 0) if(outputView.getText().length() == 0)
{ {
Toast.makeText(this, R.string.error_no_text_to_send, Toast.LENGTH_LONG).show(); Toast.makeText(this, R.string.error_no_text_to_send, Toast.LENGTH_SHORT).show();
} }
else else
{ {
Intent sendIntent = new Intent(); Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND); sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, output.getText().toString()); sendIntent.putExtra(Intent.EXTRA_TEXT, outputView.getText().toString());
sendIntent.setType("text/plain"); sendIntent.setType("text/plain");
startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to))); startActivity(Intent.createChooser(sendIntent, getResources().getText(R.string.send_to)));
} }
@ -148,25 +150,26 @@ public class MainActivity extends Activity
public void updateEnigma(View v) public void updateEnigma(View v)
{ {
int[] conf = new int[10]; int[] conf = new int[10];
conf[0] = rotor1.getSelectedItemPosition() + 1; conf[0] = rotor1View.getSelectedItemPosition() + 1;
conf[1] = rotor2.getSelectedItemPosition() + 1; conf[1] = rotor2View.getSelectedItemPosition() + 1;
conf[2] = rotor3.getSelectedItemPosition() + 1; conf[2] = rotor3View.getSelectedItemPosition() + 1;
conf[3] = reflector.getSelectedItemPosition() + 1; conf[3] = reflectorView.getSelectedItemPosition() + 1;
conf[4] = rotor1Position.getSelectedItemPosition() + 1; conf[4] = rotor1PositionView.getSelectedItemPosition() + 1;
conf[5] = rotor2Position.getSelectedItemPosition() + 1; conf[5] = rotor2PositionView.getSelectedItemPosition() + 1;
conf[6] = rotor3Position.getSelectedItemPosition() + 1; conf[6] = rotor3PositionView.getSelectedItemPosition() + 1;
conf[7] = ringSettings[0]; conf[7] = ringSettings[0];
conf[8] = ringSettings[1]; conf[8] = ringSettings[1];
conf[9] = ringSettings[2]; conf[9] = ringSettings[2];
enigma = new Enigma(); enigma = new Enigma();
int[][] plugboardConfiguration = null; int[][] plugboardConfiguration;
plugboard.setText(plugboard.getText().toString().toUpperCase()); plugboardView.setText(plugboardView.getText().toString().toUpperCase());
plugboardConfiguration = Plugboard.parseConfigurationString(plugboard.getText().toString()); plugboardConfiguration = Plugboard.parseConfigurationString(plugboardView.getText().toString());
enigma.setConfiguration(conf); enigma.setConfiguration(conf);
enigma.setPlugboard(new Plugboard(plugboardConfiguration)); enigma.setPlugboard(new Plugboard(plugboardConfiguration));
enigma.setPrefAnomaly(prefAnomaly); enigma.setPrefAnomaly(prefAnomaly);
enigma.setInputPreparator(InputPreparator.createInputPreparator(prefNumericLanguage));
} }
/** /**
@ -177,12 +180,12 @@ public class MainActivity extends Activity
*/ */
public void doCrypto(View v) public void doCrypto(View v)
{ {
if(input.getText().length()!=0) { if(inputView.getText().length()!=0) {
updateEnigma(null); updateEnigma(null);
String m = input.getText().toString(); String m = inputView.getText().toString();
m = Enigma.prepare(m); m = enigma.getInputPreparator().prepareString(m);
input.setText(m); inputView.setText(m);
output.setText(enigma.encrypt(m)); outputView.setText(enigma.encrypt(m));
updateSpinner(enigma.getConfiguration()); updateSpinner(enigma.getConfiguration());
} }
} }
@ -193,17 +196,17 @@ public class MainActivity extends Activity
*/ */
private void resetLayout() private void resetLayout()
{ {
rotor1.setSelection(0); rotor1View.setSelection(0);
rotor2.setSelection(1); rotor2View.setSelection(1);
rotor3.setSelection(2); rotor3View.setSelection(2);
reflector.setSelection(1); reflectorView.setSelection(1);
rotor1Position.setSelection(0); rotor1PositionView.setSelection(0);
rotor2Position.setSelection(0); rotor2PositionView.setSelection(0);
rotor3Position.setSelection(0); rotor3PositionView.setSelection(0);
ringSettings = new int[]{0,0,0}; ringSettings = new int[]{0,0,0};
plugboard.setText(""); plugboardView.setText("");
input.setText(""); inputView.setText("");
output.setText(""); outputView.setText("");
} }
/** /**
@ -214,63 +217,62 @@ public class MainActivity extends Activity
Character[] charArray = new Character[26]; Character[] charArray = new Character[26];
for(int i=0; i<26; i++) {charArray[i] = (char) (65+i);} for(int i=0; i<26; i++) {charArray[i] = (char) (65+i);}
rotor1 = (Spinner) findViewById(R.id.rotor1); rotor1View = (Spinner) findViewById(R.id.rotor1);
ArrayAdapter<CharSequence> rotor1Adapter = ArrayAdapter.createFromResource(this, ArrayAdapter<CharSequence> rotor1Adapter = ArrayAdapter.createFromResource(this,
R.array.enigma_rotors, android.R.layout.simple_spinner_item); R.array.enigma_rotors, android.R.layout.simple_spinner_item);
rotor1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); rotor1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rotor1.setAdapter(rotor1Adapter); rotor1View.setAdapter(rotor1Adapter);
rotor2 = (Spinner) findViewById(R.id.rotor2); rotor2View = (Spinner) findViewById(R.id.rotor2);
ArrayAdapter<CharSequence> rotor2Adapter = ArrayAdapter.createFromResource(this, ArrayAdapter<CharSequence> rotor2Adapter = ArrayAdapter.createFromResource(this,
R.array.enigma_rotors, android.R.layout.simple_spinner_item); R.array.enigma_rotors, android.R.layout.simple_spinner_item);
rotor2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); rotor2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rotor2.setAdapter(rotor2Adapter); rotor2View.setAdapter(rotor2Adapter);
rotor3 = (Spinner) findViewById(R.id.rotor3); rotor3View = (Spinner) findViewById(R.id.rotor3);
ArrayAdapter<CharSequence> rotor3Adapter = ArrayAdapter.createFromResource(this, ArrayAdapter<CharSequence> rotor3Adapter = ArrayAdapter.createFromResource(this,
R.array.enigma_rotors, android.R.layout.simple_spinner_item); R.array.enigma_rotors, android.R.layout.simple_spinner_item);
rotor3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); rotor3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rotor3.setAdapter(rotor3Adapter); rotor3View.setAdapter(rotor3Adapter);
reflector = (Spinner) findViewById(R.id.reflector); reflectorView = (Spinner) findViewById(R.id.reflector);
ArrayAdapter<CharSequence> relfectorAdapter = ArrayAdapter.createFromResource(this, ArrayAdapter<CharSequence> relfectorAdapter = ArrayAdapter.createFromResource(this,
R.array.enigma_reflectors, android.R.layout.simple_spinner_item); R.array.enigma_reflectors, android.R.layout.simple_spinner_item);
relfectorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); relfectorAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
reflector.setAdapter(relfectorAdapter); reflectorView.setAdapter(relfectorAdapter);
rotor1Position = (Spinner) findViewById(R.id.rotor1position); rotor1PositionView = (Spinner) findViewById(R.id.rotor1position);
ArrayAdapter<Character> rotor1PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(), ArrayAdapter<Character> rotor1PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(),
android.R.layout.simple_spinner_item,charArray); android.R.layout.simple_spinner_item,charArray);
rotor1PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); rotor1PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rotor1Position.setAdapter(rotor1PositionAdapter); rotor1PositionView.setAdapter(rotor1PositionAdapter);
rotor2Position = (Spinner) findViewById(R.id.rotor2position); rotor2PositionView = (Spinner) findViewById(R.id.rotor2position);
ArrayAdapter<Character> rotor2PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(), ArrayAdapter<Character> rotor2PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(),
android.R.layout.simple_spinner_item,charArray); android.R.layout.simple_spinner_item,charArray);
rotor2PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); rotor2PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rotor2Position.setAdapter(rotor2PositionAdapter); rotor2PositionView.setAdapter(rotor2PositionAdapter);
rotor3Position = (Spinner) findViewById(R.id.rotor3position); rotor3PositionView = (Spinner) findViewById(R.id.rotor3position);
ArrayAdapter<Character> rotor3PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(), ArrayAdapter<Character> rotor3PositionAdapter = new ArrayAdapter<>(this.getApplicationContext(),
android.R.layout.simple_spinner_item,charArray); android.R.layout.simple_spinner_item,charArray);
rotor3PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); rotor3PositionAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
rotor3Position.setAdapter(rotor3PositionAdapter); rotor3PositionView.setAdapter(rotor3PositionAdapter);
plugboard = (EditText) findViewById(R.id.plugboard); plugboardView = (EditText) findViewById(R.id.plugboard);
plugboard.setOnFocusChangeListener(new View.OnFocusChangeListener() { plugboardView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override @Override
public void onFocusChange(View v, boolean hasFocus) { public void onFocusChange(View v, boolean hasFocus) {
if(!hasFocus) if (!hasFocus) {
{ plugboardView.setText(plugboardView.getText().toString().toUpperCase());
plugboard.setText(plugboard.getText().toString().toUpperCase());
} }
} }
}); });
input = (EditText) findViewById(R.id.input); inputView = (EditText) findViewById(R.id.input);
output = (EditText) findViewById(R.id.output); outputView = (EditText) findViewById(R.id.output);
input.requestFocus(); inputView.requestFocus();
} }
/** /**
@ -279,40 +281,40 @@ public class MainActivity extends Activity
*/ */
public void updateSpinner(int[] c) public void updateSpinner(int[] c)
{ {
rotor1.setSelection(c[0] - 1); rotor1View.setSelection(c[0] - 1);
rotor2.setSelection(c[1] - 1); rotor2View.setSelection(c[1] - 1);
rotor3.setSelection(c[2] - 1); rotor3View.setSelection(c[2] - 1);
rotor1Position.setSelection(c[4]); rotor1PositionView.setSelection(c[4]);
rotor2Position.setSelection(c[5]); rotor2PositionView.setSelection(c[5]);
rotor3Position.setSelection(c[6]); rotor3PositionView.setSelection(c[6]);
} }
/** /**
* Show the dialog where the user can pick the ringsettings and set them if the user doesn't * Show the dialog where the user can pick the ringsettings and set them if the user doesn't
* abort. * abort.
*/ */
public void showRingsettingsDialog() public void showRingSettingsDialog()
{ {
View ringsettingsView = View.inflate(this, R.layout.dialog_ringsettings, null); View ringSettingsView = View.inflate(this, R.layout.dialog_ringsettings, null);
Integer[] ringArray = new Integer[26]; Integer[] ringArray = new Integer[26];
for(int i=1; i<=26; i++) {ringArray[i-1] = i;} for(int i=1; i<=26; i++) {ringArray[i-1] = i;}
final Spinner ring1 = (Spinner) ringsettingsView.findViewById(R.id.rotor1ring); final Spinner ring1 = (Spinner) ringSettingsView.findViewById(R.id.rotor1ring);
ArrayAdapter<Integer> ring1Adapter = new ArrayAdapter<>(this.getApplicationContext(), ArrayAdapter<Integer> ring1Adapter = new ArrayAdapter<>(this.getApplicationContext(),
android.R.layout.simple_spinner_item,ringArray); android.R.layout.simple_spinner_item,ringArray);
ring1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); ring1Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
ring1.setAdapter(ring1Adapter); ring1.setAdapter(ring1Adapter);
ring1.setSelection(ringSettings[0]); ring1.setSelection(ringSettings[0]);
final Spinner ring2 = (Spinner) ringsettingsView.findViewById(R.id.rotor2ring); final Spinner ring2 = (Spinner) ringSettingsView.findViewById(R.id.rotor2ring);
ArrayAdapter<Integer> ring2Adapter = new ArrayAdapter<>(this.getApplicationContext(), ArrayAdapter<Integer> ring2Adapter = new ArrayAdapter<>(this.getApplicationContext(),
android.R.layout.simple_spinner_item,ringArray); android.R.layout.simple_spinner_item,ringArray);
ring2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); ring2Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
ring2.setAdapter(ring2Adapter); ring2.setAdapter(ring2Adapter);
ring2.setSelection(ringSettings[1]); ring2.setSelection(ringSettings[1]);
final Spinner ring3 = (Spinner) ringsettingsView.findViewById(R.id.rotor3ring); final Spinner ring3 = (Spinner) ringSettingsView.findViewById(R.id.rotor3ring);
ArrayAdapter<Integer> ring3Adapter = new ArrayAdapter<>(this.getApplicationContext(), ArrayAdapter<Integer> ring3Adapter = new ArrayAdapter<>(this.getApplicationContext(),
android.R.layout.simple_spinner_item,ringArray); android.R.layout.simple_spinner_item,ringArray);
ring3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); ring3Adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@ -321,7 +323,7 @@ public class MainActivity extends Activity
AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.title_ringsetting); builder.setTitle(R.string.title_ringsetting);
builder.setView(ringsettingsView) builder.setView(ringSettingsView)
.setCancelable(true) .setCancelable(true)
.setPositiveButton(R.string.dialog_positiv, new DialogInterface.OnClickListener() .setPositiveButton(R.string.dialog_positiv, new DialogInterface.OnClickListener()
{ {
@ -379,8 +381,12 @@ public class MainActivity extends Activity
{ {
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
this.prefAnomaly = sharedPrefs.getBoolean("prefAnomaly", true); this.prefAnomaly = sharedPrefs.getBoolean("prefAnomaly", true);
this.prefNumericLanguage = sharedPrefs.getString("prefNumericLanguage",getResources().
getStringArray(R.array.pref_alias_numeric_spelling_language)[0]);
;
break; break;
} }
} }
} }

View file

@ -6,7 +6,23 @@ import android.widget.Toast;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* Created by vanitas on 12.08.15. * Plugboard of the enigma
* 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 Plugboard public class Plugboard
{ {
@ -30,7 +46,7 @@ public class Plugboard
/** /**
* Configure the plugboard according to the given array. * Configure the plugboard according to the given array.
* *
* @param configuration * @param configuration two dimensional array of plugs
*/ */
public void setConfiguration(int[][] configuration) public void setConfiguration(int[][] configuration)
{ {
@ -68,7 +84,7 @@ public class Plugboard
} }
else { else {
input = input.toUpperCase(); input = input.toUpperCase();
ArrayList<int[]> plugList = new ArrayList<int[]>(); ArrayList<int[]> plugList = new ArrayList<>();
int[] plug = new int[2]; int[] plug = new int[2];
for (int i = 0; i < input.length(); i++) { for (int i = 0; i < input.length(); i++) {
int c = input.charAt(i) - 65; int c = input.charAt(i) - 65;

View file

@ -1,7 +1,25 @@
package de.vanitasvitae.enigmandroid.rotors; package de.vanitasvitae.enigmandroid.rotors;
/** /**
* Created by vanitas on 11.08.15. * Reflector of the enigma machine.
* The reflector was used to reflect the scrambled signal at the end of the wiring back to
* go through another (reversed but not inverting) process of scrambling.
* 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 Reflector public class Reflector
{ {
@ -9,6 +27,14 @@ public class Reflector
protected int type; protected int type;
protected Integer[] connections; protected Integer[] connections;
/**
* This constructor is not accessible from outside this class file.
* Use the one of the createReflector* methods instead to create concrete Reflectors from
* outside this class file
* @param name phonetic name of the reflector (usually A,B,C)
* @param type type indicator of the reflector (A->1,...,C->3)
* @param connections wiring of the reflector as Integer array
*/
protected Reflector(String name, int type, Integer[] connections) protected Reflector(String name, int type, Integer[] connections)
{ {
this.name = name; this.name = name;
@ -16,21 +42,41 @@ public class Reflector
this.connections = connections; this.connections = connections;
} }
/**
* Creates a reflector of type A
* @return ReflectorA
*/
public static Reflector createReflectorA() public static Reflector createReflectorA()
{ {
return new ReflectorA(); return new ReflectorA();
} }
/**
* Creates a reflector of type B
* @return ReflectorB
*/
public static Reflector createReflectorB() public static Reflector createReflectorB()
{ {
return new ReflectorB(); return new ReflectorB();
} }
/**
* Creates a reflector of type C
* @return ReflectorC
*/
public static Reflector createReflectorC() public static Reflector createReflectorC()
{ {
return new ReflectorC(); return new ReflectorC();
} }
/**
* Factory method to create reflectors.
* @param type type of the created reflector
* 1 -> ReflectorA
* 2 -> ReflectorB
* anything else -> ReflectorC
* @return ReflectorA | ReflectorB | ReflectorC
*/
public static Reflector createReflector(int type) public static Reflector createReflector(int type)
{ {
switch (type) switch (type)
@ -41,31 +87,61 @@ public class Reflector
} }
} }
/**
* Substitute an input signal via the wiring of the reflector with a different (!) output.
* The output MUST not be equal to the input for any input, since this was not possible
* due to the electronic implementation of the historical enigma machine.
* @param input input signal
* @return encrypted (substituted) output
*/
public int encrypt(int input) public int encrypt(int input)
{ {
return this.connections[normalize(input)]; return this.connections[normalize(input)];
} }
/**
* Return the type indicator of the reflector
* @return type
*/
public int getType() public int getType()
{ {
return this.type; return this.type;
} }
/**
* Return phonetic name of the reflector
* @return name
*/
public String getName() public String getName()
{ {
return this.name; return this.name;
} }
/**
* Return the size (ie the number of wires/length of the connections array) of the reflector
* @return size
*/
private int getRotorSize() private int getRotorSize()
{ {
return this.connections.length; return this.connections.length;
} }
/**
* 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 reflector. This is necessary since java allows negative modulo values,
* which can break this implementation
* @param input input signal
* @return "normalized" input signal
*/
private int normalize(int input) private int normalize(int input)
{ {
return (input + this.getRotorSize()) % this.getRotorSize(); return (input + this.getRotorSize()) % this.getRotorSize();
} }
/**
* Concrete implementation of ReflectorA
*/
private static class ReflectorA extends Reflector private static class ReflectorA extends Reflector
{ {
public ReflectorA() public ReflectorA()
@ -74,6 +150,9 @@ public class Reflector
} }
} }
/**
* Concrete implementation of ReflectorB
*/
private static class ReflectorB extends Reflector private static class ReflectorB extends Reflector
{ {
public ReflectorB() public ReflectorB()
@ -82,6 +161,9 @@ public class Reflector
} }
} }
/**
* Concrete implementation of ReflectorC
*/
private static class ReflectorC extends Reflector private static class ReflectorC extends Reflector
{ {
public ReflectorC() public ReflectorC()

View file

@ -1,7 +1,28 @@
package de.vanitasvitae.enigmandroid.rotors; package de.vanitasvitae.enigmandroid.rotors;
/** /**
* Created by vanitas on 11.08.15. * Rotor super class and inner concrete implementations
* The rotors were the key feature of the enigma used to scramble up input signals into
* encrypted signals difficult to predict. The rotors rotated to achieve a poly-alphabetic
* substitution which was hard to break. Each signal passes the rotor twice. Once in "forward"-
* direction and once in "backwards"-direction. There was a set of 3 out of 5 rotors inside the
* enigma machine M4.
* 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 Rotor public class Rotor
{ {
@ -14,6 +35,22 @@ public class Rotor
protected int ringSetting; protected int ringSetting;
protected int rotation; protected int rotation;
/**
* This constructor is not accessible from outside this class file.
* Use one of the createRotor* factory methods instead to create concrete Rotors.
* Note that connections and reversedConnections MUST be of the same size and that
* neither connections nor reversedConnections respectively MUST have any number between
* 0 and connections.length-1 only once (ie they represent permutations)
* @param name phonetic name of the rotor (usually I,II,...V)
* @param type type indicator (I -> 1,...,V -> 5)
* @param connections wiring of the rotor as Integer array
* @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 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, protected Rotor(String name, int type, Integer[] connections, Integer[] reversedConnections,
int turnOverNotch, int ringSetting, int rotation) int turnOverNotch, int ringSetting, int rotation)
{ {
@ -26,6 +63,14 @@ public class Rotor
this.rotation = rotation; this.rotation = rotation;
} }
/**
* 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 ringSetting setting of the outer ring (0..25)
* @param rotation rotation of the rotor
* @return Concrete rotor
*/
public static Rotor createRotor(int type, int ringSetting, int rotation) public static Rotor createRotor(int type, int ringSetting, int rotation)
{ {
switch (type) switch (type)
@ -38,53 +83,103 @@ public class Rotor
} }
} }
/**
* 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) public static Rotor createRotorI(int ringSetting, int rotation)
{ {
return new RotorI(ringSetting, 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) public static Rotor createRotorII(int ringSetting, int rotation)
{ {
return new RotorII(ringSetting, 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) public static Rotor createRotorIII(int ringSetting, int rotation)
{ {
return new RotorIII(ringSetting, 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) public static Rotor createRotorIV(int ringSetting, int rotation)
{ {
return new RotorIV(ringSetting, 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) public static Rotor createRotorV(int ringSetting, int rotation)
{ {
return new RotorV(ringSetting, rotation); return new RotorV(ringSetting, rotation);
} }
/**
* Encrypt an input signal via the internal wiring in "forward" direction (using connections)
* @param input signal
* @return encrypted signal
*/
public int encryptForward(int input) public int encryptForward(int input)
{ {
return this.connections[normalize(input)]; return this.connections[normalize(input)];
} }
/**
* Encrypt an input signal via the internal wiring in "backwards" direction (using
* reversedConnections)
* @param input signal
* @return encrypted signal
*/
public int encryptBackward(int input) public int encryptBackward(int input)
{ {
return this.reversedConnections[normalize(input)]; return this.reversedConnections[normalize(input)];
} }
/**
* Return the type indicator (usually 1..5)
* @return type indicator
*/
public int getType() public int getType()
{ {
return this.type; return this.type;
} }
/**
* Return the current rotation of the rotor.
* The rotation consists of the actual rotation - the ringSetting
* @return rotation-ringSetting
*/
public int getRotation() public int getRotation()
{ {
return this.rotation - this.getRingSetting(); return this.rotation - this.getRingSetting();
} }
/** /**
* increment rotation of the rotor by one. * Increment rotation of the rotor by one.
*/ */
public void rotate() public void rotate()
{ {
@ -92,9 +187,8 @@ public class Rotor
} }
/** /**
* Return true, if rotor is at a position, where it turns over the next rotor * Return true, if the rotor is at a position, where it turns over the next rotor by one
* * @return rotation==turnOverNotch
* @return boolean
*/ */
public boolean isAtTurnoverPosition() public boolean isAtTurnoverPosition()
{ {
@ -102,31 +196,28 @@ public class Rotor
} }
/** /**
* The Double Turn Anomaly (deutsch: Doppelsprung Anomalie) is an anomaly in the rotor movement * Return true, if the rotor is in a position where the double turn anomaly happens.
* The double turn anomaly (german: Doppelsprung-Anomalie) is an anomaly in the rotor movement
* caused by the mechanical implementation of the enigma. * caused by the mechanical implementation of the enigma.
* Whenever the rightmost rotor turns the middle rotor AND the middle rotor is only one move * Whenever the rightmost rotor turns the middle rotor AND the middle rotor is only one move
* from turning the leftmost rotor, the middle rotor turns again with the next character. * from turning the leftmost rotor, the middle rotor turns again with the next character.
* So technically there are only 26*25*26 possible rotor settings for any but firmly 3 rotors. * So technically there are only 26*25*26 possible rotor settings for any but firmly 3 rotors.
* * @return rotation == turnOverNotch-1
* @return boolean
*/ */
public boolean doubleTurnAnomaly() public boolean doubleTurnAnomaly()
{ {
return this.rotation == this.turnOverNotch - 1; return this.rotation == this.getTurnOver() - 1;
} }
@SuppressWarnings("unused")
/** /**
* Returns the position of the turnover notch * Returns the position of the turnover notch
* * @return turnOverNotch
* @return turnOver
*/ */
public int getTurnOver() public int getTurnOver()
{ {
return this.turnOverNotch; return this.turnOverNotch;
} }
@SuppressWarnings("unused")
/** /**
* Return ringSettings of the rotor * Return ringSettings of the rotor
* @return ringSetting * @return ringSetting
@ -136,21 +227,41 @@ public class Rotor
return this.ringSetting; return this.ringSetting;
} }
/**
* Returns the phonetic name of the rotor
* @return name
*/
public String getName() public String getName()
{ {
return this.name; return this.name;
} }
/**
* Returns the size (ie the number of wires/size of the connections array)
* of the rotor
* @return size
*/
public int getRotorSize() public int getRotorSize()
{ {
return this.connections.length; return this.connections.length;
} }
/**
* 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 rotor. 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) public int normalize(int input)
{ {
return (input + this.getRotorSize()) % this.getRotorSize(); return (input + this.getRotorSize()) % this.getRotorSize();
} }
/**
* Concrete implementation of Rotor of type 1 (I)
*/
private static class RotorI extends Rotor private static class RotorI extends Rotor
{ {
public RotorI(int ringSetting, int rotation) public RotorI(int ringSetting, int rotation)
@ -162,6 +273,9 @@ public class Rotor
} }
} }
/**
* Concrete implementation of Rotor of type 2 (II)
*/
private static class RotorII extends Rotor private static class RotorII extends Rotor
{ {
public RotorII(int ringSetting, int rotation) public RotorII(int ringSetting, int rotation)
@ -173,6 +287,9 @@ public class Rotor
} }
} }
/**
* Concrete implementation of Rotor of type 3 (III)
*/
private static class RotorIII extends Rotor private static class RotorIII extends Rotor
{ {
public RotorIII(int ringSetting, int rotation) public RotorIII(int ringSetting, int rotation)
@ -184,6 +301,9 @@ public class Rotor
} }
} }
/**
* Concrete implementation of Rotor of type 4 (IV)
*/
private static class RotorIV extends Rotor private static class RotorIV extends Rotor
{ {
public RotorIV(int ringSetting, int rotation) public RotorIV(int ringSetting, int rotation)
@ -195,6 +315,9 @@ public class Rotor
} }
} }
/**
* Concrete implementation of Rotor of type 5 (V)
*/
private static class RotorV extends Rotor private static class RotorV extends Rotor
{ {
public RotorV(int ringSetting, int rotation) public RotorV(int ringSetting, int rotation)

View file

@ -144,6 +144,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="30pt" android:layout_height="30pt"
android:id="@+id/plugboard" android:id="@+id/plugboard"
android:inputType="textNoSuggestions"
android:hint="@string/hint_enigma_plugboard" android:hint="@string/hint_enigma_plugboard"
android:layout_below="@+id/lin_lay_2"/> android:layout_below="@+id/lin_lay_2"/>
@ -158,13 +159,14 @@
android:layout_weight=".50" android:layout_weight=".50"
android:layout_height="match_parent" android:layout_height="match_parent"
android:id="@+id/input" android:id="@+id/input"
android:inputType="textNoSuggestions"
android:hint="@string/hint_enigma_type_here" /> android:hint="@string/hint_enigma_type_here" />
<EditText <EditText
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight=".50" android:layout_weight=".50"
android:layout_height="match_parent" android:layout_height="match_parent"
android:editable="false" android:inputType="none"
android:textIsSelectable="true" android:textIsSelectable="true"
android:id="@+id/output" android:id="@+id/output"
android:hint="@string/hint_enigma_code"/> android:hint="@string/hint_enigma_code"/>
@ -178,7 +180,6 @@
<Button <Button
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/text_layer"
android:id="@+id/button_crypt" android:id="@+id/button_crypt"
android:onClick="doCrypto" android:onClick="doCrypto"
android:text="@string/button_crypt" android:text="@string/button_crypt"

View file

@ -7,5 +7,11 @@
<string name="pref_title_simulate_anomaly">Doppelschritt Anomalie</string> <string name="pref_title_simulate_anomaly">Doppelschritt Anomalie</string>
<string name="pref_description_simulate_anomaly">Die Doppelschritt Anomalie lässt die mittlere Walze zweimal rotieren, falls sie sich vor dem Übertragspunkt befindet. <string name="pref_description_simulate_anomaly">Die Doppelschritt Anomalie lässt die mittlere Walze zweimal rotieren, falls sie sich vor dem Übertragspunkt befindet.
</string> </string>
<string name="pref_title_numeric_spelling_language">Zahlenbuchstabiersprache</string>
<string name="pref_description_numeric_spelling_language">Sprache in der Zahlen buchstabiert werden sollen.</string>
<string-array name="pref_list_numeric_spelling_language">
<item>Deutsch</item>
<item>Englisch</item>
</string-array>
</resources> </resources>

View file

@ -25,10 +25,10 @@
<string name="error_unable_to_plug_a_b">Unable to plug </string> <string name="error_unable_to_plug_a_b">Unable to plug </string>
<string name="error_plug_already_in_use">Error: One or more of these plugs are already in use:</string> <string name="error_plug_already_in_use">Error: One or more of these plugs are already in use:</string>
<string name="error_no_text_to_send">Can\'t send empty text.</string> <string name="error_no_text_to_send">Can\'t send empty text.</string>
<string name="title_ringsetting">Ringsettings</string> <string name="title_ringsetting">Ring-Settings</string>
<string name="dialog_positiv">OK</string> <string name="dialog_positiv">OK</string>
<string name="dialog_negativ">Cancel</string> <string name="dialog_negativ">Cancel</string>
<string name="dialog_ringsettings_success">Set Ringsettings to</string> <string name="dialog_ringsettings_success">Set Ring-Settings to</string>
<string name="dialog_ringsettings_abort">No changes</string> <string name="dialog_ringsettings_abort">No changes</string>
<string name="message_reset">Enigma reset</string> <string name="message_reset">Enigma reset</string>

View file

@ -8,5 +8,15 @@
<string name="pref_description_simulate_anomaly">The double step anomaly causes the middle rotor <string name="pref_description_simulate_anomaly">The double step anomaly causes the middle rotor
to rotate twice, if it is one step before its turnover point. to rotate twice, if it is one step before its turnover point.
</string> </string>
<string name="pref_title_numeric_spelling_language">Number spelling language</string>
<string name="pref_description_numeric_spelling_language">Language, in which numbers are spelled.</string>
<string-array name="pref_list_numeric_spelling_language">
<item>German</item>
<item>English</item>
</string-array>
<string-array translatable="false" name="pref_alias_numeric_spelling_language">
<item>de</item>
<item>en</item>
</string-array>
</resources> </resources>

View file

@ -7,6 +7,13 @@
android:title="@string/pref_title_simulate_anomaly" android:title="@string/pref_title_simulate_anomaly"
android:summary="@string/pref_description_simulate_anomaly" android:summary="@string/pref_description_simulate_anomaly"
android:defaultValue="true" /> android:defaultValue="true" />
<ListPreference
android:key="prefNumericLanguage"
android:title="@string/pref_title_numeric_spelling_language"
android:summary="@string/pref_description_numeric_spelling_language"
android:entries="@array/pref_list_numeric_spelling_language"
android:entryValues="@array/pref_alias_numeric_spelling_language"
android:defaultValue="de" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>