Here is my code.
AppUpdateManager appUpdateManager = AppUpdateManagerFactory.create(context);
Task<AppUpdateInfo> appUpdateInfoTask = appUpdateManager.getAppUpdateInfo();
Log.d(LOG_TAG, "App update?" + appUpdateInfoTask.isSuccessful()); // Line L
appUpdateInfoTask.addOnSuccessListener(appUpdateInfo -> { // Line K
Log.d(LOG_TAG, "APP UPDATE: checking new version... ");
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)) {
// Request the update.
showUpdateDialog(); // my customized update popup window
}
});
The app version on prod is 123. I changed the version to 122, 123 or 124, the Line L always returns FALSE, and the code jumps into Line K and triggers my code(showUpdateDialog).
Any ideas? What's the problem?
Thanks a lot.
Related
Configuration:-
Device :- Samsung s21 ultra with no sim!
OS - 12 (31 API level)
Package name = com.example.reactnativesimcardsmanager
Library Reference Link : https://github.com/odemolliens/react-native-sim-cards-manager/tree/develop
For Android code : https://github.com/odemolliens/react-native-sim-cards-manager/tree/develop/android/src/main/java/com/reactnativesimcardsmanager
Case 1
Settings:
When library support Target version = 31, compile version 31.
Example Target Version = 30 and compile version 30.
Result = When clicking on ACTIVATE ESIM
Getting permission popup “Allow ‘package name’ to access your phone’s eUICC Profile?
After clicking on “Allow” getting below error.
“2 EMBEDDED_SUBSCRIPTION_RESULT_ERROR - Can't add an Esim subscription”
Case 2
Settings:
When library support Target version = 31, compile version 31.
Example Target Version = 31 and compile version 31
Result= When clicking on ACTIVATE ESIM
Getting permission popup “Allow ‘package name’ to access your phone’s eUICC Profile? With below ERROR.
E/Error: 3 EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR - Can't setup eSim du to Activity error null
After clicking on “Allow” getting below error
“2 EMBEDDED_SUBSCRIPTION_RESULT_ERROR - Can't add an Esim subscription”
Reference link :- https://source.android.com/devices/tech/connect/esim-overview
public void setupEsim(ReadableMap config, Promise promise) {
initMgr();
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
promise.reject("0", "EuiccManager is not available or before Android 9 (API 28)");
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && mgr != null && !mgr.isEnabled()) {
promise.reject("1", "The device doesn't support a cellular plan (EuiccManager is not available)");
return;
}
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (!ACTION_DOWNLOAD_SUBSCRIPTION.equals(intent.getAction())) {
Log.e("Error", "3 Can't setup eSim du to wrong Intent:" + intent.getAction() + " instead of " + ACTION_DOWNLOAD_SUBSCRIPTION);
promise.reject("3", "Can't setup eSim du to wrong Intent:" + intent.getAction() + " instead of " + ACTION_DOWNLOAD_SUBSCRIPTION);
showAlert("Error", "3 Can't setup eSim du to wrong Intent:" + intent.getAction() + " instead of " + ACTION_DOWNLOAD_SUBSCRIPTION);
return;
}
int resultCode = getResultCode();
if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR && mgr != null) {
try {
promise.resolve(3);
PendingIntent callbackIntent = PendingIntent.getBroadcast(mReactContext, 3,
new Intent(ACTION_DOWNLOAD_SUBSCRIPTION), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
mgr.startResolutionActivity(mReactContext.getCurrentActivity(), 3, intent, callbackIntent);
} catch (Exception e) {
e.printStackTrace();
Log.e("Error", "3 EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR - Can't setup eSim du to Activity error" + e.getLocalizedMessage());
promise.reject("3", "EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR - Can't setup eSim du to Activity error" + e.getLocalizedMessage());
showAlert("Error", "3 EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR - Can't setup eSim du to Activity error" + e.getLocalizedMessage());
}
} else if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
promise.resolve(true);
Log.e("Success", "EMBEDDED_SUBSCRIPTION_RESULT_OK");
showAlert("Success", "Success : EMBEDDED_SUBSCRIPTION_RESULT_OK");
} else if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_ERROR) {
Log.e("Error", "2 EMBEDDED_SUBSCRIPTION_RESULT_ERROR - Can't add an Esim subscription");
promise.reject("2", "EMBEDDED_SUBSCRIPTION_RESULT_ERROR - Can't add an Esim subscription");
showAlert("Error", "2 EMBEDDED_SUBSCRIPTION_RESULT_ERROR - Can't add an Esim subscription");
int detailedCode = intent.getIntExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0);
Log.e("detailedCode", detailedCode+"");
} else {
Log.e("Error", "3 Can't add an Esim subscription due to unknown error, resultCode is:" + String.valueOf(resultCode));
promise.reject("3", "Can't add an Esim subscription due to unknown error, resultCode is:" + String.valueOf(resultCode));
showAlert("Error", "3 Can't add an Esim subscription due to unknown error, resultCode is:" + String.valueOf(resultCode));
}
}
};
mReactContext.registerReceiver(
receiver,
new IntentFilter(ACTION_DOWNLOAD_SUBSCRIPTION),
null,
null);
DownloadableSubscription sub = DownloadableSubscription.forActivationCode(
config.getString("confirmationCode"));
PendingIntent callbackIntent = PendingIntent.getBroadcast(
mReactContext,
0,
new Intent(ACTION_DOWNLOAD_SUBSCRIPTION),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
mgr.downloadSubscription(sub, true, callbackIntent);
}
What are the detailedCode and operationCode you are receiving in all cases? I have a similar issue but the detailedCode and operationCode are never set.
What is the correct way to remotely open Google Play from the phone on the watch (Wear OS)? I am trying this:
Intent intentOnWatch = new Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse("https://play.google.com/store/apps/details?id=\" + getPackageName()"));
RemoteIntent.startRemoteActivity(getApplicationContext(), intentOnWatch, null, null);
But nothing happens.
The last parameter is the nodeId. I left it as zero because the documentation says :
nodeId String: Wear OS node id for the device where the activity should be started. If null, and the current device is a watch, the activity will start on the companion phone device. Otherwise, the activity will start on all connected watch devices.
Source: https://developer.android.com/reference/com/google/android/wearable/intent/RemoteIntent#startremoteactivity
I could determine the nodeId, but it seems difficult to do. Plus in this case, starting the activity on all connected watch devices would be fine.
Is it possible to download a file using DownloadManager?
This example (sorry it's Kotlin) should work
val remoteActivityHelper =
RemoteActivityHelper(application, Dispatchers.IO.asExecutor())
val nodes = Wearable.getNodeClient(application).connectedNodes.await()
val nodeId = nodes.firstOrNull { it.displayName == "XXX" }?.id
if (nodeId == null) {
Toast.makeText(application, "No connected wear watch", Toast.LENGTH_SHORT).show()
} else {
try {
remoteActivityHelper.startRemoteActivity(
Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(
Uri.parse("https://www.bbc.co.uk/sounds/play/${programme.code}")
),
).await()
} catch (e: Exception) {
toaster.showToast("Unable to open mobile app: ${e.message}")
}
}
}
But the main thing in your example is that you are not checking the result of startRemoteActivity, it returns a ListenableFuture, so you could check for an error. In the example above, I'm using the .await() extension function which does the same thing.
There are more complete examples in https://github.com/android/wear-os-samples/blob/d18c489ff415aa0fbb25c260e3aacdf50f7716e3/WearVerifyRemoteApp/Application/src/main/java/com/example/android/wearable/wear/wearverifyremoteapp/MainMobileActivity.kt
I'm not sure about the exact implementation for Java, it's really messy with the Task and Future APIs mixed here. Maybe
RemoteActivityHelper remoteActivityHelper = new RemoteActivityHelper(application, executor);
NodeClient client = Wearable.getNodeClient(application);
client.getConnectedNodes().addOnSuccessListener(nodes -> {
if (nodes.size() > 0) {
String nodeId = nodes.get(0).getId();
ListenableFuture<Void> result = remoteActivityHelper.startRemoteActivity(
new Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(
Uri.parse("https://www.bbc.co.uk/sounds/play/${programme.code}")
)
, nodeId);
result.addListener(() -> {
try {
result.get();
} catch (Exception e) {
Toast.makeText(application, "Failed " + e, Toast.LENGTH_SHORT).show();
}
}, executor);
} else {
Toast.makeText(application, "No connected wear watch", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(failure -> {
Toast.makeText(application, "Unable to open mobile app: ${e.message}", Toast.LENGTH_SHORT).show();
});
I've been fighting with exatly the same problem last two days.
Works for me the next code:
Intent intent = new Intent(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
.setData(Uri.parse(PLAY_STORE_APP_URI));
for (Node node : nodesWithoutApp) {
RemoteActivityHelper remoteActivityHelper =
new RemoteActivityHelper(this, Executors.newSingleThreadExecutor());
remoteActivityHelper.startRemoteActivity(intent, node.getId());
}
It didn't work with RemoteIntent.startRemoteActivity for some reason.
I migrated Google Play Billing Library in Android Studio from 3.0.3 (was working fine) to 4.0.0.
I've checked my Google Play Billing and all seems OK and the SKU status is ACTIVE (no red flags).
I've tried my best to follow migration instructions # https://developer.android.com/google/play/billing/integrate#establish_a_connection_to_google_play
So far, all I can muster is an OK connection to Google Play Billing, that is, after onBillingSetupFinished() method, the BillingClient.BillingResponseCode.OK responds nicely, without error messages.
My problem begins somehere with the call to querySkuDetailsAsync(): There is no response here, not even an error notification. The google website puts a lot of stress emphasis on this call so I sense this is where the fun begins.
I have provided the sample code with the problem. I have used many many fixes from Stack Overflow but now I'm really really stuck and really need this to work.
My problem code below:
'''
/*
//Using the following library in build.graddle for app module
dependencies {
def billing_version = "4.0.0"
implementation "com.android.billingclient:billing:$billing_version"
}
*/
StringBuilder builder4SKUInfo;
private void get_Subscribe2_Characters() {
Subscribe2_Characters_Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//I Toggle Visibility of Views Here
billingClient.startConnection(new BillingClientStateListener() {
//Android Studio auto-prompts to generate onBillingSetupFinished & onBillingServiceDisconnected
#Override
public void onBillingSetupFinished(#NonNull BillingResult billingResultC) {
if (billingResultC.getResponseCode() == BillingClient.BillingResponseCode.OK) {
//BillingResponseCode is OK here: Works Just Fine!
//The problem starts below
String skuToSell = "MySKU_Character_001"; //In my project, the SKU is cut-pasted from Google Play Console
List<String> skuList = new ArrayList<> ();
skuList.add(skuToSell);
SkuDetailsParams.Builder params = SkuDetailsParams
.newBuilder()
.setSkusList(sku_Details) //
.setType(BillingClient.SkuType.SUBS);
billingClient.querySkuDetailsAsync(params.build(),
new SkuDetailsResponseListener() {
#Override
public void onSkuDetailsResponse(#NonNull BillingResult billingResult, #NonNull List<SkuDetails> PurchaseDetailsList) {
//NOTHING! Not getting BillingResult
//Problem seems to at this point
if (PurchaseDetailsList.size() > 0) {
//NOTHING! Not getting size
for (SkuDetails PurchaseSKU_Info : PurchaseDetailsList) {
builder4SKUInfo = new StringBuilder(300);
if (PurchaseSKU_Info.getSku().contains("MySKU_Character_001")) {
String getSKUInfo = (
"\nTitle [Query]: " + PurchaseSKU_Info.getTitle()
+ "\n\nDetails: " + PurchaseSKU_Info.getDescription()
+ "\n\nDuration: " + PurchaseSKU_Info.getSubscriptionPeriod()
+ "\n\nPrice" + PurchaseSKU_Info.getPrice()
+ "\n\nAvoid Problems:\nUpdated Subscription Settings on Google Play"
+ "\n\nIMPORTANT: NOT Transferable"
+ "\n\n For this device only\n");
//+ "\nOther SKUs: " + SKU_Info.getSku()
//"001 = " + billingResultB.getResponseCode()
//+ "\nList Size: " + PurchaseDetailsList.size());
builder4SKUInfo.append(getSKUInfo); //The result I need to use elsewhere
}
}
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_ALREADY_OWNED) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.ITEM_NOT_OWNED) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
//Do something about cancels
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.BILLING_UNAVAILABLE) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_DISCONNECTED) {
//No Google Play response for this
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.SERVICE_TIMEOUT) {
//No Google Play response for this
} else {
//Following Toast does not show
String SomethingWrong = "Somethings is Wrong" +
"\nUpdate Your Google Play Billing Info" +
"\nCheck Internet Connection";
Toast.makeText(KH.this, SomethingWrong, Toast.LENGTH_LONG).show();
}
}
});
}
}
#Override
public void onBillingServiceDisconnected() {
//Following Toast does not show
String BillingServiceDisconnected = "Billing Service Disconnected" +
"\nUpdate Your Google Play Billing Info" +
"\nCheck Internet Connection";
Toast.makeText(KH.this, BillingServiceDisconnected, Toast.LENGTH_LONG).show();
}
});
}
});
}
'''
So I braved to ask the folks at Google on the issue tracker page and they appropriately and promptly responded, "We now post the results to the background thread instead of the UIThread . . ."
Right away, I knew I had the wrong approach. If the result is delivered to background thread, I had to ditch the 3.x billing approach and start from scratch.
I reached back again to Google for an example and they sent me their GitHub # https://github.com/android/play-billing-samples/tree/main/TrivialDriveJava
The example is akin to an "Intent" but with a lot more code declaration than function selection: has several classes, methods and files to work through. So to fix billing 4.x, the easiest path was to just rip the example into my app, whittled down the errors, gray out methods I don't need and finally overlay my views, refactor classes (fix errors again) and create new user workflows.
Following #Maasaivatar's answer, it works after running the SkuDetailsResponseListener on the main thread:
billingClient.querySkuDetailsAsync(params.build(), (billingResult, list) ->
runOnUiThread(() -> {
// same code as before
}));
I've problem with max repeat count for request that returns error. In my case I need to send a new request to backend, if previous was falling, but 3 time max. I'm using option to get result, that returns data or error:
fun sendRequest(): Single<Option<BeResponse>
Inside of sendRequest() function I transform it from Either<ApiError, Data> to BeResponse
data class BeReponse(
val error: ApiError? = null,
val data: Data? = null
)
When I'm trying to get data I need to send request if previous one failed. So I'm using:
sendRequest().flatMap{ option ->
option.map {
if (option.error != null) {
Single.just(null.toOption())
} else {
Single.just(it)
}
}.getOrElse{ sendRequest() }
}
But in case when I have error I will call sendRequest endless. How I can set max attempts count for it?
create temp var, then increase it when the program do looping..
See this
var temp = 0
sendRequest().flatMap{ option ->
option.map {
if (option.error != null) {
Single.just(null.toOption())
} else {
Single.just(it)
}
}.getOrElse{
if (temp < 3){
sendRequest()
temp = temp+1
} else{
//do your code to take action
}
}
My app is making repeat API requests to download chunks of data. Unfortunately the server limits the API requests to 3 per second. In the code below, how can I rate limit the requests to X per second ?
private void getHistoricalPrices(String currency, String start_date, String end_date, int granularity){
// show download status
currentDlPageIndex++;
view.showDownloadingStatus(currentDlPageIndex, totalDlPages);
// make the API call
addDisposable(
model.isNetworkAvailable()
.doOnSuccess(isNetworkAvailable -> {
if (!isNetworkAvailable) {
showErrorMessage();
Timber.v("no internet");
}
})
.filter(isNetworkAvailable -> true)
.flatMapSingle(isNetworkAvailable -> model.getHistoricalPrices(currency, start_date, end_date, String.valueOf(granularity)))
.subscribeOn(rxSchedulers.io())
.observeOn(rxSchedulers.mainThread())
.subscribe((Response<List<List<String>>> responseData) -> {
if (responseData != null && responseData.code() == HTTP_OK) {
List<List<String>> response = responseData.body();
if (response != null) {
// create list from downloaded data
ArrayList<HistoricPrice> tmpHistoricPriceList = new ArrayList<>(response.size());
for (List<String> rawHistoricPrice : response) {
HistoricPrice historicPrice = new HistoricPrice(rawHistoricPrice.get(0), rawHistoricPrice.get(1), rawHistoricPrice.get(2), rawHistoricPrice.get(3), rawHistoricPrice.get(4), rawHistoricPrice.get(5));
tmpHistoricPriceList.add(0, historicPrice);
}
// add the downloaded list to the main list being recreated
model.historicPriceList.addAll(tmpHistoricPriceList);
Timber.d("added %d records to memory", response.size());
// if there's more to download, download another chunk
if (intermediateDateSecs != null && intermediateDateSecs < endDateSecs){
startDateSecs = tmpHistoricPriceList.get(tmpHistoricPriceList.size()-1).time + granularity;// add "granularity" to startDateSecs to avoid getting two exact data for the same time
Date startDate = new Date(startDateSecs * 1000L);//requires milliseconds, not epoch
String startStrDate = DateUtils.fromDateToString(startDate);
intermediateDateSecs = startDateSecs + ((ApiService.MAX_HISTORIC_RETURN_VALUES - 1) * granularity);
if (intermediateDateSecs > endDateSecs) intermediateDateSecs = endDateSecs;
Date intermediateDate = new Date(intermediateDateSecs * 1000L);
String intermediateStrDate = DateUtils.fromDateToString(intermediateDate);
getHistoricalPrices(currency, startStrDate, intermediateStrDate, granularity);
} else {
// no more to download, save data
Timber.d("downloaded total of %d records", model.historicPriceList.size());
view.hideDownloadingStatus();
showSaveDataMessage();
}
}
}
}, error -> {
showErrorMessage();
})
);
}
you can see that the method getHistoricalPrices() calls itself to continue downloading. This implementation works well, except for the server complaining when there's too many API requests per second.
You can perform your request every X milliseconds / seconds like so:
long interval = 0L; // Your desired interval here in milliseconds
Observable.interval(0, interval, TimeUnit.MILLISECONDS)
.takeUntil(x -> isTaskComplete == true)
// Other configuration calls go here
.subscribe(); // make request here or in doOnNext()