Библиотека мобильного разработчика | Android, iOS, Swift, Retrofit, Moshi, Chuck
9.62K subscribers
1.64K photos
80 videos
52 files
4.45K links
Все самое полезное для мобильного разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/b60af5a4

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a4adec1b17b35b6c0d8389
Download Telegram
Эффект свечения в стиле Apple Intelligence

Новый язык дизайна Apple представил эффект светящейся анимированной обводки, которая изящно и динамично подсвечивает формы и компоненты. Давайте рассмотрим, как воссоздать этот эффект в SwiftUI с помощью многоразовых расширений.

🔹 Расширения для View

extension View {
@MainActor
func intelligenceBackground<S: InsettableShape>(in shape: S) -> some View {
background(shape.intelligenceStroke())
}

@MainActor
func intelligenceOverlay<S: InsettableShape>(in shape: S) -> some View {
overlay(shape.intelligenceStroke())
}
}


🔹 Базовая реализация для фигур

extension InsettableShape {
@MainActor
func intelligenceStroke(
lineWidths: [CGFloat] = [6, 9, 11, 15],
blurs: [CGFloat] = [0, 4, 12, 15],
updateInterval: TimeInterval = 0.4
) -> some View {
IntelligenceStrokeView(
shape: self,
lineWidths: lineWidths,
blurs: blurs,
updateInterval: updateInterval
)
.allowsHitTesting(false)
}
}


🔹 Рендеринг слоёв свечения

private struct IntelligenceStrokeView<S: InsettableShape>: View {
let shape: S
let lineWidths: [CGFloat]
let blurs: [CGFloat]
let updateInterval: TimeInterval

@Environment(\.accessibilityReduceMotion) private var reduceMotion
@State private var stops: [Gradient.Stop] = .intelligenceStyle

var body: some View {
let layerCount = min(lineWidths.count, blurs.count)
let gradient = AngularGradient(stops: stops, center: .center)

ZStack {
ForEach(0..<layerCount, id: \.self) { i in
shape
.strokeBorder(gradient, lineWidth: lineWidths[i])
.blur(radius: blurs[i])
.animation(
reduceMotion ? nil : .easeInOut(duration: 0.5 + Double(i) * 0.2),
value: stops
)
}
}
.task {
while !Task.isCancelled {
stops = .intelligenceStyle
try? await Task.sleep(for: .seconds(updateInterval))
}
}
}
}


🔹 Цветовая палитра

private extension Array where Element == Gradient.Stop {
static var intelligenceStyle: [Gradient.Stop] {
let colors = [
Color(red: 188/255, green: 130/255, blue: 243/255),
Color(red: 245/255, green: 185/255, blue: 234/255),
Color(red: 141/255, green: 159/255, blue: 255/255),
Color(red: 255/255, green: 103/255, blue: 120/255),
Color(red: 255/255, green: 186/255, blue: 113/255)
]
return colors
.map { Gradient.Stop(color: $0, location: Double.random(in: 0...1)) }
.sorted { $0.location < $1.location }
}
}


🔹 Использование


// Фон
Text("Текст")
.padding(22)
.intelligenceBackground(in: .capsule)

// Наложение
Text("Текст")
.padding(22)
.intelligenceOverlay(in: .rect(cornerRadius: 22))


🔹 Заключение

Эта реализация показывает, как объединить несколько обводок, размытий и анимированных градиентов для достижения эффекта свечения, аналогичного интерфейсу Apple Intelligence. Результат работает с любым объектом InsettableShape. Его можно использовать для современной и выразительной подсветки кнопок, карточек или текстовых контейнеров.

🐸 Библиотека мобильного разработчика

#PixelPerfect #MiddlePath #SwiftUI
Please open Telegram to view this post
VIEW IN TELEGRAM
3
⚙️ adb shell input keyeventуправление устройством с помощью системных событий

Отправляйте системные события на Android-устройство через ADB – имитируйте нажатия аппаратных кнопок, жесты и другие действия без физического доступа к устройству.

✏️ Основные keyevent коды:

# Базовые кнопки
adb shell input keyevent 3 # HOME
adb shell input keyevent 4 # BACK
adb shell input keyevent 26 # POWER (вкл/выкл)
adb shell input keyevent 24 # VOLUME_UP
adb shell input keyevent 25 # VOLUME_DOWN

