Ответ 1
Вы все же можете предлагать настройку звука и вибрации в своем приложении, но для этого требуется другой подход. Короче говоря, идея состоит в том, чтобы играть звук и вибрацию вручную в Android O вместо использования канала уведомлений (это проще, чем кажется).
Вот как я это сделал:
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId);
// builder.setSmallIcon(...)
// builder.setContentTitle(...)
// builder.setContentText(...)
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// play vibration
vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(VibrationEffect.createWaveform(vibrationPattern, -1));
// play sound
Intent serviceIntent = new Intent(context, SoundService.class);
serviceIntent.setAction("ACTION_START_PLAYBACK");
serviceIntent.putExtra("SOUND_URI", soundUri.toString());
context.startForegroundService(serviceIntent);
// the delete intent will stop the sound when the notification is cleared
Intent deleteIntent = new Intent(context, SoundService.class);
deleteIntent.setAction("ACTION_STOP_PLAYBACK");
PendingIntent pendingDeleteIntent =
PendingIntent.getService(context, 0, deleteIntent, 0);
builder.setDeleteIntent(pendingDeleteIntent);
} else {
builder.setVibrate(vibrationPattern);
builder.setSound(soundUri);
}
notificationManager.notify(notificationId, builder.build());
SoundService.class - это место, где я воспроизвожу звук с помощью MediaPlayer:
public class SoundService extends Service {
MediaPlayer mMediaPlayer;
@Override
public IBinder onBind(Intent intent) {
return null;
}
public int onStartCommand(Intent intent, int flags, int startId) {
// foreground notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this, otherChannelId);
builder.setSmallIcon(...)
.setContentTitle(...)
.setContentText(...)
.setAutoCancel(true);
startForeground(foregroundNotificationId, builder.build());
}
// check action
String action = intent.getAction();
switch (action) {
case "ACTION_START_PLAYBACK":
startSound(intent.getStringExtra("SOUND_URI"));
break;
case "ACTION_STOP_PLAYBACK":
stopSound();
break;
}
// service will not be recreated if abnormally terminated
return START_NOT_STICKY;
}
private void startSound(String uriString) {
// parse sound
Uri soundUri;
try {
soundUri = Uri.parse(uriString);
} catch (Exception e) {
cleanup();
return;
}
// play sound
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
cleanup();
}
});
}
try {
mMediaPlayer.setDataSource(this, soundUri);
mMediaPlayer.prepareAsync();
} catch (Exception e) {
cleanup();
}
}
private void stopSound() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
cleanup();
}
private void cleanup() {
stopSelf();
}
}
рекомендации
- Создайте свой канал уведомлений с IMPORTANCE_DEFAULT (для пользователя это "Высокий"), нулевой звук (
setSound(null,null)
) и нулевая вибрация (setVibrationPattern(null)
) и объясните в описании канала, что это рекомендуемая настройка во избежание конфликтов с собственной настройкой приложения. - Превратите все это в свою пользу: вместо удаления функции дайте пользователям новую. Вы можете дать им возможность использовать свои функции настройки или функции канала уведомлений (например, вы можете проверить текущее значение канала и в зависимости от уровня, который вы можете использовать, или что-то другое).
Уведомление о начале работы
Начиная Android O, службы, запущенные из фона, должны быть запущены в качестве служб переднего плана. Это означает, что для SoundService требуется предварительное уведомление. У вас есть несколько вариантов для этого:
-
Создайте приятное уведомление переднего плана с помощью кнопки, которая говорит "Остановить воспроизведение", чтобы пользователь мог остановить звук, не удаляя уведомление, которое его запустило.
-
Создайте простое уведомление и отправьте его на отключенный канал (вы можете создавать отключенные каналы, если вы создаете их с помощью параметра IMPORTANCE_NONE). При этом по умолчанию будет отображаться уведомление по умолчанию "Приложение работает в фоновом режиме", вместо уведомления переднего плана, но пользователи могут скрыть это уведомление из строки состояния, если они этого захотят.
EDIT: в Android 8.1 кажется, что создание отключенного канала с IMPORTANCE_NONE не является полезным, так как канал будет включен автоматически при отправке уведомления. Возможно, лучше создать его с IMPORTANCE_LOW с самого начала и позволить пользователям изменять важность, если они этого захотят.