В предыдущей статье, мы создавали простой "Генератор Паролей", сегодня мы расширим функционал приложения и добавим копирования в буфер обмена. Вот модифицированный код:
1. Файл манифеста (AndroidManifest.xml).
//----------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.passwordgenerator2"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PasswordGenerator2"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
//----------------------------------------------
2. res/layout/activity_main.xml:
//----------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">
<!-- Поле ввода длины -->
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/length_hint"
app:errorEnabled="true">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/lengthInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
android:minHeight="48dp"
android:text="12"/>
</com.google.android.material.textfield.TextInputLayout>
<!-- Чекбоксы -->
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/uppercaseCheckbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/uppercase_label"
android:checked="true"/>
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/symbolsCheckbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/symbols_label"
android:checked="true"/>
<!-- Кнопка генерации -->
<com.google.android.material.button.MaterialButton
android:id="@+id/generateButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/generate_button"
android:layout_marginTop="8dp"
style="@style/Widget.Material3.Button.OutlinedButton"/>
<!-- Блок с паролем -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="24dp"
android:gravity="center_vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/passwordOutput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="18sp"
android:textStyle="bold"
android:fontFamily="monospace"
android:text="@string/default_password"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/copyButton"
style="@style/Widget.Material3.Button.Icon"
android:layout_width="48dp"
android:layout_height="48dp"
android:contentDescription="@string/copy_desc"
app:icon="@drawable/ic_content_copy"
/>
</LinearLayout>
</LinearLayout>
//----------------------------------------------
3. MainActivity.kt:
//----------------------------------------------
package com.example.passwordgenerator2
import android.content.ClipData
import android.content.ClipboardManager
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.button.MaterialButton
import com.google.android.material.checkbox.MaterialCheckBox
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textview.MaterialTextView
class MainActivity : AppCompatActivity() {
private lateinit var clipboardManager: ClipboardManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Инициализация элементов
val lengthInput = findViewById<TextInputEditText>(R.id.lengthInput)
val uppercaseCheck = findViewById<MaterialCheckBox>(R.id.uppercaseCheckbox)
val symbolsCheck = findViewById<MaterialCheckBox>(R.id.symbolsCheckbox)
val generateButton = findViewById<MaterialButton>(R.id.generateButton)
val passwordOutput = findViewById<MaterialTextView>(R.id.passwordOutput)
val copyButton = findViewById<MaterialButton>(R.id.copyButton)
clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
// Генерация пароля
generateButton.setOnClickListener {
val length = try {
lengthInput.text.toString().toInt().coerceIn(4, 64)
} catch (e: NumberFormatException) {
12
}
val password = generatePassword(
length = length,
useUppercase = uppercaseCheck.isChecked,
useSymbols = symbolsCheck.isChecked
)
passwordOutput.text = password
}
// Копирование в буфер
copyButton.setOnClickListener {
val password = passwordOutput.text.toString()
if (password.isNotBlank() && password != getString(R.string.default_password)) {
clipboardManager.setPrimaryClip(
ClipData.newPlainText("password", password)
)
Toast.makeText(this, R.string.copy_success, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, R.string.copy_error, Toast.LENGTH_SHORT).show()
}
}
}
private fun generatePassword(
length: Int,
useUppercase: Boolean,
useSymbols: Boolean
): String {
val lowercase = "abcdefghijklmnopqrstuvwxyz"
val uppercase = if (useUppercase) "ABCDEFGHIJKLMNOPQRSTUVWXYZ" else ""
val symbols = if (useSymbols) "!@#$%^&*()_+-=[]{}|;:,.<>?/" else ""
val numbers = "0123456789"
val charset = (lowercase + uppercase + symbols + numbers).toCharArray()
return List(length) { charset.random() }.joinToString("")
}
}
//----------------------------------------------
4. res/values/strings.xml:
//----------------------------------------------
<resources>
<string name="app_name">Password Generator</string>
<string name="length_hint">Password length</string>
<string name="uppercase_label">Include uppercase letters</string>
<string name="symbols_label">Include special symbols</string>
<string name="generate_button">Generate Password</string>
<string name="default_password">Tap generate to start</string>
<string name="copy_desc">Copy to clipboard</string>
<string name="copy_success">Password copied!</string>
<string name="copy_error">Generate password first!</string>
</resources>
//----------------------------------------------
5. Иконка (res/drawable/ic_content_copy.xml).
//----------------------------------------------
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M19,21H8V7h11m0-2H8a2,2 0 0,0 -2,2v14a2,2 0,0 0,2 2h11a2,2 0,0 0,2 -2V7a2,2 0,0 0,-2 -2m-3,-4H4a2,2 0,0 0,-2 2v14h2V3h12V1z"/>
</vector>
//----------------------------------------------
Ключевые особенности реализации:
- Использование Material Components для лучшего UI.
- Валидация длины пароля (4-64 символа).
- Оптимизированная работа с буфером обмена.
- Локализация через strings.xml.
- Векторная иконка для кнопки копирования.
- Обработка ошибок ввода.
- Семантически правильная разметка.
- Ripple-эффекты и современный дизайн.