Android O - каналы уведомлений - изменение шаблона вибрации или типа звука

С Android O мы получаем " Каналы уведомлений ".

Насколько я понимаю, это означает, что пользователь больше не может установить тон уведомления или другие связанные с ним настройки уведомления внутри APP.

Пользователь должен перейти в "Notification Каналов" и изменить тон или вибрации и т.д., потому что здесь все методы NotificationBuilder как setSound становятся игнорироваться.

Таким образом, действительно нет способа изменить тон до молчания с помощью кода?
Или изменить шаблон вибрации с помощью кода?

Например, у пользователя есть возможность установить шаблон вибрации в моем приложении.
Или он может выбирать тональные сигналы из типа тревоги вместо типа уведомления.

Все это невозможно?
Правильно ли это или есть ли способ сделать это?

Ответ 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 с самого начала и позволить пользователям изменять важность, если они этого захотят.

Ответ 2

Могу помочь в вашем случае. Если у вас есть одно уведомление, вы можете отключить звук при обновлении этого уведомления, установив параметр .setOnlyAlertOnce(true). Это решение работает только при обновлении уведомления.

Ответ 3

Это правильно, как только канал создан, вы больше не можете вносить в него изменения.

Вы не можете программным образом изменять поведение канала уведомления после его создания и отправляться в диспетчер уведомлений

https://developer.android.com/preview/features/notification-channels.html

Что бы вам нужно было удалить канал и создать новый с другим идентификатором

Ответ 4

Для меня правильный путь для Android> = 8.0 - скрыть настройки звука/вибрации и перенаправить пользователя в настройки уведомлений приложения, используя это:

  Intent intent = new Intent();
                intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
                intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName());
                context.startActivity(intent);

Ответ 6

Не рекомендуется воспроизводить звук вручную. Лучшим подходом было бы иметь два (или более) channels- по одному для каждого звука/вибрации, которые вы хотели бы играть.

В коде вы можете выбрать, какой канал использовать в зависимости от того, какой звук вы хотите играть.

Вот мой код, где я воспроизвожу либо звук уведомления по умолчанию, либо настраиваемый звук в зависимости от настроек клиента. Код также заботится о устройствах под управлением Android до API 26:

String sound = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("NotificationsSound", getString(R.string.settingsNotificationSiren));
Uri soundUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"+ getApplicationContext().getPackageName() + "/" + R.raw.siren);
NotificationManager mNotificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel mChannel;
String channel_id = Utils.CHANNEL_DEFAULT_ID;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    if (sound.toLowerCase().equals(getString(R.string.settingsNotificationSiren).toLowerCase())) {
        channel_id = Utils.CHANNEL_SIREN_ID;
        mChannel = new NotificationChannel(Utils.CHANNEL_SIREN_ID, Utils.CHANNEL_SIREN_NAME, NotificationManager.IMPORTANCE_HIGH);
        mChannel.setLightColor(Color.GRAY);
        mChannel.enableLights(true);
        mChannel.setDescription(Utils.CHANNEL_SIREN_DESCRIPTION);
        AudioAttributes audioAttributes = new AudioAttributes.Builder()
                .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                .setUsage(AudioAttributes.USAGE_NOTIFICATION)
                .build();
        mChannel.setSound(soundUri, audioAttributes);
    } else {
        mChannel = new NotificationChannel(Utils.CHANNEL_DEFAULT_ID, Utils.CHANNEL_DEFAULT_NAME, NotificationManager.IMPORTANCE_HIGH);
        mChannel.setLightColor(Color.GRAY);
        mChannel.enableLights(true);
        mChannel.setDescription(Utils.CHANNEL_DEFAULT_DESCRIPTION);
    }
    if (mNotificationManager != null) {
        mNotificationManager.createNotificationChannel( mChannel );
    }
}

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, channel_id)
        .setSmallIcon(R.drawable.ic_stat_maps_local_library)
        .setLargeIcon(BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_launcher))
        .setTicker(title)
        .setContentTitle(contentTitle)
        .setContentText(contentText)
        .setAutoCancel(true)
        .setLights(0xff0000ff, 300, 1000) // blue color
        .setWhen(System.currentTimeMillis())
        .setPriority(NotificationCompat.PRIORITY_DEFAULT);

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
    if (sound.toLowerCase().equals(getString(R.string.settingsNotificationSiren).toLowerCase())) {
        mBuilder.setSound(soundUri);
    } else {
        mBuilder.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION));
    }
}

int NOTIFICATION_ID = 1; // Causes to update the same notification over and over again.
if (mNotificationManager != null) {
    mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}

Ответ 7

У меня такая же проблема, когда я хочу обновить звук канала уведомлений.
И тогда я нашел одно решение для звука канала уведомления об обновлении только

Слава Богу!!

Я создал демо Github для обновления различных звуков в одном канале уведомлений.

пожалуйста, проверьте эту ссылку => https://github.com/TejasTrivedi1996/NotificationChannels