feat: Refactor SearchResultsList component for improved loading and empty state handling

- Updated UI to use AnimatedVisibility for loading indicator and empty state message.
- Enhanced user feedback with a centered loading spinner and message during search.
- Improved layout and spacing for search results and user items.
- Adjusted colors and sizes for better visual consistency.

chore: Update gradle.properties to use new Java 17 path

- Changed Java home path to reflect new installation location.

docs: Add comprehensive README for project setup and development

- Included quick start instructions, available emulators, technologies used, and useful commands.
- Documented project structure and debugging tips.
- Added license information and contact details for the development team.

build: Add development scripts for quick rebuild and installation

- Created dev.sh for fast rebuild and install without cleaning.
- Added run.sh for a complete build and install process with emulator checks.
- Introduced watch-dev.sh for automatic rebuild on file changes.
This commit is contained in:
senseiGai
2026-01-11 17:14:02 +05:00
parent 5f21f120f1
commit 5e3b9d0882
9 changed files with 1428 additions and 1086 deletions

69
.vscode/tasks.json vendored
View File

@@ -29,6 +29,75 @@
"type": "shell", "type": "shell",
"command": "./gradlew installDebug", "command": "./gradlew installDebug",
"problemMatcher": [] "problemMatcher": []
},
{
"label": "Start Emulator (Pixel 9 Pro)",
"type": "shell",
"command": "$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro_API_35 &",
"problemMatcher": [],
"isBackground": true
},
{
"label": "Start Emulator (Pixel 6)",
"type": "shell",
"command": "$ANDROID_HOME/emulator/emulator -avd Pixel_6_API_Baklava &",
"problemMatcher": [],
"isBackground": true
},
{
"label": "Start Emulator (Pixel 4)",
"type": "shell",
"command": "$ANDROID_HOME/emulator/emulator -avd Pixel_4_API_35 &",
"problemMatcher": [],
"isBackground": true
},
{
"label": "Check Connected Devices",
"type": "shell",
"command": "adb devices",
"problemMatcher": []
},
{
"label": "Build & Install on Device",
"type": "shell",
"command": "./gradlew assembleDebug && ./gradlew installDebug",
"problemMatcher": []
},
{
"label": "🔥 Quick Dev - Fast Rebuild",
"type": "shell",
"command": "./dev.sh",
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": false
}
},
{
"label": "👀 Watch Mode - Auto Rebuild",
"type": "shell",
"command": "./watch-dev.sh",
"problemMatcher": [],
"isBackground": true
},
{
"label": "Launch App on Device",
"type": "shell",
"command": "adb shell am start -n com.rosetta.messenger/.MainActivity",
"problemMatcher": []
},
{
"label": "View App Logs",
"type": "shell",
"command": "adb logcat | grep -E 'rosetta|MainActivity'",
"problemMatcher": [],
"isBackground": true
},
{
"label": "Uninstall App",
"type": "shell",
"command": "adb uninstall com.rosetta.messenger",
"problemMatcher": []
} }
] ]
} }

181
README.md Normal file
View File

