Добавить в корзинуПозвонить
Найти в Дзене
КвадроKot

18. Копирование в буфер обмена "Генератор Паролей 2.0" на Kotlin?

В предыдущей статье, мы создавали простой "Генератор Паролей", сегодня мы расширим функционал приложения и добавим копирования в буфер обмена. Вот модифицированный код: //---------------------------------------------- <?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" />
Оглавление

В предыдущей статье, мы создавали простой "Генератор Паролей", сегодня мы расширим функционал приложения и добавим копирования в буфер обмена. Вот модифицированный код:

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>

//----------------------------------------------

Ключевые особенности реализации:

  1. Использование Material Components для лучшего UI.
  2. Валидация длины пароля (4-64 символа).
  3. Оптимизированная работа с буфером обмена.
  4. Локализация через strings.xml.
  5. Векторная иконка для кнопки копирования.
  6. Обработка ошибок ввода.
  7. Семантически правильная разметка.
  8. Ripple-эффекты и современный дизайн.