Skip to content

Instantly share code, notes, and snippets.

@rjlutz
Last active January 18, 2024 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rjlutz/3c26a1be877529de8dd28049d12415d0 to your computer and use it in GitHub Desktop.
Save rjlutz/3c26a1be877529de8dd28049d12415d0 to your computer and use it in GitHub Desktop.
Bits to build a palindrome app, class with static method that tests a string to see if its a palindrome
// useful link to understand the use of state
// https://developer.android.com/jetpack/compose/state#:~:text=mutableStateOf%20creates%20a%20MutableState%20%2C%20which,preserve%20the%20state%20across%20recompositions.
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Info
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.toLowerCase
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import edu.ggc.lutz.palindrome.ui.theme.PalindromeComposeTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PalindromeComposeTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
UI()
}
}
}
}
}
@Preview(showBackground = true)
@Composable
fun UI() {
val snackbarVisibleState = remember { mutableStateOf(false) }
Scaffold(
floatingActionButton = {
FloatingActionButton(
onClick = { snackbarVisibleState.value = !snackbarVisibleState.value }
) {
Icon(Icons.Filled.Info, "")
}
}
) {
Column(
Modifier
.fillMaxSize()
.padding(10.dp),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
val textState = remember { mutableStateOf(TextFieldValue()) }
OutlinedTextField(
modifier = Modifier.fillMaxWidth().padding(top = 16.dp),
value = textState.value,
onValueChange = {
textState.value = it
Log.v("Palindrome", textState.value.text)
},
label = { Text("Enter Text") }
)
Results(name = textState.value.text)
Snacktime(snackbarVisibleState)
}
}
}
@Composable
private fun Snacktime(snackbarVisibleState: MutableState<Boolean>) {
if (snackbarVisibleState.value) {
Snackbar(
modifier = Modifier.padding(bottom = 60.dp),
action = {
Button(onClick = { snackbarVisibleState.value = false }) {
Text("Dismiss")
}
}
) {
Text(text = "Palindrome Checker 1.0.0")
}
}
}
@Composable
fun Results(name: String) {
var imageResult = R.drawable.check
var textResult = "Is a Palindrome"
// if (!Palindrome.check(name)) { // the java way
if (!check(name)) { // the kotlin way
imageResult = R.drawable.x
textResult = "Is NOT a Palindrome"
}
Image(
modifier = Modifier.size(200.dp, 200.dp),
painter = painterResource(imageResult),
contentDescription = null
)
Text(
textResult,
modifier = Modifier.padding(bottom = 46.dp),
style = MaterialTheme.typography.h4
)
}
private fun check(_s: String): Boolean {
//mutate string, removing leading and trailing garbage
var mutant = _s.replace("^[^a-zA-Z0-9]+".toRegex(), "")
.replace("[^a-zA-Z0-9]+$".toRegex(), "")
if (mutant.length <= 1) return true // base case
var matches = mutant.first().toLowerCase() == mutant.last().toLowerCase()
return if (matches)
// clip first and last chars, call recursively
check(mutant.substring(1, mutant.lastIndex))
else
false // call again or fail
}
public class Palindrome {
public static boolean check(String _s) {
return check(_s, 0, _s.length()-1);
}
private static boolean check(String _s, int _start, int _end) {
// base case
if (_start >= _end) return true;
// do some work
while(!Character.isLetterOrDigit(_s.charAt(_start)))
(_start)++;
while(!Character.isLetterOrDigit(_s.charAt(_end)))
_end--;
char downcaseStart = Character.toLowerCase(_s.charAt((_start)));
char downcaseEnd = Character.toLowerCase(_s.charAt(_end));
if (downcaseStart != downcaseEnd) return false;
// call simpler version
return check(_s, (_start)+1, _end-1);
}
}
class Palindrome {
static check(String s) {
return _check(s, 0, s.length - 1);
}
static _check(String s, start, end) {
if (start > end) return true;
while (!isLetterOrDigit(s, start)) start++;
while (!isLetterOrDigit(s, end)) end--;
bool _matching = (s[start].toLowerCase() == s[end].toLowerCase());
return _matching ? _check(s, start + 1, end - 1) : false;
}
}
bool isLetterOrDigit(String s, int idx) =>
s[idx].contains(RegExp(r'[\da-zA-Z]'));
// could be a top-level function too, perhaps too much java influence here!
object Palindrome {
fun check(_s: String): Boolean {
return check(_s, 0, _s.length - 1)
}
private fun check(_s: String, _start: Int, _end: Int): Boolean {
var start = _start; // we need start to be mutable, kotlin only passes vals (not vars)
var end = _end; // this makes a local copy
if (start >= end) return true // base case
while (!Character.isLetterOrDigit(_s[start])) start++ // do some work
while (!Character.isLetterOrDigit(_s[end])) end--
val downcaseStart = Character.toLowerCase(_s[start])
val downcaseEnd = Character.toLowerCase(_s[end])
return if (downcaseStart == downcaseEnd) check(_s, start + 1, end - 1) else false
}
}
// another implementation in dart, more succinct, more elegant, more confusing!
class TersePalindrome {
static check(String s) {
return s == String.fromCharCodes(s.runes.toList().reversed);
}
}
TextWatcher textWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
String entered = input.getText().toString();
if (Palindrome.check(entered)) {
Log.v(TAG, "is a palindrome");
result.setImageResource(R.drawable.check);
} else {
Log.v(TAG, "is not a palindrome");
result.setImageResource(R.drawable.x);
}
}
};
input.addTextChangedListener(textWatcher);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment