Search code examples
androidionic-frameworkservicecapacitorforeground-service

Android Java get MainActivity from sub-module in app project at runtime in foreground service


I am writing an audio player type app with Ionic and Capacitor, everything is fine.

Setup:

  • My application is playing audio on Android.
  • I have added foreground service to my plugin (required by Android to play audio if you leave the app, so you can show a notification to start/stop).
  • This is all fine.

Problem:

  • What isn’t working is getting access to the MainActivity from app from within module capacitor-plugin-remote-audio.MyForegroundService in the project. Is this even possible?

Illustrated screenshot to show the problem:

enter image description here

Code block that's relevant

    /* Used to build and start foreground service. */
    public void startForegroundService()
    {
        Log.d(TAG, "Start foreground service.");

        // Create notification default intent to open the MainActivity from Capacitor app when tapped.
        Intent intent = new Intent(this, "What goes here to get the MainActivity");
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, mainAppIntent, 0);

        createNotificationChannel();

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
            .setOngoing(true)
            .setPriority(NotificationManager.IMPORTANCE_MIN)
            .setCategory(Notification.CATEGORY_SERVICE)
            .setShowWhen(false)
            .setSmallIcon(android.R.drawable.ic_media_play) // TODO: fix this to use the app icon.
            .setContentTitle("Content Title");

        if (mainAppPendingIntent != null) {
            builder.setContentIntent(mainAppPendingIntent);
        }

        addPlayAndPauseButtons(builder);

        startForeground(1, builder.build());
    }


Solution

  • When starting the service:

    Add an extra of the package name (probably don't hard code, but whatever):

    // Start the foreground service + show required notification.
    String packageName = getActivity().getPackageName();
    Intent intent = new Intent(getContext(), MyForegroundService.class);
    intent.putExtra(MyForegroundService.EXTRA_Top_Level_Package_Name, packageName);
    intent.setAction(MyForegroundService.ACTION_START);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        getContext().startForegroundService(intent);
    } else {
        getContext().startService(intent);
    }
    

    In the service:

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if(intent != null)
        {
            String action = intent.getAction();
    
            switch (action)
            {
                case ACTION_START:
                    String packageName = intent.getExtras().get(EXTRA_Top_Level_Package_Name).toString();
                    startForegroundService(packageName);
                    break;
                case ACTION_STOP:
                    stopForegroundService();
                    break;
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }
    

    Start method

    /* Used to build and start foreground service. */
    public void startForegroundService(String packageName)
    {
        Log.d(TAG, "Start foreground service.");
    
        // Create notification default intent to open the MainActivity from Capacitor app when tapped.
        Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
    
        createNotificationChannel();
    
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
            .setOngoing(true)
            .setPriority(NotificationManager.IMPORTANCE_MIN)
            .setCategory(Notification.CATEGORY_SERVICE)
            .setShowWhen(false)
            .setSmallIcon(android.R.drawable.ic_media_play) // TODO: fix this to use the app icon.
            .setContentTitle("Content Title")
            .setContentIntent(pendingIntent);
    
        addPlayAndPauseButtons(builder);
    
        startForeground(1, builder.build());
    }