@@ -0,0 +1,181 @@
# 🚀 Rosetta Android
Безопасный мессенджер на базе криптографии для Android.
## ✅ Статус проекта
- ✅ Проект настроен и готов к разработке
-Все зависимости установлены
- ✅ Сборка проходит успешно
- ✅ APK файлы генерируются корректно
## 🎯 Быстрый старт
### 🚀 Запуск одной командой:
```bash
./run.sh
```
Этот скрипт автоматически запустит эмулятор, соберет приложение, установит и запустит его.
### ⚡ Быстрая разработка (Hot Reload):
**Вариант 1: Ручная пересборка**
```bash
./dev.sh
```
Быстрая пересборка и установка без очистки проекта (~10-20 секунд)
**Вариант 2: Автоматическая пересборка**
```bash
# Установите fswatch (один раз)
brew install fswatch
# Запустите watch mode
./watch-dev.sh
```
После запуска скрипт будет автоматически:
- Отслеживать изменения в `.kt` файлах
- Пересобирать проект
- Устанавливать на эмулятор
- Перезапускать приложение
💡 **Теперь просто редактируйте код и сохраняйте - изменения появятся на эмуляторе!**
### 🛠 Или вручную:
1. **Запустите эмулятор:**
```bash
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro_API_35 &
```
2. **Соберите и установите:**
```bash
./gradlew assembleDebug
./gradlew installDebug
```
3. **Запустите приложение:**
```bash
adb shell am start -n com.rosetta.messenger/.MainActivity
```
### Через VS Code:
1. Нажмите `Cmd+Shift+P`
2. Выберите "Tasks: Run Task"
3. Выберите нужную задачу (например, "Build & Install on Device")
## 📱 Доступные эмуляторы
- Pixel_9_Pro_API_35 (рекомендуется)
- Pixel_6_API_Baklava
- Pixel_4_API_35
- Pixel_2_API_35
## 🛠 Технологии
- **Язык:** Kotlin
- **UI:** Jetpack Compose + Material3
- **Навигация:** Navigation Component
- **Хранилище:** Room Database + DataStore
- **Безопасность:**
- BitcoinJ для криптографии
- Biometric Authentication
- BouncyCastle для шифрования
- **Анимации:** Lottie
## 📦 Сборка
```bash
# Debug версия
./gradlew assembleDebug
# Release версия
./gradlew assembleRelease
# Очистка проекта
./gradlew clean
```
## 📖 Документация
Подробная документация находится в [DEVELOPMENT.md](./DEVELOPMENT.md)
## 🐛 Отладка
Просмотр логов:
```bash
adb logcat | grep rosetta
```
Или через VS Code Task: "View App Logs"
## 📂 Структура проекта
```
app/src/main/
├── java/com/rosetta/messenger/
│ ├── MainActivity.kt # Главная активность
│ ├── crypto/ # Криптография
│ ├── data/ # Управление данными
│ └── ui/ # UI компоненты
│ ├── auth/ # Авторизация
│ ├── chats/ # Чаты
│ ├── onboarding/ # Онбординг
│ └── theme/ # Тема
├── res/ # Ресурсы
└── AndroidManifest.xml # Манифест
```
## 🔧 Полезные команды
```bash
# Проверить подключенные устройства
adb devices
# Удалить приложение
adb uninstall com.rosetta.messenger
# Очистить данные приложения
adb shell pm clear com.rosetta.messenger
# Остановить приложение
adb shell am force-stop com.rosetta.messenger
```
## ⚙️ Настройка VS Code
Расширения установлены автоматически:
- Kotlin Language (fwcd.kotlin)
- Language Support for Java (redhat.java)
- Gradle for Java (vscjava.vscode-gradle)
## 🎨 Особенности
- 🔐 Генерация seed-фразы (12/24 слова)
- 🔑 Создание криптографических ключей
- 🔒 Биометрическая аутентификация
- 💾 Защищенное хранилище данных
- 🎨 Современный UI с Material3
- 🌙 Поддержка темной темы
- ✨ Плавные анимации с Lottie
## 📝 Лицензия
MIT
---
**Разработка:** Rosetta Messenger Team
**Контакт:** k1ngsterr1

View File

