I am using firebase to track my device on my app. When network is disconnected on my device, I want to notify on the firebase. Based on the below link,
https://firebase.google.com/docs/database/android/offline-capabilities
I am using the following code:
#if __ANDROID__
FirebaseDatabase firebase = FirebaseDatabase.Instance;
FirebaseDatabase.Instance.SetPersistenceEnabled(true);
var userListRef = firebase.GetReference("USERS_ONLINE");
var myUserRef = userListRef.Push();
DatabaseReference connectedRef = firebase.GetReference(".info/connected");
connectedRef.AddValueEventListener(new ValueEventListener());
ValueEventListener Class:
internal class ValueEventListener : Java.Lang.Object, IValueEventListener
{
// public IntPtr Handle => throw new NotImplementedException();
public void Dispose()
{
throw new NotImplementedException();
}
public void OnCancelled(DatabaseError error)
{
throw new NotImplementedException();
}
public void OnDataChange(DataSnapshot snapshot)
{
FirebaseDatabase firebase = FirebaseDatabase.Instance;
DatabaseReference myConnectionsRef = firebase.GetReference("users/joe/connections");
bool connected = Convert.ToBoolean(snapshot.GetValue(true));
myConnectionsRef.KeepSynced(true);
if (connected)
{
DatabaseReference con = myConnectionsRef.Push();
con.OnDisconnect().RemoveValue();
myConnectionsRef.SetValue(Boolean.TrueString);
}
else
{
DatabaseReference con = myConnectionsRef.Push();
con.OnDisconnect().RemoveValue();
myConnectionsRef.SetValue(Boolean.FalseString);
}
}
}
But it is working only if online only. Can anyone help me to resolve this issue.
In my xaml.cs file:
#if __ANDROID__
string myConnectionsRefNode = "users/Truckers/Joe/connections";
Constants.connectedRef = FirebaseDatabase.Instance.GetReference(".info/connected");
Constants.myConnectionsRef = FirebaseDatabase.Instance.GetReference(myConnectionsRefNode);
Constants.connectedRef.AddValueEventListener(new MyProject.Droid.Services.ValueEventListener());
#elif __IOS__
var context = AppDelegate.Instance;
if(context!=null)
{
context.FCMOfflineAccess();
}
#endif
In Constants.cs file:
public static bool IsFCMOffline = true;
public static DatabaseReference connectedRef;
public static DatabaseReference myConnectionsRef;
In Droid/ValueEventListener.cs file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Firebase.Database;
using MyProject.Common;
namespace MyProject.Droid.Services
{
public class ValueEventListener : Java.Lang.Object, IValueEventListener
{
public void OnCancelled(DatabaseError error)
{
throw new NotImplementedException();
}
public void OnDataChange(DataSnapshot snapshot)
{
DatabaseReference con = Constants.myConnectionsRef.Ref;
con.OnDisconnect().SetValue("Offline");
con.SetValue("Online");
}
}
}
In iOS/AppDelegate.cs file:
public void FCMOfflineAccess()
{
var connectedRef = Database.DefaultInstance;
var MyConnectedRef = connectedRef.GetReferenceFromPath("users/Truckers/" + TruckerId);
var connectedRef1 = connectedRef.GetReferenceFromPath(".info/connected");
connectedRef1.ObserveEvent(DataEventType.Value, HandleAction);
}
private void HandleAction(DataSnapshot snapshot)
{
try
{
var connectedRef = Database.DefaultInstance;
var con = connectedRef.GetReferenceFromPath("users/Truckers/Joe");
string[] statOffline = { "Offline" };
string[] keyOffline = { "connections" };
var dataOffline = NSDictionary.FromObjectsAndKeys(statOffline, keyOffline, keyOffline.Length);
con.SetValueOnDisconnect(dataOffline);
string[] statOnline = { "Online" };
string[] keyOnline = { "connections" };
var dataOnline = NSDictionary.FromObjectsAndKeys(statOnline, keyOnline, keyOnline.Length);
con.SetValue(dataOnline);
}
catch (Exception ex)
{
}
}
Related
I am using xamarin forms for my app. I am using foreground service for my app to work on background. I kill the App and when I try to relaunch the App, I can't open the App. Here I am adding My service code below. Please see the code and give me suggestions to solve this issue.
My Service Code is below:
MyPrjService.cs:
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Java.Lang;
using System.Threading;
using System.Threading.Tasks;
using Android.Support.V4.App;
using Android;
using Android.Media;
using MyPrj.Interface;
using Xamarin.Forms;
using System.IO;
using Android.Net;
using MyPrj.Common;
using MyPrj.Services;
using Android.App;
using MyPrj.BusinessLogic;
using MyPrj.Helper;
using System;
using Plugin.Connectivity;
using MyPrj.Form;
using Android.Graphics;
namespace MyPrj.Droid.Services
{
[Service]
public class MyPrjService : Service
{
CancellationTokenSource _cts;
private static ILogger logger = DependencyService.Get<ILogManager>().GetLog();
public const string LOCATION_CHANNEL = "default";
NotificationManager manager;
NotificationCompat.Builder notification;
public override void OnCreate()
{
base.OnCreate();
manager = (NotificationManager)Forms.Context.GetSystemService("notification");
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
StartLocationServiceForeground();
return StartCommandResult.Sticky;
}
void StartLocationServiceForeground()
{
try
{
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var chan1 = new NotificationChannel(LOCATION_CHANNEL,
new Java.Lang.String("Primary"), NotificationImportance.High);
manager.CreateNotificationChannel(chan1);
notification = new NotificationCompat.Builder(Forms.Context, LOCATION_CHANNEL);
notification.SetOngoing(true)
//.SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.icon))
.SetSmallIcon(Resource.Drawable.icon_transparent)
.SetContentTitle("MyPrj is running background")
.SetContentText("Tab for more information or to stop the app")
.SetColor(0x9c6114)
.SetPriority(NotificationCompat.PriorityHigh);
StartForeground(1, notification.Build());
}
}
catch(System.Exception ex)
{
}
}
public override void OnDestroy()
{
StopForeground(true);
if (manager!=null)
{
manager.CancelAll();
}
base.OnDestroy();
}
}
}
App.xaml.cs:
protected override async void OnSleep()
{
try
{
var context = Droid.MainActivity.Instance;
if (context != null)
{
context.StartServiceFromApp();
}
}
}
protected async override void OnResume()
{
var context = Droid.MainActivity.Instance;
if (context != null)
{
context.StopServiceFromApp();
}
}
MainActivity.cs:
public void StartServiceFromApp()
{
os 9
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var intent1 = new Intent(this, typeof(MyPrjService));
StartService(intent1);
}
}
public void StopServiceFromApp()
{
var intent1 = new Intent(this, typeof(ScoularService));
StopService(intent1);
}
I am using the Xamarin forms and getting the firebase token by using this code.
using System;
using Android.Content;
using Android.Gms.Auth.Api;
using Android.Gms.Auth.Api.SignIn;
using Android.Gms.Common;
using Android.Gms.Common.Apis;
using Android.OS;
using GoogleNativeLogin.Models;
using GoogleNativeLogin.Services.Contracts;
using Plugin.CurrentActivity;
namespace GoogleNativeLogin.Droid
{
public class GoogleManager : Java.Lang.Object, IGoogleManager, GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
{
public Action<GoogleUser, string> _onLoginComplete;
public static GoogleApiClient _googleApiClient { get; set; }
public static GoogleManager Instance { get; private set; }
private const string webClientId = "********************************.apps.googleusercontent.com";
public GoogleManager()
{
Instance = this;
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DefaultSignIn)
.RequestIdToken(webClientId)
.RequestEmail()
.Build();
_googleApiClient = new GoogleApiClient.Builder(CrossCurrentActivity.Current.AppContext)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(this)
.AddApi(Auth.GOOGLE_SIGN_IN_API, gso)
.AddScope(new Scope(Scopes.Profile))
.Build();
}
public void Login(Action<GoogleUser, string> onLoginComplete)
{
_onLoginComplete = onLoginComplete;
Intent signInIntent = Auth.GoogleSignInApi.GetSignInIntent(_googleApiClient);
CrossCurrentActivity.Current.Activity.StartActivityForResult(signInIntent, 1);
_googleApiClient.Connect();
}
public void Logout()
{
_googleApiClient.Disconnect();
}
public void OnAuthCompleted(GoogleSignInResult result)
{
if (result.IsSuccess)
{
GoogleSignInAccount accountt = result.SignInAccount;
_onLoginComplete?.Invoke(new GoogleUser()
{
Token = accountt.IdToken,
Name = accountt.DisplayName,
Email = accountt.Email,
Picture = new Uri((accountt.PhotoUrl != null ? $"{accountt.PhotoUrl}" : $"https://autisticdating.net/imgs/profile-placeholder.jpg"))
}, string.Empty);
}
else
{
_onLoginComplete?.Invoke(null, "An error occured!");
}
}
public void OnConnected(Bundle connectionHint)
{
}
public void OnConnectionSuspended(int cause)
{
_onLoginComplete?.Invoke(null, "Canceled!");
}
public void OnConnectionFailed(ConnectionResult result)
{
_onLoginComplete?.Invoke(null, result.ErrorMessage);
}
}
}
And I am testing the token locally using Postman to my local Asp.Net core Web API. The authentication code is this.
public static IServiceCollection AddFirebaseAuthentication(this IServiceCollection services, string issuer, string audience)
{
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{issuer}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
services.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.IncludeErrorDetails = true;
o.RefreshOnIssuerKeyNotFound = true;
o.MetadataAddress = $"{issuer}/.well-known/openid-configuration";
o.ConfigurationManager = configurationManager;
o.Audience = audience;
});
return services;
}
public static IServiceCollection AddFirebaseAuthentication(this IServiceCollection services, string firebaseProject)
{
return services.AddFirebaseAuthentication("https://securetoken.google.com/" + firebaseProject, firebaseProject);
}
I am passing the Firebase Project Id to this function.
IServiceCollection AddFirebaseAuthentication(this IServiceCollection services, string firebaseProject)
And calling the API with Authorize attribute. And I am getting this error in the
Visual studio Output.
Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException:
IDX10501: Signature validation failed. Unable to match keys:
I'm using Branch.io in a Xamarin Forms app, and my goal is to send push notifications through Azure Notification Hubs with Branch links in them to open the app and do something. I seem to have it all configured perfectly fine for iOS, but in Android I'm receiving the push notification and the correct activity is coming up, but InitSessionComplete is not called on my IBranchBUOSessionInterface object.
I'm suspecting the issue is in the way I'm creating the PendingIntent, but I could be totally wrong. Again, InitSessionComplete is called in every other circumstance except for when I receive a push notification with a Branch link.
All relevant code is below. Thanks!
MainApplication.cs
using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using BranchXamarinSDK;
using Plugin.CurrentActivity;
namespace MyCompany.MyApp.Droid
{
//You can specify additional application information in this attribute
[Application]
[MetaData("io.branch.sdk.auto_link_disable", Value = "false")]
[MetaData("io.branch.sdk.TestMode", Value = "true")]
[MetaData("io.branch.sdk.BranchKey", Value = "#string/branch_key")]
public class MainApplication : Application, Application.IActivityLifecycleCallbacks
{
public MainApplication (IntPtr handle, JniHandleOwnership transer)
: base (handle, transer)
{
}
public override void OnCreate ()
{
base.OnCreate ();
RegisterActivityLifecycleCallbacks (this);
BranchAndroid.GetAutoInstance(ApplicationContext);
}
public override void OnTerminate ()
{
base.OnTerminate ();
UnregisterActivityLifecycleCallbacks (this);
}
public void OnActivityCreated (Activity activity, Bundle savedInstanceState)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityDestroyed (Activity activity)
{
}
public void OnActivityPaused (Activity activity)
{
}
public void OnActivityResumed (Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivitySaveInstanceState (Activity activity, Bundle outState)
{
}
public void OnActivityStarted (Activity activity)
{
CrossCurrentActivity.Current.Activity = activity;
}
public void OnActivityStopped (Activity activity)
{
}
}
}
MainActivity.cs
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Util;
using BranchXamarinSDK;
using MyCompany.Shared.Droid.Modules;
using App = MyCompany.MyApp.Core.App;
namespace MyCompany.MyApp.Droid
{
[Activity (Theme = "#style/Custom.Holo",
Label = "MyApp",
Icon = "#drawable/icon",
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Portrait,
LaunchMode = LaunchMode.SingleTask,
MainLauncher = true)]
[IntentFilter(new[] { "android.intent.action.VIEW" },
Categories = new[] { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE" },
DataScheme = "myapp",
DataHost = "open")]
[IntentFilter(new[] { "android.intent.action.VIEW" },
Categories = new[] { "android.intent.category.DEFAULT", "android.intent.category.BROWSABLE" },
DataScheme = "https",
DataHost = "mycompanymyapp.test-app.link")]
public class MainActivity : Xamarin.Forms.Platform.Android.FormsApplicationActivity, IBranchBUOSessionInterface
{
public const string Tag = "MainActivity";
private App _app;
internal static readonly string ChannelId = "MyCompany";
internal static readonly int NotificationId = 100;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
if (key == null) { continue; }
var value = Intent.Extras.GetString(key);
Log.Debug(Tag, "Key: {0} Value: {1}", key, value);
}
}
// Removes icon from android navbar
ActionBar.SetIcon(Android.Resource.Color.Transparent);
Xamarin.Forms.Forms.Init (this, savedInstanceState);
BranchAndroid.Debug = true;
var androidModule = new MyCompanyAndroidServicesModule ();
var app = new App (androidModule);
BranchAndroid.Init(this, GetString(Resource.String.branch_key), this);
LoadApplication(app);
_app = app;
}
#region IBranchSessionInterface implementation
public void InitSessionComplete(BranchUniversalObject buo, BranchLinkProperties blp)
{
_app.InitSessionComplete(buo, blp);
}
public void SessionRequestError(BranchError error)
{
_app.SessionRequestError(error);
}
#endregion
protected override void OnNewIntent(Intent intent)
{
Intent = intent;
}
}
}
MyCompany.MyApp.Core.cs
#region Libraries
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Autofac;
using BranchXamarinSDK;
using MyCompany.Core;
using MyCompany.Core.Factories;
using MyCompany.Core.Helpers;
using MyCompany.Core.Services;
using MyCompany.Core.ViewModels;
using MyCompany.MyApp.Core.ViewModels;
using Xamarin.Forms;
using Device = Xamarin.Forms.Device;
#endregion Libraries
namespace MyCompany.MyApp.Core
{
public class App : Application, ILoginApp, IBranchBUOSessionInterface
{
#region Variables
readonly IViewFactory _viewFactory;
readonly INavigationService _navService;
#endregion Variables
#region Constructor
public App (Module platformServiceModule = null)
{
var bootstrapper = new TimeAppBootstrapper ();
if (platformServiceModule != null)
bootstrapper.AddModule (platformServiceModule);
bootstrapper.Run ();
_viewFactory = bootstrapper.Container.Resolve<IViewFactory> ();
_navService = bootstrapper.Container.Resolve<INavigationService> ();
Page startView = new NavigationPage (_viewFactory.Resolve<LoginV2ViewModel> ()) {
BarBackgroundColor = Color.White,
BarTextColor = Color.FromRgb(34, 149, 236)
};
MainPage = startView;
}
#endregion Constructor
protected override void OnStart ()
{
// Handle when your app starts
}
protected override void OnSleep ()
{
// Handle when your app sleeps
}
/// <summary>
/// Application developers override this method to perform actions
/// when the application resumes from a sleeping state.
/// </summary>
protected override void OnResume ()
{
}
#region IBranchSessionInterface implementation
public void InitSessionComplete(BranchUniversalObject buo, BranchLinkProperties blp)
{
if (blp.feature == "feature")
{
_navService.NavigateTo<FeatureViewModel>();
}
}
public void SessionRequestError(BranchError error)
{
var e = error;
}
#endregion
}
}
FirebaseMessagingService.cs
using Android.App;
using Android.Content;
using Android.Util;
using Firebase.Messaging;
using Xamarin.Essentials;
namespace Crowdkeep.Time.Droid.Services
{
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
const string TAG = "MyFirebaseMessagingService";
public override void OnMessageReceived(RemoteMessage message)
{
Log.Debug(TAG, "From: " + message.From);
var notificationManager = NotificationManager.FromContext(this);
InitializeChannels(notificationManager);
if (IsProductionNotification(message))
{
SendProductionNotification(message, notificationManager, this);
}
else
{
SendTestNotification(message, notificationManager);
}
}
private static bool IsProductionNotification(RemoteMessage message)
{
return message.GetNotification() != null;
}
private void SendTestNotification(RemoteMessage message, NotificationManager notificationManager)
{
var notification = CreateNotification(message.Data["message"], message.Data["branch"], this);
notificationManager.Notify(0, notification);
}
private static void SendProductionNotification(RemoteMessage message, NotificationManager manager, Context context)
{
Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
var notification = CreateNotification(message.GetNotification().Body, "https://mycompanymyapp.test-app.link/feature", context);
manager.Notify(0, notification);
}
private static void InitializeChannels(NotificationManager manager)
{
if (DeviceInfo.Version.Major < 8 || manager.GetNotificationChannel(MainActivity.ChannelId) != null)
{
return;
}
var channel = new NotificationChannel(MainActivity.ChannelId, "Crowdkeep", NotificationImportance.Default)
{
Description = "Default Channel"
};
manager.CreateNotificationChannel(channel);
}
private static Notification CreateNotification(string messageBody, string link, Context context)
{
var pendingIntent = SetupNotificationIntent(link, context);
var notificationBuilder = new Notification.Builder(context, MainActivity.ChannelId)
.SetContentTitle("Message")
.SetSmallIcon(Resource.Drawable.icon)
.SetContentText(messageBody)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent)
.SetVisibility(NotificationVisibility.Public);
return notificationBuilder.Build();
}
private static PendingIntent SetupNotificationIntent(string link, Context context)
{
var intent = new Intent(context, typeof(MainActivity));
intent.SetFlags(ActivityFlags.ClearTop);
intent.PutExtra("branch", link);
intent.PutExtra("branch_force_new_session", true);
var pendingIntent = PendingIntent.GetActivity(context, MainActivity.NotificationId, intent, PendingIntentFlags.OneShot);
return pendingIntent;
}
}
}
Json Sent via Notification:
{"data":{"message":"Click this notification to go to the best feature of the app!", "branch": "https://mycompanymyapp.test-app.link/feature"}}
EDIT (1/7/2019)
I'm able to reproduce this in the Test Bed. I forked my own copy of Branch and added a branch called initsessioncomplete-push-notification-issue, which you can access here. I followed these instructions to setup Firebase and Azure Notification Hubs. In the solution, the only thing you'll have to modify are the two strings in the AppConstants class in the TestBed.Droid project, and add your google-services.json file to the root of the Droid project (the .csproj is already configured properly to read it).
As far as I can tell, I see a successful call being made to the Branch API when I click the push notification, but InitSessionComplete is never called.
Joon from Branch here. Can you double check that you are using the correct Branch Key in your AndroidManifest? I noticed that you are testing with a test Branch Link so you'll have to use your test Branch Key.
I run into this problem:
Forms.context is obsolete.Context is obsolete as of version 2.5,please use a local context instead.
I m trying to login using Azure Active Directory with code below.
Please help.
using Xamarin.Forms;
using myMobile.Service;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
[assembly: Dependency(typeof(myMobile.Droid.Authenticator))]
namespace myMobile.Droid
{
class Authenticator: IAuthenticator
{
public async Task<AuthenticationResult> Authenticate(string tenantUrl, string graphResourceUri, string ApplicationID, string returnUri)
{
try
{
var authContext = new AuthenticationContext(tenantUrl);
if (authContext.TokenCache.ReadItems().Any())
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().FirstOrDefault().Authority);
var authResult = await authContext.AcquireTokenAsync(graphResourceUri, ApplicationID, new Uri(returnUri), new PlatformParameters((Activity)Forms.Context));
return authResult;
}
catch (Exception)
{
return null;
}
}
}
}
// err encountered on this line :(Activity)Forms.Context)
Forms.context is obsolete.Context is obsolete as of version2.5,please use a local context instead.
var authResult = await authContext.AcquireTokenAsync(graphResourceUri, ApplicationID, new Uri(returnUri), new PlatformParameters((Activity)Forms.Context));
//--- Update :
//-------- Login Page:
private async void Login()
{
try
{
var data = await DependencyService.Get<IAuthenticator>()
.Authenticate(AzureSettings.tenanturl, AzureSettings.GraphResourceUri, AzureSettings.ApplicationID, AzureSettings.ReturnUri);
AzureSettings.AuthenticationResult = data;
//NavigateTopage(data);
}
catch (Exception)
{ }
}
}
//--------- in Shared Project :
//-- interface: IAuthenticator
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
namespace myMobile.Service
{
public interface IAuthenticator
{
Task<AuthenticationResult> Authenticate(string tenantUrl, string graphResourceUri, string ApplicationID, string returnUri);
}
}
//-------- in Android Project: add
1) Class : Authenticator.cs
using Android.App;
using Android.Content;
using Xamarin.Forms;
using myMobile.Service;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading.Tasks;
[assembly: Dependency(typeof(myMobile.Droid.Authenticator))]
namespace myMobile.Droid
{
class Authenticator: IAuthenticator
{
private readonly Context _context;
public static void Init(Context context)
{
_context = context; //--error
}
public async Task<AuthenticationResult> Authenticate(string tenantUrl, string graphResourceUri, string ApplicationID, string returnUri)
{
try
{
var authContext = new AuthenticationContext(tenantUrl);
if (authContext.TokenCache.ReadItems().Any())
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().FirstOrDefault().Authority);
var authResult = await authContext.AcquireTokenAsync(graphResourceUri, ApplicationID, new Uri(returnUri), new PlatformParameters((Activity) _context));
return authResult;
}
catch (Exception)
{
return null;
}
}
}
}
error :
An Object reference is required for non-static field,method or property.Authenticator._context
//------- Class: MainActivity
namespace myMobile.Droid
{
[Activity(Label = "myMobile", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
DependencyService.Get<IAuthenticator>().Init(this); //<-- Error
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
}
}
Error Message:
IUAthenticator does not contain a definition for Init and no extension method accepting
a first argument of type IAuthenticator
You now have to implement a custom constructor that takes a Context put that in a local variable and use that instead of this for instance new PlatformParameters((Activity)Forms.Context).
For a custom renderer, you can use the solution underneath. Do like this:
public MyControlRenderer : ControlRenderer
{
private readonly Context _context;
public MyControlRenderer(Context context) : base(context)
{
_context = context;
}
}
For a dependency service like yours, you will have to find a way to supply the Context. Since Xamarin.Forms uses a single activity, you can get away with some kind of init method.
Add this to your code:
public class MyService : IMyService
{
private static Context _context;
public static void Init(Context context)
{
_context = context;
}
}
Now call the Init from your MainActivity and you should be good after that. So do: DependencyService.Get<IMyService>().Init(this);
For other people coming across this for multiple activities, please refer to the documentation here: https://www.davidbritch.com/2017/11/xamarinforms-25-and-local-context-on.html that is what this is inspired on.
I'm using Xamarin, also my SQLite tables contain a large amount of data.
Because I want to avoid UIThread problems in OnCreate(), I need to perform database actions asynchronously.
I'm looking for guidance if I am handling this properly.
First method, which I found on the net:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.InventoryPreviewMain);
Thread thread = new Thread(() =>
{
SQLiteConnection db = new SQLiteConnection(dpPath);
var table = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where CategoryID =" + Connection.CategoryID + "");
mItems = new List<InventoryPreviewClass>();
foreach (var item in table)
{
mItems.Add(new InventoryPreviewClass() { InventoryItemID = item.InventoryItemID, InventoryItemName = item.InventoryItemName, InventoryItemPrice = item.InventoryItemPrice });
}
MyListViewAdapterInventory adapter = new MyListViewAdapterInventory(this, Resource.Layout.InventoryPreview, mItems);
mlistview.Adapter = adapter;
});
thread.Start();
Second Method, using async
public async void StartTimer()
{
SQLiteConnection db = new SQLiteConnection(dpPath);
var table = db.Query<InventoryPreviewClass>("select * from InventoryPreviewClass where CategoryID =" + Connection.CategoryID + "");
mItems = new List<InventoryPreviewClass>();
foreach (var item in table)
{
mItems.Add(new InventoryPreviewClass() { InventoryItemID = item.InventoryItemID, InventoryItemName = item.InventoryItemName, InventoryItemPrice = item.InventoryItemPrice });
}
MyListViewAdapterInventory adapter = new MyListViewAdapterInventory(this, Resource.Layout.InventoryPreview, mItems);
mlistview.Adapter = adapter;
await Task.Delay(500);
}
Which of two examples are more safe for keeping alive UIthread? Is there any other solution for making this?What is more reccomended to do?
Answer
Use the Cross-platform SQLite Library made by #FrankKruger to create/access SQLite databases for Xamarin mobile apps.
This library has a built-in asynchronous connection, so you'll never need to worry about accessing the database from the UI Thread again!
Xamarin.Android Example
"Second Method"
public async Task StartTimer()
{
mItems = await InventoryPreviewClassDatabase.GetAllInventoryPreviewClassAsync();
MyListViewAdapterInventory adapter = new MyListViewAdapterInventory(this, Resource.Layout.InventoryPreview, mItems);
mlistview.Adapter = adapter;
}
BaseDatabase Class
using System;
using System.Threading.Tasks;
using SQLite;
namespace SampleApp
{
public abstract class BaseDatabase
{
#region Constant Fields
static readonly Lazy<SQLiteAsyncConnection> _databaseConnectionHolder = new Lazy<SQLiteAsyncConnection>(() => GetDatabaseConnection());
#endregion
#region Fields
static bool _isInitialized;
#endregion
#region Properties
static SQLiteAsyncConnection DatabaseConnection => _databaseConnectionHolder.Value;
#endregion
#region Methods
protected static async Task<SQLiteAsyncConnection> GetDatabaseConnectionAsync()
{
if (!_isInitialized)
await Initialize().ConfigureAwait(false);
return DatabaseConnection;
}
static async Task Initialize()
{
await DatabaseConnection.CreateTableAsync<InventoryPreviewClass>().ConfigureAwait(false);
_isInitialized = true;
}
SQLiteAsyncConnection GetDatabaseConnection()
{
var sqliteFilename = "YourDatabaseName.db3";
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal); // Documents folder
var path = Path.Combine(documentsPath, sqliteFilename);
var conn = new SQLiteAsyncConnection(path, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.SharedCache);
return conn;
}
#endregion
}
}
Parent Database Class
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace SampleApp
{
public abstract class InventoryPreviewClassDatabase : BaseDatabase
{
#region Methods
public static async Task<IList<InventoryPreviewClass>> GetAllInventoryPreviewClassAsync()
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.Table<InventoryPreviewClass>().ToListAsync().ConfigureAwait(false);
}
public static async Task<InventoryPreviewClass> GetInventoryPreviewClassByIDAsync(int id)
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.Table<InventoryPreviewClass>().Where(x => x.ID.Equals(id)).FirstOrDefaultAsync().ConfigureAwait(false);
}
public static async Task<int> SaveInventoryPreviewClassAsync(InventoryPreviewClass inventoryPreview)
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
var isObjectInDatabase = await GetInventoryPreviewClassByIDAsync(inventoryPreview.ID).ConfigureAwait(false) != null;
if (isObjectInDatabase)
return await databaseConnection.UpdateAsync(inventoryPreview).ConfigureAwait(false);
return await databaseConnection.InsertAsync(inventoryPreview).ConfigureAwait(false);
}
public static async Task<int> DeleteItemAsync(OpportunityModel opportunity)
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.DeleteAsync(opportunity).ConfigureAwait(false);
}
public static async Task<int> GetNumberOfRowsAsync()
{
var databaseConnection = await GetDatabaseConnectionAsync().ConfigureAwait(false);
return await databaseConnection.Table<InventoryPreviewClass>().CountAsync().ConfigureAwait(false);
}
#endregion
}
}
This code was inspired from this Xamarin.Forms sample app