Requesting Android permissions in a class (Xamarin) - xamarin

I'm trying to request a permission at runtime for my app. I use a service provider to talk between the portable class and Android.
I start by calling this code on button press in the PCL:
using (new Busy(this))
{
var locationHelper = scope.Resolve<ILocationHelper>();
locationHelper.GetLocation(this);
}
This calls my Android level service:
public class AndroidLocationHelper : ILocationHelper, ILocationListener
{
readonly string[] PermissionsLocation =
{
Manifest.Permission.AccessCoarseLocation
};
const int RequestLocationId = 0;
public void GetLocation(SearchViewModel viewModel)
{
try
{
const string permission = Manifest.Permission.AccessCoarseLocation;
if (((int)Build.VERSION.SdkInt < 23) || (CheckSelfPermission(permission) == Permission.Granted))
{
}
else
RequestPermissions(PermissionsLocation, RequestLocationId);
}
catch (Exception ex)
{
Debug.WriteLine("Error while getting Location service");
Debug.WriteLine(ex.Message);
Messaging.AlertUser("There was an error with determining your location");
}
}
However, I get two errors on CheckSelfPermission and RequestPermissions. These two methods are only available to activities. The code works fine in MainActivity; however, I want to ask for permissions when the user hits a button, not in OnCreate or OnResume, etc.
Thanks for any help.

In your Android project, You can use this and use the Dependency Service to call it in Xamarin.Forms PCL project later:
var thisActivity = Forms.Context as Activity;
ActivityCompat.RequestPermissions(thisActivity, new string[] {
Manifest.Permission.AccessFineLocation }, 1);
ActivityCompat.RequestPermissions(thisActivity,
new String[] { Manifest.Permission.AccessFineLocation },
1);

You can try with ContextCompat.CheckSelfPermission, passing the application context, like this:
ContextCompat.CheckSelfPermission(Android.App.Application.Context, permission)
Update
In case of ActivityCompat.RequestPermissions, which requires an activity reference, you can keep track of the current activity. There is a very handy lib for that, called "CurrentActivityPlugin". You can find at https://github.com/jamesmontemagno/CurrentActivityPlugin

Rafael came up with a solution but I found another option that is a lot less effort just using MessagingCenter. In the MainActivity's OnCreate add a receiver that runs all the location code, that way you have access to all of the activities methods (and there are a bunch of tutorials on doing location services in MainActivity). Then add the Send inside of your service (the class).

To expound Rafael Steil's answer, I tried the suggested CurrentActivityPlugin and it worked on me. In my case I am trying to execute a voice call which needs CALL_PHONE permission. Here is the code snippet in your case: I used the ContextCompat & ActivityCompat so that I don't need to check the VERSION.SdkInt
using Plugin.CurrentActivity;
public void GetLocation(SearchViewModel viewModel){
var context = CrossCurrentActivity.Current.AppContext;
var activity = CrossCurrentActivity.Current.Activity;
int YOUR_ASSIGNED_REQUEST_CODE = 9;
if (ContextCompat.CheckSelfPermission(context, Manifest.Permission.AccessCoarseLocation) == (int)Android.Content.PM.Permission.Granted)
{
//Permission is granted, execute stuff
}
else
{
ActivityCompat.RequestPermissions(activity, new string[] { Manifest.Permission.AccessCoarseLocation }, YOUR_ASSIGNED_REQUEST_CODE);
}
}

It's dead simple
public bool CheckPermission()
{
const string permission = Manifest.Permission.ReceiveSms;
return ContextCompat.CheckSelfPermission(Forms.Context, permission) == (int) Permission.Granted;
}

Related

Luis Skill Recognizer Enumerations Error - Need to Update .luis & .qna files with changes

I have a virtual assistant dispatch that hands off control to a knowledge base skill that makes use of QnA maker. I will eventually have several QnA knowledgebases, but right now, I'm running into an issue getting the first one working. I've created/edited, trained, and publised/released the QnA maker KB, Knowledge Base Skill Luis model, and the Virtual Assistant's dispatch model.
Exception is being generated after the successful skill dispatch when my Knowledge Base Skill's Luis model returns the intent. I have a switch statement that will eventually point to the knowledge base corresponding to the user's question.
text: "Exception Message: Could not convert string 'helpdesk_faq' to dictionary key type
'Luis.KnowledgeBaseSkillLuis+Intent'. Create a TypeConverter to convert
from the string to the key type object.
I updated my KnowledgeBaseSkillLuis.cs intent enumeration with the name of the new intent (shown below), but I'm wondering if I didn't need to do this. I noticed my KnowledgeBaseSkill.luis and Faq.qna files do not have the updated changes; which leads me to my question -
How do I pull the updated models into my local environment? Do I need to run a botskills or dispatch command to pull the newly published intents into the code, or was I correct in manually updating the intent enumeration with my new skill? Do I need to re-publish the Assistant and/or Skill from my local machine to Azure to get them?
I've read these articles, but I'm struggling to make use of them:
Use Multiple LUIS and QnA Models
Add NLP to your Bot
Use QnA Maker to Answer Questions
// Full file included lower in the post
public enum Intent
{
Sample,
q_Faq,
helpdesk_faq, // Newly created intent (others were auto generated with Deploy scripts provided in skill template
None
};
MainDialog.cs:
...
switch (intent)
{
case KnowledgeBaseSkillLuis.Intent.Sample:
{
await innerDc.BeginDialogAsync(_sampleDialog.Id);
break;
}
case KnowledgeBaseSkillLuis.Intent.helpdesk_faq:
{
cognitiveModels.QnAServices.TryGetValue("Faq", out var qnaService); // "Faq" is the name of the QnA maker knowledge base.
if (qnaService == null)
{
await innerDc.Context.SendActivityAsync("I'm having issues looking up the information for you.");
throw new Exception("QnA Maker Service could not be found in the Bot Services Configuration.");
}
else
{
var answers = await qnaService.GetAnswersAsync(innerDc.Context, null, null);
if (answers != null && answers.Count() > 0)
{
await innerDc.Context.SendActivityAsync(answers[0].Answer);
}
else
{
await innerDc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("ConfusedMessage"));
}
}
break;
}
case KnowledgeBaseSkillLuis.Intent.None:
default:
{
// intent was identified but not yet implemented
await innerDc.Context.SendActivityAsync(_templateEngine.GenerateActivityForLocale("UnsupportedMessage"));
break;
}
}
...
KnowledgeBaseSkillLuis.cs:
public class KnowledgeBaseSkillLuis : IRecognizerConvert
{
public string Text;
public string AlteredText;
public enum Intent
{
Sample,
q_Faq,
helpdesk_faq,
None
};
public Dictionary<Intent, IntentScore> Intents;
public class _Entities
{
// Instance
public class _Instance
{
}
[JsonProperty("$instance")]
public _Instance _instance;
}
public _Entities Entities;
[JsonExtensionData(ReadData = true, WriteData = true)]
public IDictionary<string, object> Properties { get; set; }
public void Convert(dynamic result)
{
var app = JsonConvert.DeserializeObject<KnowledgeBaseSkillLuis>(JsonConvert.SerializeObject(result));
Text = app.Text;
AlteredText = app.AlteredText;
Intents = app.Intents;
Entities = app.Entities;
Properties = app.Properties;
}
public (Intent intent, double score) TopIntent()
{
Intent maxIntent = Intent.None;
var max = 0.0;
foreach (var entry in Intents)
{
if (entry.Value.Score > max)
{
maxIntent = entry.Key;
max = entry.Value.Score.Value;
}
}
return (maxIntent, max);
}
}
LUISGen is the tool that creates/updates the recognizer class (KnowledgeBaseSkillLuis) in your case.
How do I pull the updated models into my local environment? Do I need to run a botskills or dispatch command to pull the newly published intents into the code, or was I correct in manually updating the intent enumeration with my new skill?
You should use the update_cognitive_models.ps1 script (in Deployments\Scripts folder) with the RemoteToLocal switch. This will update from the online models to the local files.
Do I need to re-publish the Assistant and/or Skill from my local
machine to Azure to get them?
You should republish the new code (for the updated KnowledgeBaseSkillLuis) once it is updated with the script.
More info:
https://microsoft.github.io/botframework-solutions/virtual-assistant/handbook/deployment-scripts/#scripts
https://microsoft.github.io/botframework-solutions/virtual-assistant/handbook/devops/

How to create a never ending background service in Xamarin.Forms?

I am monitoring the user's location every 15 minutes and I just want the application to continue sending the location even if the user closes the application in the taskbar.
I tried this sample but it's in Xamarin.Android https://learn.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services i have to create a dependencyservice but i don't know how.
i have to create a dependencyservice but i don't know how.
First, create an Interface in the Xamarin.forms project:
public interface IStartService
{
void StartForegroundServiceCompat();
}
And then create a new file let's call it itstartServiceAndroid in xxx.Android project to implement the service you want:
[assembly: Dependency(typeof(startServiceAndroid))]
namespace DependencyServiceDemos.Droid
{
public class startServiceAndroid : IStartService
{
public void StartForegroundServiceCompat()
{
var intent = new Intent(MainActivity.Instance, typeof(myLocationService));
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
MainActivity.Instance.StartForegroundService(intent);
}
else
{
MainActivity.Instance.StartService(intent);
}
}
}
[Service]
public class myLocationService : Service
{
public override IBinder OnBind(Intent intent)
{
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
// Code not directly related to publishing the notification has been omitted for clarity.
// Normally, this method would hold the code to be run when the service is started.
//Write want you want to do here
}
}
}
Once you want to call the StartForegroundServiceCompat method in Xamarin.forms project, you can use:
public MainPage()
{
InitializeComponent();
//call method to start service, you can put this line everywhere you want to get start
DependencyService.Get<IStartService>().StartForegroundServiceCompat();
}
Here is the document about dependency-service
For iOS, if the user closes the application in the taskbar, you will no longer be able to run any service. If the app is running, you can read this document about ios-backgrounding-walkthroughs/location-walkthrough
You might want to have a look at Shiny by Allan Ritchie. It's currently in beta but I would still suggest using it, as it will save you a lot of trouble writing this code yourself. Here's a blog post by Allan, explaining what you can do with Shiny in terms of background tasks - I think Scheduled Jobs are the thing you're looking for.

how to wait for the android activity to fully resume after calling ZXing qr scanner

I have an application which when starting requests a qr code from the user, and according to the qr scanned, a different fragment is loaded in the activity
I am using ZXing mobile scanner to do this
unfortunatelly the scanner returns a reply way before it shuts down and returns to the calling activity
this means that when I call the transaction code to replace the current fragment with the new one, the activity is not yet in the foreground so nothing happens
To solve this I created a ManualResetEvent (I'm using Xamarin, but I will use a Semaphore when I have to convert this to Android Studio) that I set before starting the scan , and then reset in the OnResume part of the activity
this seems to solve the problem, but it feels like there is a much better solution
Is there something I am missing?
thanks in advance for any help you can provide
Edit: the code I am currently using
public class MyActivity : Activity {
ManualResetEvent _has_resumed = new ManualResetEvent(false);
.....
protected override void OnResume() {
base.OnResume();
_has_resumed.Set();
}
......
void scan_qr(Action<string> finished_callback) {
#region initialize the scanner
MobileBarcodeScanner scanner = new MobileBarcodeScanner();
MobileBarcodeScanningOptions options = new MobileBarcodeScanningOptions();
options.UseNativeScanning = true; //use native scan
options.AutoRotate = false;//do not rotate the screen
options.PossibleFormats = new List<BarcodeFormat> { BarcodeFormat.QR_CODE }; // only allow qr_codes;
#endregion
#region perform the actual scan, when it finishes return to the main thread and execute the callback
_has_resumed.Reset();
scanner.Scan(this, options) //do scan
.ContinueWith(result => {
_has_resumed.WaitOne();
return result.Result;
})//wait until the activity has resumed
.ContinueWith((task_result) => { //then return the result
Result result = task_result.Result;
if (result == null) {
show_toast( Resource.String.questions_select_error_no_qr_scanned );
} else {
finished_callback(result.Text);
}
}, System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());
#endregion
}
}

How To: Caliburn.Micro.Autofac and Windows Phone

Is there and example, tutorial or anything that shows how to use Caliburn.Micro.Autofac with Windows Phone?
I created a basic application with Caliburn.Micro only, and that runs fine. Then I decided to use Caliburn.Micro.Autofac, so I derived my Bootstrapper from Caliburn.Micro.Autofac.AutofacBootstrapper and called base.Configure() inside the Bootstrapper Configure() method. Now wen I ran the application I get "The type 'AppBootstrapper' was not found." exception.
Appreciate any help.
This is the bootstrapper I wrote for a WP7 project. It's based on Caliburn.Micro.Autofac.AutofacBootstrapper but fixes some bugs.
public class AppBootstrapper : PhoneBootstrapper
{
private IContainer container;
protected void ConfigureContainer(ContainerBuilder builder)
{
// put any custom bindings here
}
#region Standard Autofac/Caliburn.Micro Bootstrapper
protected override void Configure()
{
// configure container
var builder = new ContainerBuilder();
// register phone services
var caliburnAssembly = AssemblySource.Instance.Union(new[] { typeof(IStorageMechanism).Assembly }).ToArray();
// register IStorageMechanism implementors
builder.RegisterAssemblyTypes(caliburnAssembly)
.Where(type => typeof(IStorageMechanism).IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface)
.As<IStorageMechanism>()
.SingleInstance();
// register IStorageHandler implementors
builder.RegisterAssemblyTypes(caliburnAssembly)
.Where(type => typeof(IStorageHandler).IsAssignableFrom(type)
&& !type.IsAbstract
&& !type.IsInterface)
.As<IStorageHandler>()
.SingleInstance();
// The constructor of these services must be called
// to attach to the framework properly.
var phoneService = new PhoneApplicationServiceAdapter(RootFrame);
var navigationService = new FrameAdapter(RootFrame, false);
builder.Register<IPhoneContainer>(c => new AutofacPhoneContainer(c)).SingleInstance();
builder.RegisterInstance<INavigationService>(navigationService).SingleInstance();
builder.RegisterInstance<IPhoneService>(phoneService).SingleInstance();
builder.Register<IEventAggregator>(c => new EventAggregator()).SingleInstance();
builder.Register<IWindowManager>(c => new WindowManager()).SingleInstance();
builder.Register<IVibrateController>(c => new SystemVibrateController()).SingleInstance();
builder.Register<ISoundEffectPlayer>(c => new XnaSoundEffectPlayer()).SingleInstance();
builder.RegisterType<StorageCoordinator>().AsSelf().SingleInstance();
builder.RegisterType<TaskController>().AsSelf().SingleInstance();
// allow derived classes to add to the container
ConfigureContainer(builder);
// build the container
container = builder.Build();
// start services
container.Resolve<StorageCoordinator>().Start();
container.Resolve<TaskController>().Start();
// add custom conventions for the phone
AddCustomConventions();
}
protected override object GetInstance(Type service, string key)
{
object instance;
if (string.IsNullOrEmpty(key))
{
if (container.TryResolve(service, out instance))
return instance;
}
else
{
if (container.TryResolveNamed(key, service, out instance))
return instance;
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", key ?? service.Name));
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return container.Resolve(typeof(IEnumerable<>).MakeGenericType(service)) as IEnumerable<object>;
}
protected override void BuildUp(object instance)
{
container.InjectProperties(instance);
}
private static void AddCustomConventions()
{
ConventionManager.AddElementConvention<Pivot>(Pivot.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Pivot.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Pivot.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
ConventionManager.AddElementConvention<Panorama>(Panorama.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if (ConventionManager
.GetElementConvention(typeof(ItemsControl))
.ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager
.ConfigureSelectedItem(element, Panorama.SelectedItemProperty, viewModelType, path);
ConventionManager
.ApplyHeaderTemplate(element, Panorama.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
}
#endregion
}
EDIT I have created a fork of Caliburn.Micro.Autofac and fixed the issue on GitHub. Hopefully the pull request will be accepted and this will become part of the main repository.
For now, you can access the bootstrapper, and AutofacPhoneContainer from here - https://github.com/distantcam/Caliburn.Micro.Autofac/tree/master/src/Caliburn.Micro.Autofac-WP7
I have implemented a proper version (in my opinion) of Caliburn.Micro.Autofac for Windows Phone. You can download it and test project from my blog. The blog post is in Russian but you'll find the link to ZIP file in the top of the post. The code is too big to post here, so please take from the blog. I've send this to David Buksbaum (the author of Caliburn.Micro.Autofac). Hope he will incorporate it into his code base soon.
UPDATE
What is fixed:
Components realizing IPhoneService and INavigationService services must be instantiated before registering in container.
Realized component implementing IPhoneContainer. Without it you can't use Autofac in Caliburn.Micro.

Adding profile values for auto-generated user

I'm creating a ASP.NET MVC 3.0 website, and have a couple of different database initializations based on whether the site is intended for development, testing, or production. I'm stuck on the testing initialization, as I'm trying to get a test user created. I can get the user to create just fine, however when I try to add some profile values, I get: System.Web.HttpException: Request is not available in this context. Is there a way to add Profile values in a situation where the request isn't going to be available?
Following code is what is being run:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
if (ApplicationServices.GetInitialCatalog() != "tasktracker")
{
Database.SetInitializer(new TaskTrackerDropCreateDatabaseIfModelChanges());
}
else
{
Database.SetInitializer(new TaskTrackerCreateDatabaseIfNotExists());
}
using (var db = new TaskTrackerContext())
{
db.Database.Initialize(false);
}
}
public class TaskTrackerDropCreateDatabaseIfModelChanges : DropCreateDatabaseIfModelChanges<TaskTrackerContext>
{
protected override void Seed(TaskTrackerContext context)
{
// Set up the membership, roles, and profile systems.
ApplicationServices.InstallServices(SqlFeatures.Membership | SqlFeatures.Profile | SqlFeatures.RoleManager);
// Create the default accounts and roles.
if (ApplicationServices.GetInitialCatalog() == "tasktracker_testing")
{
if (Membership.GetUser("testuser", false) == null)
{
Membership.CreateUser("testuser", "password", "testuser#test.com");
MembershipUser user = Membership.GetUser("testuser", false);
user.IsApproved = true;
var profile = ProfileBase.Create("testuser");
profile.SetPropertyValue("FirstName", "test");
profile.SetPropertyValue("LastName", "user");
profile.SetPropertyValue("TimeZone", "US Mountain Standard Time");
profile.Save();
}
}
}
}
Interesting question. Have you looked at using the new Universal Providers? Dunno if you will run into the same httpcontext issue but may be worth a look: http://www.hanselman.com/blog/IntroducingSystemWebProvidersASPNETUniversalProvidersForSessionMembershipRolesAndUserProfileOnSQLCompactAndSQLAzure.aspx
Did you try to do a call of "Initialize()" :
profile.Initialize(username, true)
after your create action to see if the context should be Initialized.
By using Reflector i saw the ProfileBase of Initialize (see below) creates this kind of context from the settings:
public void Initialize(string username, bool isAuthenticated)
{
if (username != null)
{
this._UserName = username.Trim();
}
else
{
this._UserName = username;
}
SettingsContext context = new SettingsContext();
context.Add("UserName", this._UserName);
context.Add("IsAuthenticated", isAuthenticated);
this._IsAuthenticated = isAuthenticated;
base.Initialize(context, s_Properties, ProfileManager.Providers);
}
It seems working here, the SettingsContext() seems taking account of my custom properties declared in the web.config.
Regards,
I come back again because the solution I added with the "Initialize()" function in fact not run really after an other test. So in fact I found a way which runs correctly.
The problem of "request is not available in this context" in application_start in your case could be due to the application mode "Integrated" which is new from II7 instead of the Classic mode.
To see a good explain you ca go on the Mike Volodarsky's blog IIS7 Integrated mode: Request is not available in this context exception in Application_Start .
I copy/paste an extract which could indicate the main reason:
" *This error is due to a design change in the IIS7 Integrated pipeline that makes the request context unavailable in Application_Start event. When using the Classic mode (the only mode when running on previous versions of IIS), the request context used to be available, even though the Application_Start event has always been intended as a global and request-agnostic event in the application lifetime. Despite this, because ASP.NET applications were always started by the first request to the app, it used to be possible to get to the request context through the static HttpContext.Current field.* "
To solve this you can use a workaround that moves your first-request initialization from Application_Start to BeginRequest and performs the request-specific initialization on the first request.
A good example of code is done in his blog :
void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
HttpContext context = app.Context;
// Attempt to peform first request initialization
FirstRequestInitialization.Initialize(context);
}
class FirstRequestInitialization
{
private static bool s_InitializedAlready = false;
private static Object s_lock = new Object();
// Initialize only on the first request
public static void Initialize(HttpContext context)
{
if (s_InitializedAlready)
{
return;
}
lock (s_lock)
{
if (s_InitializedAlready)
{
return;
}
// Perform first-request initialization here
//
// You can use your create profile code here....
//---
s_InitializedAlready = true;
}
}
}

Resources