Home | History | Annotate | Download | only in notification
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.dialer.notification;
     18 
     19 import android.annotation.TargetApi;
     20 import android.app.NotificationChannel;
     21 import android.app.NotificationManager;
     22 import android.content.Context;
     23 import android.media.AudioAttributes;
     24 import android.os.Build.VERSION_CODES;
     25 import android.provider.Settings;
     26 import android.support.annotation.NonNull;
     27 import android.support.annotation.Nullable;
     28 import android.support.v4.os.BuildCompat;
     29 import android.telecom.PhoneAccount;
     30 import android.telecom.PhoneAccountHandle;
     31 import android.telecom.TelecomManager;
     32 import android.telephony.TelephonyManager;
     33 import android.text.TextUtils;
     34 import android.util.ArraySet;
     35 import com.android.dialer.common.Assert;
     36 import com.android.dialer.common.LogUtil;
     37 import java.util.ArrayList;
     38 import java.util.List;
     39 import java.util.Set;
     40 
     41 /** Utilities for working with voicemail channels. */
     42 @TargetApi(VERSION_CODES.O)
     43 /* package */ final class VoicemailChannelUtils {
     44   private static final String GLOBAL_VOICEMAIL_CHANNEL_ID = "phone_voicemail";
     45   private static final String PER_ACCOUNT_VOICEMAIL_CHANNEL_ID_PREFIX = "phone_voicemail_account_";
     46 
     47   static Set<String> getAllChannelIds(@NonNull Context context) {
     48     Assert.checkArgument(BuildCompat.isAtLeastO());
     49     Assert.isNotNull(context);
     50 
     51     Set<String> result = new ArraySet<>();
     52     if (isSingleSimDevice(context)) {
     53       result.add(GLOBAL_VOICEMAIL_CHANNEL_ID);
     54     } else {
     55       for (PhoneAccountHandle handle : getAllEligableAccounts(context)) {
     56         result.add(getChannelIdForAccount(handle));
     57       }
     58     }
     59     return result;
     60   }
     61 
     62   static void createAllChannels(@NonNull Context context) {
     63     Assert.checkArgument(BuildCompat.isAtLeastO());
     64     Assert.isNotNull(context);
     65 
     66     if (isSingleSimDevice(context)) {
     67       createGlobalVoicemailChannel(context);
     68     } else {
     69       for (PhoneAccountHandle handle : getAllEligableAccounts(context)) {
     70         createVoicemailChannelForAccount(context, handle);
     71       }
     72     }
     73   }
     74 
     75   @NonNull
     76   static String getChannelId(@NonNull Context context, @Nullable PhoneAccountHandle handle) {
     77     Assert.checkArgument(BuildCompat.isAtLeastO());
     78     Assert.isNotNull(context);
     79 
     80     // Most devices we deal with have a single SIM slot. No need to distinguish between phone
     81     // accounts.
     82     if (isSingleSimDevice(context)) {
     83       return GLOBAL_VOICEMAIL_CHANNEL_ID;
     84     }
     85 
     86     // We can get a null phone account at random points (modem reboot, etc...). Gracefully degrade
     87     // by using the default channel.
     88     if (handle == null) {
     89       LogUtil.i(
     90           "VoicemailChannelUtils.getChannelId",
     91           "no phone account on a multi-SIM device, using default channel");
     92       return NotificationChannelId.DEFAULT;
     93     }
     94 
     95     // Voicemail notifications should always be associated with a SIM based phone account.
     96     if (!isChannelAllowedForAccount(context, handle)) {
     97       LogUtil.i(
     98           "VoicemailChannelUtils.getChannelId",
     99           "phone account is not for a SIM, using default channel");
    100       return NotificationChannelId.DEFAULT;
    101     }
    102 
    103     // Now we're in the multi-SIM case.
    104     String channelId = getChannelIdForAccount(handle);
    105     if (!doesChannelExist(context, channelId)) {
    106       LogUtil.i(
    107           "VoicemailChannelUtils.getChannelId",
    108           "voicemail channel not found for phone account (possible SIM swap?), creating a new one");
    109       createVoicemailChannelForAccount(context, handle);
    110     }
    111     return channelId;
    112   }
    113 
    114   private static boolean doesChannelExist(@NonNull Context context, @NonNull String channelId) {
    115     return context.getSystemService(NotificationManager.class).getNotificationChannel(channelId)
    116         != null;
    117   }
    118 
    119   private static String getChannelIdForAccount(@NonNull PhoneAccountHandle handle) {
    120     Assert.isNotNull(handle);
    121     return PER_ACCOUNT_VOICEMAIL_CHANNEL_ID_PREFIX + ":" + handle.getId();
    122   }
    123 
    124   /**
    125    * Creates a voicemail channel but doesn't associate it with a SIM. For devices with only one SIM
    126    * slot this is ideal because there won't be duplication in the settings UI.
    127    */
    128   private static void createGlobalVoicemailChannel(@NonNull Context context) {
    129     NotificationChannel channel = newChannel(context, GLOBAL_VOICEMAIL_CHANNEL_ID, null);
    130 
    131     TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
    132     PhoneAccountHandle handle =
    133         telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
    134     if (handle == null) {
    135       LogUtil.i(
    136           "VoicemailChannelUtils.createGlobalVoicemailChannel",
    137           "phone account is null, not migrating sound settings");
    138     } else if (!isChannelAllowedForAccount(context, handle)) {
    139       LogUtil.i(
    140           "VoicemailChannelUtils.createGlobalVoicemailChannel",
    141           "phone account is not eligable, not migrating sound settings");
    142     } else {
    143       migrateVoicemailSoundSettings(context, channel, handle);
    144     }
    145     context.getSystemService(NotificationManager.class).createNotificationChannel(channel);
    146   }
    147 
    148   private static List<PhoneAccountHandle> getAllEligableAccounts(@NonNull Context context) {
    149     List<PhoneAccountHandle> handles = new ArrayList<>();
    150     TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
    151     for (PhoneAccountHandle handle : telecomManager.getCallCapablePhoneAccounts()) {
    152       if (isChannelAllowedForAccount(context, handle)) {
    153         handles.add(handle);
    154       }
    155     }
    156     return handles;
    157   }
    158 
    159   private static void createVoicemailChannelForAccount(
    160       @NonNull Context context, @NonNull PhoneAccountHandle handle) {
    161     PhoneAccount phoneAccount =
    162         context.getSystemService(TelecomManager.class).getPhoneAccount(handle);
    163     NotificationChannel channel =
    164         newChannel(context, getChannelIdForAccount(handle), phoneAccount.getLabel());
    165     migrateVoicemailSoundSettings(context, channel, handle);
    166     context.getSystemService(NotificationManager.class).createNotificationChannel(channel);
    167   }
    168 
    169   private static void migrateVoicemailSoundSettings(
    170       @NonNull Context context,
    171       @NonNull NotificationChannel channel,
    172       @NonNull PhoneAccountHandle handle) {
    173     TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
    174     channel.enableVibration(telephonyManager.isVoicemailVibrationEnabled(handle));
    175     channel.setSound(
    176         telephonyManager.getVoicemailRingtoneUri(handle),
    177         new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
    178   }
    179 
    180   private static boolean isChannelAllowedForAccount(
    181       @NonNull Context context, @NonNull PhoneAccountHandle handle) {
    182     PhoneAccount phoneAccount =
    183         context.getSystemService(TelecomManager.class).getPhoneAccount(handle);
    184     if (phoneAccount == null) {
    185       return false;
    186     }
    187     if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
    188       return false;
    189     }
    190     return true;
    191   }
    192 
    193   private static NotificationChannel newChannel(
    194       @NonNull Context context, @NonNull String channelId, @Nullable CharSequence nameSuffix) {
    195     CharSequence name = context.getText(R.string.notification_channel_voicemail);
    196     // TODO: Use a string resource template after v10.
    197     if (!TextUtils.isEmpty(nameSuffix)) {
    198       name = TextUtils.concat(name, ": ", nameSuffix);
    199     }
    200 
    201     NotificationChannel channel =
    202         new NotificationChannel(channelId, name, NotificationManager.IMPORTANCE_DEFAULT);
    203     channel.setShowBadge(true);
    204     channel.enableLights(true);
    205     channel.enableVibration(true);
    206     channel.setSound(
    207         Settings.System.DEFAULT_NOTIFICATION_URI,
    208         new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
    209     return channel;
    210   }
    211 
    212   private static boolean isSingleSimDevice(@NonNull Context context) {
    213     return context.getSystemService(TelephonyManager.class).getPhoneCount() <= 1;
    214   }
    215 
    216   private VoicemailChannelUtils() {}
    217 }
    218