android studio обновление приложения без google play

Как обновлять Android-приложения без Google Play

Существует несколько причин, по которым Google Play может отсутствовать в прошивке смартфонов под управлением Android. Первая – китайская поставка. Если аппарат предназначался для продажи на рынке КНР, он будет лишен доступа к официальному каталогу приложений Google. Вторая – действие новой модели распространения приложений и сервисов поискового гиганта, по которой производители смартфонов могут отказаться от Google Play и другого ПО компании. Но как в таком случае искать и устанавливать обновления приложений?

appsupd

APKGrabber (скачать) – это удобная утилита для Android, которая позволит вам скачивать обновления всех ваших приложений в обход Google Play. APKGrabber сам проанализирует все программы, установленные на устройство, и отыщет файл с апдейтом вне зависимости от способа их установки. Такой способ обновления придется кстати тем, кто предпочитает получать платное ПО безвозмездно, скачивая из интернета APK-файлы популярных игр, приложений и утилит.

Как обновлять APK-приложения

apkgrabber

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

APKGrabber производит поиск обновлений только на проверенных онлайн-площадках вроде APKMirror, Uptodown и даже Google Play. Поэтому переживать за содержание апдейтов не стоит. Все файлы, которые попадают в базу данных таких платформ, тщательно проверяются модераторами, что позволяет свести к минимуму вероятность установки обновлений с зараженными компонентами. Таким образом, процесс обновления через APKGrabber становится не менее безопасным, чем через официальные источники.

Подписывайся на наш канал в Яндекс.Дзен, чтобы не пропустить новые полезные материалы, которые не выходят на сайте.

Источник

Как устанавливать и обновлять приложения без Google Play

Большинство пользователей, как правило, устанавливают приложения на свои мобильные устройства из официальных источников. В случае с ОС Android – это магазин Google Play. Главное преимущество такого метода установки программ – регулярные обновления. Google не рекомендует, но все же позволяет устанавливать приложения на смартфон или планшет, загруженные из других источников. Однако при этом вы теряете возможность получать для них обновления. Как оказалось, и здесь существуют решения, как всегда иметь актуальные версии приложений на своем устройстве.

Зачем загружать приложения из других источников?

Причин для этого может быть несколько. Например, одни разработчики не желают публиковать свои программы в Google Play, другим запрещает это делать компания Google, если их программный продукт противоречит правилам размещения. Зачастую крупные компании по той или иной причине могут устанавливать региональные ограничения доступа к своим приложениям — ярким примером такого случая является игра Pokémon GO или официальный клиент музыкального стримингового сервиса Spotify, которые до сих пор не доступны в украинском сегменте магазина приложений.

pic 01

Не стоит исключать и того факта, что в некоторых странах, включая Китай, не работает Google Play. Соответственно, фирменные оболочки китайских мобильных устройств, предназначенных для продажи на внутреннем рынке, не содержат сервисы Google. И последняя причина – это то, что в последнее время пользователи смартфонов и планшетов самостоятельно отказываются от сервисов Google в пользу увеличения автономности устройства.

Единственный способ установить приложение на смартфон или планшет в подобных случаях – загрузить его APK-файл из других ресурсов.

Где брать приложения для Android, кроме Google Play?

Таких ресурсов в сети предостаточно, но авторитетных и, что немаловажно, безопасных – не так уж много. Одним из наиболее популярных является веб-сервис APK Downloader, который позволяет скачивать из магазина Google Play установочные файлы приложений. Для этого достаточно вставить в поисковую строку адрес страницы нужной программы в Google Play и нажать кнопку «Generate Download Link». APK Downloader загрузит приложение и выдаст прямую ссылку на APK-файл.

pic 02

Другими источниками для загрузки программ являются ресурсы APKMirror и APKPure. Сразу оговоримся, эти сайты не содержат платных программ, а представляют собой репозитории бесплатных приложений, по сути своеобразное «зеркало» Google Play. APKMirror, в свою очередь, включает неопубликованные в официальном магазине программы, а также экспериментальные версии приложений.

Источник

Встраиваем In-App Updates в приложение

android in app updates api

