I am working on xamarin.forms app. I created a xaml page in Global folder. I have a button on the page. I want to open the gallery of android device on click of that button. So I created a method in MainActivity.cs in Android folder. But I don't know how to access this method from button click event from xaml.cs page. I tried to create an object of MainActivity and access the method with the object. But getting null reference exception on "StartActivityForResult". following is my code. Please tell me how I can access the gallery open method in xamarin.forms.
My code is below that I am using to open gallery in MainActivity.cs class:
public void ImagePicker()
{
try
{
var imageIntent = new Intent ();
imageIntent.SetType ("image/*");
imageIntent.PutExtra(Intent.ExtraAllowMultiple,true);
imageIntent.SetAction (Intent.ActionGetContent);
this.StartActivityForResult (Intent.CreateChooser (imageIntent, "Select photo"), 0);
}
catch(Exception ex) {
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult (requestCode, resultCode, data);
if ((requestCode == 0) && (resultCode == Result.Ok) && (data != null))
{
}
}
Regards,
Anand Dubey
Related
I am trying to make a page (lets call it page #2) within my Xamarin.Android app that has a button, when you press on that button the camera app opens. Then you take a picture, accept the picture you took, and then you should be brought back to the page that originally had the camera button on it (page #2), and the image you took will be displayed there under the button.
The problem is that my overridden OnActivityResult() method never gets called after you accept the picture. You press the button, the camera app opens, you take a picture, accept the picture, the camera app closes and you are brought back to the page BEFORE the camera button page (so you're on page #1 now). I think the camera app itself is crashing? I am not sure how to find those logs. Sometimes a popup will show that says "Unfortunately, the camera has stopped." I am guessing that it is the way I am trying to save the photo taken to the phone itself? The only errors I have seen are within the device log, and occasionally it will throw a permission error saying I dont have access to write to the storage device on the phone, but I have the "write" permissions in the manifest file. I am targeting API 26.
Any ideas?
Here is the method for clicking the button:
private int PIC_REQUEST_CODE = 1;
private ImageView imageView;
private Java.IO.File image;
private void Button_Click(object sender, EventArgs e)
{
Intent takePictureIntent = new Intent(MediaStore.ActionImageCapture);
if (takePictureIntent.ResolveActivity(PackageManager) != null)
{
try
{
// Create an image file name
string timeStamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string imageFileName = "JPEG_" + timeStamp + "_";
Java.IO.File storageDir = GetExternalFilesDir(global::Android.OS.Environment.DirectoryPictures);
Java.IO.File image = Java.IO.File.CreateTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
global::Android.Net.Uri photoURI = FileProvider.GetUriForFile(ApplicationContext, PackageName + ".fileprovider", image);
takePictureIntent.PutExtra(MediaStore.ExtraOutput, photoURI);
takePictureIntent.AddFlags(ActivityFlags.GrantWriteUriPermission);
StartActivityForResult(takePictureIntent, PIC_REQUEST_CODE);
}
catch (Exception ex)
{
//Not sure what to do, but here's a break point at least.
Toast.MakeText(this, "blah blah something went wrong", ToastLength.Short).Show();
}
}
}
And here is my simple OnActivityResult that gets skipped/ never called:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == PIC_REQUEST_CODE && resultCode == Result.Ok)
{
Bitmap bitmap = (Bitmap)data.Extras.Get("data");
imageView.SetImageBitmap(bitmap);
}
}
I figured out the current issue of the OnActivityResult() not being called. At the top above the class I was declaring
[Activity(Label = "#string/page_name", ScreenOrientation = ScreenOrientation.Portrait, NoHistory = true)]
The important note here is the "NoHistory=True" apparently that makes the page exit and return back to the previous page. Deleting the "NoHistory=true" worked and made it so the OnActivityResult() gets called. Some documentation about it says "A value of 'true' means that the activity will not leave a historical trace. It will not remain in the activity stack for the task, so the user will not be able to return to it" so I guess that makes sense.
I am working with Xamarin.Forms app. I am trying to add Speech to Text functionality to Xamarin.Forms through a toolbar item click. When I click the button on tool I want the builtin phone speech service to open and convert my speech to text and add to page's label.
Problem: In order to trigger the speech service which is android specific. I need a custom renderer for the toolbar item. So, I can add the code for speech to text in that custom renderer's OnClick method. but I can't seem to find the renderer class for toolbar item.
Here is code of my current attempt at Toolbar Item renderer
VoiceToolbarItemRenderer.cs
[assembly: ExportRenderer(typeof(VoiceToolbarItem), typeof(VoiceToolbarItemRenderer))]
namespace Xamarin_App.Droid
{
public class VoiceToolbarItemRenderer : PageRenderer, Android.Views.View.IOnClickListene
{
private bool isRecording;
private readonly int VOICE = 10;
private MainActivity activity;
private VoiceToolbarItem sharedToolbarItem;
private Toolbar nativeButton;
private SpeechRecognizer mSpeechRecognizer;
private Intent mSpeechRecognizerIntent;
public VoiceToolbarItemRenderer(Context context) : base(context)
{
isRecording = false;
}
protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
{
base.OnElementChanged(e);
activity = this.Context as MainActivity;
nativeButton = new global::Android.Widget.Toolbar(Context);
if (e.OldElement == null)
{
// perform initial setup
//SetNativeControl();
nativeButton.Clickable = true;
nativeButton.Focusable = true;
nativeButton.SetOnClickListener(this);
}
if (e.OldElement != null)
{
activity.ActivityResult -= HandleActivityResult;
}
if (e.NewElement != null)
{
activity.ActivityResult += HandleActivityResult;
sharedToolbarItem = e.NewElement.ToolbarItems as VoiceToolbarItem;
}
}
public void OnClick(Android.Views.View view)
{
try
{
string rec = Android.Content.PM.PackageManager.FeatureMicrophone;
if (rec != "android.hardware.microphone")
{
// no microphone, no recording. Disable the button and output an alert
var alert = new AlertDialog.Builder(Context);
alert.SetTitle("You don't seem to have a microphone to record with");
alert.SetPositiveButton("OK", (sender, e) => {
return;
});
alert.Show();
}
else
{
// create the intent and start the activity
var voiceIntent = new Intent(RecognizerIntent.ActionRecognizeSpeech);
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguageModel, RecognizerIntent.LanguageModelFreeForm);
// if there is more then 1.5s of silence, consider the speech over
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputPossiblyCompleteSilenceLengthMillis, 1500);
voiceIntent.PutExtra(RecognizerIntent.ExtraSpeechInputMinimumLengthMillis, 15000);
voiceIntent.PutExtra(RecognizerIntent.ExtraMaxResults, 1);
// you can specify other languages recognised here, for example
// voiceIntent.PutExtra(RecognizerIntent.ExtraLanguage, Java.Util.Locale.German);
// if you wish it to recognise the default Locale language and German
// if you do use another locale, regional dialects may not be recognised very well
voiceIntent.PutExtra(RecognizerIntent.ExtraLanguage, Java.Util.Locale.Default);
activity.StartActivityForResult(voiceIntent, VOICE);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private void HandleActivityResult(object sender, ActivityResultEventArgs e)
{
if (e.RequestCode == VOICE)
{
if (e.ResultCode == Result.Ok)
{
var matches = e.Data.GetStringArrayListExtra(RecognizerIntent.ExtraResults);
if (matches.Count != 0)
{
string textInput = matches[0];
// limit the output to 500 characters
if (textInput.Length > 500)
textInput = textInput.Substring(0, 500);
sharedToolbarItem.OnTextChanged?.Invoke(textInput);
//textBox.Text = textInput;
}
else
sharedToolbarItem.OnTextChanged?.Invoke("No speech was recognised");
}
}
}
}
}
If someone has an Idea of making toolbarItem's custom renderer or any other suggestions please let me know.
By toolbar I suppose you mean the navigation bar (or the bar where you get the Page Title , right? )
If that is the case , then you have two options:
Wait for the next release of Xamarin.Forms they are working on being able to add stuff to the toolbar. (for example you can have your own back button)
You can make your own toolbar , just make sure to not show the Navigation bar on the NavigationPage, and make your own toolbar (e.g. a horizontal stacklayout (or flexlayout) where you place the title and the buttons you need . and has a background color .
I have tried to do custom renderer for the toolbar and it seems that it is not an easy job.
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;
}
}
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);
}
I am using monodroid to make an application. In the application the user clicks a button which goes to a listview, that listview has multiple options, the user selects on option and then that option is sent back to the original class where a textview is changed to the same text as the option on the listview that was selected.
Below is the class that sends the data
protected override void OnListItemClick(ListView l, View v, int position, long id)
{
int t = labels[position];
String text = t + "";
Intent send = new Intent(this, typeof(CreateVehicle));
send.SetFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop);
send.PutExtra("t", "Data from FirstActivity");
StartActivity(send);
}
Below is the receiving class:
protected override void OnResume()
{
base.OnResume(); // Always call the superclass first.
TextView tvYearChange = FindViewById<TextView>(Resource.Id.tvYearchange);
string text = Intent.GetStringExtra("t") ?? "work";
tvYearChange.SetText(text, null);
}
If someone could figure out why I'm not receiving the data that would be great.
I am not sure if I got your problem correctly but I guess your Main activity is calling an activity that has a ListView and then you want the result back in the Main activity, right?
If so, the code you displayed is not the right way to do what you want. What you are looking for is to use StartActivityForResult and Overriding the onActivityResult method in your main activity.
I don't know exactly how to use Monodroid and C# but I can give you an example in Java that I am sure will help you understand how to get what you want:
Let's say my ListView activity is called myList and extends a ListActivity and my main activity is called MainActivity. Below is the OnListItemClick method of myList:
#Override
protected void onListItemClick(ListView l, View v, int position, long id){
super.onListItemClick(l, v, position, id);
String text = String.valueOf(labels[position]);
// Create the intent with the extras
Intent send = new Intent();
send.putExtra("text_from_list", text);
// Set the result to OK (meaning the extras are put as expected by the caller)
setResult(RESULT_OK, send);
// you need this so the caller (MainActivity) knows that the user completed
// this activity (myList) as expected (clicking on the item to return a result)
// and didn't just leave the activity (back button)
// Close this List Activity
finish();
}
Below is the method in my Main Activity that calls myList:
private void callListActivity(){
Intent call = new Intent(MainActivity.this, myList.class);
startActivityForResult(call, 1);
// The second field is just a number to identify which activity
// returned a result when a result is received (you can have
// several calls to different activities expecting results from
// each one of them). This number is called requestCode.
}
You will have to Override the onActivityResult in your MainActivity with something like this:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Check if the returned data came from myList (requestCode 1) and if
// data was actually received (check if resultCode = RESULT_OK)
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
String text = data.getStringExtra("text_from_list");
// you have to use the same string identifier you used
// when you put the extra in the Intent in myList
TextView tvYearChange = (TextView)findViewById(R.id.tvYearchange);
tvYearChange.setText(text);
}
else {
// Do something if the Activity didn't return data
// (resultCode != RESULT_OK)
}
}
// If you are expecting results from other Activities put more if
// clauses here with the appropriate request code, for example:
// if (requestCode == 2) {
// DoSomething;
// }
// And so on...
}
It shouldn't be hard to modify this Java code to C# code that you can use with Monodroid. Also, take a look at this link from the Android Official Documentation, they have a lot of useful stuff there.
I hope this helps you.
in Receiver Activity get data from intent as below
Intent intent = getIntent();
string text = intent.getStringExtra("t");