ionic-team / capacitor-plugins

Official plugins for Capacitor ⚡️
485 stars 562 forks source link

[Push Notifications Sound] Files in /res/raw not found #1552

Closed Wilmer-SHF closed 10 months ago

Wilmer-SHF commented 1 year ago

Bug Report

Plugin(s)

Push Notification

Capacitor Version

Capacitor 4.7.3

Platform(s)

The android release bundle is affected

Current Behavior

An audio.mp3 file has been successfully added to the /res/raw folder. In the debug bundle everything works correctly, the apk stores this file in the path /res/raw/audio.mp3 and the notification sound works correctly. But when doing the release bundle, and analyzing the apk, the file is not found in /res/raw/audio.mp3, in fact, the raw folder no longer exists. Therefore, the app does not play the notification sound.

Expected Behavior

Is there a way to ensure that the audio.mp3 file exists in the folder that is needed? or optionally you can select an audio file from the application's assets folder.

Code Reproduction

Service inside code app.

async initializeNotification() {
    await PushNotifications.createChannel({
      id: 'citix-service-channel',
      name: 'Servicios',
      description: 'Nuevo servicio disponible',
      sound: 'audio.mp3',
      importance: 5,
      visibility: 1,
      vibration: true,
    });
}

Additional Context

This is the configuration tried so that the audio.mp3 file is not removed from the bundle in release. It has not achieved any effect.

Keep file, android/app/src/main/res/raw/keep.xml

<?xml version="1.0" encoding="utf-8"?>
<resources tools:keep="@raw/audio.mp3, @raw/audio, @raw/*"
  xmlns:tools="http://schemas.android.com/tools">
</resources>

Proguard rules android/app/proguard-rules.pro

-keepclassmembers class **.R$* {public static <fields>;}
-keep class **.R$*
josuelmm commented 1 year ago

My Example

Code in app.component.ts async createChannelGeneral(){ let dataChannel:any = { description: "", id: "general", name: "General", importance: 4, lightColor: "#FFFFFF", lights: true, visibility:0, vibration: true, sound:'beep.mp3' }; await PushNotifications.createChannel(dataChannel).then((data:any) =>{}).catch((err) => {}); }

File copy_files_android.js

`#!/usr/bin/env node var fs = require('fs'); var path = require('path');

try { var dirpath = './android/app/src/main/res/raw/'; fs.mkdirSync(dirpath, { recursive: true }); } catch (error) {

} var filestocopy = [{ "../resources/beep.mp3": "../android/app/src/main/res/raw/beep.mp3" }];

var path = require('path'); var rootdir = path.resolve(__dirname);

filestocopy.forEach(function(obj) { Object.keys(obj).forEach(function(key) { var val = obj[key]; var srcfile = path.join(rootdir, key); var destfile = path.join(rootdir, val); console.log("copying " + srcfile + " to " + destfile); var destdir = path.dirname(destfile); if (fs.existsSync(srcfile) && fs.existsSync(destdir)) { fs.createReadStream(srcfile).pipe( fs.createWriteStream(destfile)); } }); });`

Put file beep.mp3 in folder resources in root

Modify package.json in scripts, add this line "capacitor:sync:after": "node scripts/copy_files_android.js"

josuelmm commented 1 year ago

File package.json image

File app.component.js image

File copy_files_android.js image

Ionitron commented 1 year ago

This issue may need more information before it can be addressed. In particular, it will need a reliable Code Reproduction that demonstrates the issue.

Please see the Contributing Guide for how to create a Code Reproduction.

Thanks! Ionitron 💙

Wilmer-SHF commented 1 year ago

Answer to josuelmm

The problem is not copying the files dynamically into the res/raw folder. The file I want to add is only one, and I can manually copy it into the respective folder. The problem, in this case audio.mp3, is that the file no longer exists in the release bundle.

I show how the folder structure is inside the apk in a bundle made with debug. Notice how the audio.mp3 file exists.

.
└── apk
    ├── assets
    │   └── ...
    ...
    ...
    │   └──  ...
    └── res
        ├── color
        ├── color-night-v8
        ├── color-v21
        ├── color-v23
        └── color-v31
        └── raw
            ├──  audio.mp3

Now I show how is the folder structure inside the apk in a bundle made with release. See how the raw folder does not exist

.
└── apk
    ├── assets
    │   └── ...
    ...
    ...
    │   └──  ...
    └── res
        ├── color
        ├── color-night-v8
        ├── color-v21
        ├── color-v23
        └── color-v31

This means that the push notification will not sound with the custom audio.

Replication method

This can be easily replicated, without the need to use the push notification plugin (but the problem affects this plugin, so it needs to be fixed).

To replicate:

  1. Start an ionic app: ionic start
  2. Configure capacitor: ionic integrations enable capacitor
  3. Add android platform: ionic capacitor add android
  4. Create raw file: create audio.mp3 in /android/app/src/main/res/raw/
  5. Build: ionic build --release && npx cap copy && npx cap sync && npx cap open android
  6. Create apk signed as release .
  7. Unzip apk: unzip *.apk -d apk
  8. Check if the audio.mp3 file has been added to the apk or no (If not added, push notification will not sound): find . -iname '*audio.mp3*'
WilmerRS commented 1 year ago

Here I present a repository with a simple project of an ionic app that reproduces the mentioned error.

https://github.com/WilmerRS/ionic-push-notification-raw-files

Build, create apk as release and review the generated apk.

Solution attempt

I thought if you can conditionally change where the audio file is taken from, it could be passed to ionic app assets. However it doesn't work for me.

File: NotificationChannelManager.java file inside the plugin.

  ...
  public void createChannel(JSObject channel) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
      ...
      String sound = channel.getString(CHANNEL_SOUND, null);
      if (sound != null && !sound.isEmpty()) {
        AudioAttributes audioAttributes = new AudioAttributes.Builder()
          .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
          .setUsage(AudioAttributes.USAGE_NOTIFICATION)
          .build();
        Uri soundUri = Uri.parse("file:///android_asset/public/assets/" + sound);
        notificationChannel.setSound(soundUri, audioAttributes);
      }
      notificationManager.createNotificationChannel(notificationChannel);
    }
  }
  ...
josuelmm commented 1 year ago

I have tried creating several channels and each one assigning a different sound, even assigning a sound to the default channel and everything is correct, without modifying the android code

WilmerRS commented 1 year ago

Any idea why in my release build there is no res/raw folder left? Again I tell you that in the debug build it works correctly and the sound does sound.

Again I share this simple test project in which this is evidenced. Am I missing some configuration? https://github.com/WilmerRS/ionic-push-notification-raw-files

If I don't get the audio.mp3 file to exist within res/raw, the custom notification sound will never play.

jcesarmobile commented 10 months ago

I can reproduce, but I can also reproduce on a native Android app without Capacitor involved, so looks like it's a bug on Android Studio or the intended behavior. Something on the signed apk creation is renaming the .mp3 file, if you inspect the generated apk you'll see the .mp3 file is there, but with a different name.

I've tried a few configurations such as the keep.xml file that the provided sample app already included and some proguard rules but none of them worked.

The only thing that worked was to create a signed AAB (Android App Bundle) instead of an APK and that keeps the mp3 name. But not sure if later on Google Play will present the same problem once the AAB gets signed there as I think they convert it to .apk.

You should report the issue to Google.

ionitron-bot[bot] commented 10 months ago

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of the plugin, please create a new issue and ensure the template is fully filled out.