# Медиа и специальные
adb shell input keyevent 85 # PLAY/PAUSE
adb shell input keyevent 86 # STOP
adb shell input keyevent 87 # NEXT
adb shell input keyevent 88 # PREVIOUS
adb shell input keyevent 164 # MUTE


📌 Полезные сценарии:

1. Автоматизация тестов:

# Сценарий: открыть приложение, сделать действия, вернуться домой
adb shell am start -n com.yourapp/.MainActivity
sleep 2
adb shell input keyevent 4 # BACK
adb shell input keyevent 3 # HOME


2. Тестирование обработки прерываний:

# Во время работы приложения
adb shell input keyevent 26 # POWER (блокировка)
sleep 2
adb shell input keyevent 26 # POWER (разблокировка)


3. Управление медиа в фоне:


adb shell input keyevent 85   # PLAY/PAUSE музыки


⚡️ Продвинутые комбинации:

Скриншот через комбинацию:

adb shell input keyevent 120  # SYSRQ (скриншот)


Перезагрузка устройства:


adb shell input keyevent 116  # POWER + перезагрузка через меню


Какие keyevent вы используете чаще всего? 💬

Библиотека мобильного разработчика

#буст #MiddlePath #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
📎 Блок инициализации (init блок)

Основной конструктор не может в себе содержать какую-либо логику по инициализации свойств (исполняемый код). Он предназначен исключительно для объявления свойств и присвоения им полученных значений. Поэтому вся логика может быть помещена в блок инициализации — блок кода, обязательно выполняемый при создании объекта независимо от того, с помощью какого конструктора этот объект создаётся. Помечается он словом init.

class Person(val name: String, var age: Int) {
var id: Int = 0

// require выдает ошибку с указанным текстом, если условие в левой части false
init {
require(name.isNotBlank(), { "У человека должно быть имя!" })
require(age > -1, { "Возраст не может быть отрицательным." })
}

constructor(name: String, age: Int, id: Int) : this(name, age) {
if (id > 0) this.id = id * 2
}
}


По сути блок инициализации — это способ настроить переменные или значения, а также проверить, что были переданы допустимые параметры. Код в блоке инициализации выполняется сразу после создания экземпляра класса, т.е. сразу после вызова основного конструктора. В классе может быть один или несколько блоков инициализации и выполняться они будут последовательно.

class Person(val name: String, var age: Int) {
// сначала вызывается основной конструктор и создаются свойства класса
// далее вызывается первый блок инициализации
init {
...
}

// после первого вызывается второй блок инициализации
init {
...
}

// и т.д.
}


Блок инициализации может быть добавлен, даже если у класса нет основного конструктора. В этом случае его код будет выполнен раньше кода вторичных конструкторов.

➡️ Подробнее в статье

🐸 Библиотека мобильного разработчика

#буст #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4
⚡️ Команда дня: flutter pub outdated

Хочешь понять, какие зависимости твоего проекта устарели — и стоит ли их обновлять? Вот команда, которая быстро всё покажет:

flutter pub outdated


📌 Что делает:

— Проверяет все пакеты в pubspec.yaml
— Показывает текущие, доступные и последние версии библиотек
— Помогает понять, где можно безопасно обновиться

Пример вывода:

Package         Current  Upgradable  Resolvable  Latest
http 1.2.0 1.3.0 1.3.0 1.3.0
provider 6.0.0 6.1.0 6.1.0 7.0.0


👀 Полезно, когда:

— Готовишь проект к релизу и хочешь убедиться, что всё актуально
— Нужно проверить, не тянет ли зависимость старые версии других пакетов
— Хочешь поддерживать проект «в форме» 💪

💡 Совет:

Чтобы обновить всё возможное, просто выполни:

flutter pub upgrade --major-versions


А вы как часто обновляете зависимости в своих проектах?
💬

🐸 Библиотека мобильного разработчика

#буст #MiddlePath
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🥱1
⚙️ Внедрение доступности (Accessibility) в мобильное приложение

Хотите сделать ваше приложение доступным для всех пользователей? Этот промпт поможет реализовать полноценную поддержку accessibility.

📝 Промпт:

Implement comprehensive accessibility features for a mobile app that includes:

— Add proper content descriptions for all UI elements
— Implement logical focus order and navigation
— Support screen readers (TalkBack/VoiceOver)
— Ensure sufficient color contrast and text sizing
— Add accessibility labels and hints
— Support voice control and switch access
— Test with accessibility services enabled


💡 Расширения:

— Добавьте Implement custom accessibility actions для сложных UI компонентов
— Добавьте Add haptic feedback for key interactions для невизуального взаимодействия
— Добавьте Support dynamic font sizing without breaking layouts для адаптивности текста

🐸 Библиотека мобильного разработчика

#буст #MiddlePath
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
✏️ adb shell dumpsys meminfo – детальный анализ использования памяти приложением

Мощный инструмент для мониторинга использования памяти вашим приложением в реальном времени. Помогает находить утечки памяти и оптимизировать потребление RAM.

📌 Ключевые метрики:

1. PSS (Proportional Set Size):

• Реальная память, занимаемая приложением
• Включает разделяемую память (пропорционально)

2. Private Dirty:

• Память, принадлежащая только вашему приложению
• Не может быть выгружена в swap

3. Java Heap:

• Память, управляемая Dalvik/ART
• Ключевой показатель для поиска утечек

⚡️ Практические сценарии:

Мониторинг в реальном времени:

# Каждые 2 секунды обновлять информацию
watch -n 2 "adb shell dumpsys meminfo com.yourapp.package"


Сравнение состояний:


# До и после выполнения операции
adb shell dumpsys meminfo com.yourapp.package > before.txt
# Выполняем действия в приложении
adb shell dumpsys meminfo com.yourapp.package > after.txt
diff before.txt after.txt


Поиск утечек в Activity:

# Проверить, не остались ли Activity в памяти
adb shell dumpsys meminfo | grep -E "Activity|View|Context"


⚙️ Анализ конкретных компонентов:

Только Java Heap:

adb shell dumpsys meminfo com.yourapp.package | grep -A 10 "Java Heap"


Нативные allocations:

adb shell dumpsys meminfo com.yourapp.package | grep -A 5 "Native Heap"


📊 Пример вывода для анализа:

Applications Memory Usage (in KB)
Uptime: 1234567 Realtime: 1234567

** MEMINFO in pid 1234 [com.yourapp] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 12345 12000 345 100 15000 14000 1000
Dalvik Heap 5678 5600 78 50 6000 5800 200


🔧 Продвинутое использование:

Профилирование конкретной операции:

#!/bin/bash
echo "Память до:" > mem_log.txt
adb shell dumpsys meminfo com.yourapp.package >> mem_log.txt

# Запускаем тестовый сценарий
adb shell am start -n com.yourapp/.TestActivity

echo "Память после:" >> mem_log.txt
adb shell dumpsys meminfo com.yourapp.package >> mem_log.txt
Pro-совет: Используйте с procstats для долгосрочного мониторинга:

bash
adb shell dumpsys procstats --hours 1 com.yourapp.package


Как вы отслеживаете использование памяти в своих приложениях? 💬

🐸 Библиотека мобильного разработчика

#буст #MiddlePath #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
2
⚙️ Превращение меню SwiftUI в мини-панель настроек

Меню в SwiftUI часто используются для быстрых одноразовых команд: выберите пункт, выполните действие — и всё готово. Но что, если вы хотите, чтобы меню функционировало не как простой селектор, а как мини-панель настроек, где пользователи могут последовательно переключать несколько пунктов, прежде чем сделать окончательный выбор? По умолчанию меню закрывается, как только пользователь нажимает на пункт, но с помощью подходящего API это поведение можно изменить.

Переосмысление меню

Обычно при добавлении Menu к метке — например, к значку с тремя точками или кнопке — каждое касание внутри меню выполняет действие и немедленно закрывает меню. Это ожидаемый рабочий процесс «выбрать и закрыть».

Но есть сценарии, в которых может быть предпочтительнее другая модель взаимодействия:

🔘 Меню, допускающее несколько переключателей.
🔘 Меню, сохраняющее постоянное состояние, пока пользователь перемещается по набору пунктов.
🔘 Меню, которое остаётся открытым, пока пользователь пробует различные настройки, возможно, просматривая эффект в режиме реального времени, и закрывается только после того, как пользователь будет удовлетворен результатом.

В таких случаях поведение закрытия по умолчанию не является идеальным.

Настройка поведения закрытия

SwiftUI расширяет меню с помощью модификатора menuActionDismissBehavior(_:). Это даёт вам возможность точно контролировать, должно ли касание внутри меню приводить к его закрытию или оставаться открытым.

Модификатор принимает один параметр типа MenuActionDismissBehavior, который представляет собой перечисление, определяемое примерно следующим образом:

public enum MenuActionDismissBehavior {
case automatic // system-default behaviour
case enabled // explicitly force dismissal on each tap
case disabled // keep the menu open after taps
}


Применение модификатора выглядит так:

Menu("Options") {
Button("Toggle A") { /* … */ }
Button("Toggle B") { /* … */ }
Divider()
Button("Done") { /* … */ }
}
.menuActionDismissBehavior(.disabled)


При использовании .disabled меню остаётся открытым после каждого действия, позволяя пользователю выполнить несколько действий, прежде чем решить закрыть его.

Наглядный пример: пакетное переключение

Представьте, что у вас есть набор функций, которые пользователь может включить или отключить, и вы хотите, чтобы пользователь мог сделать несколько выборов в меню, прежде чем закрыть его.

struct FeatureToggleMenu: View {
@State private var featureA = false
@State private var featureB = false
@State private var featureC = false

var body: some View {
Menu {
Section {
Toggle("Feature A", isOn: $featureA)
Toggle("Feature B", isOn: $featureB)
Toggle("Feature C", isOn: $featureC)
}
.menuActionDismissBehavior(.disabled)

Button("Apply Changes") {
// commit logic here
}
} label: {
Label("Settings", systemImage: "gearshape")
}
}
}


В этом макете:

🔘 Пользователь открывает «Настройки» и переключает различные функции, не закрывая меню при каждом нажатии переключателя.
🔘 После выбора он нажимает «Применить изменения», а затем может закрыть меню (в зависимости от реализации).
🔘 Вы даже можете вручную принудительно закрыть меню, используя специальную кнопку «Готово».

Заключение

Модификатор menuActionDismissBehavior(_:) — это удобный API для преобразования типичных меню SwiftUI в более надежные мини-интерфейсы для настроек, переключателей и многошаговых рабочих процессов. Продуманное использование этого может привести к более понятному и интуитивно понятному пользовательскому интерфейсу, когда вам нужно больше, чем простое взаимодействие «выбрать и применить».

🐸 Библиотека мобильного разработчика

#буст #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
1😁1
Организация представлений SwiftUI с помощью ToolbarContent и @ToolbarContentBuilder

По мере роста проектов SwiftUI одна из частых проблем — управление сложными иерархиями представлений. Даже простой экран может быстро превратиться в десятки вложенных модификаторов. После снятия лимита в 10 вложенных представлений стало проще писать глубоко вложенные body, но код стал труднее читать и сопровождать.

🔹 Разбираем крупные реализации body

Когда body растягивается на десятки строк, страдает читаемость. Лучше разбивать большие представления SwiftUI на мелкие подпредставления или выделять повторно используемые части в вычисляемые свойства или функции. Это сохраняет лаконичность и упрощает понимание, тестирование и повторное использование.

Тот же принцип применим к невизуальным элементам, например панелям инструментов, которые быстро разрастаются при добавлении множества кнопок и пунктов меню.

🔹 Проблема с панелями инструментов

Модификатор .toolbar позволяет создавать кнопки, меню и элементы управления, адаптируемые под разные платформы. Но если элементов становится много, код внутри .toolbar { ... } быстро теряет читаемость.
Перенести логику в вычисляемое свойство нельзя — .toolbar ожидает содержимое, соответствующее ToolbarContent.

🔹 ToolbarContent и @ToolbarContentBuilder

SwiftUI решает это с помощью:

ToolbarContent — протокола для элементов панели инструментов;
@ToolbarContentBuilder — билдера, создающего набор элементов панели.

Объединив их, можно вынести содержимое панели инструментов в отдельный блок, сделав body чище и понятнее.

Пример

