I'm trying to implement OAuth through google in my xamarin app. My code looks like :
var auth = new OAuth2Authenticator
(
clientId: clientId,
scope: scope,
authorizeUrl: new Uri(oauthUrl),
redirectUrl: new Uri(redirectUrl),
clientSecret: clientSecret,
accessTokenUrl: new Uri(accessTokenUrl),
getUsernameAsync: null,
isUsingNativeUI:true
);
auth.Completed += AuthOnCompleted;
auth.Error += AuthOnError;
var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
presenter.Login(auth);
private async void AuthOnCompleted(object sender, AuthenticatorCompletedEventArgs e)
{
//breakpoint, which never reach
}
private async void AuthOnError(object sender, AuthenticatorErrorEventArgs e)
{
//breakpoint, which never reach
}
The flow goes correct until redirection.
redirectUrl = https://accounts.google.com/o/oauth2/token
After successfully logging I got to url kind of this :
https://accounts.google.com/o/oauth2/token?state=ojgjqhcgyidimcec&code=4/0AX4XfwjvhStjH5RAAzLyXCu_dTPvPyZ_eee-gHqKZoglVJ-7PCR6HDkPAo9mfEMYnWdjyA&scope=https://www.googleapis.com/auth/youtube.readonly
It's clear that we have got user token, but app doesn't go next step, to AuthOnCompleted method. App just staying on redirect URL. How to close browser and get back to app into AuthOnCompleted?
UPD
MainActivity.cs:
[Activity(Label = "MyApp", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation |
ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize)]
[IntentFilter(
actions: new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataSchemes = new[]
{
"com.googleusercontent.apps.Project ID from https://console.cloud.google.com/home/dashboard?project=my_proj",
},
DataPaths = new[]
{
"/oauth2redirect",
})]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
global::Xamarin.Auth.Presenters.XamarinAndroid.AuthenticationConfiguration.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
Ok I found a working solution here
https://github.com/TimLariviere/Sample-XamarinAuth-Google
My mistake was I wrote DataSchemes as com.googleusercontent.apps.Project ID but correct is com.companyname.testapp package name
Related
I've found this: How to download image and save it in local storage using Xamarin-Forms.?
This partially adresses my problem except for two points:
I'd need to download the image to the gallery, not the apps'path
I need this to work for both, android and IOs. This seems to only work for Android.
Basically i know the URL of a file online, and need to download it to the gallery. It would be great if ic ould "save" it from inside the application, instead of "downloading". It would be nice if the client cant figure out the URL of the images he wants to save.
EDIT:
Now I am using FFImageLoading.. here is my current (not working) code..
private async void SaveToGallery_Clicked(object sender, EventArgs e)
{
var img = await MyImage.GetImageAsJpgAsync(quality: 100);
string fileName = uri.ToString().Split('/').Last();
DependencyService.Get<IMediaService>().SaveImageFromByte(img, fileName);
}
Android MediaService.cs
[assembly: Xamarin.Forms.Dependency(typeof(MediaService))]
namespace GalShare.Droid
{
public class MediaService : IMediaService
{
Context CurrentContext => CrossCurrentActivity.Current.Activity;
public void SaveImageFromByte(byte[] imageByte, string fileName)
{
try
{
Java.IO.File storagePath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
string path = System.IO.Path.Combine(storagePath.ToString(), fileName);
System.IO.File.WriteAllBytes(path, imageByte);
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(path)));
CurrentContext.SendBroadcast(mediaScanIntent);
}
catch (Exception ex)
{
}
}
}
}
Android MainActivity.cs:
namespace GalShare.Droid
{
[Activity(Label = "GalShare", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public int STORAGE_PERMISSION_CODE = 101;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
FFImageLoading.Forms.Platform.CachedImageRenderer.Init(enableFastRenderer: false);
base.OnCreate(savedInstanceState);
Forms.SetFlags("CollectionView_Experimental");
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
CachedImageRenderer.InitImageViewHandler();
string fileName = "galleries.db3";
string folderPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string completePath = Path.Combine(folderPath, fileName);
checkPermission("android.permission.write_external_storage", STORAGE_PERMISSION_CODE);
LoadApplication(new App(completePath));
}
public void checkPermission(String permission, int requestCode)
{
var thisActivity = Android.App.Application.Context as Activity;
// Checking if permission is not granted
if (ContextCompat.CheckSelfPermission(
Android.App.Application.Context,
permission)
== Android.Content.PM.Permission.Denied)
{
RequestPermissions(new String[] { Manifest.Permission.WriteExternalStorage }, requestCode);
}
else
{
}
}
}
}
Initialilzing CrossCurrentActivity in the MainActivity.cs solved the problem:
CrossCurrentActivity.Current.Init(this, bundle);
I have a plan and want to periodically check a URL every 5 minutes(NOTIFY CENTER SERVER(Listener)).
My problem: Once the program closes, the process closes
Is it possible that the project will not be shut down if the original program is closed ?
My Code After Changed Worked with : Matcha.BackgroundService
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Matcha.BackgroundService.Droid;
using Matcha.BackgroundService;
using System.Threading.Tasks;
using Android.Util;
using System.Threading;
using AndroidApp = Android.App.Application;
using Android.Content;
using Android.Support.V4.App;
using Android.Graphics;
namespace Solution.Droid
{
[Activity(Label = "Solution", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
private NotificationManager _manager;
private bool _channelInitialized = false;
public const int _pendingIntentId = 0;
public int _channelID = 10001;
private long _mssageID=0;
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
BackgroundAggregator.Init(this);
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
protected override void OnStart()
{
base.OnStart();
//Register Periodic Tasks
var _notifyTASK = new DevinuxTaskPeriodic(10);
_notifyTASK.DoTask += () =>
{
SendNotify("salam", DateTime.Now.ToString());
};
BackgroundAggregatorService.Add(() => _notifyTASK);
BackgroundAggregatorService.StartBackgroundService();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
public int SendNotify(string title, string message)
{
_mssageID++;
if (!_channelInitialized)
{
CreateNotificationChannel();
}
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
PendingIntent pendingIntent = PendingIntent.GetActivity(AndroidApp.Context, _pendingIntentId, intent, PendingIntentFlags.OneShot);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, _channelID.ToString())
.SetContentIntent(pendingIntent)
.SetContentTitle(title)
.SetContentText(message)
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.notification_template_icon_bg))
.SetSmallIcon(Resource.Drawable.notification_template_icon_bg)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate);
Notification notification = builder.Build();
_manager.Notify((int)_mssageID, notification);
return (int)_mssageID;
}
void CreateNotificationChannel()
{
_manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String("Solution");
var channel = new NotificationChannel(_channelID.ToString(), channelNameJava, NotificationImportance.Default)
{
Description = "My Company Notify Camp."
};
_manager.CreateNotificationChannel(channel);
}
_channelInitialized = true;
}
public class DevinuxTaskPeriodic : IPeriodicTask
{
public bool use { set; get; } = false;
public delegate void DoArgs();
public event DoArgs DoTask;
public DevinuxTaskPeriodic(int seconds)
{
Interval = TimeSpan.FromSeconds(seconds);
}
public TimeSpan Interval { get; set; }
public Task<bool> StartJob()
{
if (!use)
{
Timer tmr = new Timer((o) => {
if (DoTask != null)
{
DoTask();
}
}, null, 0, (int)Interval.TotalSeconds*1000);
}
use = true;
return new Task<bool>(() => true);
}
}
}
}
Yes, it is possible to run processes even when the original program/app is not in the foreground.
You are entering the territory of "backgrounding" which is less simple to do. There isn't an inbuilt/official way of performing backgrounding using Xamarin.Forms, so you will have to either create a dependency service (shown here), or try using Shiny.
If you follow the dependency services route, you just need to follow the official iOS & Android tutorials and implement them in your Native project. Note that if you only need a periodic alarm, Android provides a simpler Alarm/PowerManager that you can use.
This question already has answers here:
How to extend application class in xamarin android
(2 answers)
Closed 3 years ago.
I am trying to implement the very promising Shiny library (for background jobs) from Allan Ritchie. I have only tried this in a simple File/New Project for Android thus far (haven't implemented the code for iOS or UWP), but I am not able to get it to run.
I am following the article https://allancritchie.net/posts/shinyjobs. However, when I run I get the following exception...
And I never hit this breakpoint...
My code can be cloned from https://github.com/JohnLivermore/SampleXamarinApp.git
But here it is inline as well...
App.xaml.cs
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new MainPage();
}
protected override async void OnStart()
{
var job = new JobInfo
{
Identifier = "YourFirstJob",
Type = typeof(YourFirstJob),
// these are criteria that must be met in order for your job to run
BatteryNotLow = false,
DeviceCharging = false,
RequiredInternetAccess = InternetAccess.Any,
Repeat = true //defaults to true, set to false to run once OR set it inside a job to cancel further execution
};
// lastly, schedule it to go - don't worry about scheduling something more than once, we just update if your job name matches an existing one
await ShinyHost.Resolve<Shiny.Jobs.IJobManager>().Schedule(job);
}
protected override void OnSleep()
{
}
protected override void OnResume()
{
}
}
Startup.cs
public class Startup : ShinyStartup
{
public override void ConfigureServices(IServiceCollection services)
{
}
}
YourFirstJob.cs
public class YourFirstJob : IJob
{
public YourFirstJob()
{
}
public async Task<bool> Run(JobInfo jobInfo, CancellationToken cancelToken)
{
//await this.dependency.SomeAsyncMethod(id);
return true; // this is for iOS - try not to lie about this - return true when you actually do receive new data from the remote method
}
}
MainActivity.cs
[Activity(Label = "SampleApp", 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 savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
Shiny.AndroidShinyHost.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
MainApplication.cs
[Application]
public class MainApplication : Application
{
public override void OnCreate()
{
base.OnCreate();
Shiny.AndroidShinyHost.Init(this, new SampleApp.Startup());
}
}
Any help would be greatly appreciated!
You might want to add following constructor to your MainApplication class.
public MainApplication(IntPtr handle, JniHandleOwnership ownerShip) : base(handle, ownerShip)
{
}
My app showing notification on specific time. Notification has got pending intent with note id. When user tapped on notification, this note id is use to launch app and load note with id that come from notification.
Basically it works fine but problem starts when user leave app using "back button". On next application start that note id is still in memory and app takes user to that note again.
How can I clear that intent from memory after first use.
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Context AndroidContext { get; private set; }
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
//noteId is a id of Note thet notification was dispaly and tapped by user
//Id is pass to xamarin PCL and proper note basic on this id is displayed
int noteId = Intent.GetIntExtra("noteId", 0);
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
CalligraphyConfig.InitDefault(new CalligraphyConfig.Builder()
.SetDefaultFontPath("Dosis-Regular.ttf")
.SetFontAttrId(Resource.Attribute.fontPath)
.Build()
);
AndroidContext = this;
global::Xamarin.Forms.Forms.Init(this, bundle);
Xamarin.FormsGoogleMaps.Init(this, bundle);
LoadApplication(new App(noteId));
}
protected override void AttachBaseContext(Context context)
{
base.AttachBaseContext(CalligraphyContextWrapper.Wrap(context));
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
BroadcastReciver
class AlamReciver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
int noteId = intent.GetIntExtra("id",0);
var noteTitle = intent.GetStringExtra("title");
var noteContent = intent.GetStringExtra("content");
var resultIntent = new Intent(context, typeof(MainActivity));
resultIntent.PutExtra("noteId", noteId);
resultIntent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.ClearTask);
var PI = PendingIntent.GetActivity(context, noteId, resultIntent, PendingIntentFlags.OneShot);
var builder = new Notification.Builder(context)
.SetContentTitle(noteTitle)
.SetContentText(noteContent)
.SetSmallIcon(Resource.Drawable.ic_gnote_logo)
.SetPriority(1)
.SetAutoCancel(true)
.SetDefaults(NotificationDefaults.All)
.SetContentIntent(PI);
var notification = builder.Build();
var manager = NotificationManager.FromContext(context);
manager.Notify(noteId, notification);
}
}
Thank you.
Xamarin I updated to version 4, Forms and 2.0 versions. On Android, I use AppCompat.
I had a problem. Previously, the Android keyboard caused resize view. Now this does not happen. The keyboard appears on the top view. And the desired Elements to be hiding.
I've tried:
[Activity(WindowSoftInputMode = SoftInput.AdjustResize, Label = "Title", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
ToolbarResource = Resource.Layout.toolbar;
TabLayoutResource = Resource.Layout.tabs;
LoadApplication(new App());
Window.DecorView.SetFitsSystemWindows(true);
}
}
Daylight AppCompat has been made on this lesson: https://blog.xamarin.com/material-design-for-your-xamarin-forms-android-apps/
Thank you.
I have solved the problem. Decompile class
"Forms AppCompatActivity" and watched how the method works OnCreate.
As a result, I turned out the following code:
[Activity(Label = "Title", Icon = "#drawable/icon", Theme = "#style/MyTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
Window.SetSoftInputMode(SoftInput.AdjustResize);
AndroidBug5497WorkaroundForXamarinAndroid.assistActivity(this);
ToolbarResource = Resource.Layout.toolbar;
TabLayoutResource = Resource.Layout.tabs;
LoadApplication(new App());
}
public class AndroidBug5497WorkaroundForXamarinAndroid
{
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
// CREDIT TO Joseph Johnson (http://stackoverflow.com/users/341631/joseph-johnson) for publishing the original Android solution on stackoverflow.com
public static void assistActivity(Activity activity)
{
new AndroidBug5497WorkaroundForXamarinAndroid(activity);
}
private Android.Views.View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private AndroidBug5497WorkaroundForXamarinAndroid(Activity activity)
{
FrameLayout content = (FrameLayout)activity.FindViewById(Android.Resource.Id.Content);
mChildOfContent = content.GetChildAt(0);
ViewTreeObserver vto = mChildOfContent.ViewTreeObserver;
vto.GlobalLayout += (object sender, EventArgs e) => {
possiblyResizeChildOfContent();
};
frameLayoutParams = (FrameLayout.LayoutParams)mChildOfContent.LayoutParameters;
}
private void possiblyResizeChildOfContent()
{
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious)
{
int usableHeightSansKeyboard = mChildOfContent.RootView.Height;
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
frameLayoutParams.Height = usableHeightSansKeyboard - heightDifference;
mChildOfContent.RequestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight()
{
Rect r = new Rect();
mChildOfContent.GetWindowVisibleDisplayFrame(r);
if (Build.VERSION.SdkInt < BuildVersionCodes.Lollipop)
{
return (r.Bottom - r.Top);
}
return r.Bottom;
}
}
}
It is important to add "Window.SetSoftInputMode(SoftInput.AdjustResize);" After calling base.OnCreate(bundle);