Xamarin.Android 无法在 Android 10 (Q) 中将录制的自定义声音用于推送通知

Posted

技术标签:

【中文标题】Xamarin.Android 无法在 Android 10 (Q) 中将录制的自定义声音用于推送通知【英文标题】:Xamarin.Android Cannot use recorded custom sound for push notifications in Android 10 (Q) 【发布时间】:2019-10-08 05:00:41 【问题描述】:

这曾经可以工作,录制声音以用于推送通知通道的自定义声音。现在它不再了。 请问有什么变化,为什么不再起作用了,通知进来时没有声音?

为了将录制的声音用于推送通知,我曾经将其复制到外部存储中,这样可以在运行时访问它:

外部路径的路径是这样的:

/storage/emulated/0/customNotificationSoundRecording.wav

这里是完整代码,先录声音:

private async Task RecordAudio()
        
            buttonSave.IsEnabled = false;
            try
            
                if (!recorder.IsRecording)
                                
                    buttonRecord.IsEnabled = false;
                    buttonPlay.IsEnabled = false;               

                    // only if ios
                    if (Device.RuntimePlatform == Device.iOS)
                        DependencyService.Get<IAudioService>().PrepareRecording();


                    Increment = 1 / (double)AppConstants.MAX_RECORD_TIME;
                    Set_Timer();

                    var recordTask = await recorder.StartRecording();

                    buttonRecord.Text = "Stop";
                    buttonRecord.IsEnabled = true;

                    // get the recorded file
                    var recordedAudioFile = await recordTask;
                    // isRecording = false;                  

                    if (recordedAudioFile != null)
                    
                        // first save the file from cache to AppData
                        var recordingFileDestinationPath = Path.Combine(FileSystem.AppDataDirectory, AppConstants.CUSTOM_ALERT_FILENAME);                     

                        //if (File.Exists(recordingFileDestinationPath))
                        if (CustomAlertSoundExists(recordingFileDestinationPath))
                        
                            File.Delete(recordingFileDestinationPath);
                        

                        File.Copy(recordedAudioFile, recordingFileDestinationPath);                     

                        Preferences.Set(AppConstants.CUSTOM_RECORDING_EXISTS_KEY, true);

                        if (Device.RuntimePlatform == Device.iOS)
                        
                            DependencyService.Get<IGroupUserPrefs>().SetBoolValueForKey("isCustomAlert", true);
                        

                        buttonSave.IsEnabled = true;
                    

                    if (secondsElapsed >= AppConstants.MAX_RECORD_TIME)
                    
                        Preferences.Set(AppConstants.RECORDING_LENGTH_SECONDS_KEY, secondsElapsed);
                        secondsElapsed = 0;
                        Reset_Timer();
                    

                    buttonRecord.Text = "Record";
                    buttonPlay.IsEnabled = true;

                
                else // stop button clicked
                                                  

                    buttonRecord.IsEnabled = false;

                    await recorder.StopRecording();
                    buttonSave.IsEnabled = true;

                    // isRecording = false;

                    Reset_Timer();

                    // save last recording length in seconds
                    Preferences.Set(AppConstants.RECORDING_LENGTH_SECONDS_KEY, secondsElapsed);

                    //reset seconds elapsed 
                    secondsElapsed = 0;

                    buttonRecord.IsEnabled = true;
                
            
            catch (Exception ex)
            
                Console.WriteLine(ex.Message);
            
        

然后将录音复制到外部存储,它曾经是它可以用于推送通知声音的唯一位置:

public async void CopyRecordingToExternalStorage(string filePath)
        
            var recordingFileExternalPath = Path.Combine(android.OS.Environment.ExternalStorageDirectory.Path, AppConstants.CUSTOM_ALERT_FILENAME);

             // made some other attempts with different paths.
            // var recordingFileExternalPath = Path.Combine(FileSystem.AppDataDirectory, AppConstants.CUSTOM_ALERT_FILENAME);
            //var recordingFileExternalPath = Path.Combine(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryAlarms).AbsolutePath, AppConstants.CUSTOM_ALERT_FILENAME);

            var storageStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(Plugin.Permissions.Abstractions.Permission.Storage);

            if (storageStatus != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
            
                var results = await CrossPermissions.Current.RequestPermissionsAsync(new[]  Plugin.Permissions.Abstractions.Permission.Storage );
                storageStatus = results[Plugin.Permissions.Abstractions.Permission.Storage];
            

            if (storageStatus == Plugin.Permissions.Abstractions.PermissionStatus.Granted)
            
                try
                
                    if (File.Exists(recordingFileExternalPath))  // if file exists already in external storage
                    
                        File.Delete(recordingFileExternalPath); // erase the file
                    

                    File.Copy(filePath, recordingFileExternalPath); // and overwrite with new one
                
                catch (Exception ex)
                
                    Console.WriteLine(ex.Message);
                
            
            else
            
                UserDialogs.Instance.Alert("Permission to write to External Storage denied, cannot save settings.", "Permission Denied", "Ok");
                         
        

然后我会用这样的声音创建一个频道:

private void createCustomNotificationChannel()
        
            try
            
                // the custom channel
                var urgentChannelName = GetString(Resource.String.noti_chan_custom);
                var customChannelDescription = GetString(Resource.String.noti_chan_custom_description);

                long[] customVibrationPattern =  100, 30, 100, 30, 100, 200, 200, 30, 200, 30, 200, 200, 100, 30, 100, 30, 100, 100, 30, 100, 30, 100, 200, 200, 30, 200, 30, 200, 200, 100, 30, 100, 30, 100 ;

                // Creating an Audio Attribute
                var alarmAttributes = new AudioAttributes.Builder()
                    .SetContentType(AudioContentType.Sonification)
                    .SetUsage(AudioUsageKind.Notification).Build();

                var alarmSourcePath = System.IO.Path.Combine(Android.OS.Environment.ExternalStorageDirectory.Path, AppConstants.CUSTOM_ALERT_FILENAME);

                 // also tried here with other paths
                // var alarmSourcePath = Path.Combine(FileSystem.AppDataDirectory, AppConstants.CUSTOM_ALERT_FILENAME);
                // var alarmSourcePath = System.IO.Path.Combine(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryAlarms).AbsolutePath, AppConstants.CUSTOM_ALERT_FILENAME);

                var urgentAlarmUri = Android.Net.Uri.Parse(alarmSourcePath);

                // checking here if file exists after voice recording and
                //  the bool is true
                bool soundFileExists = File.Exists(alarmSourcePath);

                var chan3 = new NotificationChannel(TERTIARY_CHANNEL_ID, urgentChannelName, NotificationImportance.High)
                
                    Description = customChannelDescription
                ;

                // set the urgent channel properties
                chan3.EnableLights(true);
                chan3.LightColor = Android.Graphics.Color.Red;
                chan3.SetSound(urgentAlarmUri, alarmAttributes);
                chan3.EnableVibration(true);
                chan3.SetVibrationPattern(customVibrationPattern);
                chan3.SetBypassDnd(true);
                chan3.LockscreenVisibility = NotificationVisibility.Public;

                var manager = (NotificationManager)GetSystemService(NotificationService);

                // create chan1  which is the urgent notifications channel
                manager.CreateNotificationChannel(chan3);
            
            catch (Exception ex)
            
                Console.WriteLine(ex.Message);
            
        

【问题讨论】:

【参考方案1】:

为了让用户更好地控制他们的文件并限制文件混乱,默认情况下,面向 Android 10(API 级别 29)及更高版本的应用被授予对外部存储设备或范围存储的范围访问权限。此类应用程序只能看到其应用程序特定的目录(使用Context.getExternalFilesDir() 访问)和特定类型的媒体。使用范围存储是最佳做法,除非您的应用需要访问不在应用特定目录或MediaStore 中的文件。您可以参考Manage scoped external storage access。

如果你不想改变,你想坚持以前的工作。

对于 Android Q,您可以尝试将 android:requestLegacyExternalStorage="true" 添加到清单中的 &lt;application&gt; 元素。

【讨论】:

朱感谢,我也遇到了。我尝试将“android:reguestLegacyExternalStorage="true"” 添加到清单中的应用程序标记,但在 Visual Studio 2019 中构建时出现错误,提示找不到该指令。至于使用 MediaStore,我还没有尝试过,但会研究一下。 @RazvanEmil 你把Target Framework version 设置为Android 10.0 了吗? 朱,其实我现在只是在做这个。以前我没有将目标设置为 Android 10,但现在我做到了。即使 android:requestLegacyExternalStorage="true" 到清单中的 元素,它仍然对我不起作用。我确实将存储保存位置更新为此字符串 externalDirPath = CrossCurrentActivity.Current.AppContext.GetExternalFilesDir(Android.OS.Environment.DirectoryNotifications).AbsolutePath;字符串recordingFileExternalPath = Path.Combine(externalDirPath, AppConstants.CUSTOM_ALERT_FILENAME);照原样,仍然无法正常工作,有什么想法吗? Zhu 老实说,我不知道如何使用 MediaStore,也找不到明确的文档。如何在其中保存文件,以及在创建推送通知通道时如何检索?也许有一个问题,因为我使用的是 wav 文件?另外我认为前面评论中的代码已经存储到 MediaStore 了?! @RazvanEmil 是的,你应该从谷歌渠道安装 Android 10,我可以将它设置为目标框架版本,我确实看到了通知(Android Q 不支持),也许你可以尝试设置首先将wav文件放到Resources/Raw,看看它是否适用于Android Q,确保wav首先适用于Android Q

以上是关于Xamarin.Android 无法在 Android 10 (Q) 中将录制的自定义声音用于推送通知的主要内容,如果未能解决你的问题,请参考以下文章

“重新启动接收器”无法正常工作 android [Xamarin.Android]

Xamarin - 无法通过 Android(和 iOS)访问本地 api

加载程序集时Xamarin Android异常:System.IO.FileNotFoundException:无法加载程序集'Xamarin.Android.Support.Compat&#

无法在 Xamarin Android 上播放本地视频

无法使用 Xamarin.Android 中的 Xamarin.Mobile 组件保存联系人

Xamarin - Android - Visual Studio - 应用程序无法启动