struct DemoView: View {
@State private var message = "Hello, world!"

var body: some View {
NavigationStack {
Text(message)
.font(.title2)
.padding()
.navigationTitle("Home")
.toolbar { toolbarContent }
}
}

@ToolbarContentBuilder
private var toolbarContent: some ToolbarContent {
ToolbarItem(placement: .navigationBarLeading) {
Button { message = "Left tapped" } label: {
Label("Left", systemImage: "line.3.horizontal")
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button { message = "Right tapped" } label: {
Label("Right", systemImage: "star")
}
}
}
}


🔹 Заключение

Использование @ToolbarContentBuilder для отделения содержимого панели инструментов от основного окна имеет ряд преимуществ:

Улучшена читаемость: Ваш body текст по-прежнему сосредоточен на вёрстке, а логика панели инструментов реализована в другом месте.
Лучшая организация: Группировка элементов панели инструментов в отдельном блоке позволяет с первого взгляда оценить их структуру и расположение.
Масштабируемость: Когда на панели инструментов появляется несколько кнопок, меню или условная логика, поддерживать её в рабочем состоянии становится намного проще.

🐸 Библиотека мобильного разработчика

#PixelPerfect #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
1
🌛 Реализация офлайн-режима

Хотите, чтобы ваше приложение работало даже без интернета? Этот промпт поможет реализовать надежный офлайн-режим и синхронизацию данных.

📝 Промпт:

Implement offline-first approach for a mobile app that includes:

— Set up local database (Room/SQLite/CoreData)
— Implement data synchronization strategy
— Handle conflict resolution for concurrent modifications
— Manage offline queue for pending operations
— Add network connectivity detection
— Implement cache management and expiration policies
— Provide user feedback for sync status


💡 Расширения:

— Добавьте Implement reactive UI updates using observables для автоматического обновления интерфейса
— Добавьте Add retry mechanisms with exponential backoff для надежной синхронизации
— Добавьте Support large file caching with storage management для работы с медиа в офлайне

🐸 Библиотека мобильного разработчика

#буст #MiddlePath
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱2🔥1🤔1
Эффект ЭЛТ-экрана в Jetpack Compose

ЭЛТ-мониторы — это размытые края, линии сканирования и лёгкое свечение. Такой эффект можно воспроизвести в Compose с помощью GraphicsLayer, градиентов и размытия.

🔹 Базовый принцип

Мы один раз записываем контент во внеэкранный буфер и многократно перерисовываем его разными слоями.

val graphicsLayer = rememberGraphicsLayer()

Box(Modifier.drawWithContent {
graphicsLayer.record { drawContent() }
}) {
content()
}


Теперь drawLayer(graphicsLayer) можно использовать в любых эффектах.

🔹 Линии сканирования

Создаём повторяющиеся градиенты — вертикальные и горизонтальные:

private fun DrawScope.drawScanLines(alpha: Float, blend: BlendMode) {
val c = Colors.Black.copy(alpha)
drawRect(
brush = Brush.verticalGradient(
0f to c, 0.4f to c, 0.4f to Colors.Transparent, 1f to Colors.Transparent,
tileMode = TileMode.Repeated, endY = 10f
),
blendMode = blend
)
}


Добавляем их поверх слоя:

.drawBehind {
layer {
drawLayer(graphicsLayer)
drawScanLines(alpha = 1f, blend = BlendMode.DstOut)
}
}


DstOut вычитает градиент и создаёт характерный "CRT-срез".

🔹 Размытие и свечение

Для реалистичного свечения рисуем несколько слоёв с разным blur/scale/alpha:

val blurLayers = listOf(
Triple(5.dp, .3f, 1.02f to 1.03f),
Triple(0.dp, .8f, 1f to 1f),
Triple(10.dp, .6f, 1.001f to 1f),
)


Каждый слой:

Box(
Modifier
.matchParentSize()
.blur(blur, BlurredEdgeTreatment.Unbounded)
.graphicsLayer { scaleX = scale.first; scaleY = scale.second; this.alpha = alpha }
.drawBehind {
layer {
drawLayer(graphicsLayer)
drawScanLines(1f, BlendMode.DstOut)
}
}
)


🔹 Дрожание экрана

var shake by remember { mutableStateOf(Offset.Zero) }

LaunchedEffect(Unit) {
while (true) {
shake = Offset(
Random.nextFloat() * Random.nextInt(-1, 1),
Random.nextFloat() * Random.nextInt(-1, 1),
)
delay(32)
}
}


И применяем:

.graphicsLayer {
translationX = shake.x
translationY = shake.y
}


🐸 Библиотека мобильного разработчика

#PixelPerfect #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
🎮 Почему ваша ViewModel технически нестабильна и почему Compose не возражает

А вы знали, что почти все ViewModels нестабильны?

Когда мы только изучаем Compose,
нас учат использовать стабильный класс, а не нестабильный.

Но ViewModels нестабильны. Так почему же никто ничего не говорит о том, что мы используем нестабильные ViewModels?

🔹 Как Compose определяет стабильность?

Компилятор Compose считает класс стабильным, если:

🔘 Все его свойства (val/var) неизменяемы (val).

🔘 Или их тип «отслеживается» рантаймом Compose (например, MutableState<T>).

Взгляните на примеры:

// Стабильный класс
data class Stable(
val a: Int, // Стабильно
val b: MutableState<Int>, // Стабильно, отслеживается Compose
val c: SnapshotStateList<Int> // Стабильно, отслеживается Compose
)

// Нестабильный класс
data class Unstable(
var b: Int // Нестабильно из-за `var`
)

// "Неопределенная" стабильность
data class Runtime(
val i: Interface // Компилятор не знает, какая реализация будет на runtime.
)


Но есть важный нюанс: это правило работает только внутри модуля, где подключен компилятор Compose.

🔹 Что происходит на границах модулей?

Допустим, вы создали стабильную data class в слое данных (data) и внедрили её в ViewModel в слое презентации.

Логично ожидать, что ViewModel тоже будет стабильным. Но на практике — нет!

Compose-компилятор в модуле презентации не может заглянуть в модуль данных, чтобы проверить стабильность вашего класса. Поэтому он перестраховывается и помечает любой класс извне как нестабильный.

А раз наш ViewModel зависит от репозиториев и UseCase из других модулей (domain/data), то и он сам автоматически становится нестабильным.

🔹 Так почему же нестабильный ViewModel — это норма?

Ответ простой и лежит на поверхности: мы не передаем сам ViewModel в дочерние композаблы.

Вместо этого мы:

1. Создаем ViewModel один раз наверху (например, в NavGraph).

2. Коллектим его состояние (state), которое уже является стабильным.

3. Пробрасываем это стабильное состояние вниз по дереву композиции.

@Composable
fun Screen(viewModel: TestViewModel) { // ViewModel нестабилен, и это ок
val state by viewModel.state.collectAsState() // Состояние - стабильно

Child(state) // Передаем стабильный state
}

@Composable
fun Child(state: TestState) { // Стабильный пропс -> рекомпозиции оптимизированы
Text(state.data)
}


Compose-рантайм следит за изменениями в state. Сам ViewModel как объект не «пробрасывается» глубже и не триггерит лишних рекомпозиций.

🔹 Итоги

🔘 Для монолитных проектов: Это не проблема, компилятор видит все классы и корректно определяет стабильность.

🔘 Для многомодульных проектов: ViewModel почти всегда будет нестабильным из-за зависимостей из других модулей. И это нормально и безопасно, если вы передаете в композаблы не его самого, а его состояние.

Так что можете спать спокойно — с вашим кодом всё в порядке.

🐸 Библиотека мобильного разработчика

#АрхитектурныйКод #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
1❤‍🔥1🔥1🤔1
✏️ adb shell am broadcast – отправка broadcast-интентов из командной строки

Мощный инструмент для тестирования обработки broadcast-сообщений в вашем приложении без необходимости эмулировать системные события через UI.

🔥 Базовое использование:

# Отправка кастомного broadcast
adb shell am broadcast -a "com.yourapp.action.CUSTOM_ACTION"

# С дополнительными данными
adb shell am broadcast -a "com.yourapp.action.CUSTOM_ACTION" --es "key" "value"


📌 Практические сценарии:

1. Тестирование системных событий:

# Имитация подключения зарядки
adb shell am broadcast -a android.intent.action.ACTION_POWER_CONNECTED

# Имитация изменения языка системы
adb shell am broadcast -a android.intent.action.LOCALE_CHANGED


2. Тестирование кастомных broadcast receivers:

# Отправка с дополнительными extras
adb shell am broadcast -a "com.yourapp.action.DATA_REFRESH" \
--es "refresh_type" "full" \
--ei "user_id" 12345 \
--ez "force_update" true


3. Отправка конкретному пакету:

# Только для вашего приложения
adb shell am broadcast -a "com.yourapp.action.TEST" -n com.yourapp.package/.ReceiverName


⚡️ Полезные системные действия:

Уведомление о низком заряде:

adb shell am broadcast -a android.intent.action.BATTERY_LOW


Изменение конфигурации:

adb shell am broadcast -a android.intent.action.CONFIGURATION_CHANGED


События времени:

adb shell am broadcast -a android.intent.action.TIME_SET


Какие broadcast-ы вы чаще всего тестируете через командную строку? 💬

🐸 Библиотека мобильного разработчика

#буст #MiddlePath #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝1