1

I want my app to be a viewer and send target for PDFs but don't want it to create new instances everytime. How do I catch the view intent action in my MainActivity? I tried OnNewIntent() but it doesn't get called. Only if the app wasn't already running, I get the action in OnCreate(). What am I missing?

[Activity (Theme = "@style/MainTheme", Label = "MyPdfViewer", Icon = "@drawable/icon", /*MainLauncher = true, --> SplashActivity is now the MainLauncher */LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
[IntentFilter(new[] { Intent.ActionSend }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = @"application/pdf")]
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataMimeType = @"application/pdf")]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        LoadApplication (new App ());

        // handle clipboard data "send to" or "view document" actions
        if (Intent.Type == "application/pdf")
        {
            HandleSendOrViewAction();
        }

    }

    protected virtual void OnNewIntent()
    {
        var data = this.Intent.Data;  // <-- never called
        // do similar thing like in HandleSendOrViewAction()
    }

    private bool HandleSendOrViewAction()
    {
        // Get the info from ClipData 
        var pdf = Intent.ClipData.GetItemAt(0);

        // Open a stream from the URI 
        byte[] bytes;
        Stream inputStream;
        if (Intent.Action == Intent.ActionSend)
            inputStream = ContentResolver.OpenInputStream(pdf.Uri);
        else if (Intent.Action == Intent.ActionView)
            inputStream = ContentResolver.OpenInputStream(Intent.Data);
        else
            return false;

        using (StreamReader sr = new StreamReader(inputStream))
        {
            MemoryStream ms = new MemoryStream();
            inputStream.CopyTo(ms);
            bytes = ms.ToArray();
        }

        Services.PdfReceiver.Base64Data = Convert.ToBase64String(bytes);  

        return true;
    }
thomiel
  • 2,467
  • 22
  • 37

2 Answers2

0

but don't want it to create new instances everytime.

The standard and singleTop of Launch Mode would create multiple instances. if you do not want create instance every time, you could use singleTask and singleInstance instead.

For singleTop Launch Mode, you need to know, if an instance of the activity already exists at the top of the target task, the system routes the intent to that instance through a call to its onNewIntent() method, rather than creating a new instance of the activity. If the instance of the activity which already exists is not at the top, it would not call onNewIntent() method.

That's why i suggest to use singleTask. The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.

Wendy Zang - MSFT
  • 10,509
  • 1
  • 7
  • 17
  • I experimented with SingleTask as well but it doesn't receive a ``OnNewIntent()`` as well. I noticed that this was an ``override`` in earlier Xamarin.Android versions and now it's a ``virtual``. Is it possible that the ``OnNewIntent()`` doesn't get call in certain cases? ``SingleTask`` seemed too destructive to me to use it (though when I tried it, it didn't replace the running activity at all, which was unexpected). – thomiel Feb 15 '22 at 16:41
  • The ideal case would be that the app just receives the pdf and seamlessly handles the incoming data in a Xamarin.Forms page without destroying anything -- just adding the pdf to a list, for example. I thought about just allowing a new instance to be created, store the pdf in isolated storage, and use an uri for opening my app where I read the file from isolated storage. I hope, I can avoid this bad hack somehow. Maybe it's just that I misunderstand some basic concepts of Android like actions, activities, tasks... – thomiel Feb 15 '22 at 16:49
  • If you do not start a new intent, normally it would not generate a new one. `OnNewIntent` needs to meet some conditions. Different Launch Mode uses the different conditions. – Wendy Zang - MSFT Feb 16 '22 at 08:30
  • Zang But there must be a safe way to catch the clipboard data or file uri in an event somehow. ``OnResume`` doesn't get neither. Where can I find an exhaustive documentation of all the conditions and states I need to consider to just get the data? – thomiel Feb 18 '22 at 12:11
  • Android provides `ClipboardManager`. Check the document for more details. https://developer.android.com/guide/topics/text/copy-paste – Wendy Zang - MSFT Feb 21 '22 at 05:58
0

Using SingleTop launch mode is correct. The reason that OnNewIntent() is not being called is that you have declared it like this:

protected virtual void OnNewIntent()

That isn't correct. The signature is wrong. You need to declare it like this:

protected override void OnNewIntent(Intent intent)
David Wasser
  • 93,459
  • 16
  • 209
  • 274