How to grab intent on initial launch of application - c#

I currently have a xamarin application. I redirect the client based on what intent is received. It works perfectly when the app in in the background or foreground.
The issue I currently have is the redirect bit of code doesn't trigger when the app is removed from the system tray.
protected async override void OnNewIntent(Intent intent)
{
var data = intent.Data; // this returns testurl.com/?firstname=john&lastname=doe
App.Firstname= intent.GetStringExtra("firstname");
App.Lastname= intent.GetStringExtra("lastname");
base.OnNewIntent(intent);
}
May I ask how do I keep my intents on app launch? I am unsure how to even debug this, when the app is removed from the system tray, visual studio stops running.

You can use the Bundle that you get from your MainActivity OnCreate method:
protected override void OnCreate(Bundle bundle)
{
Android.Net.Uri uri = Intent.Data;
if (uri != null)
{
App.Firstname = uri.GetQueryParameter("firstname");
App.Lastname = uri.GetQueryParameter("lastname");
}
}

Related

How to select multiple audio and/or video files in mobile device using xamarin.forms?

I am writing audio player in Xamarin, I want to create a playlist and save. To create playlist, I want to select audio/video files. I come to know that there is no FileUpload kind of control in Xamarin.
I'm referring Jason's media plugin https://github.com/jamesmontemagno/MediaPlugin , I want to use this plugin or any other plugin for creating playlist. I don't know how to configure/change to load audio/video files.
I have added nuget package and trying to configure below information:
Android Current Activity Setup
This plugin uses the Current Activity Plugin to get access to the current Android Activity. Be sure to complete the full setup if a MainApplication.cs file was not automatically added to your application. Please fully read through the Current Activity Plugin Documentation. At an absolute minimum you must set the following in your Activity's OnCreate method:
csharp
Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity = this;
==========================
I know the project I have created is not Xamarin.Android, it is simple .NET 2.0 Core project for mobile application.
I'm referring Jason's media plugin https://github.com/jamesmontemagno/MediaPlugin , I want to use this plugin or any other plugin for creating playlist.
After you added NuGet package, you need to add WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE permissions and add Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity = this; in OnCreate method. Also need override OnRequestPermissionsResult() method.
Here is the code:
using Plugin.Permissions;
using Plugin.Permissions.Abstractions;
namespace selectmultipleaudio
{
[Activity(Label = "selectmultipleaudio", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity = this;
Button button = FindViewById<Button>(Resource.Id.button1);
button.Click += Button_ClickAsync;
}
private async void Button_ClickAsync(object sender, System.EventArgs e)
{
var crossMedia = CrossMedia.Current;
var media = await crossMedia.PickVideoAsync();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
And you could use intent to select multiple video file. For example:
private void Button_Click(object sender, System.EventArgs e)
{
var videoIntent = new Intent(
Intent.ActionPick);
videoIntent.SetType("video/*");
videoIntent.PutExtra(Intent.ExtraAllowMultiple, true);
videoIntent.SetAction(Intent.ActionGetContent);
((Activity)this).StartActivityForResult(
Intent.CreateChooser(videoIntent, "Select videos"), 0);
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
if (data.ClipData != null)
{
for (var i = 0; i < data.ClipData.ItemCount; i++)
{
var video = data.ClipData.GetItemAt(i);
Uri videouri = video.Uri;
var path = videouri.Path;
}
}
else
{
Uri videouri = data.Data;
var path = videouri.Path;
}
}

xamarin forms app link - open PCL view from ios app delegate

In a Xamarin Forms cross platform app I can open the app from an external email app link.
It opens in android just fine, by adding an intent to the manifest, then within the activity which starts, I create another intent to fire the main activity
public class AppLinkActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
string code = null;
if (Intent.Data.Query != null)
{
code = Intent.Data.Query.Substring(Intent.Data.Query.LastIndexOf('=') + 1);
}
if (Intent.Data != null)
{
var uri = Intent.Data;
if (uri != null)
{
Intent i = new Intent(this, typeof(MainActivity));
i.AddFlags(ActivityFlags.ReorderToFront);
i.PutExtra("code", code);
i.PutExtra("flag", true);
this.StartActivity(i);
}
}
this.FinishActivity(0);
}
}
In ios, the applink triggers the override of OpenUrl in the app delegate, but I'm not sure how to navigate to a particular PCL page from here, what happens is the app opens at it's last open page
public override bool OpenUrl(UIApplication app, NSUrl url, string sourceApp, NSObject annotation)
{
string _uri = url.ToString();
string code = _uri.Substring(_uri.LastIndexOf('=') + 1);
LoadApplication(new App(true, code));
return true;
}
Can anyone point me in the right direction with this? All I really need to do is, from the OpenUrl method, navigate to a view within the PCL
for anyone interested, I sorted this by replacing
LoadApplication(new App(true, code));
with
App.Current.MainPage = enterPin();
which calls
public Page enterPin()
{
return new EnterPinPage(SimpleIoc.Default.GetInstance<ISecureStorage>(), code, 1);
}

How do I get PendingIntent using the MvvmCross 5 IMvxNavigationService?

I have a method I used in MvvmCross 4.x that was used with the NotificationCompat.Builder to set a PendingIntent of a notification to display a ViewModel when the notification is clicked by the user. I'm trying to convert this method to use the MvvmCross 5.x IMvxNavigationService but can't see how to setup the presentation parameters, and get a PendingIntent using the new navigation API.
private PendingIntent RouteNotificationViewModelPendingIntent(int controlNumber, RouteNotificationContext notificationContext, string stopType)
{
var request = MvxViewModelRequest<RouteNotificationViewModel>.GetDefaultRequest();
request.ParameterValues = new Dictionary<string, string>
{
{ "controlNumber", controlNumber.ToString() },
{ "notificationContext", notificationContext.ToString() },
{ "stopType", stopType }
};
var translator = Mvx.Resolve<IMvxAndroidViewModelRequestTranslator>();
var intent = translator.GetIntentFor(request);
intent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask);
return PendingIntent.GetActivity(Application.Context,
_notificationId,
intent,
PendingIntentFlags.UpdateCurrent);
}
The RouteNotificationViewModel does appear when I click the notification but Prepare and Initialize are not being called. What is necessary to convert this method from MvvmCross 4.x style of navigation to MvvmCross 5.x style of navigation?
It's possible to do this in MvvmCross 5+ but it's not as clean as it previously was.
For starters you want to specify the singleTop launch mode for your activity:
[Activity(LaunchMode = LaunchMode.SingleTop, ...)]
public class MainActivity : MvxAppCompatActivity
Generate the notification PendingIntent like this:
var intent = new Intent(Context, typeof(MainActivity));
intent.AddFlags(ActivityFlags.SingleTop);
// Putting an extra in the Intent to pass data to the MainActivity
intent.PutExtra("from_notification", true);
var pendingIntent = PendingIntent.GetActivity(Context, notificationId, intent, 0);
Now there are two places to handle this Intent from MainActivity while still allowing the use of MvvmCross navigation service:
If the app was not running while the notification was clicked then OnCreate will be called.
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
if (bundle == null && Intent.HasExtra("from_notification"))
{
// The notification was clicked while the app was not running.
// Calling MvxNavigationService multiple times in a row here won't always work as expected. Use a Task.Delay(), Handler.Post(), or even an MvvmCross custom presentation hint to make it work as needed.
}
}
If the app was running while the notification was clicked then OnNewIntent will be called.
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
if (intent.HasExtra("from_notification"))
{
// The notification was clicked while the app was already running.
// Back stack is already setup.
// Show a new fragment using MvxNavigationService.
}
}

