Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
smite1921 committed Dec 17, 2023
1 parent 7f9b35b commit 117590f
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 92 deletions.
4 changes: 2 additions & 2 deletions enigma-machine-android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ android {
applicationId "com.smitpatel.enigmamachine"
minSdk 21
targetSdk 33
versionCode 10
versionName "2.0"
versionCode 12
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
package com.smitpatel.enigmamachine

import android.content.Context
import com.smitpatel.enigmamachine.models.Rotor

internal fun Char.letterToNumber() = this.code - 65

internal fun Int.numberToLetter() = Char(this + 65)

internal fun Rotor.RotorOption.toRotorLabelText(context: Context): String {
val rotorOptions = context.resources.getStringArray(R.array.rotor_options)
return when (this) {
Rotor.RotorOption.ROTOR_ONE -> rotorOptions[0]
Rotor.RotorOption.ROTOR_TWO -> rotorOptions[1]
Rotor.RotorOption.ROTOR_THREE -> rotorOptions[2]
Rotor.RotorOption.ROTOR_FOUR -> rotorOptions[3]
Rotor.RotorOption.ROTOR_FIVE -> rotorOptions[4]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.smitpatel.enigmamachine

import android.content.Context
import com.smitpatel.enigmamachine.models.EnigmaHistoryItem
import com.smitpatel.enigmamachine.models.Reflector
import com.smitpatel.enigmamachine.models.Rotor
import org.json.JSONArray
import org.json.JSONObject
import kotlin.Exception

class DeserializeException : Exception()

object Serializer {

private const val rotorOptionsKey: String = "rotorOptions"
private const val rotorPositionsKey: String = "rotorPositions"
private const val ringPositionsKey: String = "ringPositions"
private const val reflectorKey: String = "reflector"
private const val plugboardPairsKey: String = "plugboardPairs"


fun serializeJson(context: Context, settings: EnigmaHistoryItem): String {
val json = JSONObject()

json.put(rotorOptionsKey, JSONArray(arrayOf(
settings.rotorOneOption.toRotorLabelText(context),
settings.rotorTwoOption.toRotorLabelText(context),
settings.rotorThreeOption.toRotorLabelText(context),
)))

json.put(rotorPositionsKey, JSONArray(arrayOf(
settings.rotorOnePosition.numberToLetter(),
settings.rotorTwoPosition.numberToLetter(),
settings.rotorThreePosition.numberToLetter(),
)))

json.put(ringPositionsKey, JSONArray(arrayOf(
settings.ringOneOption.numberToLetter(),
settings.ringTwoOption.numberToLetter(),
settings.ringThreeOption.numberToLetter(),
)))

json.put(reflectorKey, settings.reflectorOption.toReflectorLabelText(context))

json.put(plugboardPairsKey, JSONArray(settings.plugboardPairs.map {
JSONArray(arrayOf(it.first.numberToLetter(), it.second.numberToLetter()))
}))

return json.toString()
}

@Throws(DeserializeException::class)
fun deserializeJson(context: Context, settings: String): EnigmaHistoryItem {
try {
val json = JSONObject(settings)

val jsonRotorOptions = json.getJSONArray(rotorOptionsKey)
val rotorOptions = (0 until 3).map {
jsonRotorOptions.getString(it).toRotorOption(context)
}

val jsonRotorPositions = json.getJSONArray(rotorPositionsKey)
val rotorPositions = (0 until 3).map {
jsonRotorPositions.getString(it).toNumber()
}

val jsonRingPosition = json.getJSONArray(ringPositionsKey)
val ringPositions = (0 until 3).map {
jsonRingPosition.getString(it).toNumber()
}

val reflector = json.getString(reflectorKey).toReflector(context)

val jsonPlugboardPairs = json.getJSONArray(plugboardPairsKey)
val plugboardPairs = mutableSetOf<Pair<Int, Int>>()
for (i in 0 until jsonPlugboardPairs.length()) {
val jsonPair = jsonPlugboardPairs.getJSONArray(i)
plugboardPairs.add(
Pair(
first = jsonPair.getString(0).toNumber(),
second = jsonPair.getString(1).toNumber(),
)
)
}

return EnigmaHistoryItem(
rotorOneOption = rotorOptions[0],
rotorTwoOption = rotorOptions[1],
rotorThreeOption = rotorOptions[2],
rotorOnePosition = rotorPositions[0],
rotorTwoPosition = rotorPositions[1],
rotorThreePosition = rotorPositions[2],
ringOneOption = ringPositions[0],
ringTwoOption = ringPositions[1],
ringThreeOption = ringPositions[2],
reflectorOption = reflector,
plugboardPairs = plugboardPairs,
)
} catch (exception: Exception) {
throw DeserializeException()
}
}

private fun Reflector.toReflectorLabelText(context: Context) = when(this) {
Reflector.REFLECTOR_UKW_A -> context.getString(R.string.reflector_a)
Reflector.REFLECTOR_UKW_B -> context.getString(R.string.reflector_b)
Reflector.REFLECTOR_UKW_C -> context.getString(R.string.reflector_c)
}

@Throws(IllegalArgumentException::class, NoSuchElementException::class)
private fun String.toNumber(): Int {
val char = this.trim().single().uppercaseChar()
if (!char.isLetter()) {
throw IllegalArgumentException("Input must be a letter: $this")
}
return char.letterToNumber()
}

@Throws(IllegalArgumentException::class)
private fun String.toRotorOption(context: Context): Rotor.RotorOption {
val rotorOptions = context.resources.getStringArray(R.array.rotor_options)
return when (this.uppercase().trim()) {
rotorOptions[0] -> Rotor.RotorOption.ROTOR_ONE
rotorOptions[1] -> Rotor.RotorOption.ROTOR_TWO
rotorOptions[2] -> Rotor.RotorOption.ROTOR_THREE
rotorOptions[3] -> Rotor.RotorOption.ROTOR_FOUR
rotorOptions[4] -> Rotor.RotorOption.ROTOR_FIVE
else -> throw IllegalArgumentException("Invalid Roman numeral for RotorOption: $this")
}
}

@Throws(IllegalArgumentException::class)
private fun String.toReflector(context: Context) = when (this.uppercase().trim()) {
context.getString(R.string.reflector_a) -> Reflector.REFLECTOR_UKW_A
context.getString(R.string.reflector_b) -> Reflector.REFLECTOR_UKW_B
context.getString(R.string.reflector_c) -> Reflector.REFLECTOR_UKW_C
else -> throw IllegalArgumentException("Invalid reflector string: $this")
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.smitpatel.enigmamachine.events

import com.smitpatel.enigmamachine.models.EnigmaHistoryItem
import com.smitpatel.enigmamachine.ui.RotorPosition

/**
Expand All @@ -11,6 +12,7 @@ sealed class EnigmaEvent {
data class RotorStartPositionChanged(val rotorPosition: RotorPosition, val start: Int): EnigmaEvent()
data class SettingMenuClosed(val didSettingsChanged : Boolean) : EnigmaEvent()
data class PasteRawText(val rawText: String) : EnigmaEvent()
data class PasteEnigmaSettings(val enigmaSettings: EnigmaHistoryItem?): EnigmaEvent()
object InputSpacePressed : EnigmaEvent()
object InputDeletePressed : EnigmaEvent()
object InputLongDeletePressed : EnigmaEvent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import android.view.View
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.smitpatel.enigmamachine.DeserializeException
import com.smitpatel.enigmamachine.R
import com.smitpatel.enigmamachine.Serializer
import com.smitpatel.enigmamachine.databinding.ActivityEnigmaMainBinding
import com.smitpatel.enigmamachine.ui.EnigmaSounds
import com.smitpatel.enigmamachine.ui.SoundEffects
import com.smitpatel.enigmamachine.ui.setting.SettingsFragment
import com.smitpatel.enigmamachine.events.EnigmaEvent
import com.smitpatel.enigmamachine.letterToNumber
import com.smitpatel.enigmamachine.models.Reflector
import com.smitpatel.enigmamachine.models.Rotor
import com.smitpatel.enigmamachine.numberToLetter
import com.smitpatel.enigmamachine.toRotorLabelText
import com.smitpatel.enigmamachine.ui.RotorPosition
import com.smitpatel.enigmamachine.ui.paste_error.PasteErrorFragment
import com.smitpatel.enigmamachine.viewmodels.EnigmaViewModel
Expand All @@ -33,6 +33,8 @@ class EnigmaMainActivity : AppCompatActivity() {

private val sounds : SoundEffects = SoundEffects

private val serializer : Serializer = Serializer

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityEnigmaMainBinding.inflate(layoutInflater)
Expand All @@ -54,16 +56,6 @@ class EnigmaMainActivity : AppCompatActivity() {
}

private fun setupRenderer() {
fun getRotorLabelText(rotor: Rotor.RotorOption): String {
val rotorOptions = resources.getStringArray(R.array.rotor_options)
return when (rotor) {
Rotor.RotorOption.ROTOR_ONE -> rotorOptions[0]
Rotor.RotorOption.ROTOR_TWO -> rotorOptions[1]
Rotor.RotorOption.ROTOR_THREE -> rotorOptions[2]
Rotor.RotorOption.ROTOR_FOUR -> rotorOptions[3]
Rotor.RotorOption.ROTOR_FIVE -> rotorOptions[4]
}
}

fun showToast(text: String) {
Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT).show()
Expand All @@ -75,9 +67,9 @@ class EnigmaMainActivity : AppCompatActivity() {
binding.rotors.rotor2.value = it.rotorTwoPosition + 1
binding.rotors.rotor3.value = it.rotorThreePosition + 1

binding.rotors.rotor1Label.text = getRotorLabelText(it.rotorOneLabel)
binding.rotors.rotor2Label.text = getRotorLabelText(it.rotorTwoLabel)
binding.rotors.rotor3Label.text = getRotorLabelText(it.rotorThreeLabel)
binding.rotors.rotor1Label.text = it.rotorOneLabel.toRotorLabelText(applicationContext)
binding.rotors.rotor2Label.text = it.rotorTwoLabel.toRotorLabelText(applicationContext)
binding.rotors.rotor3Label.text = it.rotorThreeLabel.toRotorLabelText(applicationContext)

binding.textboxes.textRaw.setText(it.rawMessage)
binding.textboxes.textCode.setText(it.encodedMessage)
Expand Down Expand Up @@ -113,53 +105,18 @@ class EnigmaMainActivity : AppCompatActivity() {
)
}

if (it.showSettingsErrorToast) {
showToast(text = resources.getString(R.string.settings_not_changed_toast_message))
viewModel.handleEvent(
event = EnigmaEvent.ToastMessageDisplayed
)
}

if (it.clipboardCopyState != null) {
val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
when (val settingsState = it.clipboardCopyState.settingsState) {
null -> {
clipboardManager.setPrimaryClip(ClipData.newPlainText(
"",
it.clipboardCopyState.text,
))
}
else -> {

fun getReflectorLabelText(reflector: Reflector) = when (reflector) {
Reflector.REFLECTOR_UKW_A -> getString(R.string.reflector_a)
Reflector.REFLECTOR_UKW_B -> getString(R.string.reflector_b)
Reflector.REFLECTOR_UKW_C -> getString(R.string.reflector_c)
}

val rotorOptions = "${getRotorLabelText(settingsState.rotorOneLabel)} " +
"${getRotorLabelText(settingsState.rotorTwoLabel)} " +
getRotorLabelText(settingsState.rotorThreeLabel)

val rotorPositions = "${settingsState.rotorOnePosition.numberToLetter()} " +
"${settingsState.rotorTwoPosition.numberToLetter()} " +
settingsState.rotorThreePosition.numberToLetter()

val ringPositions = "${settingsState.rotorOneRing.numberToLetter()} " +
"${settingsState.rotorTwoRing.numberToLetter()} " +
settingsState.rotorThreeRing.numberToLetter()

val plugboardPairs = settingsState.plugboardPairs.map { plugboardPair ->
Pair(
first = plugboardPair.first.numberToLetter(),
second = plugboardPair.second.numberToLetter()
)
}

clipboardManager.setPrimaryClip(ClipData.newPlainText(
"",
getString(R.string.copy_settings_text,
rotorOptions,
rotorPositions,
ringPositions,
getReflectorLabelText(settingsState.reflector),
plugboardPairs,
)
))
}
null -> clipboardManager.setPrimaryClip(ClipData.newPlainText("", it.clipboardCopyState.text))
else -> clipboardManager.setPrimaryClip(ClipData.newPlainText("", serializer.serializeJson(applicationContext, settingsState)))
}

if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
Expand Down Expand Up @@ -328,6 +285,19 @@ class EnigmaMainActivity : AppCompatActivity() {
}
true
}
R.id.paste_enigma_settings -> {
val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val clipboardText = clipboardManager.primaryClip?.getItemAt(0)?.text
if (!clipboardText.isNullOrEmpty()) {
try {
val enigmaSettings = serializer.deserializeJson(applicationContext, clipboardText.toString())
viewModel.handleEvent(EnigmaEvent.PasteEnigmaSettings(enigmaSettings))
} catch (error: DeserializeException) {
viewModel.handleEvent(EnigmaEvent.PasteEnigmaSettings(enigmaSettings = null))
}
}
true
}
else -> super.onContextItemSelected(item)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.smitpatel.enigmamachine.ui.main

import com.smitpatel.enigmamachine.models.Reflector
import com.smitpatel.enigmamachine.models.EnigmaHistoryItem
import com.smitpatel.enigmamachine.models.Rotor

data class EnigmaUiState(
Expand All @@ -15,24 +15,11 @@ data class EnigmaUiState(
val encodedMessage: String,
val clipboardCopyState: ClipboardCopyState?,
val showSettingsChangedToast: Boolean,
val showSettingsErrorToast: Boolean,
val pasteError: String?,
)

data class ClipboardCopyState(
val text: String,
val settingsState: SettingsCopyState?,
) {
data class SettingsCopyState(
val rotorOneLabel: Rotor.RotorOption,
val rotorTwoLabel: Rotor.RotorOption,
val rotorThreeLabel: Rotor.RotorOption,
val rotorOnePosition: Int,
val rotorTwoPosition: Int,
val rotorThreePosition: Int,
val rotorOneRing: Int,
val rotorTwoRing: Int,
val rotorThreeRing: Int,
val reflector: Reflector,
val plugboardPairs: Set<Pair<Int, Int>>,
)
}
val settingsState: EnigmaHistoryItem?,
)
Loading

0 comments on commit 117590f

Please sign in to comment.