@@ -1,6 +1,7 @@
plugins { plugins {
id("com.android.application") id("com.android.application")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
} }
android { android {
@@ -79,12 +80,17 @@ dependencies {
// Room for database // Room for database
implementation("androidx.room:room-runtime:2.6.1") implementation("androidx.room:room-runtime:2.6.1")
implementation("androidx.room:room-ktx:2.6.1") implementation("androidx.room:room-ktx:2.6.1")
annotationProcessor("androidx.room:room-compiler:2.6.1") kapt("androidx.room:room-compiler:2.6.1")
// Biometric authentication // Biometric authentication
implementation("androidx.biometric:biometric:1.1.0") implementation("androidx.biometric:biometric:1.1.0")
// Testing dependencies
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.13.8")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
testImplementation("androidx.arch.core:core-testing:2.2.0")
androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01")) androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))

View File

@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bookmark import androidx.compose.material.icons.filled.Bookmark
import androidx.compose.material3.* import androidx.compose.material3.*
@@ -22,12 +21,12 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.rosetta.messenger.network.SearchUser import com.rosetta.messenger.network.SearchUser
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
import com.rosetta.messenger.ui.components.VerifiedBadge import com.rosetta.messenger.ui.components.VerifiedBadge
import com.rosetta.messenger.ui.onboarding.PrimaryBlue
/** /**
* Компонент отображения результатов поиска пользователей * Компонент отображения результатов поиска пользователей Аналогичен результатам поиска в React
* Аналогичен результатам поиска в React Native приложении * Native приложении
*/ */
@Composable @Composable
fun SearchResultsList( fun SearchResultsList(
@@ -38,62 +37,53 @@ fun SearchResultsList(
onUserClick: (SearchUser) -> Unit, onUserClick: (SearchUser) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val backgroundColor = if (isDarkTheme) Color(0xFF2A2A2A) else Color.White
val borderColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
val textColor = if (isDarkTheme) Color.White else Color.Black val textColor = if (isDarkTheme) Color.White else Color.Black
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666) val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
Column( Box(modifier = modifier.fillMaxSize()) {
modifier = modifier AnimatedVisibility(
.fillMaxWidth() visible = isSearching,
.padding(horizontal = 16.dp) enter = fadeIn(animationSpec = tween(durationMillis = 200)),
.clip(RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp)) exit = fadeOut(animationSpec = tween(durationMillis = 200))
.background(backgroundColor)
) { ) {
// Разделительная линия сверху // Индикатор загрузки в центре
Divider( Column(
color = borderColor, modifier = Modifier.fillMaxSize(),
thickness = 1.dp horizontalAlignment = Alignment.CenterHorizontally,
) verticalArrangement = Arrangement.Center
when {
isSearching -> {
// Индикатор загрузки
Box(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 20.dp),
contentAlignment = Alignment.Center
) { ) {
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier.size(24.dp), modifier = Modifier.size(32.dp),
color = if (isDarkTheme) Color(0xFF9E9E9E) else PrimaryBlue, color = if (isDarkTheme) Color(0xFF9E9E9E) else PrimaryBlue,
strokeWidth = 2.dp strokeWidth = 3.dp
) )
Spacer(modifier = Modifier.height(16.dp))
Text(text = "Searching...", fontSize = 14.sp, color = secondaryTextColor)
} }
} }
searchResults.isEmpty() -> {
// Пустые результаты - подсказка AnimatedVisibility(
Box( visible = !isSearching && searchResults.isEmpty(),
modifier = Modifier enter = fadeIn(animationSpec = tween(durationMillis = 300)),
.fillMaxWidth() exit = fadeOut(animationSpec = tween(durationMillis = 200))
.padding(vertical = 20.dp),
contentAlignment = Alignment.Center
) { ) {
// Подсказка в центре экрана
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text( Text(
text = "You can search by username or public key.", text = "Search by username or public key",
fontSize = 12.sp, fontSize = 15.sp,
color = secondaryTextColor color = secondaryTextColor
) )
} }
} }
else -> {
// Список результатов AnimatedVisibility(
LazyColumn( visible = !isSearching && searchResults.isNotEmpty(),
modifier = Modifier enter = fadeIn(animationSpec = tween(durationMillis = 300)),
.fillMaxWidth() exit = fadeOut(animationSpec = tween(durationMillis = 200))
.heightIn(max = 300.dp)
) { ) {
// Список результатов без серой плашки
LazyColumn(modifier = Modifier.fillMaxSize()) {
itemsIndexed(searchResults) { index, user -> itemsIndexed(searchResults) { index, user ->
SearchResultItem( SearchResultItem(
user = user, user = user,
@@ -107,11 +97,8 @@ fun SearchResultsList(
} }
} }
} }
}
/** /** Элемент результата поиска - пользователь */
* Элемент результата поиска - пользователь
*/
@Composable @Composable
private fun SearchResultItem( private fun SearchResultItem(
user: SearchUser, user: SearchUser,
@@ -125,25 +112,29 @@ private fun SearchResultItem(
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8) val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
// Получаем цвета аватара // Получаем цвета аватара
val avatarColors = getAvatarColor( val avatarColors =
getAvatarColor(
if (isOwnAccount) "SavedMessages" else (user.title.ifEmpty { user.publicKey }), if (isOwnAccount) "SavedMessages" else (user.title.ifEmpty { user.publicKey }),
isDarkTheme isDarkTheme
) )
Column { Column {
Row( Row(
modifier = Modifier modifier =
.fillMaxWidth() Modifier.fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(horizontal = 12.dp, vertical = 10.dp), .padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// Аватар // Аватар
Box( Box(
modifier = Modifier modifier =
.size(40.dp) Modifier.size(48.dp)
.clip(CircleShape) .clip(CircleShape)
.background(if (isOwnAccount) PrimaryBlue else avatarColors.backgroundColor), .background(
if (isOwnAccount) PrimaryBlue
else avatarColors.backgroundColor
),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
if (isOwnAccount) { if (isOwnAccount) {
@@ -155,7 +146,8 @@ private fun SearchResultItem(
) )
} else { } else {
Text( Text(
text = if (user.title.isNotEmpty()) { text =
if (user.title.isNotEmpty()) {
getInitials(user.title) getInitials(user.title)
} else { } else {
user.publicKey.take(2).uppercase() user.publicKey.take(2).uppercase()
@@ -177,12 +169,13 @@ private fun SearchResultItem(
horizontalArrangement = Arrangement.spacedBy(4.dp) horizontalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
Text( Text(
text = if (isOwnAccount) { text =
if (isOwnAccount) {
"Saved Messages" "Saved Messages"
} else { } else {
user.title.ifEmpty { user.publicKey.take(10) } user.title.ifEmpty { user.publicKey.take(10) }
}, },
fontSize = 14.sp, fontSize = 16.sp,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
color = textColor, color = textColor,
maxLines = 1, maxLines = 1,
@@ -191,10 +184,7 @@ private fun SearchResultItem(
// Значок верификации // Значок верификации
if (!isOwnAccount && user.verified > 0) { if (!isOwnAccount && user.verified > 0) {
VerifiedBadge( VerifiedBadge(verified = user.verified, size = 16)
verified = user.verified,
size = 16
)
} }
} }
@@ -202,12 +192,13 @@ private fun SearchResultItem(
// Юзернейм или публичный ключ // Юзернейм или публичный ключ
Text( Text(
text = if (isOwnAccount) { text =
if (isOwnAccount) {
"Notes" "Notes"
} else { } else {
"@${user.username.ifEmpty { user.publicKey.take(10) + "..." }}" "@${user.username.ifEmpty { user.publicKey.take(10) + "..." }}"
}, },
fontSize = 12.sp, fontSize = 14.sp,
color = secondaryTextColor, color = secondaryTextColor,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
@@ -218,7 +209,7 @@ private fun SearchResultItem(
// Разделитель между элементами // Разделитель между элементами
if (!isLastItem) { if (!isLastItem) {
Divider( Divider(
modifier = Modifier.padding(start = 64.dp), modifier = Modifier.padding(start = 80.dp),
color = dividerColor, color = dividerColor,
thickness = 0.5.dp thickness = 0.5.dp
) )

35
dev.sh Executable file
View File

@@ -0,0 +1,35 @@
#!/bin/bash
# Quick Dev Script - Fast rebuild and install
echo "🔄 Quick rebuild and install..."
# Check if emulator is running
if ! adb devices | grep -q "emulator"; then
echo "⚠️ Эмулятор не запущен. Запускаю..."
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro_API_35 &
adb wait-for-device
# Wait for boot complete
while [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" != "1" ]; do
sleep 2
done
fi
echo "📱 Эмулятор готов!"
# Fast install (no clean)
./gradlew installDebug --no-daemon
if [ $? -eq 0 ]; then
echo "✅ Установлено!"
# Auto-launch
adb shell am start -n com.rosetta.messenger/.MainActivity
echo ""
echo "🎉 Приложение запущено!"
echo "📝 Для просмотра логов: adb logcat | grep rosetta"
else
echo "❌ Ошибка установки"
fi

View File

@@ -7,7 +7,7 @@ android.useAndroidX=true
kotlin.code.style=official kotlin.code.style=official
# Use Java 17 for build # Use Java 17 for build
org.gradle.java.home=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home org.gradle.java.home=/opt/homebrew/Cellar/openjdk@17/17.0.14/libexec/openjdk.jdk/Contents/Home
# Increase heap size for Gradle # Increase heap size for Gradle
org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError --add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError --add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

57
run.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/bash
# Rosetta Android - Quick Start Script
echo "🚀 Rosetta Android Quick Start"
echo "================================"
echo ""
# Check if emulator is already running
if adb devices | grep -q "emulator"; then
echo "✅ Эмулятор уже запущен"
else
echo "📱 Запускаем эмулятор (Pixel 9 Pro API 35)..."
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro_API_35 &
EMULATOR_PID=$!
echo "⏳ Ожидаем загрузки эмулятора..."
adb wait-for-device
# Wait for boot to complete
while [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" != "1" ]; do
echo " Загрузка..."
sleep 2
done
echo "✅ Эмулятор готов!"
fi
echo ""
echo "🔨 Собираем приложение..."
./gradlew assembleDebug
if [ $? -eq 0 ]; then
echo "✅ Сборка успешна!"
echo ""
echo "📦 Устанавливаем приложение..."
./gradlew installDebug
if [ $? -eq 0 ]; then
echo "✅ Приложение установлено!"
echo ""
echo "🎉 Запускаем приложение..."
adb shell am start -n com.rosetta.messenger/.MainActivity
echo ""
echo "✅ Готово! Приложение запущено на эмуляторе"
echo ""
echo "📋 Для просмотра логов запустите:"
echo " adb logcat | grep rosetta"
else
echo "❌ Ошибка установки приложения"
exit 1
fi
else
echo "❌ Ошибка сборки приложения"
exit 1
fi

58
watch-dev.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
# Auto-rebuild on file changes
# Requires: fswatch (install via: brew install fswatch)
echo "👀 Watching for changes..."
echo "📝 Редактируйте файлы - приложение будет пересобираться автоматически"
echo ""
# Check if fswatch is installed
if ! command -v fswatch &> /dev/null; then
echo "⚠️ fswatch не установлен!"
echo "📦 Установите через: brew install fswatch"
echo ""
echo "Или используйте ./dev.sh для ручной пересборки"
exit 1
fi
# Ensure emulator is running
if ! adb devices | grep -q "emulator"; then
echo "🚀 Запускаю эмулятор..."
$ANDROID_HOME/emulator/emulator -avd Pixel_9_Pro_API_35 &
adb wait-for-device
while [ "$(adb shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" != "1" ]; do
sleep 2
done
fi
echo "✅ Эмулятор готов!"
echo ""
# Initial build
./gradlew installDebug
adb shell am start -n com.rosetta.messenger/.MainActivity
echo ""
echo "👂 Слежу за изменениями в app/src/main/java..."
echo ""
# Watch for changes in Kotlin files
fswatch -o app/src/main/java | while read f; do
echo "🔄 Изменение обнаружено! Пересобираю..."
./gradlew installDebug --no-daemon
if [ $? -eq 0 ]; then
echo "✅ Обновлено! Перезапускаю приложение..."
adb shell am force-stop com.rosetta.messenger
sleep 1
adb shell am start -n com.rosetta.messenger/.MainActivity
echo "🎉 Готово!"
else
echo "❌ Ошибка сборки"
fi
echo ""
echo "👂 Жду следующих изменений..."
done