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:
69
.vscode/tasks.json
vendored
69
.vscode/tasks.json
vendored
@@ -29,6 +29,75 @@
|
||||
"type": "shell",
|
||||
"command": "./gradlew installDebug",
|
||||
"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
181
README.md
Normal 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
|
||||
@@ -1,6 +1,7 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
@@ -79,12 +80,17 @@ dependencies {
|
||||
// Room for database
|
||||
implementation("androidx.room:room-runtime: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
|
||||
implementation("androidx.biometric:biometric:1.1.0")
|
||||
|
||||
// Testing dependencies
|
||||
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.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.10.01"))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Bookmark
|
||||
import androidx.compose.material3.*
|
||||
@@ -22,147 +21,140 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
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.onboarding.PrimaryBlue
|
||||
|
||||
/**
|
||||
* Компонент отображения результатов поиска пользователей
|
||||
* Аналогичен результатам поиска в React Native приложении
|
||||
* Компонент отображения результатов поиска пользователей Аналогичен результатам поиска в React
|
||||
* Native приложении
|
||||
*/
|
||||
@Composable
|
||||
fun SearchResultsList(
|
||||
searchResults: List<SearchUser>,
|
||||
isSearching: Boolean,
|
||||
currentUserPublicKey: String,
|
||||
isDarkTheme: Boolean,
|
||||
onUserClick: (SearchUser) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
searchResults: List<SearchUser>,
|
||||
isSearching: Boolean,
|
||||
currentUserPublicKey: String,
|
||||
isDarkTheme: Boolean,
|
||||
onUserClick: (SearchUser) -> Unit,
|
||||
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 secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(bottomStart = 12.dp, bottomEnd = 12.dp))
|
||||
.background(backgroundColor)
|
||||
) {
|
||||
// Разделительная линия сверху
|
||||
Divider(
|
||||
color = borderColor,
|
||||
thickness = 1.dp
|
||||
)
|
||||
|
||||
when {
|
||||
isSearching -> {
|
||||
// Индикатор загрузки
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 20.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(24.dp),
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
AnimatedVisibility(
|
||||
visible = isSearching,
|
||||
enter = fadeIn(animationSpec = tween(durationMillis = 200)),
|
||||
exit = fadeOut(animationSpec = tween(durationMillis = 200))
|
||||
) {
|
||||
// Индикатор загрузки в центре
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(32.dp),
|
||||
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() -> {
|
||||
// Пустые результаты - подсказка
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 20.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "You can search by username or public key.",
|
||||
fontSize = 12.sp,
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = !isSearching && searchResults.isEmpty(),
|
||||
enter = fadeIn(animationSpec = tween(durationMillis = 300)),
|
||||
exit = fadeOut(animationSpec = tween(durationMillis = 200))
|
||||
) {
|
||||
// Подсказка в центре экрана
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
text = "Search by username or public key",
|
||||
fontSize = 15.sp,
|
||||
color = secondaryTextColor
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
// Список результатов
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 300.dp)
|
||||
) {
|
||||
itemsIndexed(searchResults) { index, user ->
|
||||
SearchResultItem(
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = !isSearching && searchResults.isNotEmpty(),
|
||||
enter = fadeIn(animationSpec = tween(durationMillis = 300)),
|
||||
exit = fadeOut(animationSpec = tween(durationMillis = 200))
|
||||
) {
|
||||
// Список результатов без серой плашки
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
itemsIndexed(searchResults) { index, user ->
|
||||
SearchResultItem(
|
||||
user = user,
|
||||
isOwnAccount = user.publicKey == currentUserPublicKey,
|
||||
isDarkTheme = isDarkTheme,
|
||||
isLastItem = index == searchResults.size - 1,
|
||||
onClick = { onUserClick(user) }
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Элемент результата поиска - пользователь
|
||||
*/
|
||||
/** Элемент результата поиска - пользователь */
|
||||
@Composable
|
||||
private fun SearchResultItem(
|
||||
user: SearchUser,
|
||||
isOwnAccount: Boolean,
|
||||
isDarkTheme: Boolean,
|
||||
isLastItem: Boolean,
|
||||
onClick: () -> Unit
|
||||
user: SearchUser,
|
||||
isOwnAccount: Boolean,
|
||||
isDarkTheme: Boolean,
|
||||
isLastItem: Boolean,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
val textColor = if (isDarkTheme) Color.White else Color.Black
|
||||
val secondaryTextColor = if (isDarkTheme) Color(0xFF8E8E93) else Color(0xFF666666)
|
||||
val dividerColor = if (isDarkTheme) Color(0xFF3A3A3A) else Color(0xFFE8E8E8)
|
||||
|
||||
// Получаем цвета аватара
|
||||
val avatarColors = getAvatarColor(
|
||||
if (isOwnAccount) "SavedMessages" else (user.title.ifEmpty { user.publicKey }),
|
||||
isDarkTheme
|
||||
)
|
||||
val avatarColors =
|
||||
getAvatarColor(
|
||||
if (isOwnAccount) "SavedMessages" else (user.title.ifEmpty { user.publicKey }),
|
||||
isDarkTheme
|
||||
)
|
||||
|
||||
Column {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// Аватар
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(if (isOwnAccount) PrimaryBlue else avatarColors.backgroundColor),
|
||||
contentAlignment = Alignment.Center
|
||||
modifier =
|
||||
Modifier.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
if (isOwnAccount) PrimaryBlue
|
||||
else avatarColors.backgroundColor
|
||||
),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (isOwnAccount) {
|
||||
Icon(
|
||||
Icons.Default.Bookmark,
|
||||
contentDescription = "Saved Messages",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(20.dp)
|
||||
Icons.Default.Bookmark,
|
||||
contentDescription = "Saved Messages",
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = if (user.title.isNotEmpty()) {
|
||||
getInitials(user.title)
|
||||
} else {
|
||||
user.publicKey.take(2).uppercase()
|
||||
},
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = avatarColors.textColor
|
||||
text =
|
||||
if (user.title.isNotEmpty()) {
|
||||
getInitials(user.title)
|
||||
} else {
|
||||
user.publicKey.take(2).uppercase()
|
||||
},
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = avatarColors.textColor
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -173,28 +165,26 @@ private fun SearchResultItem(
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
// Имя и значок верификации
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = if (isOwnAccount) {
|
||||
"Saved Messages"
|
||||
} else {
|
||||
user.title.ifEmpty { user.publicKey.take(10) }
|
||||
},
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
text =
|
||||
if (isOwnAccount) {
|
||||
"Saved Messages"
|
||||
} else {
|
||||
user.title.ifEmpty { user.publicKey.take(10) }
|
||||
},
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = textColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
// Значок верификации
|
||||
if (!isOwnAccount && user.verified > 0) {
|
||||
VerifiedBadge(
|
||||
verified = user.verified,
|
||||
size = 16
|
||||
)
|
||||
VerifiedBadge(verified = user.verified, size = 16)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,15 +192,16 @@ private fun SearchResultItem(
|
||||
|
||||
// Юзернейм или публичный ключ
|
||||
Text(
|
||||
text = if (isOwnAccount) {
|
||||
"Notes"
|
||||
} else {
|
||||
"@${user.username.ifEmpty { user.publicKey.take(10) + "..." }}"
|
||||
},
|
||||
fontSize = 12.sp,
|
||||
color = secondaryTextColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
text =
|
||||
if (isOwnAccount) {
|
||||
"Notes"
|
||||
} else {
|
||||
"@${user.username.ifEmpty { user.publicKey.take(10) + "..." }}"
|
||||
},
|
||||
fontSize = 14.sp,
|
||||
color = secondaryTextColor,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -218,9 +209,9 @@ private fun SearchResultItem(
|
||||
// Разделитель между элементами
|
||||
if (!isLastItem) {
|
||||
Divider(
|
||||
modifier = Modifier.padding(start = 64.dp),
|
||||
color = dividerColor,
|
||||
thickness = 0.5.dp
|
||||
modifier = Modifier.padding(start = 80.dp),
|
||||
color = dividerColor,
|
||||
thickness = 0.5.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
35
dev.sh
Executable file
35
dev.sh
Executable 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
|
||||
@@ -7,7 +7,7 @@ android.useAndroidX=true
|
||||
kotlin.code.style=official
|
||||
|
||||
# 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
|
||||
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
57
run.sh
Executable 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
58
watch-dev.sh
Executable 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
|
||||
Reference in New Issue
Block a user