Обновление приложений и поддержка актуальной версии у пользователей это очень важный момент в жизненном цикле каждого приложения. От части это зависит от того, что Android постоянно изменяется: какие-то методы и классы добавляются, какие-то становятся устаревшими, и примерно раз в год с выходом новой версии следует обновлять targetSdkVersion и переписывать код, если это требуется. Кроме того, постоянно обновляются сторонние библиотеки, которые нужно также обновить у себя в приложении, добавляются новые функции и особенности в само приложение. И про багфиксы не стоит забывать, нужно постоянно отслеживать баги в приложениях и оперативно исправлять, потому что недовольный пользователь скорее всего испортит статистику приложению негативным отзывом.

Всё это вынуждает постоянно поддерживать приложение. Отсюда вытекает следующий момент: как установить актуальную версию приложения максимальному числу пользователей.

Этот вопрос от части решает сам Google Play. Он присылает уведомления пользователю о том, что доступна новая версия приложения, предлагает включить автоматическое обновление приложений, что обеспечивает самую свежую версию приложения на устройстве. Однако не все пользователи использую эту возможность, многие обновляют от случая к случаю, игнорируют уведомления.

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

В прошлом году Google выпустили механизм In-App Updates с целью ещё больше ускорить обновление приложений у пользователей. In-App Updates позволяет показывать пользователю при входе в приложение диалог, где ему будет предложено обновить приложение до последней версии. Этот функционал работает, начиная с версии Android 5.0 (API 21). Кроме того, приложение должно быть опубликовано в Google Play.

In-App Updates поддерживает два способа обновления приложения:

Рассмотрим первый вариант обновления приложения на примере одного из наших приложений «Страны мира«.

Перед началом работы нужно добавить библиотеку Play Core в приложение. Для этого в файле build.gradle модуля приложения добавим следующую зависимость:

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

Перед тем, как показывать диалог, следует проверить наличие новой версии. Для этого воспользуемся классом AppUpdateManager для запроса к Google Play.

К экземпляру класса AppUpdateManager привязываем слушатель OnSuccessListener, который будет получать результат запроса. Если новая версия приложения доступна, отправляем интент, отображающий диалог для скачивания новой версии.

Если пользователь согласится на обновление, начнётся процесс загрузки в фоновом режиме. Обратите внимание, что с помощью метода startUpdateFlowForResult() мы можем узнать, какое действие выбрал пользователь. Для этого мы передаём в него константу UPDATE_REQUEST_CODE, результат с этим кодом придёт в метод onActivityResult() активности.

Здесь мы можем получить один из следующих результатов:

Поскольку мы используем Flexible метод обновления, нам нужно узнать, когда завершится загрузка новой версии. Для этого нам нужно добавить слушатель InstallStateUpdatedListener и привязать его к экземпляру AppUpdateManager после того, как начнётся загрузка. О том, что пользователь начал загрузку, мы узнаём, получим результат в методе onActivityResult() активности.

С помощью этого слушателя мы можем узнать, загружается обновление или уже загружено, и если загружается — получить прогресс выполнения. Таким образом, при желании можно показывать прогресс загрузки прямо в приложении, но в данном случае мы этого делать не будем.

Когда загрузка завершена, отвязываем слушатель и проверяем, активно приложение или нет. Если приложение активно, то показываем сообщение о том, что обновление готово для установки, в противном случае запускаем установку обновления. Делается это с помощью метода completeUpdate() экземпляра AppUpdateManager. Также для этих целей мы добавим слушатель UpdateListener к нашему классу UpdateManager. Если этот слушатель привязан к активности — значит приложение активно, если отвязан — значит находится в фоне.

Когда обновление будет загружено, пользователь увидит внизу экрана Snackbar с сообщением об этом и кнопкой для перезагрузки приложения. При желании можно Snackbar заменить на что-нибудь ещё.

Сам же Snackbar выглядит очень просто, при нажатии на Restart мы вызываем completeUpdate(), как делали бы это, будучи в фоновом режиме.

