【问题标题】:Android O - Notification Channels - Change Vibration Pattern or Sound TypeAndroid O - 通知通道 - 更改振动模式或声音类型
【发布时间】:2017-12-18 07:21:04
【问题描述】:

使用 Android O,我们可以获得“通知渠道”。

据我了解,这意味着用户无法设置通知音或其他相关的通知设置APP内了.

用户需要进入“通知渠道设置”并更改铃声振动这里 strong>,因为 NotificationBuilder 中的所有方法(如 setSound)都将被忽略。

那么真的有NO方法可以通过代码音调变为静音吗?
或者通过代码改变振动模式?

例如,用户可以在我的应用中设置振动模式
或者他可以从警报类型而不是通知类型中选择音调。

这一切都不再可能了吗?
这是正确的还是有什么办法可以做到这一点?

【问题讨论】:

标签: android notifications


【解决方案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 创建它们,则可以创建禁用的频道)。这样做,系统默认的“应用程序正在后台运行”通知将出现,而不是您的前台通知,但用户可以根据需要从状态栏中隐藏此通知。

编辑:在 Android 8.1 中,使用 IMPORTANCE_NONE 创建禁用的频道似乎没有用,因为当您发送通知时该频道将自动启用。最好从一开始就使用 IMPORTANCE_LOW 创建它,并让用户根据需要更改重要性。

【讨论】:

  • 这还能用吗?我试过了,我得到了这个错误 1- 不推荐使用流类型来进行音量控制以外的操作 2- 请参阅 setSound() 的文档,了解使用 android.media.AudioAttributes 来代替使用什么来限定您的播放用例
  • @EduardoBonfa 是的,我在我的应用程序中就是这样做的。我会说您看到的错误与此代码无关,因为例如我没有更改流。
  • 所以我不知道我做得对不对,你能看看这个测试吗? github.com/eduardobonfa/NotificationTest
  • @syntakks 是的,声音将被播放。
  • 这是一个聪明的方法,但在用户启用“请勿打扰”的情况下可能会出现问题,上面发布的代码将忽略此用例。我认为更用户友好的方法是删除然后根据应用内设置重新创建频道,如果通过该应用的系统通知设置在应用外修改了任何设置,则需要在删除之前阅读这些设置,合并 w/ 更改应用内设置,然后在新频道中重新创建。
【解决方案2】:

可能对您的情况有所帮助。如果您显示一个通知,您可以在更新此通知时通过设置.setOnlyAlertOnce(true) 禁用声音。 此解决方案仅在更新通知时有效。

【讨论】:

    【解决方案3】:

    没错,频道一旦创建,就无法再对其进行更改。

    您不能以编程方式修改通知的行为 频道一旦创建并提交给通知管理器

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

    你必须做什么delete the channel 并创建一个具有不同 id 的新的

    【讨论】:

    • 好吧,这确实是倒退了一步。因为我必须从我的应用程序中删除一些功能,因为这不像以前那样灵活:-(
    • whatsapp 就是这样做的。您可以判断,因为“删除的频道”增加了
    【解决方案4】:

    手动播放声音不是一个好主意。最好的方法是拥有两个(或更多)通道 - 一个用于您想要播放的每种声音/振动。

    在代码中,您可以根据要播放的声音来决定使用哪个频道。

    这是我的代码,我根据客户端的设置播放默认通知声音或自定义声音。该代码还负责在 API 26 之前运行 Android 的设备:

    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());
    }
    

    【讨论】:

      【解决方案5】:

      对我来说,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);
      

      【讨论】:

      • 当然可以,但应用通知设置不提供设置振动模式的方法;-)
      • 如何重定向到orio上面的notification category设置?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-11
      • 1970-01-01
      • 1970-01-01
      • 2011-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多