/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.dialer.notification; import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.media.AudioAttributes; import android.net.Uri; import android.os.Build.VERSION_CODES; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.RequiresApi; import android.support.annotation.StringDef; import android.support.v4.os.BuildCompat; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import com.android.contacts.common.compat.TelephonyManagerCompat; import com.android.dialer.buildtype.BuildType; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutors; import com.android.dialer.telecom.TelecomUtil; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Objects; /** Contains info on how to create {@link NotificationChannel NotificationChannels} */ public class NotificationChannelManager { private static final String PREFS_FILENAME = "NotificationChannelManager"; private static final String PREF_NEED_FIRST_INIT = "needFirstInit"; private static NotificationChannelManager instance; public static NotificationChannelManager getInstance() { if (instance == null) { instance = new NotificationChannelManager(); } return instance; } /** * Set the channel of notification appropriately. Will create the channel if it does not already * exist. Safe to call pre-O (will no-op). * *
phoneAccount should only be null if channelName is {@link Channel#DEFAULT} or {@link
* Channel#MISSED_CALL} since these do not have account-specific settings.
*/
@TargetApi(26)
public static void applyChannel(
@NonNull Notification.Builder notification,
@NonNull Context context,
@Channel String channelName,
@Nullable PhoneAccountHandle phoneAccount) {
checkNullity(channelName, phoneAccount);
if (BuildCompat.isAtLeastO()) {
NotificationChannel channel =
NotificationChannelManager.getInstance().getChannel(context, channelName, phoneAccount);
notification.setChannelId(channel.getId());
}
}
private static void checkNullity(
@Channel String channelName, @Nullable PhoneAccountHandle phoneAccount) {
if (phoneAccount != null || channelAllowsNullPhoneAccountHandle(channelName)) {
return;
}
// TODO (b/36568553): don't throw an exception once most cases have been identified
IllegalArgumentException exception =
new IllegalArgumentException(
"Phone account handle must not be null on channel " + channelName);
if (BuildType.get() == BuildType.RELEASE) {
LogUtil.e("NotificationChannelManager.applyChannel", null, exception);
} else {
throw exception;
}
}
private static boolean channelAllowsNullPhoneAccountHandle(@Channel String channelName) {
switch (channelName) {
case Channel.DEFAULT:
case Channel.MISSED_CALL:
return true;
default:
return false;
}
}
/** The base Channel IDs for {@link NotificationChannel} */
@Retention(RetentionPolicy.SOURCE)
@StringDef({
Channel.INCOMING_CALL,
Channel.ONGOING_CALL,
Channel.ONGOING_CALL_OLD,
Channel.MISSED_CALL,
Channel.VOICEMAIL,
Channel.EXTERNAL_CALL,
Channel.DEFAULT
})
public @interface Channel {
@Deprecated String ONGOING_CALL_OLD = "ongoingCall";
String INCOMING_CALL = "incomingCall";
String ONGOING_CALL = "ongoingCall2";
String MISSED_CALL = "missedCall";
String VOICEMAIL = "voicemail";
String EXTERNAL_CALL = "externalCall";
String DEFAULT = "default";
}
@Channel
private static final String[] prepopulatedAccountChannels =
new String[] {Channel.INCOMING_CALL, Channel.ONGOING_CALL, Channel.VOICEMAIL};
@Channel
private static final String[] prepopulatedGlobalChannels =
new String[] {Channel.MISSED_CALL, Channel.DEFAULT};
private NotificationChannelManager() {}
public void firstInitIfNeeded(@NonNull Context context) {
if (BuildCompat.isAtLeastO()) {
DialerExecutors.createNonUiTaskBuilder(this::firstInitIfNeededSync)
.build()
.executeSerial(context);
}
}
private boolean firstInitIfNeededSync(@NonNull Context context) {
if (needsFirstInit(context)) {
initChannels(context);
return true;
}
return false;
}
public boolean needsFirstInit(@NonNull Context context) {
return (BuildCompat.isAtLeastO()
&& getSharedPreferences(context).getBoolean(PREF_NEED_FIRST_INIT, true));
}
@RequiresApi(VERSION_CODES.N)
private SharedPreferences getSharedPreferences(@NonNull Context context) {
// Use device protected storage since in some cases this will need to be accessed while device
// is locked
context = context.createDeviceProtectedStorageContext();
return context.getSharedPreferences(PREFS_FILENAME, Context.MODE_PRIVATE);
}
@RequiresApi(26)
public Intent getSettingsIntentForChannel(
@NonNull Context context, @Channel String channelName, PhoneAccountHandle accountHandle) {
checkNullity(channelName, accountHandle);
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(
Settings.EXTRA_CHANNEL_ID, getChannel(context, channelName, accountHandle).getId());
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
return intent;
}
@TargetApi(26)
@SuppressWarnings("AndroidApiChecker")
public void initChannels(@NonNull Context context) {
if (!BuildCompat.isAtLeastO()) {
return;
}
LogUtil.enterBlock("NotificationChannelManager.initChannels");
List