Если пользователь проигнорирует это сообщение, обновление останется неустановленным и будет храниться в памяти. Поэтому при каждом запуске приложения мы будем проверять, имеются ли загруженные обновления, и если да — выводить сообщение. В противном случае, как и раньше, проверяем наличие обновлений в Google Play. Для этого вернёмся к слушателю OnSuccessListener из начала и добавим в него следующее условие.

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

Итоговый листинг класса UpdateManager выглядит следующий образом:

Итак, мы встроили In-App Updates в приложение, осталось только проверить его работу. Для этого отлично подойдёт такая особенность Google Play, как внутренний совместный доступ (Internal app-sharing). Он позволяет загружать APK или Android App Bundle на специальную страницу загрузки, к которым будут иметь доступ только тестировщики. Это позволяет быстро проверить приложение, при этом его не обязательно подписывать релизным ключом, можно делиться также и debug-версиями. Тестировщикам отправляется специальная ссылка на скачивание, которая перенаправляет их в Google Play, где им будет предложено установить данную версию приложения.

Для того, чтобы настроить внутренний совместный доступ, нам нужно задать список тестировщиков для этого приложения. Зайдём в консоль разработчика Google Play и откроем нужное нам приложение. В меню «Инструменты разработки» выберем «Внутренний доступ к приложению«, после чего откроется страница для настройки. Здесь нас интересует только «Управление пользователями с правами загрузки«.

3

Тут мы можем формировать различные списки тестировщиков, включать их и отключать. При нажатии на «Создать список» появится окно, где будет предложено ввести электронную почту каждого из участников тестирования, а также задать имя новому списку. Создадим список и добавим в него себя как тестировщика.

4

Теперь мы можем перейти на страницу загрузки для внутреннего совместного доступа. Сюда мы может публиковать различные APK для тестирования. Соберём два APK нашего приложения с разными кодами версий (например, 23 и 24) и загрузим их на эту страницу. При загрузке мы также можем задать название данной версии, чтобы было проще их различать.

5

После того, как APK будут загружены, они отобразятся в списке.

6

С этого момента мы готовы тестировать эти версии. Зайдём на устройство, с которого будем запускать приложение, и на нём перейдём по ссылке от версии 1.23. Откроется специальная страница, которая предложит перейти в Google Play для установки.

Screenshot 1587630331

После перехода в Google Play нам будет предложено установить эту версию приложения.
Screenshot 1587630415

Screenshot 1587619435

Примечание! Если вместо страницы приложения вы видите сообщение «Настройки для разработчиков отключены», то нужно будет выполнить следующее:

Как только установка завершится, открываем приложение. В нашем приложении под списком меню указывается текущий код и версия приложения, поэтому будем ориентироваться на неё, что обновление прошло успешно. Как видно, сейчас текущая версия 1.23.
Screenshot 1587630796

Теперь закрываем приложение, снова открываем браузер и переходим уже по ссылке от версии 1.24. Нам аналогично будет предложено перейти в Google Play, только на этот раз мы можем не установить, а обновить или удалить приложение. Важно: не нужно обновлять приложение с этой страницы! Смотрим только, что она есть, и закрываем.

Screenshot 1587630923

Снова заходим в наше приложение и, если всё сделано верно, отобразится диалог с предложением обновить версию.

Screenshot 1587631100

Нажимаем на «Обновить«, после чего начинается скачивание обновления. За его прогрессом можно следить в шторке уведомлений.

Screenshot 1587631144

Как только загрузка завершится, в наш слушатель придёт состояние DOWNLOADED, после чего внизу экрана отобразится сообщение с предложением перезагрузить приложение.

Screenshot 1587631154

Нажимаем Restart, и здесь в работу включаются сервисы Google Play, которые обновят приложение до новой версии.

Screenshot 1587631159

После того, как установка завершится, приложение запустится автоматически. Скроллим список вниз и видим, что версия приложения теперь стала 1.24, значит обновление прошло успешно.

Screenshot 1587631170

Таким образом, благодаря In-App Updates, пользователи смогут более быстро получать новые версии, чем при обновлении непосредственно через Google Play.

Встраиваем In-App Updates в приложение : 1 комментарий

А как сослаться на UpdateManager например в MainActivity?

Источник

Есть ли жизнь без Google Play? Альтернативы и обновление приложений

47c5febcdd804edc85acb6aa044fbb47
На днях я сделал приложение для обхода блокировки rutracker, однако уже дважды получил от Google отбой по разным надуманным причинам. Это очень огорчило, и возник вопрос — что делать? Поиск по хабру показал, что такие проблемы возникли очень у многих (пруфы: один, два, три, четыре… Тысячи их).
А помимо бана есть ещё много причин, по которым ваше приложение может оказаться «за бортом» Google Play — например, если оно предназначено для использования только в компании, для друзей или для другого круга лиц. Так же возможно, что ваше приложение по определению не может быть выложено на Google Play — например, если оно является само по себе установщиком приложений.

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

1. Используем альтернативный магазин приложений

Тут довольно богато. Amazon App Shop, Samsung Apps, Yandex.Store, blackmart, humble bundle, F-Droid… В общем-то неплохо. Я из них пока попробовал только Yandex.Store. Получилось зарегистрироваться и опубликовать приложение минут за 5 — оно там уже доступно. Правда, недоумение вызывает полная невозможность как-либо дать ссылку на установку приложения в Yandex.Store. Может, я слепой, но кнопочки «Share» там просто нет. Ну и никакого веб интерфейса — только мобильный. Кстати, вопрос к представителям Yandex — а Yandex.Store вообще живой? Последний пост в твиттере датируется 2014 годом.

Плюсы:

Минусы:

2. Самостоятельное решение

Как ни страшно это звучит, на самом деле здесь мы приходим к аналогу обычного десктопного софта. Сама программа знает, когда и как ей обновляться, рассказывает об этом, в ней интегрированы платёжные возможности и монетизация. Рекламой и продвижением занимаетесь вы сами. Ужас, ужас! Но на самом деле, мы просто избаловались. Нужно просто находить правильные пути. Продвигать приложение можно, например, на тематических блогах и форумах — в том числе на 4pda с очень неплохим объёмом аудитории. Или прямо на хабре.

Кстати, даже если ваше приложение успешно опубликовано на Google Play — имеет смысл подстраховаться и как минимум сделать в нём свой алгоритм обновления, который активизируется, если, например, приложение давно не обновлялось. Или по проверке бана на вашем сервере или прямо на Google Play. Иначе, если вас на какое-то время забанят, пользователи не смогут обновить приложение. А если есть запасной вариант — вреда будет заметно меньше.

Плюсы

Ваше приложение практически невозможно заблокировать — только вместе с ресурсом, на котором вы держите обновление и информацию.

Минусы

Итак, что же вам придётся делать:

Размещение приложения для скачивания

На самом деле, это всего лишь означает, что вам нужно выложить APK куда-то в доверенное место. Их не так уж мало — можно использовать всё тот же 4pda, можно класть релизы прямо в github, можно на свой сайт… Вариантов уйма.

Отладка ошибок

Да, привыкли мы к хорошему — Google сам поймает ошибку, всё расскажет о её месте, устройстве пользователя и так далее. Но… Никак проблем это реализовать самостоятельно. Как простейший вариант — ловим все эксепшны и отсылаем куда-то на сервер по HTTP или даже на почту.

Сбор статистики

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

Сложные библиотеки

Собственно придётся забыть о всех сложных и хороших вещах, которые даёт Google Play. Впрочем, то же самое касается, пускай и в чуть меньшей степени, любых магазинов приложений.

Обновление

Чтобы показать, как всё просто — покажу примитивную реализацию третьего варианта,

1. Добавляем куда-то свою актуальную версию приложения. Я засунул прямо в исходники на Github в build.gradle:

2. Публикуем где-то релизы приложения. У меня они так же лежат на github, в формате

3. Пишем собственно алгоритм обновления:

3.1 в MainActivity.OnCreate добавляем вызов проверки:

3.2 Пишем простенький класс для проверки:

3.3 Добавляем класс для хранения информации о том, что пользователь решил проигнорировать этот релиз:

3.4 Добавляем функцию, которая покажет пользователю диалог с предложением перехода:

