How to pass string from android render page to PCL page? - c#

How to pass string value to android render page to PCL page.
I want to send token of eventArgs.Account.Properties["access_token"] to PCL page.
how can i do ?Please help.
[assembly: ExportRenderer(typeof(LoginPage), typeof(LoginRender))]
namespace TestApp.Droid.Renderers
{
public class LoginRender : PageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
// this is a ViewGroup - so should be able to load an AXML file and FindView<>
var activity = this.Context as Activity;
var auth = new OAuth2Authenticator(
clientId: "", // your OAuth2 client id
scope: "user_about_me", // the scopes for the particular API you're accessing, delimited by "+" symbols
authorizeUrl: new Uri("https://www.facebook.com/dialog/oauth"), // the auth URL for the service
redirectUrl: new Uri("https://www.facebook.com/connect/login_success.html")); // the redirect URL for the service
auth.Completed += (sender, eventArgs) => {
if (eventArgs.IsAuthenticated)
{
Toast.MakeText(this.Context, eventArgs.Account.Properties["access_token"], ToastLength.Long).Show();
App.SuccessfulLoginAction.Invoke();
App.SaveToken(eventArgs.Account.Properties["access_token"]);
}
else
{
// The user cancelled
}
};
activity.StartActivity(auth.GetUI(activity));
}
}
}
App.cs
public class App
{
static NavigationPage _NavPage;
public static Page GetMainPage()
{
var profilePage = new ProfilePage();
_NavPage = new NavigationPage(profilePage);
return _NavPage;
}
public static bool IsLoggedIn
{
get { return !string.IsNullOrWhiteSpace(_Token); }
}
static string _Token;
public static string Token
{
get { return _Token; }
}
public static void SaveToken(string token)
{
_Token = token;
}
public static Action SuccessfulLoginAction
{
get
{
return new Action(() => {
_NavPage.Navigation.PopModalAsync();
});
}
}
}
above is my App.cs file code. static method can't return token.
ProfilePage.cs in PCL
public class ProfilePage : BaseContentPage
{
public ProfilePage()
{
string tk = App.Token;
var lbltoken = new Label()
{
FontSize = 20,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Text = tk,
};
var stack = new StackLayout
{
VerticalOptions = LayoutOptions.StartAndExpand,
Children = { lbltoken },
};
Content = stack;
}
}

I'm presuming that you have followed this example here: How to login to facebook in Xamarin.Forms
In that case you can use it in your PCL by calling App.Token
If that isn't working create a static property of the field you are using by calling App.SaveToken(eventArgs.Account.Properties["access_token"]);
With the edits you have made it is apparent that you set the value of your Label before the App.Token has a value.
A quick fix here could be to hook in to the Page.Appearing event, like so;
public class ProfilePage : BaseContentPage
{
private Label _lbltoken;
public ProfilePage()
{
Appearing += (object s, EventArgs a) => {
_lbltoken.Text = App.Token;
};
string tk = App.Token;
_lbltoken = new Label()
{
FontSize = 20,
HorizontalOptions = LayoutOptions.CenterAndExpand,
Text = tk,
};
var stack = new StackLayout
{
VerticalOptions = LayoutOptions.StartAndExpand,
Children = { _lbltoken },
};
Content = stack;
}
}
I've made your Label control a private variable so we can easily refer to it from elsewhere, and create a Event-handler for when your ProfilePage appears.
So every time you Page appears, it will set the value of App.Token in your Label.
This should work. However you would probably be better of checking out techniques like MVVM.

Related

Attempt to present QLPreviewController whose view is not in the window hierarchy issue

I am trying to open a downloaded file in Xamarin Forms for IOS platform using UIDocumentInteractionController and access platform specific class through webservice but it is giving me the issue as 'Attempt to present QLPreviewController whose view is not in the window hierarchy'
public void viewFile(string path)
{
var PreviewController = UIDocumentInteractionController.FromUrl
(NSUrl.FromFilename(path));
PreviewController.Delegate = new UIDocumentInteractionControllerDelegateClass
(UIApplication.SharedApplication.KeyWindow.RootViewController.PresentedViewController);
Device.BeginInvokeOnMainThread(() =>
{
PreviewController.PresentPreview(true);
});
}
Following is the UIDocumentInteractionControllerDelegateClass code:
public class UIDocumentInteractionControllerDelegateClass : UIDocumentInteractionControllerDelegate {
UIViewController ownerVC;
public UIDocumentInteractionControllerDelegateClass(UIViewController vc)
{
ownerVC = vc;
}
public override UIViewController ViewControllerForPreview(UIDocumentInteractionController controller)
{
return ownerVC;
}
public override UIView ViewForPreview(UIDocumentInteractionController controller)
{
return ownerVC.View;
}
}
Try this code
var fileinfo = new FileInfo(path);
var previewController = new QLPreviewController();
previewController.DataSource = new PreviewControllerDataSource(fileinfo.FullName, fileinfo.Name);
UINavigationController controller = FindNavigationController();
if (controller != null)
{
controller.PresentViewController((UIViewController)previewController, true, (Action)null);
}

How to update badge counter in Parent tab page xamarin forms

I have a App that uses tabbed pages, In the xaml of the Parent tab page
I populate all my other tab pages, I have a viewmodel that binds to the Parent tab page and viewmodels for each of the other Tab pages. I have a badge on one of the tabs that has a counter which shows how many messages there are.
I am having trouble updating the counter.
So I have a call to retrieve the amount of unread messages from the database which is populating into the counter on app load. When i Navigate to view the message it updates the database of that the message has been read , I then navigate back to the tabbed page with a popasync , I then pull to refresh which executes the call to get amount of messages read but it not updating the the counter, if i put a break point on the GetCounter method i see it is updating the counter with the right amount but not changing in on the badge.
Hope that makes sense.
If anyone can help i will be very grateful.
Master Tab Page:
<NavigationPage Title="Message" Icon="email.png" plugin:TabBadge.BadgeText="{Binding counter}"
plugin:TabBadge.BadgeColor="Red"
plugin:TabBadge.BadgePosition="PositionTopRight"
plugin:TabBadge.BadgeTextColor="Green">
<x:Arguments>
<local:MessagePage BindingContext="{Binding messages}" />
</x:Arguments>
</NavigationPage>
public partial class MasterTabPage : TabbedPage
{
Master_PageViewModel vm;
public MasterTabPage ()
{
InitializeComponent ();
this.BindingContext = vm = new Master_PageViewModel(Navigation);
}
}
Master Tab Page ViewModel:
public class Master_PageViewModel : INotifyPropertyChanged
{
INavigation Navigation;
private int _counter;
public int counter
{
get => _counter;
set
{
_counter = value;
OnPropertyChanged(nameof(counter));
}
}
public MessagePageViewModel messages { get; set; }
public Master_PageViewModel(INavigation navigation)
{
Navigation = navigation;
messages = new MessagePageViewModel(Navigation);
Init();
counter = 0;
}
public async void Init()
{
await GetCounter();
}
public async Task GetCounter()
{
try
{
using (HttpClient client = new HttpClient())
{
List<MessageModel> msg = new List<MessageModel>();
using (HttpResponseMessage response = await client.GetAsync("http://localhost:53665/api/GetMessagesCount/Id=" + 2 + "/" ))
{
if (response.IsSuccessStatusCode)
{
using (HttpContent content = response.Content)
{
var textresponse = await content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<List<MessageModel>>(textresponse);
foreach (var i in json)
{
msg.Add(new MessageModel
{
msgCounter = i.msgCounter,
});
}
counter = msg[0].msgCounter;
}
}
else
{
}
}
}
}
catch (Exception)
{
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Message Tab ViewModel:
public class MessagePageViewModel : BaseViewModel
{
public ICommand MessageDetailsCommand { get; set; }
INavigation Navigation;
private ObservableCollection<MessageModel> _messagesList;
public ObservableCollection<MessageModel> MessagesList
{
get { return _messagesList; }
set
{
if (_messagesList != value)
{
_messagesList = value;
}
}
}
public ICommand ReloadCommand { get; set; }
public ICommand RefreshCommand
{
get
{
return new Command(async () =>
{
await GetMessages();
Master_PageViewModel vm = new Master_PageViewModel(Navigation,multiMediaPickerService);
await vm.GetCounter();
});
}
}
bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
}
}
public MessagePageViewModel(INavigation navigation)
{
ReloadCommand = new Command(async () => await ReloadPage());
Navigation = navigation;
MessageDetailsCommand = new Command(async (object obj) => await MessageDetails(obj));
Initialize();
}
private async void Initialize()
{
await GetMessages();
}
private async Task ReloadPage()
{
await GetMessages();
}
public async Task GetMessages()
{
List<MessageModel> msg = new List<MessageModel>
.........
MessagesList = new ObservableCollection<MessageModel>(msg);
}
private async Task MessageDetails(object obj)
{
var item = (obj as MessageModel);
await Navigation.PushAsync(new MessageDetailsPage(....));
}
}
}
}
This is because you created a new instance of Master_PageViewModel in your RefreshCommand. It is not the parent tabbed page's binding context so the tab's badge won't be updated even though the GetCounter has been triggered.
You have to pass the parent tabbed view model to your MessagePageViewModel like:
public Master_PageViewModel(INavigation navigation)
{
Navigation = navigation;
messages = new MessagePageViewModel(Navigation, this);
Init();
counter = 0;
}
And change your message page view model's constructor:
Master_PageViewModel parentViewModel
public MessagePageViewModel(INavigation navigation, Master_PageViewModel viewModel)
{
ReloadCommand = new Command(async () => await ReloadPage());
Navigation = navigation;
parentViewModel = viewModel;
// ...
}
At last, trigger the method in your refresh command:
public ICommand RefreshCommand
{
get
{
return new Command(async () =>
{
await GetMessages();
await parentViewModel.GetCounter();
});
}
}
Moreover, I noticed that your MessagePageViewModel used the parent tabbed view model's navigation. I don't think this is a good approach as it has its own NavigationPage so that it should utilize its own navigation instead of the parent's.

How to display local notification when the app is not in the recent history of the device using Xamarin Crossplatform

I want to display local notification when the app is closed and also app is not in the recent history of the device.How can I do this.
Please help me..
Here is my code MainPage.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
var startTimeSpan = TimeSpan.Zero;
var periodTimeSpan = TimeSpan.FromMinutes(5);
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
GetData();
return true;
});
}
public void GetData()
{
IAlarmReceiver _local = DependencyService.Get<IAlarmReceiver>();
_local.LocalNotifications("Xamarin", "Hello world");
}
private void btn_Click_Clicked(object sender, EventArgs e)
{
GetData();
}
}
Here I call the notification method in every 5 seconds.
Here is my AlarmReciever.CS
public void GetNotification(string Title, string Text)
{
Intent _intent = new Intent(Application.Context, typeof(MainActivity));
const int _pendingIntentId = 0;
PendingIntent _pintent = PendingIntent.GetActivity(Application.Context, _pendingIntentId, _intent, PendingIntentFlags.CancelCurrent);
Notification.Builder builder = new Notification.Builder(Application.Context)
.SetContentIntent(_pintent)
.SetContentTitle(Title)
.SetContentText(Text)
.SetDefaults(NotificationDefaults.Sound)
.SetAutoCancel(true)
.SetSmallIcon(Resource.Drawable.icon);
Notification _notification = builder.Build();
NotificationManager _notificationmanager = Android.App.Application.Context.GetSystemService(Context.NotificationService)
as NotificationManager;
_notificationmanager.Notify(0, _notification);
you can use ACR Notifications Plugin for Xamarin this will display the local notification when app is closed.
Example await CrossNotifications.Current.Send("Happy Birthday", "I sent this a long time ago", when = TimeSpan.FromDays(50));
If you tested your program using the debugger the notification will be terminated with the rest of your application as soon as you close it.
I tested your code (assuming _local.LocalNotifications should be _local.GetNotification) and it worked, when the app is started on the device without the debugger.
EDIT
To display the notification all x minutes, use a ScheduledJob
Add a StartJob Method to your IAlarmReceiver inteface and its implementation
public void StartJob()
{
var builder = new JobInfo.Builder(1234, new ComponentName(Android.App.Application.Context, Java.Lang.Class.FromType(typeof(ScheduledJob))));
builder.SetPeriodic(15L * 60L * 1000L); // <- change the intervall
var jobInfo = builder.Build();
var jobScheduler = (JobScheduler)Android.App.Application.Context.GetSystemService(Context.JobSchedulerService);
jobScheduler.Schedule(jobInfo);
}
This method starts the ScheduledJob (defined in the android project)
[Service(Exported = true, Permission = "android.permission.BIND_JOB_SERVICE")]
public class ScheduledJob : JobService
{
public override bool OnStartJob(JobParameters #params)
{
new AlarmReceiver().GetNotification("Hello World!", "Xamarin");
return false;
}
public override bool OnStopJob(JobParameters #params)
{
return false;
}
}
Replace
Device.StartTimer(TimeSpan.FromSeconds(5), () =>
{
GetData();
return true;
});
with
DependencyService.Get<IAlarmReceiver>().StartJob();

Touch Id with Xamarin Forms Application

We are trying to use Touch Id with iOS using our Xamarin Forms application.
In our Xamarin Forms Application, in the App.Xaml.cs constructor we are using an interface to reference the iOS native touch id implementation:
bool _authenticatedWithTouchID = DependencyService.Get<ITouchID>().AuthenticateUserIDWithTouchID();
if (_authenticatedWithTouchID)
{
MainPage = new NavigationPage(new MainPage());
}
else
{
MainPage = new NavigationPage(new LoginPage());
}
This is the interface signature within Forms Application:
public interface ITouchID
{
bool AuthenticateUserIDWithTouchID();
}
This is the implementation of the interface within the iOS project:
[assembly: Dependency(typeof(TouchID))]
namespace GetIn.iOS
{
public class TouchID : ITouchID
{
public bool AuthenticateUserIDWithTouchID()
{
bool outcome = false;
var context = new LAContext();
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError AuthError))
{
var replyHandler = new LAContextReplyHandler((success, error) => {
Device.BeginInvokeOnMainThread(() => {
if (success)
{
outcome = true;
}
else
{
outcome = false;
}
});
});
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, "Logging with touch ID", replyHandler);
};
return outcome;
}
}
}
We get a response from the outcome variable (which is true if user is authenticated) but that is not being passed back to the forms application.
We have also tried using async tasks with no luck.
Is there a recommended way we do this? We are trying to keep this as simple as possible.
You need to change your code to handle asynchronous behavior.
public class TouchID : ITouchID
{
public Task<bool> AuthenticateUserIDWithTouchID()
{
var taskSource = new TaskCompletionSource<bool>();
var context = new LAContext();
if (context.CanEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, out NSError AuthError))
{
var replyHandler = new LAContextReplyHandler((success, error) => {
taskSource.SetResult(success);
});
context.EvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, "Logging with touch ID", replyHandler);
};
return taskSource.Task;
}
}
Remember add the using on top
using System.Threading.Tasks;
And change your interface declaration
public interface ITouchID
{
Task<bool> AuthenticateUserIDWithTouchID();
}
And finally your Xamarin.Forms code...
var touchId = DependencyService.Get<ITouchID>();
var _authenticatedWithTouchID = await touchId.AuthenticateUserIDWithTouchID();
if (_authenticatedWithTouchID)
{
MainPage = new NavigationPage(new MainPage());
}
else
{
MainPage = new NavigationPage(new LoginPage());
}
Managed to get this working by using the async changes above (although you can do this without using the async method), and then doing the following:
By moving and adapting the following code from the app.xaml.cs to our MainPage.xaml.cs (our tabbed page) constructor.
var touchId = DependencyService.Get<ITouchID>();
var _authenticatedWithTouchID = await
touchId.AuthenticateUserIDWithTouchID();
if (_authenticatedWithTouchID)
{
//Do Nothing as on this page
}
else
{
//Go back to login page
Navigation.InsertPageBefore(new LoginPage(), this);
await Navigation.PopAsync();
}

