Недавно я столкнулся с новым типом потока обновлений приложений, предоставляемым Google Play API. Мне понравился этот плавный процесс обновления приложения для Android. Я наблюдал следующие шаги в приложении Hotstar.
Как я могу этого добиться? Должен быть способ связи с Google Play. Я просмотрел множество блогов. Но, не нашел никакого решения. Это может быть отличной функцией для разработчика, если автоматическое обновление приложения отключено пользователем.
Вы также можете сослаться на этот ответ stackoverflow.com/questions/15213211/…
Я предполагаю, что им управляет само приложение, а не Google Play. Я разработал приложения, которые при запуске вызывают вызов API, чтобы прочитать «последний» номер версии и указать, является ли эта версия «обязательным» обновлением или нет, и сравнить его с работающей версией приложения. Если доступна новая версия, пользователю предоставляется диалоговое окно, похожее на то, которое вы показали (хотя их диалоговое окно намного лучше), предупреждающее пользователя о наличии обновления. Если обновление является «обязательным», то в сообщении сообщается, что они должны обновить приложение, прежде чем продолжить. Если они нажимают «Обновить», они переходят на страницу App Store, где они начинают загрузку обновления, как обычно, и приложение закрывается. Если они нажмут «Закрыть», приложение просто закроется. Если обновление не является обязательным, их спрашивают, хотят ли они выполнить обновление сейчас или продолжить. Если они нажимают «Обновить», они переходят на страницу App Store, где они начинают загрузку обновления, как обычно, и приложение закрывается. Если они нажмут «Продолжить», они просто перейдут в существующую версию приложения.
Я не уверен, как они управляли фоновой загрузкой, а затем запускали обновление приложения перед выходом из приложения. Это было бы очень хорошо, но наш метод, описанный выше, также очень прост и дает разработчику много возможностей.
Спасибо за ваше время. Мы можем легко разработать это в приложении. Но вы должны привести пользователя в Play Store. Однако, если вы можете догадаться, есть некоторые обратные вызовы из Google Play. Вот почему они могут показывать SnackBar по завершении загрузки.
Прохладный! Похоже, Google Play сейчас тестирует API обновлений в приложении с некоторыми партнерами. Хотел бы проверить, когда он станет доступен... android-developers.googleblog.com/2018/11/…
Google тестирует раннюю версию API обновлений в приложениях, как описано в статье этот пост в блоге.
Сейчас он доступен только для некоторых партнеров по раннему тестированию, но в конечном итоге он должен быть доступен для всех разработчиков. Следите за блогом разработчиков Android и за объявлениями в консоли Play.
Сегодня Android официально объявил об обновлениях в приложении для всех.
https://developer.android.com/guide/playcore/in-app-updates
Обновлять: Обработка как НЕМЕДЛЕННЫХ, так и ГИБКИХ обновлений в одном действии; Котлинский способ.
import android.app.Activity
import android.content.Intent
import android.content.IntentSender
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.google.android.material.snackbar.Snackbar
import com.google.android.play.core.appupdate.AppUpdateManager
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
import com.google.android.play.core.install.InstallState
import com.google.android.play.core.install.InstallStateUpdatedListener
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import timber.log.Timber
class BaseUpdateCheckActivity : AppCompatActivity() {
private val appUpdateManager: AppUpdateManager by lazy { AppUpdateManagerFactory.create(this) }
private val appUpdatedListener: InstallStateUpdatedListener by lazy {
object : InstallStateUpdatedListener {
override fun onStateUpdate(installState: InstallState) {
when {
installState.installStatus() == InstallStatus.DOWNLOADED -> popupSnackbarForCompleteUpdate()
installState.installStatus() == InstallStatus.INSTALLED -> appUpdateManager.unregisterListener(this)
else -> Timber.d("InstallStateUpdatedListener: state: %s", installState.installStatus())
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_ad_view)
checkForAppUpdate()
}
private fun checkForAppUpdate() {
// Returns an intent object that you use to check for an update.
val appUpdateInfoTask = appUpdateManager.appUpdateInfo
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE) {
// Request the update.
try {
val installType = when {
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE) -> AppUpdateType.FLEXIBLE
appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE) -> AppUpdateType.IMMEDIATE
else -> null
}
if (installType == AppUpdateType.FLEXIBLE) appUpdateManager.registerListener(appUpdatedListener)
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
installType!!,
this,
APP_UPDATE_REQUEST_CODE)
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == APP_UPDATE_REQUEST_CODE) {
if (resultCode != Activity.RESULT_OK) {
Toast.makeText(this,
"App Update failed, please try again on the next app launch.",
Toast.LENGTH_SHORT)
.show()
}
}
}
private fun popupSnackbarForCompleteUpdate() {
val snackbar = Snackbar.make(
findViewById(R.id.drawer_layout),
"An update has just been downloaded.",
Snackbar.LENGTH_INDEFINITE)
snackbar.setAction("RESTART") { appUpdateManager.completeUpdate() }
snackbar.setActionTextColor(ContextCompat.getColor(this, R.color.accent))
snackbar.show()
}
override fun onResume() {
super.onResume()
appUpdateManager
.appUpdateInfo
.addOnSuccessListener { appUpdateInfo ->
// If the update is downloaded but not installed,
// notify the user to complete the update.
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate()
}
//Check if Immediate update is required
try {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
APP_UPDATE_REQUEST_CODE)
}
} catch (e: IntentSender.SendIntentException) {
e.printStackTrace()
}
}
}
companion object {
private const val APP_UPDATE_REQUEST_CODE = 1991
}
}
Исходный текст: https://gist.github.com/saikiran91/6788ad4d00edca30dad3f51aa47a4c5c
как решить, является ли доступное обновление IMMEDIATE или FLEXIBLE, можем ли мы установить эти флаги в консоли разработчика Google Play, чтобы клиентское устройство знало, должно ли оно обновляться немедленно.
То, о чем думали изначально и создали суть, но оказалось, что мы не можем установить флаг в магазине Google Play; вместо этого я использовал удаленную конфигурацию firebase для завершения процесса. Я обновлю суть через некоторое время. Спасибо. @МухаммадСакиб
После комментариев я провел небольшое исследование и обнаружил, что они действительно планируют реализовать такую функцию. Вскоре они представят функцию обновления «Приоритет» в консоли разработчика, и API ядра игры сможет считывать этот приоритет. youtu.be/_o_q6hatcIs?t=577
Для тех, кто хочет реализовать этот флаг, который Google еще не выпустил, кажется, что следующим лучшим вариантом является Firebase Remote Config.
Шаг 1: Добавьте зависимость (build.gradle (приложение)):
dependencies {
implementation 'com.google.android.play:core:1.7.3'
...
}
Шаг 2. Проверьте наличие обновлений и запустите, если они доступны
private AppUpdateManager mAppUpdateManager;
private static final int RC_APP_UPDATE = 11;
В методе onStart():
mAppUpdateManager = AppUpdateManagerFactory.create(this);
mAppUpdateManager.registerListener(installStateUpdatedListener);
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE /*AppUpdateType.IMMEDIATE*/)){
try {
mAppUpdateManager.startUpdateFlowForResult(
appUpdateInfo, AppUpdateType.FLEXIBLE /*AppUpdateType.IMMEDIATE*/, MainActivity.this, RC_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
} else if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED){
//CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip
popupSnackbarForCompleteUpdate();
} else {
Log.e(TAG, "checkForAppUpdateAvailability: something else");
}
});
Шаг 3. Прослушайте состояние обновления
InstallStateUpdatedListener installStateUpdatedListener = new
InstallStateUpdatedListener() {
@Override
public void onStateUpdate(InstallState state) {
if (state.installStatus() == InstallStatus.DOWNLOADED){
//CHECK THIS if AppUpdateType.FLEXIBLE, otherwise you can skip
popupSnackbarForCompleteUpdate();
} else if (state.installStatus() == InstallStatus.INSTALLED){
if (mAppUpdateManager != null){
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
}
} else {
Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus());
}
}
};
Шаг 4. Получите обратный вызов, чтобы узнать статус обновления
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
if (resultCode != RESULT_OK) {
Log.e(TAG, "onActivityResult: app download failed");
}
}
}
Шаг 5. Гибкое обновление
private void popupSnackbarForCompleteUpdate() {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.coordinatorLayout_main),
"New app is ready!",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("Install", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
snackbar.show();
}
Шаг 6: Не забудьте отменить регистрацию слушателя (в методе onStop)
if (mAppUpdateManager != null) {
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
}
Примечание. Добавьте этот прослушиватель в любое действие в вашем приложении, предпочтительно в MainActivity (домашняя страница).
Для тестирования можно использоватьFakeAppUpdateManager
Ограничение: Обновление из приложения работает только с устройствами под управлением Android 5.0 (уровень API 21) или выше.
Официальная документация:https://developer.android.com/guide/playcore/in-app-updates
Судя по всему, документация Google содержит ошибки. appUpdateInfo объявлен как Task, поэтому вы не можете получить доступ к методам-членам .updateAvailability() и .isUpdateTypeAllowed(). Кто-то рекомендовал ссылаться на appUpdateInfo внутри Listener, но единственные примеры, которые я смог найти, были в Kotlin...
@MichaelDougan Да, я заметил некоторые ошибки в документации. Я работал над этими ошибками и реализовал их в одном из своих приложений. Он работает нормально.
Я запускаю некоторые тесты, но я не могу заставить их работать. Я всегда получаю в ответ UpdateAvailability.UPDATE_NOT_AVAILABLE. Есть ли что-то, что я пропустил?
@kevintresuelo Я думаю, ваше приложение должно быть опубликовано в магазине Play. Для тестирования уменьшите код версии тестового приложения по сравнению с опубликованным кодом версии и попробуйте.
@PratikED Братан, я не могу работать с вашим кодом, когда я просто вставляю и запускаю ваш код на своем заставке. Он всегда выдает мне ошибку Что-то другое. Можете ли вы помочь мне найти ошибку в моем приложении.
@BlackBlind Зарегистрируйте appUpdateInfo.installStatus() внутри оператора else. Вы можете найти проблему.
Бро, я сделал, но не могу найти ничего нового, можешь ли ты дать мне образец приложения для этого.
@PratikED вы проверяете НЕМЕДЛЕННО, а не ГИБКО
@kevintresuelo, можешь ли ты найти решение своей проблемы?
@vishalpatel да, проверьте мой ответ на stackoverflow.com/a/56319077/3686003
Что делать, если мобильная версия Android меньше 5.0, нужно ли нам писать отдельную реализацию для обработки этих обновлений?
@Satyam Этого можно добиться, добавив код версии вашего приложения в таблицу на вашем сервере. Этот код версии вы увеличиваете вручную, когда возникает необходимость в принудительном обновлении приложения. Вы получаете код версии каждый раз, когда пользователь запускает приложение, и сравниваете его с кодом версии установленного приложения. Чтобы обновить приложение, вы перенаправляете пользователя в Play Store.
Как узнать или установить тип обновления - гибкое/немедленное?
Привет, я тоже пытаюсь это сделать, но я не могу заставить его работать. Я всегда получаю в ответ UpdateAvailability.UPDATE_NOT_AVAILABLE. Это уже доступно для общественности?
@harminder-singh согласно исследованиям, гибкость/немедленность определяется пользователем. вы можете использовать RemoteConfig, чтобы указать, хотите ли вы, чтобы он был гибким или немедленным.
@heychar firebase — это другой подход, когда мне нужно обновить версию вручную. Я спрашиваю об этом inappupdate, предоставленном разработчиками Android developer.android.com/guide/app-bundle/in-app-updates
вы можете попробовать fakeUpdateManager для проверки
Почему многие люди просто пытаются/ловят startUpdateFlowResult и просто печатают трассировку стека? Вам это не нужно для успеха или это просто шумный вызов функции?
Я новичок в студии Android. Пожалуйста, помогите мне с тем, где эти сегменты кода должны быть добавлены.
@BumuthuDilshan Я обновил ответ! Надеюсь, что это поможет вам.
любой способ скрыть всплывающее сообщение при использовании гибкого обновления? Разве мы не можем просто запустить гибкое обновление, не показывая это сообщение об обновлении? Ведь смысл гибкого обновления в том, чтобы не беспокоить пользователя.
@MuhammadSaqib Пользователь может начать установку по своему усмотрению. Вот почему кнопка должна отображаться для начала установки.
@PratikED Я обрабатываю installation часть в следующий раз, когда пользователь запускает приложение. В настоящее время, когда доступно обновление, я запускаю поток с гибким открытием. Вы нажимаете кнопку обновления, и обновление загружается в фоновом режиме. (no UI no progress bar). Когда пользователь возвращается в приложение, я выполняю установку. Вот почему я спрашиваю, есть ли способ запустить обновление без этого всплывающего окна.
@PratikED Я реализовал тот же код, но не показывал никаких всплывающих окон. любая помощь спасибо
@PratikED Меня беспокоит только гибкий поток, а что, если пользователь перейдет на другой экран во время загрузки apk? Появится ли закусочная в новом действии? или мы можем как-то показать alertdailog поверх всех действий? или в API-интерфейсе приложения есть другой способ уведомить пользователя после загрузки apk?
При попытке реализовать это официальная документация Google, цитируемая в принятом ответе, синтаксически неверна. Потребовалось некоторое исследование, но я наконец нашел правильный синтаксис:
Вместо:
// Creates an instance of the manager.
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);
// Returns an intent object that you use to check for an update.
Task<AppUpdateInfo> appUpdateInfo = appUpdateManager.getAppUpdateInfo();
// Checks that the platform will allow the specified type of update.
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// For a flexible update, use AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// Request the update.
appUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
AppUpdateType.IMMEDIATE,
// The current activity making the update request.
this,
// Include a request code to later monitor this update request.
MY_REQUEST_CODE);
}
Сделай это:
private AppUpdateManager appUpdateManager;
...
// onCreate(){
// Creates instance of the manager.
appUpdateManager = AppUpdateManagerFactory.create(mainContext);
// Don't need to do this here anymore
// Returns an intent object that you use to check for an update.
//Task<AppUpdateInfo> appUpdateInfo = appUpdateManager.getAppUpdateInfo();
appUpdateManager
.getAppUpdateInfo()
.addOnSuccessListener(
appUpdateInfo -> {
// Checks that the platform will allow the specified type of update.
if ((appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE)
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE))
{
// Request the update.
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
REQUEST_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
Затем добавьте аналогичный фрагмент кода в переопределение onResume() на случай, если установка зависнет по пути:
//Checks that the update is not stalled during 'onResume()'.
//However, you should execute this check at all entry points into the app.
@Override
protected void onResume() {
super.onResume();
appUpdateManager
.getAppUpdateInfo()
.addOnSuccessListener(
appUpdateInfo -> {
if (appUpdateInfo.updateAvailability()
== UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
try {
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.IMMEDIATE,
this,
REQUEST_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
}
В чем ценность REQUEST_APP_UPDATE ?
@CoryRobinson это что угодно =)
Пожалуйста, попробуйте это один раз. Официальный документ для справки
Шаг 1: В файле build.gradle добавьте указанную ниже библиотеку (пожалуйста, проверьте и обновите последнюю версию плагина игрового кода)
implementation 'com.google.android.play:core:1.6.4'
Шаг 2: Объявите следующие переменные в классе (Ex MainActivity.java)
private AppUpdateManager mAppUpdateManager;
private int RC_APP_UPDATE = 999;
private int inAppUpdateType;
private com.google.android.play.core.tasks.Task<AppUpdateInfo> appUpdateInfoTask;
private InstallStateUpdatedListener installStateUpdatedListener;
Шаг 3: В методе onCreate() добавьте приведенный ниже код (инициализация переменных)
// Creates instance of the manager.
mAppUpdateManager = AppUpdateManagerFactory.create(this);
// Returns an intent object that you use to check for an update.
appUpdateInfoTask = mAppUpdateManager.getAppUpdateInfo();
//lambda operation used for below listener
//For flexible update
installStateUpdatedListener = installState -> {
if (installState.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
};
mAppUpdateManager.registerListener(installStateUpdatedListener);
Шаг 4: В методе onDestroy() просто отмените регистрацию слушателя
@Override
protected void onDestroy() {
mAppUpdateManager.unregisterListener(installStateUpdatedListener);
super.onDestroy();
}
Шаг 5: В onResume() нам нужно прослушивать как гибкие, так и немедленные обновления с помощью приведенного ниже кода.
@Override
protected void onResume() {
try {
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
if (appUpdateInfo.updateAvailability() ==
UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
// If an in-app update is already running, resume the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
inAppUpdateType,
this,
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException e) {
e.printStackTrace();
}
}
});
mAppUpdateManager.getAppUpdateInfo().addOnSuccessListener(appUpdateInfo -> {
//For flexible update
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
popupSnackbarForCompleteUpdate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
super.onResume();
}
Шаг 6: В onActivityResult() нам нужно обрабатывать действия кликов пользователя (только для гибкого обновления)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_APP_UPDATE) {
//when user clicks update button
if (resultCode == RESULT_OK) {
Toast.makeText(MainActivity.this, "App download starts...", Toast.LENGTH_LONG).show();
} else if (resultCode != RESULT_CANCELED) {
//if you want to request the update again just call checkUpdate()
Toast.makeText(MainActivity.this, "App download canceled.", Toast.LENGTH_LONG).show();
} else if (resultCode == RESULT_IN_APP_UPDATE_FAILED) {
Toast.makeText(MainActivity.this, "App download failed.", Toast.LENGTH_LONG).show();
}
}
}
Шаг 7: Создайте метод проверки доступности обновления и запустите обновление (немедленное обновление)
private void inAppUpdate() {
try {
// Checks that the platform will allow the specified type of update.
appUpdateInfoTask.addOnSuccessListener(new OnSuccessListener<AppUpdateInfo>() {
@Override
public void onSuccess(AppUpdateInfo appUpdateInfo) {
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
// For a flexible update, use AppUpdateType.FLEXIBLE
&& appUpdateInfo.isUpdateTypeAllowed(inAppUpdateType)) {
// Request the update.
try {
mAppUpdateManager.startUpdateFlowForResult(
// Pass the intent that is returned by 'getAppUpdateInfo()'.
appUpdateInfo,
// Or 'AppUpdateType.FLEXIBLE' for flexible updates.
inAppUpdateType,
// The current activity making the update request.
MainActivity.this,
// Include a request code to later monitor this update request.
RC_APP_UPDATE);
} catch (IntentSender.SendIntentException ignored) {
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Шаг 8: Точно создайте диалоговое окно с закусками или любое оповещение, чтобы показать пользователю, что гибкое обновление загружено и готово к обновлению (для запуска обновления требуется действие — только для гибкого обновления)
private void popupSnackbarForCompleteUpdate() {
try {
Snackbar snackbar =
Snackbar.make(
findViewById(R.id.id_of_root_loyout),
"An update has just been downloaded.\nRestart to update",
Snackbar.LENGTH_INDEFINITE);
snackbar.setAction("INSTALL", view -> {
if (mAppUpdateManager != null){
mAppUpdateManager.completeUpdate();
}
});
snackbar.setActionTextColor(getResources().getColor(R.color.install_color));
snackbar.show();
} catch (Resources.NotFoundException e) {
e.printStackTrace();
}
}
Шаг 9. Теперь вызовите метод с типом обновления в приложении (гибкое или немедленное) в любом месте, где вы хотите начать проверку обновлений.
//For Immediate
inAppUpdateType = AppUpdateType.IMMEDIATE; //1
inAppUpdate();
//For Flexible
inAppUpdateType = AppUpdateType.FLEXIBLE; //0
inAppUpdate();
Очки, чтобы помнить:
Гибкое обновление будет загружено первым, затем оно уведомит пользователя о том, что загрузка завершена, после чего пользователь должен начать обновление (параметры, указанные выше на шаге 8).
В консоли Google Play есть возможность протестировать совместное использование в приложении, просто мы можем загрузить обычный apk (подписанный apk не требуется) для тестирования. https://support.google.com/googleplay/android-developer/answer/9303479?hl=ru
Необходимо включить опцию обмена внутри приложения в приложении Play Store на тестовом устройстве. Как включить внутренний общий доступ к приложениям для Android?
Тем не менее, любая проблема в магазине игр, просто очистите кеш и очистите данные, затем перезагрузите устройство один раз и попробуйте.
вы всегда получаете false для 3. условия (resultCode == RESULT_IN_APP_UPDATE_FAILED) в onActivityResult() из-за неправильной логики в 2.условии. Ваше 2. условие должно быть следующим (resultCode == RESULT_CANCELED)
Попробуйте эти библиотеки, которые можно реализовать всего за несколько строк кода.
Он называется In-App Updates API и тестируется выбранными партнерами. Я тоже этого жду. К сожалению, Google любит объявлять о функциях и продуктах, выпуск которых занимает целую вечность.