简体   繁体   中英

Open specific page in Ionic (Capacitor) app from Android intent

I have a web/android app, written using Ionic 4 and Capacitor, and I've been trying without success to re-enter the Ionic app to a specific page from a notification raised from an Android service (activated via a capacitor plugin).

Here's the code that creates the notification in the service:

private Notification getNotification() {
    CharSequence contentTitle = "Fun App Background Mode Running";
    CharSequence contentText = "Fun App";
    long notificationTime = System.currentTimeMillis();

    if (_NFC == null) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel channel = new NotificationChannel("funapp", "FunApp", NotificationManager.IMPORTANCE_DEFAULT);
            channel.enableLights(false);
            channel.enableVibration(false);
            channel.setSound(null,null);
            channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
            channel.setShowBadge(true);
            manager.createNotificationChannel(channel);
        }

        Intent notificationIntent = new Intent(this, MainActivity.class);
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addNextIntentWithParentStack(notificationIntent);
        PendingIntent pendingIntent =
            stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

        _NFC = new NotificationCompat.Builder(getApplicationContext(),"funapp")
                .setSmallIcon(R.drawable.ic_sheep_notif)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher_foreground))
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setCategory(NotificationCompat.CATEGORY_SERVICE)
                .setVisibility(NotificationCompat.VISIBILITY_SECRET) 
                .setContentTitle(contentTitle)
                .setContentText(contentText)
                .setStyle(new NotificationCompat.BigTextStyle().bigText(contentText).setBigContentTitle(contentTitle))
                .setContentIntent(pendingIntent)
                .setOngoing(true);

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            _NFC.setPriority(NotificationCompat.PRIORITY_LOW);
        }
    }

    _NFC.setContentTitle(contentTitle);
    _NFC.setContentText(contentText);
    _NFC.setStyle(new NotificationCompat.BigTextStyle().bigText(contentText).setBigContentTitle(contentTitle));
    _NFC.setWhen(notificationTime);
    return _NFC.build();
}

I believe I need to put something in/around the new Intent(this, MainActivity.class) line to get Capacitor / Ionic to initialise the app to the right state, but I cannot work out what that should be!

I've poured over the Capacitor documentation and not been able to find the solution so far, I have a suspicion that I need to send a "view" intent to the activity with some sort of URL?

The current behavior is for it to start what appears to be a completely new instance of the application (it re-loads the splash screen etc), even if the app is still the foreground task on the phone.

UPDATE

My latest attempt is to create the intent like this:

Intent notificationIntent = new Intent(Intent.ACTION_VIEW,
    Uri.parse("http://localhost/event/horse"),
    this, MainActivity.class);

(assuming I have a valid route set up in Ionic/Angular for /event/horse, which I do)

No change though, this still expresses the same behavior as described above (re-entering the splash screen).

In order to realise this behavior, three different parts are needed.

Firstly, your Angular / Ionic code must hook into the events from the Capacitor App plugin and do the navigation when called with an open URL, for example:

import { Plugins, AppUrlOpen } from '@capacitor/core';
import { Router } from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  constructor(
    private platform: Platform,
    private router: Router
  ) {
    this.initializeApp();
  }

  initializeApp() {
    this.platform.ready().then(() => {
      if (this.platform.is('capacitor')) {
        Plugins.SplashScreen.hide();

        // THIS WILL BE USED IF THE APP IS ALREADY OPEN:
        Plugins.App.addListener('appUrlOpen', (urlOpen: AppUrlOpen) => {
          console.log('App URL Open', urlOpen);
          this.navigate(urlOpen.url);
        });
      }

      // THIS WILL BE USED IF THE APP HAS BEEN KILLED AND RE-OPENED:
      this.getLaunchUrl();
    });
  }

  async getLaunchUrl() {
    const urlOpen = await Plugins.App.getLaunchUrl();
    if(!urlOpen || !urlOpen.url) return;
    console.log('Launch URL', urlOpen);
    this.navigate(urlOpen.url);
  }

  navigate(uri: string) {
    // THIS MUST EQUAL THE 'custom_url_scheme' from your Android intent:
    if (!uri.startsWith('net.exampleapp.app:/')) return;
    // Strip off the custom scheme:
    uri = uri.substring(19);
    this.router.navigateByUrl(uri);
  }
}

Then, in the Android side, this is the incantation needed to get a PendingIntent to trigger this behavior:

Intent notificationIntent = getPackageManager()
        .getLaunchIntentForPackage(getPackageName())
        .setPackage(null)
        .setAction(Intent.ACTION_VIEW)
        .setData(Uri.parse(
            getResources().getString(R.string.custom_url_scheme) + 
            "://events/" + _EventId))
        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);

PendingIntent pendingIntent = PendingIntent.getActivity(this, 1234,
        notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

Lastly, in the AndroidManifest.xml for your app, you must also specify the launch mode as SingleTask or SingleTop for MainActivity (either seem to work):

<activity
        android:name=".MainActivity"
        android:launchMode="singleTask"

  android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale"
        android:label="@string/title_activity_main"
        android:theme="@style/AppTheme.NoActionBarLaunch">

With this combination, if the app is still running, the relevant page will be navigated to correctly, and if it is not running, the app will be opened then the page will be navigated to.

Note, however, that this does not set up the "back" stack within the Ionic app sensibly in the case where the app wasn't running, so hitting back will not automatically navigate upwards. But that's a different problem...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM