Migrate UI to Jetpack Compose
This commit is contained in:
parent
dff3db885d
commit
ac70718bea
@ -33,15 +33,32 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.5.10"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("com.google.android.material:material:1.11.0")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
|
||||
val composeBom = platform("androidx.compose:compose-bom:2024.02.01")
|
||||
implementation(composeBom)
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.activity:activity-compose:1.8.2")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
|
||||
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation(composeBom)
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
}
|
||||
@ -3,88 +3,296 @@ package cz.bugsy.passwordzebra
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
private const val KEY_PASSWORD = "password"
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var generateButton: Button
|
||||
private lateinit var passwordTextView: TextView
|
||||
private lateinit var switchWithoutSpaces: com.google.android.material.switchmaterial.SwitchMaterial
|
||||
private lateinit var switchSpecialChars: com.google.android.material.switchmaterial.SwitchMaterial
|
||||
private lateinit var passwordLengthPicker: android.widget.NumberPicker
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.FilledIconButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import cz.bugsy.passwordzebra.ui.components.NumberPickerView
|
||||
import cz.bugsy.passwordzebra.ui.theme.PasswordZebraTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
generateButton = findViewById(R.id.generateButton)
|
||||
passwordTextView = findViewById(R.id.passwordTextView)
|
||||
switchWithoutSpaces = findViewById(R.id.switchWithoutSpaces)
|
||||
switchSpecialChars = findViewById(R.id.switchSpecialChars)
|
||||
passwordLengthPicker = findViewById(R.id.passwordLengthPicker)
|
||||
|
||||
// Set the range of selectable values for password length (1 to 10)
|
||||
val defaultPasswordLength = 4
|
||||
passwordLengthPicker.minValue = 1
|
||||
passwordLengthPicker.maxValue = 10
|
||||
passwordLengthPicker.value = defaultPasswordLength
|
||||
|
||||
generateButton.setOnClickListener {
|
||||
generatePassword()
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
PasswordZebraTheme {
|
||||
PasswordZebraApp()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Restore password after rotation, otherwise generate a new one
|
||||
if (savedInstanceState != null) {
|
||||
passwordTextView.text = savedInstanceState.getString(KEY_PASSWORD, "")
|
||||
@Composable
|
||||
private fun PasswordZebraApp() {
|
||||
var password by rememberSaveable { mutableStateOf("") }
|
||||
var wordCount by rememberSaveable { mutableIntStateOf(4) }
|
||||
var withoutSpaces by rememberSaveable { mutableStateOf(false) }
|
||||
var addSpecialChars by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (password.isEmpty()) {
|
||||
password = generatePassword(wordCount, addSpecialChars)
|
||||
}
|
||||
}
|
||||
|
||||
val onGenerate = { password = generatePassword(wordCount, addSpecialChars) }
|
||||
val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
if (isLandscape) {
|
||||
// Landscape: controls left (vertically centered), password + generate button right
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding)
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
// Left: controls only, vertically centered
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
ControlsPanel(
|
||||
wordCount = wordCount,
|
||||
withoutSpaces = withoutSpaces,
|
||||
addSpecialChars = addSpecialChars,
|
||||
onWordCountChange = { wordCount = it },
|
||||
onWithoutSpacesChange = { withoutSpaces = it },
|
||||
onAddSpecialCharsChange = { addSpecialChars = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
// Right: password box fills remaining height, generate button pinned to bottom
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxHeight(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
PasswordBox(
|
||||
password = password,
|
||||
onClick = { copyToClipboard(context, password, withoutSpaces) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
GenerateButton(onClick = onGenerate)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
generatePassword()
|
||||
// Portrait: password box top, controls middle, generate button pinned to bottom
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding)
|
||||
.padding(horizontal = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
PasswordBox(
|
||||
password = password,
|
||||
onClick = { copyToClipboard(context, password, withoutSpaces) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(150.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(2f))
|
||||
ControlsPanel(
|
||||
wordCount = wordCount,
|
||||
withoutSpaces = withoutSpaces,
|
||||
addSpecialChars = addSpecialChars,
|
||||
onWordCountChange = { wordCount = it },
|
||||
onWithoutSpacesChange = { withoutSpaces = it },
|
||||
onAddSpecialCharsChange = { addSpecialChars = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(3f))
|
||||
GenerateButton(onClick = onGenerate)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
// Set click listener to copy password to clipboard
|
||||
passwordTextView.setOnClickListener {
|
||||
copyToClipboard(passwordTextView.text.toString(), getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(KEY_PASSWORD, passwordTextView.text.toString())
|
||||
@Composable
|
||||
private fun PasswordBox(password: String, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
)
|
||||
.clickable { onClick() }
|
||||
.padding(16.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Text(
|
||||
text = password,
|
||||
fontSize = 24.sp,
|
||||
textAlign = TextAlign.Center,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generatePassword() {
|
||||
val passwordLength = passwordLengthPicker.value
|
||||
val password = generateRandomWords(passwordLength)
|
||||
passwordTextView.text = password
|
||||
@Composable
|
||||
private fun ControlsPanel(
|
||||
wordCount: Int,
|
||||
withoutSpaces: Boolean,
|
||||
addSpecialChars: Boolean,
|
||||
onWordCountChange: (Int) -> Unit,
|
||||
onWithoutSpacesChange: (Boolean) -> Unit,
|
||||
onAddSpecialCharsChange: (Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.pw_straighten),
|
||||
contentDescription = stringResource(R.string.image_length_desc),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.textview_password_length),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
NumberPickerView(
|
||||
value = wordCount,
|
||||
minValue = 1,
|
||||
maxValue = 10,
|
||||
onValueChange = onWordCountChange,
|
||||
modifier = Modifier.widthIn(min = 80.dp),
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateRandomWords(numWords: Int): String {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.pw_letter_spacing),
|
||||
contentDescription = stringResource(R.string.image_wospaces),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.switch_without_spaces),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Switch(
|
||||
checked = withoutSpaces,
|
||||
onCheckedChange = onWithoutSpacesChange,
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.pw_special_character),
|
||||
contentDescription = stringResource(R.string.image_specialchars),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.switch_special_characters),
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Switch(
|
||||
checked = addSpecialChars,
|
||||
onCheckedChange = onAddSpecialCharsChange,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GenerateButton(onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
FilledIconButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier.size(60.dp),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.pw_generate),
|
||||
contentDescription = stringResource(R.string.button_generate_description),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generatePassword(wordCount: Int, addSpecialChars: Boolean): String {
|
||||
return buildString {
|
||||
repeat(numWords) {
|
||||
repeat(wordCount) {
|
||||
append(PasswordGenerator.generateRandomWord())
|
||||
append(" ") // Add space between words
|
||||
append(" ")
|
||||
}
|
||||
}.let { password ->
|
||||
// Check if the switch for adding special characters is checked
|
||||
if (switchSpecialChars.isChecked) {
|
||||
if (addSpecialChars) {
|
||||
PasswordGenerator.addSpecialCharacters(password)
|
||||
} else {
|
||||
password
|
||||
}
|
||||
}.trim() // Remove trailing space
|
||||
}.trim()
|
||||
}
|
||||
|
||||
private fun copyToClipboard(text: String, clipboardManager: ClipboardManager) {
|
||||
val clipData = if (switchWithoutSpaces.isChecked) {
|
||||
val textNoSpaces = text.filter { !it.isWhitespace() }
|
||||
ClipData.newPlainText("Password", textNoSpaces)
|
||||
} else {
|
||||
ClipData.newPlainText("Password", text)
|
||||
}
|
||||
private fun copyToClipboard(context: Context, text: String, withoutSpaces: Boolean) {
|
||||
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clipText = if (withoutSpaces) text.filter { !it.isWhitespace() } else text
|
||||
val clipData = ClipData.newPlainText("Password", clipText)
|
||||
clipboardManager.setPrimaryClip(clipData)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,16 +2,10 @@ package cz.bugsy.passwordzebra
|
||||
|
||||
import android.app.Application
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import com.google.android.material.color.DynamicColorsOptions
|
||||
|
||||
class PasswordZebra : Application() {
|
||||
override fun onCreate() {
|
||||
// Apply dynamic color
|
||||
DynamicColors.applyToActivitiesIfAvailable(
|
||||
this,
|
||||
DynamicColorsOptions.Builder()
|
||||
.setThemeOverlay(R.style.AppTheme_Overlay)
|
||||
.build()
|
||||
)
|
||||
super.onCreate()
|
||||
DynamicColors.applyToActivitiesIfAvailable(this)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package cz.bugsy.passwordzebra.ui.components
|
||||
|
||||
import android.widget.NumberPicker
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
|
||||
@Composable
|
||||
fun NumberPickerView(
|
||||
value: Int,
|
||||
minValue: Int,
|
||||
maxValue: Int,
|
||||
onValueChange: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
AndroidView(
|
||||
factory = { ctx ->
|
||||
NumberPicker(ctx).apply {
|
||||
this.minValue = minValue
|
||||
this.maxValue = maxValue
|
||||
this.value = value
|
||||
setOnValueChangedListener { _, _, new -> onValueChange(new) }
|
||||
}
|
||||
},
|
||||
update = { picker ->
|
||||
if (picker.value != value) picker.value = value
|
||||
},
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
92
app/src/main/java/cz/bugsy/passwordzebra/ui/theme/Theme.kt
Normal file
92
app/src/main/java/cz/bugsy/passwordzebra/ui/theme/Theme.kt
Normal file
@ -0,0 +1,92 @@
|
||||
package cz.bugsy.passwordzebra.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Color(0xFF286C2A),
|
||||
onPrimary = Color(0xFFFFFFFF),
|
||||
primaryContainer = Color(0xFFABF5A3),
|
||||
onPrimaryContainer = Color(0xFF002203),
|
||||
secondary = Color(0xFF00639A),
|
||||
onSecondary = Color(0xFFFFFFFF),
|
||||
secondaryContainer = Color(0xFFCEE5FF),
|
||||
onSecondaryContainer = Color(0xFF001D32),
|
||||
tertiary = Color(0xFF38656A),
|
||||
onTertiary = Color(0xFFFFFFFF),
|
||||
tertiaryContainer = Color(0xFFBCEBF0),
|
||||
onTertiaryContainer = Color(0xFF002023),
|
||||
error = Color(0xFFBA1A1A),
|
||||
errorContainer = Color(0xFFFFDAD6),
|
||||
onError = Color(0xFFFFFFFF),
|
||||
onErrorContainer = Color(0xFF410002),
|
||||
background = Color(0xFFFCFDF6),
|
||||
onBackground = Color(0xFF1A1C19),
|
||||
surface = Color(0xFFFCFDF6),
|
||||
onSurface = Color(0xFF1A1C19),
|
||||
surfaceVariant = Color(0xFFDEE5D8),
|
||||
onSurfaceVariant = Color(0xFF424940),
|
||||
outline = Color(0xFF72796F),
|
||||
inverseOnSurface = Color(0xFFF1F1EB),
|
||||
inverseSurface = Color(0xFF2F312D),
|
||||
inversePrimary = Color(0xFF90D889),
|
||||
surfaceTint = Color(0xFF286C2A),
|
||||
outlineVariant = Color(0xFFC2C9BD),
|
||||
scrim = Color(0xFF000000),
|
||||
)
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Color(0xFF90D889),
|
||||
onPrimary = Color(0xFF003909),
|
||||
primaryContainer = Color(0xFF085314),
|
||||
onPrimaryContainer = Color(0xFFABF5A3),
|
||||
secondary = Color(0xFF96CCFF),
|
||||
onSecondary = Color(0xFF003353),
|
||||
secondaryContainer = Color(0xFF004A75),
|
||||
onSecondaryContainer = Color(0xFFCEE5FF),
|
||||
tertiary = Color(0xFFA0CFD4),
|
||||
onTertiary = Color(0xFF00363B),
|
||||
tertiaryContainer = Color(0xFF1E4D52),
|
||||
onTertiaryContainer = Color(0xFFBCEBF0),
|
||||
error = Color(0xFFFFB4AB),
|
||||
errorContainer = Color(0xFF93000A),
|
||||
onError = Color(0xFF690005),
|
||||
onErrorContainer = Color(0xFFFFDAD6),
|
||||
background = Color(0xFF1A1C19),
|
||||
onBackground = Color(0xFFE2E3DD),
|
||||
surface = Color(0xFF1A1C19),
|
||||
onSurface = Color(0xFFE2E3DD),
|
||||
surfaceVariant = Color(0xFF424940),
|
||||
onSurfaceVariant = Color(0xFFC2C9BD),
|
||||
outline = Color(0xFF8C9388),
|
||||
inverseOnSurface = Color(0xFF1A1C19),
|
||||
inverseSurface = Color(0xFFE2E3DD),
|
||||
inversePrimary = Color(0xFF286C2A),
|
||||
surfaceTint = Color(0xFF90D889),
|
||||
outlineVariant = Color(0xFF424940),
|
||||
scrim = Color(0xFF000000),
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun PasswordZebraTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val colorScheme = when {
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val ctx = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(ctx) else dynamicLightColorScheme(ctx)
|
||||
}
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
MaterialTheme(colorScheme = colorScheme, content = content)
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<stroke android:width="1dip" android:color="@color/material_dynamic_primary80"/>
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
@ -1,167 +0,0 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/passwordTextView"
|
||||
android:layout_width="335dp"
|
||||
android:layout_height="149dp"
|
||||
android:layout_centerHorizontal="false"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:background="@drawable/password_background"
|
||||
android:gravity="center"
|
||||
android:minHeight="150dp"
|
||||
android:padding="16dp"
|
||||
android:text=""
|
||||
android:textSize="24sp"
|
||||
|
||||
app:layout_constraintBottom_toTopOf="@+id/generateButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/passwordLengthPicker"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/passwordLengthLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:text="@string/textview_password_length"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/passwordLengthPicker"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline4"
|
||||
app:layout_constraintTop_toTopOf="@+id/passwordLengthPicker" />
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/passwordLengthPicker"
|
||||
android:layout_width="69dp"
|
||||
android:layout_height="178dp"
|
||||
android:layout_marginStart="81dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/switchWithoutSpaces"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline5"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/passwordLengthLabel"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switchWithoutSpaces"
|
||||
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
|
||||
android:layout_width="299dp"
|
||||
android:layout_height="48dp"
|
||||
android:text="@string/switch_without_spaces"
|
||||
app:layout_constraintBottom_toTopOf="@id/switchSpecialChars"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline5"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline4" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switchSpecialChars"
|
||||
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
|
||||
android:layout_width="299dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="36dp"
|
||||
android:text="@string/switch_special_characters"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/guideline5"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline4" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/generateButton"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="end|bottom"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:contentDescription="@string/button_generate_description"
|
||||
android:insetLeft="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:padding="0dp"
|
||||
android:tooltipText="@string/button_generate_text"
|
||||
app:backgroundColor="@color/design_default_color_primary"
|
||||
app:cornerRadius="16dp"
|
||||
app:icon="@drawable/pw_generate"
|
||||
app:iconGravity="textTop"
|
||||
app:iconPadding="0dp"
|
||||
app:iconSize="0dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/passwordLengthPicker"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline5" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_begin="20dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewPwLength"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/image_length_desc"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/passwordLengthLabel"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toTopOf="@+id/passwordLengthLabel"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:srcCompat="@drawable/pw_straighten" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewWithoutSpaces"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="9dp"
|
||||
android:contentDescription="@string/image_wospaces"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/switchWithoutSpaces"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toTopOf="@+id/switchWithoutSpaces"
|
||||
app:layout_constraintVertical_bias="0.8"
|
||||
app:srcCompat="@drawable/pw_letter_spacing" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewAddSpecialChars"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:contentDescription="@string/image_specialchars"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/switchSpecialChars"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toTopOf="@+id/switchSpecialChars"
|
||||
app:layout_constraintVertical_bias="0.625"
|
||||
app:srcCompat="@drawable/pw_special_character" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="16dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="65dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline5"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="365dp" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,161 +0,0 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity"
|
||||
>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/passwordTextView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/password_background"
|
||||
android:gravity="center"
|
||||
android:minHeight="150dp"
|
||||
android:padding="16dp"
|
||||
android:text=""
|
||||
android:textSize="24sp"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/passwordLengthLabel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="0dp"
|
||||
android:text="@string/textview_password_length"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/passwordLengthPicker"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline4"
|
||||
app:layout_constraintTop_toTopOf="@+id/passwordLengthPicker" />
|
||||
|
||||
<NumberPicker
|
||||
android:id="@+id/passwordLengthPicker"
|
||||
android:layout_width="69dp"
|
||||
android:layout_height="178dp"
|
||||
android:layout_marginStart="81dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/switchWithoutSpaces"
|
||||
app:layout_constraintEnd_toEndOf="@id/guideline5"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/passwordLengthLabel"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="1.0" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switchWithoutSpaces"
|
||||
android:layout_width="299dp"
|
||||
android:layout_height="48dp"
|
||||
android:text="@string/switch_without_spaces"
|
||||
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
|
||||
app:layout_constraintBottom_toTopOf="@id/switchSpecialChars"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline4" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/switchSpecialChars"
|
||||
android:layout_width="299dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="36dp"
|
||||
android:text="@string/switch_special_characters"
|
||||
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
|
||||
app:layout_constraintBottom_toTopOf="@id/generateButton"
|
||||
app:layout_constraintStart_toStartOf="@id/guideline4" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/generateButton"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_gravity="end|bottom"
|
||||
app:iconGravity="textTop"
|
||||
android:padding="0dp"
|
||||
app:cornerRadius="16dp"
|
||||
app:iconPadding="0dp"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
android:insetLeft="0dp"
|
||||
android:insetRight="0dp"
|
||||
android:layout_marginBottom="44dp"
|
||||
android:tooltipText="@string/button_generate_text"
|
||||
android:contentDescription="@string/button_generate_description"
|
||||
app:icon="@drawable/pw_generate"
|
||||
app:iconSize="0dp"
|
||||
app:backgroundColor="@color/design_default_color_primary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.498"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_begin="20dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewPwLength"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/passwordLengthLabel"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toTopOf="@+id/passwordLengthLabel"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
app:srcCompat="@drawable/pw_straighten"
|
||||
android:contentDescription="@string/image_length_desc" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewWithoutSpaces"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="9dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/switchWithoutSpaces"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toTopOf="@+id/switchWithoutSpaces"
|
||||
app:layout_constraintVertical_bias="0.8"
|
||||
app:srcCompat="@drawable/pw_letter_spacing"
|
||||
android:contentDescription="@string/image_wospaces" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageViewAddSpecialChars"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/switchSpecialChars"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="@+id/guideline3"
|
||||
app:layout_constraintTop_toTopOf="@+id/switchSpecialChars"
|
||||
app:layout_constraintVertical_bias="0.625"
|
||||
app:srcCompat="@drawable/pw_special_character"
|
||||
android:contentDescription="@string/image_specialchars" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="16dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_begin="65dp" />
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/guideline5"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.92" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,18 +1,12 @@
|
||||
|
||||
<resources>
|
||||
<!-- <style name="Theme.PasswordZebra" parent="Theme.AppCompat.DayNight.DarkActionBar"> -->
|
||||
<style name="Theme.PasswordZebra" parent="Theme.Material3.DayNight">
|
||||
|
||||
<!-- <style name="Theme.PasswordZebra" parent="Theme.Material3.DayNight.NoActionBar"> -->
|
||||
<style name="Theme.PasswordZebra" parent="Theme.Material3.Dark.NoActionBar">
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<!-- status bar font color -->
|
||||
<item name="android:windowLightStatusBar">false</item>
|
||||
<!-- true = status bar semi-transparent -->
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
|
||||
<!-- action bar -->
|
||||
<!-- defined in themes_overlays - item name="actionBarStyle" -->
|
||||
|
||||
<!-- navigation bar = bottom bar with navigation buttons -->
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:windowLightNavigationBar">false</item>
|
||||
@ -44,9 +38,4 @@
|
||||
<item name="colorSurfaceInverse">@color/md_theme_dark_inverseSurface</item>
|
||||
<item name="colorPrimaryInverse">@color/md_theme_dark_inversePrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="MyActionBar" parent="@style/Widget.AppCompat.ActionBar">
|
||||
<item name="background">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
|
||||
<resources>
|
||||
<style name="Theme.PasswordZebra" parent="Theme.Material3.Light">
|
||||
<!-- <style name="Theme.PasswordZebra" parent="Theme.Material3.Light.NoActionBar"> -->
|
||||
|
||||
<style name="Theme.PasswordZebra" parent="Theme.Material3.Light.NoActionBar">
|
||||
<!-- statusbar background color -->
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
|
||||
@ -17,9 +15,4 @@
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
<item name="android:windowTranslucentNavigation">false</item>
|
||||
</style>
|
||||
|
||||
<style name="MyActionBar" parent="@style/Widget.AppCompat.ActionBar">
|
||||
<item name="background">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="AppTheme.Overlay" parent="ThemeOverlay.Material3.DynamicColors.DayNight">
|
||||
<item name="actionBarStyle">@style/MyActionBar</item>
|
||||
<!-- <item name="actionBarStyle">@style/Widget.AppCompat.ActionBar</item> -->
|
||||
</style>
|
||||
<resources>
|
||||
<style name="AppTheme.Overlay" parent="ThemeOverlay.Material3.DynamicColors.DayNight" />
|
||||
</resources>
|
||||
Loading…
x
Reference in New Issue
Block a user