Recentemente, recebi uma solicitação, como o título, para adicionar uma voz personalizada ao Youmeng Push.
A descrição é bem concisa, como pode ser visto na figura acima, o foco deste requisito é a compatibilidade de versões acima de 8.0. O código de amostra fornecido por Youmeng é o seguinte:
A partir do código acima, ele está realmente definindo uma notificação personalizada, mas o código acima não é viável para sistemas acima de 8.0. O motivo deve ser claro para todos. Um recurso NotificationChannel foi adicionado à barra de notificação acima de 8.0. Se o canal de notificação do canal não estiver definido, a notificação não será exibida.
definir som local
Agora vamos voltar ao foco do nosso requisito desta vez: personalizar o som. Pela análise acima, já sabemos que o som personalizado do Umeng é na verdade uma notificação personalizada, então o som personalizado também faz parte da notificação personalizada, como fazer isso? O método pode ser encontrado procurando na API do NotificationChannel setSound(Uri sound, AudioAttributes audioAttributes)
. Então se apresse e faça o upload do código, vamos configurar um som local primeiro:
//Android 8.0 以上需包添加渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel(newChannel, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
Uri sound = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.ding);
//只能在create一个渠道之前修改铃声,在创建之后不支持修改
notificationChannel.setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT);
manager.createNotificationChannel(notificationChannel);
}
Nota:
Preste atenção especial aos comentários no código acima aqui. Você só pode modificar o toque antes de criar um canal e não suporta modificações após a criação. A descrição foi dada nos comentários do método
de razão específica :setSound
som configurável
Vendo que algumas pessoas podem ter dúvidas aqui, o que devo fazer se quiser alterar o som e tornar o som personalizado configurável? ? ?
Como um NotificationChannel só pode vincular um som, podemos criar um novo canal para vincular um novo som? A resposta é sim. Como o som anterior não é mais usado, precisamos excluir (deleteNotificationChannel(String channelId)) o NotificationChannel anterior. Há um buraco aqui. Quando você o excluirá? Para o primeiro teste, criei um novo canal ao modificar o toque ou a vibração, e apaguei todos os canais antigos antes, mas haverá um bug neste modo, a Notificação que ainda é exibida na barra de status no canal anterior será ser excluído, todos Faça um julgamento, se o canal atual não tiver notificação exibida na barra de status, exclua-o, caso contrário, continue salvando, o código é o seguinte:
Nota:
Para adicionar aqui, o método deleteNotificationChannel realmente não exclui o canal, mas apenas define um sinalizador de exclusão. O processo de análise específico pode ser visto no código-fonte PreferencesHelper.deleteNotificationChannel().
private static void deleteNoNumberNotification(NotificationManager nm, String newChannelId) {
List<NotificationChannel> notificationChannels = nm.getNotificationChannels();
if (Utils.isEmpty(notificationChannels) || notificationChannels.size() < 5) {
return;
}
for (NotificationChannel channel : notificationChannels) {
if (channel.getId() == null || channel.getId().equals(newChannelId)) {
continue;
}
int notificationNumbers = getNotificationNumbers(nm, channel.getId());
Logger.i(TAG, "notificationNumbers: " + notificationNumbers + " channelId:" + channel.getId());
if (notificationNumbers == 0) {
Log.i(TAG, "deleteNoNumberNotification: " + channel.getId());
nm.deleteNotificationChannel(channel.getId());
}
}
}
/**
* 获取某个渠道下状态栏上通知显示个数
*
* @param mNotificationManager NotificationManager
* @param channelId String
* @return int
*/
@RequiresApi(api = Build.VERSION_CODES.O)
private static int getNotificationNumbers(NotificationManager mNotificationManager, String channelId) {
if (mNotificationManager == null || TextUtils.isEmpty(channelId)) {
return -1;
}
int numbers = 0;
StatusBarNotification[] activeNotifications = mNotificationManager.getActiveNotifications();
for (StatusBarNotification item : activeNotifications) {
Notification notification = item.getNotification();
if (notification != null) {
if (channelId.equals(notification.getChannelId())) {
numbers++;
}
}
}
return numbers;
}
Os itens acima são todos os problemas de personalização do som local, então alguém tem outro problema.Se eu quiser configurar um áudio de rede, como devo configurá-lo? ? ?
Configurar áudio de rede
Consulte o método getSound de UmengMessageHandler no Umeng SDK:
Se for áudio de rede, Umeng primeiro baixa o arquivo de áudio para o diretório de cache local e, em seguida, o define.
Aqui eu tive uma ideia preguiçosa no começo, já que Youmeng considerou tão bem para nós, posso usar este método getSound diretamente? Através do experimento, infelizmente, o som não foi reproduzido diretamente pelo método de Youmeng. Aqui está um grande ponto de interrogação para todos. Se algum parceiro souber o motivo, por favor, comente e compartilhe para discutirmos juntos.
Como não é confiável usar diretamente as notícias de Youmeng, você só pode fazê-lo diretamente.De acordo com as ideias fornecidas por Youmeng, baixe o arquivo para o local e configure-o. O diretório que baixei aqui é getExternalCacheDir(), e é estranho que ele possa ser reproduzido ( •̀ ω •́ )y.
Uma pequena análise da reprodução de áudio de notificação
Aqui está outro a acrescentar: Quando não consegui descobrir por que o arquivo baixado de Youmeng não podia ser reproduzido, tive uma ideia preguiçosa de definir diretamente o endereço de áudio http para Uri. O teste descobriu que o endereço Https pode ser reproduzido no celular Xiaomi. Os endereços Http não podem ser reproduzidos. Vamos falar sobre isso aqui, na verdade, isso não é uma solução, ou mesmo uma abordagem errada. por que?
A reprodução do som da notificação é implementada na classe NotificationPlayer, veja o código:
a reprodução do som final ainda é realizada pelo MediaPlayer. O som que definimos para a notificação antes é o DataSource do MediaPlayer. Se for um áudio de rede e depois agrupado por Uri, o player pensará que é um arquivo local e relatará um erro de arquivo não encontrado ao analisar. É incrível. Aqui é claramente um erro, mas você ainda pode ouvir o som tocando em alguns telefones celulares. Portanto, não é recomendável conectar diretamente o endereço Http ao Sound.
Como a parte do código-fonte mencionada acima é apenas um determinado ponto, se você precisar conhecer mais alunos, abra o código-fonte para comparação e compreensão detalhadas.
O código-fonte deste artigo é o seguinte:
public static String UMENG_INTERNET_SOUND_DOWNPATH = MyApplication.getInstance().getExternalCacheDir() + "/sound_cache";
//8.0版本以上自定义通知兼容
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mPushAgent.setMessageHandler(messageHandler);
}
//自定义消息进行处理
private static UmengMessageHandler messageHandler = new UmengMessageHandler() {
@Override
public void dealWithNotificationMessage(Context context, UMessage uMessage) {
if (uMessage.hasResourceFromInternet() && !MessageSharedPrefs.getInstance(context).hasMessageResourceDownloaded(uMessage.msg_id)) {
downInternetSound(context, uMessage);
return;
}
super.dealWithNotificationMessage(context, uMessage);
}
@Override
public Uri getSound(Context context, UMessage uMessage) {
return getCustomSound(context, uMessage);
}
@Override
public Notification getNotification(Context context, UMessage uMessage) {
if (uMessage.builder_id == 1) {
long curTime = System.currentTimeMillis();
String CHANNEL_ID = AppUtils.getAppName();//应用频道Id唯一值, 长度若太长可能会被截断,
String newChannel = CHANNEL_ID + curTime;
String CHANNEL_NAME = AppUtils.getAppName();//最长40个字符,太长会被截断
Uri sound = getSound(context, uMessage);
Intent hangIntent = new Intent(context, MainActivity.class);
PendingIntent hangPendingIntent = PendingIntent.getActivity(context, 1001, hangIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(context, newChannel)
.setContentTitle(uMessage.title)
.setContentText(uMessage.text)
.setSmallIcon(R.mipmap.app_logo)
.setContentIntent(hangPendingIntent)
//.setFullScreenIntent(hangPendingIntent,true)
.setSound(sound)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.app_logo))
.setAutoCancel(true)
.build();
//Android 5.0 以上锁屏通知
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notification.visibility = Notification.VISIBILITY_PUBLIC;
}
//Android 8.0 以上需包添加渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
//只能在create一个渠道之前修改铃声,在创建之后不支持修改
//只能去重新创建一个渠道设置铃声振动
//对于之前创建的渠道,通过deleteNotificationChannel(String channelId)去删除
List<NotificationChannel> channelList = manager.getNotificationChannels();
if (channelList != null && channelList.size() > 0) {
for (NotificationChannel channel : channelList) {
if (!TextUtils.isEmpty(channel.getId()) && channel.getId().startsWith(CHANNEL_ID)) {
manager.deleteNotificationChannel(channel.getId());
}
}
}
NotificationChannel notificationChannel = new NotificationChannel(newChannel, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
//只能在create一个渠道之前修改铃声,在创建之后不支持修改
notificationChannel.setSound(sound, Notification.AUDIO_ATTRIBUTES_DEFAULT);
manager.createNotificationChannel(notificationChannel);
}
Log.d("umeng", "notificationDefault: " + getNotificationDefaults(context, uMessage));
return notification;
}
//默认为0,若填写的builder_id并不存在,也使用默认。
return super.getNotification(context, uMessage);
}
};
/**
* 自定义通知声音
*
* @param context
* @param uMessage
* @return
*/
private static Uri getCustomSound(Context context, UMessage uMessage) {
String soundPath = uMessage.sound;
try {
if (soundPath == null) {
int assetsSound = com.umeng.message.common.d.a(context).j("umeng_push_notification_default_sound");
if (assetsSound > 0) {
soundPath = "android.resource://" + context.getPackageName() + "/" + assetsSound;
}
} else {
if (uMessage.isSoundFromInternet()) {
soundPath = UMENG_INTERNET_SOUND_DOWNPATH + "/" + uMessage.sound.hashCode();
} else {
int assetsSound = com.umeng.message.common.d.a(context).j(uMessage.sound);
if (assetsSound > 0) {
soundPath = "android.resource://" + context.getPackageName() + "/" + assetsSound;
}
}
}
if (soundPath != null) {
Uri soundUri = Uri.parse(soundPath);
return soundUri;
}
} catch (Throwable throwable) {
throwable.toString();
}
return null;
}
private static void downInternetSound(Context context, UMessage uMessage) {
String downPath = UMENG_INTERNET_SOUND_DOWNPATH;
String downFileName = uMessage.sound.hashCode() + "";
OkGoRequest.downLoad(context, uMessage.sound, new FileCallback(downPath, downFileName) {
@Override
public void onSuccess(Response<File> response) {
MessageSharedPrefs.getInstance(context).setMessageResourceDownloaded(uMessage.msg_id);
messageHandler.dealWithNotificationMessage(context, uMessage);
}
@Override
public void onError(Response<File> response) {
super.onError(response);
MessageSharedPrefs.getInstance(context).setMessageResourceDownloaded(uMessage.msg_id);
messageHandler.dealWithNotificationMessage(context, uMessage);
}
});
}
O código fornece apenas ideias e você mesmo pode escrever o código completo para a implementação específica.
Reabastecimento:
Recentemente, descobri que há um som personalizado do telefone celular no Android 11 que não emite som. Abra a configuração do toque na configuração do sistema e descubra que não há toque de notificação, o que é bastante estranho. Um colega propôs um esquema de toque personalizado que imita o QQ e o WeChat e usa o MediaPlayer para tocar música sozinho. Essa também é uma solução. Os alunos que encontrarem esse problema podem experimentá-lo.