Save and Load Facebook user access token

I know this is a silly question but how can I easily save the user access token after a user logs in?
Here is a part of my code that is triggered by a button:
public class FacebookLogin : MonoBehaviour {
void Awake()
{
if (!FB.IsInitialized)
{
FB.Init(InitCallback, OnHideUnity);
}
else
{
FB.ActivateApp();
}
}
private void InitCallback()
{
if (FB.IsInitialized)
{
FB.ActivateApp();
}
else
{
Debug.Log("Failed to Initialize the Facebook SDK");
}
}
private void OnHideUnity(bool isGameShown)
{
if (!isGameShown)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
public void FBLogin()
{
List<string> perms = new List<string>() { "public_profile", "email", "user_friends" };
FB.LogInWithReadPermissions(perms, AuthCallback);
}
private void AuthCallback(ILoginResult result)
{
if (FB.IsLoggedIn)
{
var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
FB.API("/me?fields=first_name", HttpMethod.GET, getName);
FB.API("/me/picture?type=square&height=128&width=128", HttpMethod.GET, getProfilePic);
SceneManager.LoadScene("Main_Menu");
}
else
{
Debug.Log("User cancelled login");
}
}
private void getName(IResult result)
{
LocalDataBase.Name = result.ResultDictionary["first_name"].ToString();
}
private void getProfilePic(IGraphResult result)
{
LocalDataBase.profilePicture = result;
}
}
Thank you in advance for all your answers.
You can save the token by making a custom serializable class that wraps around all the variables inside Facebook's AccessToken class. This custom class should contain a function that can be used to convert its data into Facebook's AccessToken class.
You can then save and load that custom class as Json.
Grab DataSaver class from this post to simplify saving and loading the data. We will call our custom class FaceBookToken.
Your Facebook Token:
var aToken = Facebook.Unity.AccessToken.CurrentAccessToken;
Save:
FaceBookToken faceBookToken = FaceBookToken.createFaceBookToken(aToken);
DataSaver.saveData(faceBookToken, "FB_Token");
Load:
FaceBookToken loadedFaceBookToken = DataSaver.loadData<FaceBookToken>("FB_Token");
//AccessToken loadedToken = loadedFaceBookToken.toAccessToken(); //OR
AccessToken loadedToken = loadedFaceBookToken;
Apply Loaded Data:
Facebook.Unity.AccessToken.CurrentAccessToken = loadedToken;
Don't know if applying it is possible or allowd but it seems to compile without problems.
Delete:
DataSaver.deleteData("FB_Token");
The FaceBookToken custom class:
[Serializable]
public class FaceBookToken
{
public static AccessToken CurrentAccessToken;
public DateTime ExpirationTime;
public DateTime? LastRefresh;
public IEnumerable<string> Permissions;
public string TokenString;
public string UserId;
//Function that let's you easily create new instance of FaceBookToken from AccessToken
public static FaceBookToken createFaceBookToken(AccessToken currentToken)
{
FaceBookToken faceBookToken = new FaceBookToken();
FaceBookToken.CurrentAccessToken = AccessToken.CurrentAccessToken;
faceBookToken.ExpirationTime = currentToken.ExpirationTime;
faceBookToken.LastRefresh = currentToken.LastRefresh;
faceBookToken.Permissions = currentToken.Permissions;
faceBookToken.TokenString = currentToken.TokenString;
faceBookToken.UserId = currentToken.UserId;
return faceBookToken;
}
//Converts our custom FaceBookToken to AccessToken
public AccessToken toAccessToken()
{
AccessToken loadedToken = new AccessToken(TokenString, UserId,
ExpirationTime, Permissions, LastRefresh);
return loadedToken;
}
//Converts our custom FaceBookToken to AccessToken(Implicit Cast)
public static implicit operator AccessToken(FaceBookToken currentToken)
{
AccessToken loadedToken = new AccessToken(currentToken.TokenString, currentToken.UserId,
currentToken.ExpirationTime, currentToken.Permissions, currentToken.LastRefresh);
return loadedToken;
}
}

Categories