Вуаля! Всё, теперь у нас обновляемое приложение. А если оно когда-нибудь появится в Google Play или в другом магазине приложений, который есть у пользователя, то оно сможет обновляться ещё и оттуда.

Выводы

Что есть в сухом остатке — думаю, что без Google Play жить можно. И не только можно — нужно, минусы наличия монополиста на рынке очевидны. В том числе, если бы была хоть какая-то осмысленная конкуренция — возможно, общение с технической поддержкой было бы хоть какое-то адекватное, а не отписка пустыми шаблонами.

Источник

Как внедрить In-app Updates в Android-приложение на React Native

image loader

В 2019 году Google выпустила In-app Updates — возможность обновлять Android-приложения без перехода в Google Play. Однако до сих пор довольно мало приложений поддерживают этот способ обновления.

Когда я внедрял In-app Updates в приложение Профи для специалистов — без сложностей не обошлось. Пришлось покопаться в документации, статьях и даже пару раз переписать реализацию.

Чтобы меньше людей наступали на мои грабли, я сделал пошаговую инструкцию по интеграции In-app Updates в Android-приложение на React Native. Если следовать ей — сможете внедрить эту опцию за день.

Оговорка

Уже разработали несколько библиотек, инкапсулирующих реализацию In-app Updates. Например, эту или эту. Моя статья для тех, кто хочет добавить интеграцию самостоятельно.

Что будем делать

Разберёмся, как тестировать эту фичу. Ведь она требует взаимодействия с Google Play.

Подготовка к тестированию

Нам придётся неоднократно тестировать код. Поэтому сперва разберёмся, как это делать.

Для проверки In-app обновлений Google разработала специальную тестовую среду, в которую можно загрузить apk-файл или bundle приложения и затем установить на устройство. Через неё же будем имитировать появление обновления.

Готовим устройство.

Устанавливать приложения из тестовой среды можно как на реальные устройства, так и на эмуляторы. В любом случае нужно настроить Google Play (названия кнопок на вашем устройстве могут отличаться, но суть одна):

Заходим в Google Play, кликаем на своё фото, выбираем «Настройки».

В разделе «Сведения» кликаем много раз подряд на «Версию Google Play», пока не станем разработчиком.

Включаем тогл «Внутренний доступ к приложениям» в разделе «Личные» или «Общее».

Собираем apk- или aab-файл приложения.

Ещё к нему можно подключить дебаггер Android Studio, чтобы отлаживать нативный код. Для этого нужно нажать на «жучка со стрелочкой» в панели инструментов Android Studio (Attach debugger to Android Process) и выбрать запущенное приложение.

Уточню, что устройство должно быть подключено к компьютеру, а тип подключения должен разрешать обмен данными. Чтобы избежать проблем с подключением, советую пользоваться эмулятором.

Загружаем полученный файл в Internal App Sharing и указываем название версии.

Советую добавлять к названию инкрементируемое число, чтобы не путаться. Тестирование вряд ли ограничится двумя-тремя сборками.

image loader

Копируем ссылку на приложение, открываем на устройстве и устанавливаем.

Готовим обновление.

Чтобы имитировать обновление, нужно повторить шаги 2–4 со следующими нюансами:

номер сборки (versionCode) должен быть больше номера установленной сборки;

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

устанавливать сборку не нужно. Надо перейти по ссылке и попасть на экран с кнопкой «Обновить» или Update.

Если не использовать Internal App Sharing — обновление будет недоступно.

Поддержка immediate-обновлений

Immediate-обновления почти полностью реализует Google. Нужно только запросить проверку на наличие новой версии. Если она есть, Google покажет пользователю полноэкранный баннер, загрузит, установит обновление и перезапустит приложение.

Создадим нативный модуль с методом проверки наличия обновления.

(Здесь и далее код на Kotlin и JavaScript)

Проверяем наличие новой версии. Если она есть, то запускаем обновление. APP_UPDATE_REQUEST_CODE — это числовая константа, определяющая наш запрос. С её помощью мы идентифицируем сигнал, если в процессе обновления произойдёт ошибка.

Позже добавим шлюз для передачи событий из натива в JS, чтобы в случае ошибки обновления можно было отправить событие и обрабатывать его в React Native.

