I'm working with the Google Billing Library Version 5. I've pretty
much gotten my code to do the right thing. I tested it on hardware
devices running API levels 16, 23, 28, and 32.
Then I did a factory reset on the device running API level 23. I
wanted to establish a brand new Google ID to test the billing library.
It's a Samsung Galaxy S5 running Android M. Purchasing no longer
works. billingClient.isReady returns false, and
billingClient.isFeatureSupported returns SERVICE_DISCONNECTED.
onBillingServiceDisconnected is not being called.
billingClient.queryPurchasesAsync() does work. It returns an OK response
code and reports zero purchases.
My code runs fine on my other devices, and it used to run fine on the
Galaxy S5 until I reset it and gave it a new user ID.
https://developer.android.com/google/play/licensing/setting-up says I
should be running on a device that has the Google Play client
pre-installed. The Applications Manager shows a Google Play Store app
with the same version number as my other devices. Ditto for Google
Play Services. The Play Store app tells me it is up to date.
What am I missing?
public void doPurchase( Activity activity )
{
billingClient = BillingClient.newBuilder(activity).enablePendingPurchases()
.setListener(this).build();
if ( ! billingClient.isReady() )
Log.i( appName, "Billing client is not ready" );
BillingResult billingResult = billingClient.isFeatureSupported( BillingClient.FeatureType.PRODUCT_DETAILS );
if ( billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK )
{
Log.i( appName, String.format( "feature not supported %s", billingResult.toString() ) );
return;
}
billingClient.startConnection(new BillingClientStateListener()
{
public void onBillingSetupFinished( #NonNull BillingResult billingResult )
{
if ( billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK )
{
Log.i( appName, String.format( "billing setup response code %s", billingResult.toString() ) );
return;
}
launchPurchase( activity );
}
public void onBillingServiceDisconnected()
{
Log.i( appName, "Disconnected" );
}
});
}
private void launchPurchase( final Activity activity )
{
ImmutableList<QueryProductDetailsParams.Product> productList
= ImmutableList.of( QueryProductDetailsParams.Product.newBuilder()
.setProductId( SKU_BUY )
.setProductType( BillingClient.ProductType.INAPP)
.build());
QueryProductDetailsParams.Builder builder = QueryProductDetailsParams.newBuilder();
builder.setProductList( productList );
billingClient.queryProductDetailsAsync( builder.build(),
new ProductDetailsResponseListener() {
#Override
public void onProductDetailsResponse( #NonNull BillingResult billingResult,
#Nullable List<ProductDetails> list ) {
if ( billingResult.getResponseCode() != BillingClient.BillingResponseCode.OK )
{
Log.i( appName, String.format( "product details response code %s", billingResult.toString() ) );
return;
}
if ( list == null || list.size() <= 0 )
{
Log.i( appName, "no products found" );
return;
}
ImmutableList<BillingFlowParams.ProductDetailsParams> deetsParams = ImmutableList.of(
BillingFlowParams.ProductDetailsParams.newBuilder()
.setProductDetails( list.get( 0 ) )
.build() );
BillingFlowParams purchaseParams = BillingFlowParams.newBuilder()
.setProductDetailsParamsList( deetsParams )
.build();
billingClient.launchBillingFlow( activity, purchaseParams );
}
} );
}
I done this according to Xamarin documentation, just copy-pasted a code.
So, in App.xaml.cs I have a code like this:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
protected override void OnStart()
{
if (!CheckPermisions())
{
AbortApp(3, "Missing required permissions!");
return ;
}
}
//[...]
public bool CheckPermisions()
{
Task<bool> v = performCheckPermisions();
if (v.Result)
initAppFolders();
return v.Result;
}
protected async Task<bool> performCheckPermisions()
{
// storage read
PermissionStatus status = await Xamarin.Essentials.Permissions.CheckStatusAsync<Permissions.StorageRead>();
if (status == PermissionStatus.Denied)
{
this.Context.ToLogger(EAppLogLevel.Warning, string.Format(" ! StorageRead: requesting..."));
status = await Permissions.RequestAsync<Permissions.StorageRead>();
}
if (status == PermissionStatus.Denied)
return false;
// storage write
status = await Xamarin.Essentials.Permissions.CheckStatusAsync<Permissions.StorageWrite>();
if (status == PermissionStatus.Denied)
{
this.Context.ToLogger(EAppLogLevel.Warning, string.Format(" ! StorageWrite: requesting..."));
status = await Permissions.RequestAsync<Permissions.StorageWrite>();
}
if (status == PermissionStatus.Denied)
return false;
return true; // Task.FromResult(true);
}
The problem is - when application started 1st time, so when the OS asks user for permissions it always hangs! :-(
And I do not understand - why?!
How to resolve this problem with hanging on 1st app run?
I tried to debug it but it never returns from await Permissions.RequestAsync<...>() back into debugger! :-
Of course - on OS request I clicked [Allow] in a UI prompt.
Unfortunately, I'm not sure - why it is not returning, it might be bug in VS2019 debugger or it might be bug in Xamarin... or maybe I'm doing something wrong.
Could you please advice?
Please note: I need exactly the blocking/synchronous call to permissions request! Application must stop and confirm if permissions granted. Without permissions - it must not even try to run.
Note: VS 2019 (16.6.5); Xamarin.Forms 4.7.0.1142; Xamarin.Essentials 1.5.3.2 - so, it seems all the latest.
Thanks.
PS.
Also I tried following variants:
Attempt# 1
Task<bool> tsk = performCheckPermisions();
bool result = false;
if (tsk.IsCompleted)
{
this.Context.ToLogger(EAppLogLevel.Info, string.Format(" . CheckPermisions: task completed without waiting..."));
result = tsk.Result;
}
else
{
TaskAwaiter<bool> aw = tsk.GetAwaiter();
int counter = 0;
while (!aw.IsCompleted)
{
Thread.Sleep(330);
counter++;
if ((counter % 10) == 0)
this.Context.ToLogger(EAppLogLevel.Info, string.Format(" . CheckPermisions: still waiting (#{0})...", counter));
if (counter > 100)
{
AbortApp(99, "Permissions were not comfirmed!");
return false;
}
}
result = aw.GetResult();
}
It simply hang because nor tsk.IsCompleted, nor aw.IsCompleted never became true despite user clicks to [Allow] button.
Attempt# 2
var task = Task.Run(async () => await performCheckPermisions());
if (task.IsFaulted && task.Exception != null)
{
throw task.Exception;
}
bool result = task.Result;
this.Context.ToLogger(EAppLogLevel.Info, string.Format(" ? CheckPermisions: {0}", result));
It reported System.AggregateException exception: Message=One or more errors occurred. (Permission request must be invoked on main thread.); Source=mscorlib.
Attempt# 3
bool result = false;
this.isCompleted = false;
MainThread.BeginInvokeOnMainThread(
async () => {
result = await performCheckPermisions();
this.isCompleted = true;
}
);
int counter = 0;
while (!this.isCompleted)
{
Thread.Sleep(330);
counter++;
if ((counter % 10) == 0)
this.Context.ToLogger(EAppLogLevel.Info, string.Format(" . CheckPermisions: still waiting (#{0})...", counter));
if (counter > 100)
{
AbortApp(99, "Permissions were not comfirmed within specified timeout!");
return false;
}
}
It simply hang. It seems there is bug in Xamarin - the await Permissions.RequestAsync<>() call never return back to application!
Below is the edited code. If you saw an earlier version then you saw it had problems. First entry so am new at this. *
I tried many things to get the permissions but it always hung the App, even with the awaits.
I wanted to put the permission requests as close to where the user required them (As recommended) and not abort the App. This is what I finally came up with:
Creating a permission interface in the Xamarin Forms project
Creating an Android implementation of the permissions in the Xamarin Forms Android project
Registered permission as dependency service in the Android Activity before loading the Forms App
In my WIFI Content page I created an async method that checks permission by calling the registered Dependency service
When I click on WIFI page scan button, it calls the async method to see if the user needs to give permission before continuing
Works like a charm.
The only caveat is that if the user selects 'Don't ask again' he will have to set location services manually. Not sure how I can tell the user since the permissions always only return Denied status. He will get a dialog informing him of insufficient permissions but no OS dialog allowing him to request permissions (Duh, because he said that that he did not want to see them)
The permissions Interface in the Xamarin Forms project
public interface ILocationWhileInUsePermission {
Task<PermissionStatus> CheckStatusAsync();
Task<PermissionStatus> RequestAsync();
}
Implementation on Xamarin Forms Android side
public class LocationWhileInUsePermission : Xamarin.Essentials.Permissions.BasePlatformPermission, ILocationWhileInUsePermission {
public override (string androidPermission, bool isRuntime)[]
RequiredPermissions => new List<(string androidPermission, bool isRuntime)> {
(Android.Manifest.Permission.AccessFineLocation, true),
(Android.Manifest.Permission.AccessCoarseLocation, true),
(Android.Manifest.Permission.AccessWifiState, true),
(Android.Manifest.Permission.ChangeWifiState, true)
}.ToArray();
}
Register in the Activity.cs OnCreate before load of the App
DependencyService.Register<ILocationWhileInUsePermission, LocationWhileInUsePermission>();
LoadApplication(new App(DI.Wrapper));
In the Wifi Page create functions to invoke permissions from DependencyService and to set results
private bool permissionsGranted = false;
private async Task SetAreGranted(bool granted) {
await Task.Run(() => this.permissionsGranted = granted);
}
public async Task<bool> GetIsGranted() {
return await Task<bool>.Run(() => { return this.permissionsGranted; });
}
public async Task<bool> ChkWifiPermissions() {
try {
await this.SetAreGranted(false);
var wifiPermissions =
DependencyService.Get<ILocationWhileInUsePermission>();
var status = await wifiPermissions.CheckStatusAsync();
if (status != PermissionStatus.Granted) {
status = await wifiPermissions.RequestAsync();
if (status != PermissionStatus.Granted) {
return await this.GetIsGranted();
}
}
await this.SetAreGranted(true);
}
catch (Exception) {
return await this.GetIsGranted();
}
return await this.GetIsGranted();
}
On my WIFI Content Page, on the button click event I call the async method
private void btnDiscover_Clicked(object sender, EventArgs e) {
Device.BeginInvokeOnMainThread(async () => {
if (await this.ChkWifiPermissions()) {
this.btnSelect.IsVisible = false;
this.ResetWifiList(new List<WifiNetworkInfo>());
this.activity.IsRunning = true;
App.Wrapper.WifiDiscoverAsync();
}
else {
this.OnErr("Insufficient permissions to continue");
}
});
}
Just to add a variance to my WIFI permissions check on WIFI scan button, here is a variance that aborts on App start. It works but I prefer the one that requests closer to the usage of the permission.
This works and never hangs the App. Still the problem if the user has requested not to be asked again.
Start by declaring service interfaces in the Xamarin Forms project to close the App, and another to check and request permissions
public interface ICloseApplication {
void CloseApp();
}
public interface ILocationWhileInUsePermission {
Task<PermissionStatus> CheckStatusAsync();
Task<PermissionStatus> RequestAsync();
}
Then add Android OS implmentations in the Xamarin Forms Android project
public class AndroidCloseApp : ICloseApplication {
public void CloseApp() {
Android.OS.Process.KillProcess(Android.OS.Process.MyPid());
}
}
public class LocationWhileInUsePermission : Xamarin.Essentials.Permissions.BasePlatformPermission, ILocationWhileInUsePermission {
public override (string androidPermission, bool isRuntime)[]
RequiredPermissions => new List<(string androidPermission, bool isRuntime)> {
(Android.Manifest.Permission.AccessFineLocation, true),
(Android.Manifest.Permission.AccessCoarseLocation, true),
(Android.Manifest.Permission.AccessWifiState, true),
(Android.Manifest.Permission.ChangeWifiState, true)
}.ToArray();
}
Register the Services in the Xamarin Forms Android project MainActivity.OnCreate(). BTW, the DI.Wrapper has the results from my dependency injector, with common and OS specific code
DependencyService.Register<ILocationWhileInUsePermission, LocationWhileInUsePermission>();
DependencyService.Register<ICloseApplication, AndroidCloseApp>();
LoadApplication(new App(DI.Wrapper));
Then in the Xamarin Forms project, in the App.OnStart() override method call an async method to request permissions and abort if necessary
protected override void OnStart() {
// This will abort the app at the start if the WIFI permissions are not given
Device.BeginInvokeOnMainThread(async () => {
if (!await this.CheckPermissions()) {
ICloseApplication closeApp = DependencyService.Get<ICloseApplication>();
await Application.Current.MainPage.DisplayAlert(
App.GetText(MsgCode.Error),
"Insufficient permissions",
App.GetText(MsgCode.Ok));
closeApp.CloseApp();
}
});
}
private async Task<bool> CheckPermissions() {
ILocationWhileInUsePermission wifiPermissions =
DependencyService.Get<ILocationWhileInUsePermission>();
PermissionStatus status = await wifiPermissions.CheckStatusAsync();
if (status != PermissionStatus.Granted) {
status = await wifiPermissions.RequestAsync();
}
return status == PermissionStatus.Granted;
}
As Cheesebaron mentioned, you always want to use await when dealing with a Task. You can modify your example like so:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new NavigationPage(new MainPage());
}
protected override async void OnStart()
{
bool result = await CheckPermisions()
if (!result)
{
AbortApp(3, "Missing required permissions!");
return ;
}
}
//[...]
public async Task<bool> CheckPermisions()
{
bool v = await performCheckPermisions();
if (v)
initAppFolders();
return v;
}
protected async Task<bool> performCheckPermisions()
{
// storage read
PermissionStatus status = await Xamarin.Essentials.Permissions.CheckStatusAsync<Permissions.StorageRead>();
if (status == PermissionStatus.Denied)
{
this.Context.ToLogger(EAppLogLevel.Warning, string.Format(" ! StorageRead: requesting..."));
status = await Permissions.RequestAsync<Permissions.StorageRead>();
}
if (status == PermissionStatus.Denied)
return false;
// storage write
status = await Xamarin.Essentials.Permissions.CheckStatusAsync<Permissions.StorageWrite>();
if (status == PermissionStatus.Denied)
{
this.Context.ToLogger(EAppLogLevel.Warning, string.Format(" ! StorageWrite: requesting..."));
status = await Permissions.RequestAsync<Permissions.StorageWrite>();
}
if (status == PermissionStatus.Denied)
return false;
return true; // Task.FromResult(true);
}
}
I have one small code i don't know how to do it.
I have Realtime database in firebase,
and i want check if child name is exist or not.
example:
Realtime database:
i tried before this code and the compiler is pass from this code and not execute it or going to check.
mDatabase.child ( "Voted" ).child ( DataTeamVote.name.get ( position ) ).addListenerForSingleValueEvent ( new ValueEventListener ( )
{
#Override
public void onDataChange ( DataSnapshot snapshot )
{
if ( snapshot.hasChild ( mAuth.getCurrentUser ( ).getUid ( ) ) )
{
check = false;
Toast.makeText ( VoteAction.this , "You are already voted for this team" , Toast.LENGTH_SHORT ).show ( );
}
else
{
check = true;
Toast.makeText ( VoteAction.this , "Open Vote" , Toast.LENGTH_SHORT ).show ( );
}
}
#Override
public void onCancelled ( #NonNull DatabaseError databaseError )
{
}
} );
I am requesting multiple permissions. All are working fine except receive sms where the result is always 'never_ask_again'.
The following is the code I am trying:
_getPermissions = async () => {
try {
const resuts = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
PermissionsAndroid.PERMISSIONS.RECEIVE_SMS,
PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE
]);
if (
resuts[PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION] !==
PermissionsAndroid.RESULTS.GRANTED
) {
alert("Permission not granted. The application may not work properly");
}
if (
resuts[PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE] !==
PermissionsAndroid.RESULTS.GRANTED
) {
alert("Permission not granted. The application may not work properly");
}
if (
resuts[PermissionsAndroid.PERMISSIONS.RECEIVE_SMS] !==
PermissionsAndroid.RESULTS.GRANTED
) {
alert("Permission not granted. The application may not work properly");
}
} catch (err) {
console.warn(err);
}
};
Fix this line as opposite condition:
if (
resuts[PermissionsAndroid.PERMISSIONS.RECEIVE_SMS] ===
PermissionsAndroid.RESULTS.DENIED
) {
Alert.alert("Permission not granted. The application may not work properly");
}
I have a non static traduction file that I periodically get from a server. I cannont change the format of the file.
When I a new page is instanciate in my Ionic App I set this.traductions from a value in storage. See constructor below :
constructor(storage: Storage, public navCtrl: NavController, public navParams: NavParams, public commandeService: CommandeService, public alertCtrl: AlertController, public translate: TranslateService) {
this.categorie = this.navParams.get('categorie');
storage.get('boissons').then((StringBoissons) => {
var boissons: Array<any> = JSON.parse(StringBoissons);
this.boissons = boissons.filter(
(value) => {
return value.tboi_id == this.categorie.tboi_id;
});
}
);
storage.get('traductions').then((val) => {
this.traductions = JSON.parse(val);
});
this.commande = commandeService.getCommande();
this.translate = translate;
}
Then my View call getTraduction(...)
getTraduction(table, champ, id, objet) {
var traduction = this.traductions.find( (value) => {
return value.trad_table == table && value.trad_champ == champ && value.trad_code_langue == this.translate.currentLang && value.trad_id_item == objet[id];
});
if ( traduction && traduction.trad_texte )
return traduction.trad_texte;
else
return objet[champ];
}
Everything works fine in browser preview but on device I get a
Cannot call method 'find' of null
at t.getTraduction
I think it is due to asynchronous results but I can't quite get my head around it and figure how to solve this.
Thanks in advance for any insight
OK quite dumb question actually Sylvain :
You should have put your getTraduction function in a provider.
#Injectable()
export class TraductionDynaService {
traductions;
constructor(
public storage: Storage,
public http: Http,
public translate: TranslateService,
) {
storage.get('traductions').then((val) => {
this.traductions = JSON.parse(val);
});
}
getTraduction(table, champ, id, objet) {
var traduction = this.traductions.find( (value) => {
return value.trad_table == table && value.trad_champ == champ && value.trad_code_langue == this.translate.currentLang && value.trad_id_item == objet[id];
});
if ( traduction && traduction.trad_texte )
return traduction.trad_texte;
else
return objet[champ];
}
}
And it worked like a charm. getTraduction(...) could even return a promise to handle when traduction is null or undefined...