I am trying to connect the in-app-billing but the OnConnected event not being hit.
my Android Manifest code is
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="alphaTestApp.alphaTestApp" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="21" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:label="alphaTestApp" android:icon="#drawable/Icon"></application>
</manifest>
And the Main Activity file code is.
[Activity(Label = "alphaTestApp", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
private InAppBillingServiceConnection _serviceConnection;
string publicKey = "REDACTED";
private IList<Product> _products;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
_serviceConnection = new InAppBillingServiceConnection(this, publicKey);
if (_serviceConnection == null)
{
Toast t = Toast.MakeText(this, "Error while connecting", ToastLength.Long);
t.Show();
}
_serviceConnection.Connect();
Toast t1 = Toast.MakeText(this, "Connected app", ToastLength.Long);
t1.Show();
_serviceConnection.OnConnected += () =>
{
Toast t2 = Toast.MakeText(this, "Retrieving Items", ToastLength.Long);
t2.Show();
_products = _serviceConnection.BillingHandler.QueryInventoryAsync(new List<string> {
"goldcoin100"
}, ItemType.Product) as IList<Product>;
if (_products == null)
return;
_serviceConnection.BillingHandler.BuyProduct(_products[0]);
};
Button button = FindViewById<Button>(Resource.Id.MyButton);
button.Click += delegate
{
Toast t3 = Toast.MakeText(this, "Retrieving Items", ToastLength.Long);
t3.Show();
_products = _serviceConnection.BillingHandler.QueryInventoryAsync(new List<string> {
"goldcoin100"
}, ItemType.Product) as IList<Product>;
if (_products == null)
return;
_serviceConnection.BillingHandler.BuyProduct(_products[0]);
};
}
}
It's probably not the reason, but it would be best to set your OnConnected handler before you call Connect just in case it connects in less time than it takes for the Toast code to run.
Also, you should make the OnConnected handler async and then await on the call to QueryInventoryAsync (in a Lambda the async keyword goes before the brackets, e.g. OnConnected += async () => ...). As it is, QueryInventoryAsync will return a Task<IList<Product>> so trying to convert it using as IList<Product> will fail and _products will always be null.
If you're running on an emulator, make sure that you've got Google Play Services installed and up-to-date. There's an article on doing this for the Xamarin Android Player here: Installing Google Play Services in XAP. Although note that BuyProduct shouldn't work on an emulator using real products, you'll need to use the test product IDs (e.g. ReservedTestProductIDs.Purchased, and note that once you've purchased this one you'll need to call ConsumePurchase on it before you can purchase it again).
Also, don't post your API key on an open forum.
Related
I want to get reminders for some Appointments that I have saved in database and they have a notificationTime property witch is the time when a notification needs to be displayed.
My approach so far is to write some kind of job that runs 1 or 2 times a day to pull the notifications that need to be registered in the next 24h and ofc register them (if you guys have a better ideea lmk :D )
This works BUT:
Only if the app is in foreground / background; then I get notification every 15min or so;
If I KILL the app I don't receive notification on my physical device (Xiaomi Redmi Note 9 Pro with Android version 12 SKQ),
only on the virtual one (Pixel 5 Android 13)
Right now I have a class that extends JobService and I use JobScheduler to schedule the Job to run every 15 min (for testing so I don't need to w8 12h xD )
Here is the JobScheduler witch I call in MainActivity file in OnCreate method
Console.WriteLine("Schedualing job");
TimeSpan interval = TimeSpan.FromMinutes(15);
var javaClass = Java.Lang.Class.FromType(typeof(NotificationService));
var componentName = new ComponentName(Application.Context, javaClass);
var jobInfo = new JobInfo.Builder(1, componentName)
.SetPeriodic(15 * 60 * 1000, 30 * 60 * 1000)
.SetRequiredNetworkType(NetworkType.Any)
.SetPersisted(true)
.Build();
var jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
var resultCode = jobScheduler.Schedule(jobInfo);
and here is the NotificationService.cs
[Service(Name = "com.companyname.deratizare_mobile.NotificationService",
Permission = "android.permission.BIND_JOB_SERVICE")]
public class NotificationService : JobService
{
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
return StartCommandResult.StickyCompatibility;
}
public override bool OnStartJob(JobParameters #params)
{
Console.WriteLine("Job started");
Task.Run(async () =>
{
//var hasSuccessful = await ProccessNotificationToRegister();
var notification = new NotificationRequest
{
Title = "Job",
Description = $"Description",
Schedule = new NotificationRequestSchedule
{
NotifyTime = DateTime.Now,
}
};
LocalNotificationCenter.Current.Show(notification);
JobFinished(#params, false);
Console.WriteLine("Job finished");
});
return true;
}
public override bool OnStopJob(JobParameters #params)
{
Console.WriteLine("Job stopped");
return true;
}
}
AndroidManifest
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.BIND_JOB_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
UPDATE
I have given permission to auto start to my app from the device and it works
SOLUTION
I ended up using FCM and a hosted service on the server that checks the cache every 5 minutes where I have stored the next notification that needs to be displaied
You can try to use Firebase push notifications.
With push notifications, you can update your users without requiring the app to run at all times or having it poll a server, potentially running down the battery.
For more information, you can check Implementing Push Notifications in Your Android Apps and Firebase Cloud Messaging.
I have been trying to authenticate via Google OAuth 2.0. Following this link.
Have been able to open the Google Auth page and login successfully.
The problem is after logging in i am redirected to the google search page.
If i close the application then in the OnAuthCompleted method of the OAuth2Authenticator i get the e.IsAuthenticated to false and not able to get any information of the user.
Xamarin Share Library code:
var authenticator = new OAuth2Authenticator(
"somekey-somekey1.apps.googleusercontent.com",
null,
"email",
new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
new Uri("com.companyname.somenameofapp:/oauth2redirect"),
new Uri("https://www.googleapis.com/oauth2/v4/"),
null,
true);
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(authenticator);
authenticator.Completed += OnAuthCompleted;
AuthenticationState.Authenticator = authenticator;
AuthenticationState Class
public class AuthenticationState
{
public static OAuth2Authenticator Authenticator;
}
The OnAuthCompleted method
private async void OnAuthCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
if (e.IsAuthenticated)
{
await Navigation.PushAsync(new GoogleLoginSuccess());
}
}
Main Activity Code
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
global::Xamarin.Auth.Presenters.XamarinAndroid.AuthenticationConfiguration.Init(this, savedInstanceState);
LoadApplication(new App());
}
CustomUrlSchemeInterceptorActivity
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
namespace XamAppGoogleAuth.Droid
{
[Activity(Label = "CustomUrlSchemeInterceptorActivity")]
[IntentFilter(
new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataSchemes = new[] { "com.companyname.somenameofapp" },
DataPath = ":/oauth2redirect",
DataHost = "com.companyname.somenameofapp")]
public class CustomUrlSchemeInterceptorActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
global::Android.Net.Uri uri_android = Intent.Data;
Uri uri_netfx = new Uri(uri_android.ToString());
// load redirect_url Page
AuthenticationState.Authenticator.OnPageLoading(uri_netfx);
var intent = new Intent(this, typeof(MainActivity));
intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
StartActivity(intent);
this.Finish();
return;
}
}
}
AndroidManifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.somenameofapp">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
<application android:label="somenameofapp.Android"></application>
<activity android:name="somenameofapp.Android.MainActivity" android:label="MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.companyname.somenameofapp" android:host="com.companyname.somenameofapp" />
</intent-filter>
</activity>
</manifest>
When i close the Custom Tab explicitly i am navigated back to the application with the below toast message.
What i would want is after the user authenticates with Google OAuth, they are redirected back to the application and I can get the access token and other information as needed. I have been to a lot of links regarding this but havent been able to find a solution. Kindly help
#kinder-cappy
I noticed you have the URL scheme interceptor activity in your application manifest as well as an activity class. You will have to choose one or else you will have multiple intent on trigger.
To remove the "customtabs login screen ...." message add this to your MainActivity (not interceptor class) after Xamarin.Auth initialization:
global::Xamarin.Auth.CustomTabsConfiguration.CustomTabsClosingMessage = null;
Also use Xamarin.Auth version 1.5.0.3 for now in android. It works.
If the current redirect URL scheme you specified does not works, try to get the correct redirect URL from your provider documentations. In my case, I used Google so I am using the inverted URL (client id).
If the current redirect URL scheme you specified does not work, try to get the correct redirect URL from your provider documentation. In my case, I used Google so I am using the inverted URL (client id).
In your CustomUrlSchemeInterceptorActivity DataSchemes and in Redirect URI you need to put inverted URL (ie. client ID) which you can get from the developer console.
client-id : "somekey-somekey1.apps.googleusercontent.com"
Redirect URI looks like : com.googleusercontent.apps.somekey-somekey1:/oauth2redirect
DataSchemes = new[] { "com.googleusercontent.apps.somekey-somekey1" } at your CustomUrlSchemeInterceptorActivity
I am using BottomNavigationBar plugin and it works fine.
My problem is I want to update badge count when notification is received while app is in running mode(i.e. open). I am using Setting Plugin for Xamarin and store count in that. On app initialize it display counts properly. But when my app is open I am changing value of Setting and immediately change the value of badge. How's that can be possible?
Where is several ways to do this.
First, if you are using Xamarin forms then you can use MessagingCenter. It is cross platform Event Bus implementation.
I used Xamarin.Form. Example for sending message
public MainPage()
{
InitializeComponent();
var count = 0;
_button.Clicked += (sender, args) =>
{
MessagingCenter.Send<MainPage, int>(this, "MyMessage", count++);
};
}
Example of reciever
public App()
{
InitializeComponent();
var mp = new MainPage();
MainPage = new MainPage();
MessagingCenter.Subscribe<MainPage, int>(this, "MyMessage", (sender, arg) => {
MainPage.DisplayAlert("MyMessage", $"Hit Count: {arg}", "ok");
});
}
There is more info and examples if you check Link
Second, add event to your settings class, and invoke it in your setter
I'm using the Xamarin.FacebookSdk to display the App Invite Dialog. For iOS everything works great but for Android, nothing displays.
Update
It turns out iOS isn't always working. I actually get the following error sometimes:
[0:] Invite Failed Error Domain=com.facebook.sdk.core Code=9 "(null)"
I can step through the following code until it gets to the AppInv.Show. However, Show never shows anything. I tried having the Facebook app installed and without it installed.
public class FacebookService : IFacebookService
{
public void InviteFriends(string appLinkUrl, string previewImageUrl)
{
if (AppInviteDialog.CanShow())
{
var activity = Xamarin.Forms.Forms.Context as Activity;
var content = new AppInviteContent.Builder().SetApplinkUrl(appLinkUrl).SetPreviewImageUrl(previewImageUrl).Build() as AppInviteContent;
//AppInviteDialog.Show(activity, content);
AppInviteDialog AppInv = new AppInviteDialog(activity);
var callbackManager = CallbackManagerFactory.Create();
var invitecallback = new CCallback();
AppInv.RegisterCallback(callbackManager, invitecallback);
AppInv.Show(content);
}
}
}
public class CCallback : Java.Lang.Object, IFacebookCallback
{
public void OnCancel()
{
System.Diagnostics.Debug.WriteLine($"Invite was cancelled");
}
public void OnError(FacebookException error)
{
System.Diagnostics.Debug.WriteLine($"Invite failed {error.Message}");
}
public void OnSuccess(Java.Lang.Object result)
{
System.Diagnostics.Debug.WriteLine($"Invite was a success: {result}");
}
}
I added the following to the AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
........
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="#string/app_id" />
<activity android:name="com.facebook.FacebookActivity" android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation" android:theme="#android:style/Theme.Translucent.NoTitleBar" android:label="#string/app_name" />
<provider android:authorities="com.facebook.app.FacebookContentProviderXXXXXMy App IdXXXX" android:name="com.facebook.FacebookContentProvider" android:exported="true" />
It turns out that AppInviteDialog was deprecated by Facebook.
It sure would have been nice for a more informative error to be displayed.
My application is completely based on internet and it does not work without it, when the internet is not available or it is slow my application is getting stopped unfortunately.
I tried to implement try, catch but it is not helping me out as it is not throwing any exception, then I thought that I have to check the internet connectivity continuously till the app is running and stop any activity from performing and set a popup to connect to the internet.
I am able to get the popup whenever I call a method which has the following code inside it,
ConnectivityManager connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
NetworkInfo networkInfo = connectivityManager.ActiveNetworkInfo;
if (networkInfo == null)
{
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.SetTitle("Network");
alert.SetMessage("Please turn of your Wifi or Mobile Data From Settings");
alert.SetPositiveButton("OK", (senderAlert, args) => {
Intent intent = new Intent(Android.Provider.Settings.ActionSettings);
StartActivity(intent);
});
alert.SetNegativeButton("Cancel", (senderAlert, args) => {
Toast.MakeText(this, "Cancelled!", ToastLength.Short).Show();
Finish();
});
Dialog dialog = alert.Create();
dialog.Show();
But I am unable to get the connection checked continuously, So Can some one Please help me to complete get this functionality in my application.
You need to use a BroadcastReceiver to monitor network changes.
[BroadcastReceiver(Enabled = true)]
[IntentFilter(new[] { "android.net.conn.CONNECTIVITY_CHANGE" })]
[Android.Runtime.Preserve(AllMembers = true)]
public class ConnectivityChangeBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action != ConnectivityManager.ConnectivityAction)
return;
//Check if is connected and raise a custom event or store
//the current in a static global variable
}
}
}
In Android 7.0, you need to remove IntentFilter from the class and register the receiver.
var receiver = new ConnectivityChangeBroadcastReceiver();
Application.Context.RegisterReceiver(receiver, new IntentFilter(ConnectivityManager.ConnectivityAction));
Another option is to use the ConnectivityPlugin https://github.com/jamesmontemagno/ConnectivityPlugin , which is easier to use.
CrossConnectivity.Current.ConnectivityChanged += HandleConnectivityChanged;
void HandleConnectivityChanged (object sender, ConnectivityChangedEventArgs e)
{
// You can check the network status in
//e.IsConnected
}
Note that The ACCESS_NETWORK_STATE and ACCESS_WIFI_STATE permissions are required.