Вызываем написанный метод из JS.

И вуаля — всё работает.

image loader

Поддержим установку обновления после сворачивания.

В процессе установки пользователь может свернуть приложение. Установка при этом должна продолжиться. А если пользователь вновь развернёт приложение, нужно убедиться, что процесс не остановился. Для этого добавим метод в нативном модуле:

Нужно вызывать его каждый раз, когда приложение становится активным. Для этого в React Native зарегистрируем слушателя изменений AppState :

Поддержка flexible-обновлений

Процесс flexible-обновлений выглядит так:

Google Play предлагает пользователю обновить приложение;

если пользователь соглашается, начинается фоновая загрузка обновления;

после загрузки мы предлагаем установить обновление с полноэкранной заставкой от Google Play и перезапуском приложения в конце;

eсли пользователь отказывается установить обновление в моменте, предлагаем ему повторно. Google рекомендует делать это на каждое разворачивание приложения, но тут воля ваша. Кстати, если приложение будет в фоне на момент завершения загрузки, то установка обновления произойдёт автоматически.

В отличие от immediate-обновлений, здесь придётся написать чуть больше кода.

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

Объявим слушателя для наблюдения за процессом.

Зарегистрируем его в методе checkForAppUpdate перед вызовом startUpdateFlowForResult :

Добавим трансляцию процесса обновления на уровень JS.

Добавим в наш модуль:

И вызовем новый метод в слушателе:

Добавим слушателя событий на стороне JS.

В JS-коде подпишемся на сообщения перед вызовом checkForAppUpdate :

Использованные здесь константы дублируются из нативного кода.

Отлично! Теперь мы запрашиваем обновление из React Native и следим за его статусом. Но после успешной загрузки ничего не произойдёт. Нужно инициировать установку.

Предложим установить обновление.

Для этого добавим сообщение в слушателя событий в JS:

Если пользователь согласится, нужно снова передать управление в натив и запустить установку обновления. Для этого добавляем и вызываем метод нативного модуля:

Обработаем отказ от установки.

Как я писал выше, в случае отказа от установки нужно напоминать пользователю об обновлении. Проще всего это делать при каждом переходе в приложение (будь то запуск или разворачивание). Тем более что в предыдущем разделе мы уже использовали этот триггер для проверки состояния immediate-обновления. Давайте добавим в нативный метод resumeUpdate проверку статуса обновления и, если оно остановится после загрузки, предложим обновиться:

Вот и всё. Обработка события на стороне React Native уже реализована, поэтому при получении статуса InstallStatus.DOWNLOADED пользователь вновь увидит предложение установить загруженное обновление.

image loader

Закругляюсь

Мы поддержали In-app обновления приложения двух видов — immediate, когда весь процесс под контролем Google, и flexible, в котором мы сами решаем, как показать пользователю прогресс и показывать ли вообще. Теперь можете решить, какой вид обновления лучше подходит вашему приложению. А может, вы захотите использовать оба варианта. Как бы то ни было, вся конфигурация управляется на стороне React Native, и писать нативный код больше не придётся.

Я рассказал про минимальную функциональность In-app обновлений. Ещё есть возможность управлять частотой показа баннера и приоритетами обновлений. Это легко поддержать на базе написанного нативного модуля. Просто эти опции выходят за рамки статьи.

Если захотите поддержать обязательные обновления в своём приложении, потребуется немного доработать код. Google рекомендует при отказе пользователя в стандартных баннерах показывать ему информационное сообщение, после которого запускать процесс обновления повторно.

Я намеренно не стал усложнять код в статье. Но рекомендую все методы взаимодействия с нативным модулем из React Native вынести в отдельный сервис, чтобы инкапсулировать всю логику в одном месте.

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

Источник

Понравилась статья? Поделить с друзьями:
Добавить комментарий
  • Как сделать успешный бизнес на ритуальных услугах
  • Выездной кейтеринг в России
  • Риски бизнеса: без чего не обойтись на пути к успеху
  • android studio не запускается приложение на эмуляторе
  • android studio не запускается приложение на телефоне