Xamarin.Android app resumed by shortcut intent - Master and Detail must be set before adding MasterDetailPage to a container

I have Xamarin.Android todo list mobile app using Prism.
The problem is:
In android system, I can create shortcut to open specific list in
app.
When I open app, and press home button, it remains on background
(thats ok)
When I then run app from desktop shortcut, it opens
android activity and when I create new PrismApplication (
LoadApplication(new App()); ) everything is running OK, but after
creating viewmodel for view, app is still using old viewmodel from
before.
I made this workaroud and I use same instance of PrismApplication:
static App xamApp;
protected override void OnCreate(Bundle bundle)
{
if (xamApp == null)
{
Forms.Init(this, bundle);
xamApp = new App();
}
LoadApplication(xamApp);
xamApp.Redirect(Intent.GetStringExtra("ListID"));
}
Now, problem is redirecting. This code:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}
leads to the error:
System.InvalidOperationException: Master and Detail must be set before adding MasterDetailPage to a container.
Prism should take care of Binding of Detail in MasterDetailPage by the "TodoList" from NavigateAsync uri.
Does enyone know what can be the problem here?
So I finally got it working.
First I used LaunchMode = LaunchMode.SingleTask in my ActivityAttribute of MainActivity
[Activity(Label = "..", LaunchMode = LaunchMode.SingleTask, Icon = "#drawable/icon", Theme = "#style/MainTheme", MainLauncher = true]
public class MainActivity : FormsAppCompatActivity
Then I used OnNewIntent method of FormsAppCompatActivity so after app is on backgroud, only this event is launched :
protected override void OnNewIntent(Intent intent)
{
var listId = intent.GetStringExtra("ListID");
((App)App.Current).Redirect(listId);
}
Now even $"MainNavigationPage/TodoList?id={listId}" works
Based on the info you provided, I am assuming that when the app is launched again, it is already running, your previous MasterDetail page is already on the stack. IN your reset method, you want to reset your navigation stack to the new uri passing in the parameter. IN this case, you should use an absolute uri. This means try adding a "/" prefix to your uri. So something like this:
public void Redirect(string listId)
{
NavigationService.NavigateAsync($"/MainPage/MainNavigationPage/TodoList?id={listId}", animated: false);
}

Loading screen for GoogleMaps activity

I'm trying to implement a loading screen since GoogleMaps sometimes takes ~2 seconds to load. I tried using a RelativeLayout and putting the ImageView above the map fragment and making it ViewStates.Gone once the map loads. However, I noticed that the loading is happening on base.OnCreateBundle and not on SetUpMap() and now I'm clueless how to implement it.
private GoogleMap mMap;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Map);
mSplash = FindViewById<ImageView>(Resource.Id.splash);
SetUpMap();
}
private void SetUpMap()
{
if (mMap == null)
{
FragmentManager.FindFragmentById<MapFragment(Resource.Id.map).GetMapAsync(this);
}
}
public void OnMapReady(GoogleMap googleMap)
{
mSplash.Visibility = ViewStates.Gone;
mMap = googleMap;
mMap.MapType = GoogleMap.MapTypeSatellite;
(...)
}
If I'm not mistaken, OnMapReady is meant to be used to verify that Google Play Services is installed and active on the device. As the documentation puts it:
If Google Play services is not installed on the device, the user will
be prompted to install it, and the onMapReady(GoogleMap) method will
only be triggered when the user has installed it and returned to the
app.
OnMapReady Documentation
Alternatively, the OnMapLoaded function is meant to be called after the map has finished loading its tiles.
Called when the map has finished rendering. This will only be called
once. You must request another callback if you want to be notified
again.
OnMapLoaded Documentation
You can implement the OnMapLoaded function by first setting the callback once you have a reference to the map
mMap.setOnMapLoadedCallback(this);
Finish by simply overriding the OnMapLoaded function and handling it accordingly
#Override
public void onMapLoaded() {
if (mMap != null) {
mSplash.Visibility = ViewStates.Gone;
}
}
Remember that if you need to load the map again and need to display another splash screen, you will first need to set the callback again, as onMapLoaded is only called once per callback.

Categories