Messaging: Implement per conversation channels. Update API level to 28.

* Get rid of in-app settings for notifications, replaced by channels features
* Remove all the notification plumbing and dead code

Test: m, manual

Signed-off-by: Luca Stefani <luca.stefani.ge1@gmail.com>
Signed-off-by: Joey <joey@lineageos.org>
Signed-off-by: Danny Baumann <dannybaumann@web.de>
Signed-off-by: Arne Coucheron <arco68@gmail.com>
Signed-off-by: Lyubo <lyubodzhamov@gmail.com>
Change-Id: Idb39ca32751d40b3376934775d2119dd6cc7e297
This commit is contained in:
Luca Stefani
2024-09-09 05:46:38 +03:00
committed by Michael Bestas
parent 2fa1f81707
commit 4246987beb
66 changed files with 249 additions and 942 deletions
@@ -171,8 +171,7 @@ public class BugleDatabaseOperations {
final ArrayList<ParticipantData> participants =
getConversationParticipantsFromRecipients(recipients, refSubId);
return getOrCreateConversation(db, threadId, senderBlocked, participants, false, false,
null);
return getOrCreateConversation(db, threadId, senderBlocked, participants);
}
/**
@@ -190,7 +189,7 @@ public class BugleDatabaseOperations {
Assert.isNotMainThread();
final ArrayList<ParticipantData> recipients = new ArrayList<>(1);
recipients.add(recipient);
return getOrCreateConversation(db, threadId, senderBlocked, recipients, false, false, null);
return getOrCreateConversation(db, threadId, senderBlocked, recipients);
}
/**
@@ -200,15 +199,11 @@ public class BugleDatabaseOperations {
* @param threadId The message's thread
* @param archived Flag whether the conversation should be created archived
* @param participants list of conversation participants
* @param noNotification If notification should be disabled
* @param noVibrate If vibrate on notification should be disabled
* @param soundUri If there is custom sound URI
* @return a conversation id
*/
@DoesNotRunOnMainThread
public static String getOrCreateConversation(final DatabaseWrapper db, final long threadId,
final boolean archived, final ArrayList<ParticipantData> participants,
boolean noNotification, boolean noVibrate, String soundUri) {
final boolean archived, final ArrayList<ParticipantData> participants) {
Assert.isNotMainThread();
// Check to see if this conversation is already in out local db cache
@@ -231,8 +226,7 @@ public class BugleDatabaseOperations {
BugleDatabaseOperations.getOrCreateParticipantInTransaction(db, self);
// Create a new conversation
conversationId = BugleDatabaseOperations.createConversationInTransaction(
db, threadId, conversationName, selfId, participants, archived,
noNotification, noVibrate, soundUri);
db, threadId, conversationName, selfId, participants, archived);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
@@ -357,15 +351,11 @@ public class BugleDatabaseOperations {
* @param threadId The message's thread
* @param selfId The selfId to make default for this conversation
* @param archived Flag whether the conversation should be created archived
* @param noNotification If notification should be disabled
* @param noVibrate If vibrate on notification should be disabled
* @param soundUri The customized sound
* @return The existing conversation id or new conversation id
*/
static String createConversationInTransaction(final DatabaseWrapper dbWrapper,
final long threadId, final String conversationName, final String selfId,
final List<ParticipantData> participants, final boolean archived,
boolean noNotification, boolean noVibrate, String soundUri) {
final List<ParticipantData> participants, final boolean archived) {
// We want conversation and participant creation to be atomic
Assert.isTrue(dbWrapper.getDatabase().inTransaction());
boolean hasEmailAddress = false;
@@ -389,15 +379,6 @@ public class BugleDatabaseOperations {
if (archived) {
values.put(ConversationColumns.ARCHIVE_STATUS, 1);
}
if (noNotification) {
values.put(ConversationColumns.NOTIFICATION_ENABLED, 0);
}
if (noVibrate) {
values.put(ConversationColumns.NOTIFICATION_VIBRATION, 0);
}
if (!TextUtils.isEmpty(soundUri)) {
values.put(ConversationColumns.NOTIFICATION_SOUND_URI, soundUri);
}
fillParticipantData(values, participants);
@@ -17,6 +17,8 @@
package com.android.messaging.datamodel;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -74,10 +76,10 @@ import com.android.messaging.util.ConversationIdSet;
import com.android.messaging.util.ImageUtils;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.NotificationPlayer;
import com.android.messaging.util.NotificationsUtil;
import com.android.messaging.util.OsUtil;
import com.android.messaging.util.PendingIntentConstants;
import com.android.messaging.util.PhoneUtils;
import com.android.messaging.util.RingtoneUtil;
import com.android.messaging.util.ThreadUtil;
import com.android.messaging.util.UriUtil;
@@ -173,18 +175,10 @@ public class BugleNotifications {
}
Assert.isNotMainThread();
checkInitialized();
if (!shouldNotify()) {
if (LogUtil.isLoggable(TAG, LogUtil.VERBOSE)) {
LogUtil.v(TAG, "Notifications disabled");
}
cancel(PendingIntentConstants.SMS_NOTIFICATION_ID);
return;
} else {
if ((coverage & UPDATE_MESSAGES) != 0) {
createMessageNotification(silent, conversationId);
}
if ((coverage & UPDATE_MESSAGES) != 0) {
createMessageNotification(silent, conversationId);
}
if ((coverage & UPDATE_ERRORS) != 0) {
MessageNotificationState.checkFailedMessages();
}
@@ -195,8 +189,7 @@ public class BugleNotifications {
*
*/
public static void playClassZeroNotification() {
final Uri ringtoneUri = RingtoneUtil.getNotificationRingtoneUri(null);
playObservableConversationNotificationSound(ringtoneUri);
playObservableConversationNotificationSound(null);
}
/**
@@ -297,56 +290,6 @@ public class BugleNotifications {
}
}
/**
* Returns {@code true} if incoming notifications should display a
* notification, {@code false} otherwise.
*
* @return true if the notification should occur
*/
private static boolean shouldNotify() {
// If we're not the default sms app, don't put up any notifications.
if (!PhoneUtils.getDefault().isDefaultSmsApp()) {
return false;
}
// Now check prefs (i.e. settings) to see if the user turned off notifications.
final BuglePrefs prefs = BuglePrefs.getApplicationPrefs();
final Context context = Factory.get().getApplicationContext();
final String prefKey = context.getString(R.string.notifications_enabled_pref_key);
final boolean defaultValue = context.getResources().getBoolean(
R.bool.notifications_enabled_pref_default);
return prefs.getBoolean(prefKey, defaultValue);
}
/**
* Returns {@code true} if incoming notifications for the given {@link NotificationState}
* should vibrate the device, {@code false} otherwise.
*
* @return true if vibration should be used
*/
public static boolean shouldVibrate(final NotificationState state) {
// The notification should vibrate if the global setting is turned on AND
// the per-conversation setting is turned on (default).
if (!state.getNotificationVibrate()) {
return false;
} else {
final BuglePrefs prefs = BuglePrefs.getApplicationPrefs();
final Context context = Factory.get().getApplicationContext();
final String prefKey = context.getString(R.string.notification_vibration_pref_key);
final boolean defaultValue = context.getResources().getBoolean(
R.bool.notification_vibration_pref_default);
return prefs.getBoolean(prefKey, defaultValue);
}
}
private static Uri getNotificationRingtoneUriForConversationId(final String conversationId) {
final DatabaseWrapper db = DataModel.get().getDatabase();
final ConversationListItemData convData =
ConversationListItemData.getExistingConversation(db, conversationId);
return RingtoneUtil.getNotificationRingtoneUri(
convData != null ? convData.getNotificationSoundUri() : null);
}
/**
* Returns a unique tag to identify a notification.
*
@@ -420,13 +363,15 @@ public class BugleNotifications {
private static void processAndSend(final NotificationState state, final boolean silent,
final boolean softSound) {
final Context context = Factory.get().getApplicationContext();
final NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(context);
notifBuilder.setCategory(Notification.CATEGORY_MESSAGE);
// TODO: Need to fix this for multi conversation notifications to rate limit dings.
final String conversationId = state.mConversationIds.first();
String id = NotificationsUtil.DEFAULT_CHANNEL_ID;
if (NotificationsUtil.getNotificationChannel(context, conversationId) != null) {
id = conversationId;
}
final NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(context, id);
notifBuilder.setCategory(Notification.CATEGORY_MESSAGE);
final Uri ringtoneUri = RingtoneUtil.getNotificationRingtoneUri(state.getRingtoneUri());
// If the notification's conversation is currently observable (focused or in the
// conversation list), then play a notification beep at a low volume and don't display an
// actual notification.
@@ -436,7 +381,7 @@ public class BugleNotifications {
"sCurrentlyDisplayedConversationId so NOT showing notification," +
" but playing soft sound. conversationId: " + conversationId);
}
playObservableConversationNotificationSound(ringtoneUri);
playObservableConversationNotificationSound(conversationId);
return;
}
state.mBaseRequestCode = state.mType;
@@ -449,7 +394,7 @@ public class BugleNotifications {
notifBuilder.setDeleteIntent(clearIntent);
}
updateBuilderAudioVibrate(state, notifBuilder, silent, ringtoneUri, conversationId);
updateBuilderAudioVibrate(state, notifBuilder, silent, conversationId);
// Set the content intent
PendingIntent destinationIntent;
@@ -607,8 +552,7 @@ public class BugleNotifications {
if (state == null) {
cancel(PendingIntentConstants.SMS_NOTIFICATION_ID);
if (softSound && !TextUtils.isEmpty(conversationId)) {
final Uri ringtoneUri = getNotificationRingtoneUriForConversationId(conversationId);
playObservableConversationNotificationSound(ringtoneUri);
playObservableConversationNotificationSound(conversationId);
}
return;
}
@@ -646,8 +590,8 @@ public class BugleNotifications {
private static void updateBuilderAudioVibrate(final NotificationState state,
final NotificationCompat.Builder notifBuilder, final boolean silent,
final Uri ringtoneUri, final String conversationId) {
int defaults = Notification.DEFAULT_LIGHTS;
final String conversationId) {
int defaults = Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE;
if (!silent) {
final BuglePrefs prefs = Factory.get().getApplicationPrefs();
final long latestNotificationTimestamp = prefs.getLong(
@@ -669,10 +613,6 @@ public class BugleNotifications {
if (lastTime == null
|| SystemClock.elapsedRealtime() - lastTime > sTimeBetweenDingsMs) {
sLastMessageDingTime.put(conversationId, SystemClock.elapsedRealtime());
notifBuilder.setSound(ringtoneUri);
if (shouldVibrate(state)) {
defaults |= Notification.DEFAULT_VIBRATE;
}
}
}
}
@@ -826,8 +766,14 @@ public class BugleNotifications {
notificationState.mNotificationBuilder.setLargeIcon(smallBitmap);
// Add a wearable page with no visible card so you can more easily see the photo.
String conversationId = notificationState.mConversationIds.first();
String id = NotificationsUtil.DEFAULT_CHANNEL_ID;
if (NotificationsUtil.getNotificationChannel(context, conversationId) != null) {
id = conversationId;
}
final NotificationCompat.Builder photoPageNotifBuilder =
new NotificationCompat.Builder(Factory.get().getApplicationContext());
new NotificationCompat.Builder(Factory.get().getApplicationContext(),
NotificationsUtil.DEFAULT_CHANNEL_ID);
final WearableExtender photoPageWearableExtender = new WearableExtender();
photoPageWearableExtender.setHintShowBackgroundOnly(true);
if (attachmentBitmap != null) {
@@ -982,6 +928,16 @@ public class BugleNotifications {
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_LIGHTS;
Context context = Factory.get().getApplicationContext();
NotificationsUtil.createNotificationChannelGroup(context,
NotificationsUtil.CONVERSATION_GROUP_NAME,
R.string.notification_channel_messages_title);
NotificationsUtil.createNotificationChannel(context,
NotificationsUtil.DEFAULT_CHANNEL_ID,
R.string.notification_channel_messages_title,
NotificationManager.IMPORTANCE_DEFAULT,
NotificationsUtil.CONVERSATION_GROUP_NAME);
notificationManager.notify(notificationTag, type, notification);
LogUtil.i(TAG, "Notifying for conversation " + conversationId + "; "
@@ -1105,7 +1061,7 @@ public class BugleNotifications {
* Play the observable conversation notification sound (it's the regular notification sound, but
* played at half-volume)
*/
private static void playObservableConversationNotificationSound(final Uri ringtoneUri) {
private static void playObservableConversationNotificationSound(final String conversationId) {
final Context context = Factory.get().getApplicationContext();
final AudioManager audioManager = (AudioManager) context
.getSystemService(Context.AUDIO_SERVICE);
@@ -1116,7 +1072,11 @@ public class BugleNotifications {
}
final NotificationPlayer player = new NotificationPlayer(LogUtil.BUGLE_TAG);
player.play(ringtoneUri, false,
NotificationChannel channel = NotificationsUtil.getNotificationChannel(context, conversationId);
if (channel == null) {
channel = NotificationsUtil.getNotificationChannel(context, NotificationsUtil.DEFAULT_CHANNEL_ID);
}
player.play(channel != null ? channel.getSound() : null, false,
AudioManager.STREAM_NOTIFICATION,
OBSERVABLE_CONVERSATION_NOTIFICATION_VOLUME);
@@ -1211,7 +1171,8 @@ public class BugleNotifications {
final PendingIntent destinationIntent = UIIntents.get()
.getPendingIntentForConversationActivity(context, conversationId, null /* draft */);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
final NotificationCompat.Builder builder =
new NotificationCompat.Builder(context, NotificationsUtil.DEFAULT_CHANNEL_ID);
builder.setTicker(line1)
.setContentTitle(line1)
.setContentText(line2)
@@ -1221,6 +1182,15 @@ public class BugleNotifications {
.setSound(UriUtil.getUriForResourceId(context, R.raw.message_failure));
final String tag = context.getPackageName() + ":emergency_sms_error";
NotificationsUtil.createNotificationChannelGroup(context,
NotificationsUtil.CONVERSATION_GROUP_NAME,
R.string.notification_channel_messages_title);
NotificationsUtil.createNotificationChannel(context,
NotificationsUtil.DEFAULT_CHANNEL_ID,
R.string.notification_channel_messages_title,
NotificationManager.IMPORTANCE_HIGH,
null);
NotificationManagerCompat.from(context).notify(
tag,
PendingIntentConstants.MSG_SEND_ERROR,
@@ -136,15 +136,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
/* Participant count not including self (so will be 1 for 1:1 or bigger for group) */
public static final String PARTICIPANT_COUNT = "participant_count";
/* Should notifications be enabled for this conversation? */
public static final String NOTIFICATION_ENABLED = "notification_enabled";
/* Notification sound used for the conversation */
public static final String NOTIFICATION_SOUND_URI = "notification_sound_uri";
/* Should vibrations be enabled for the conversation's notification? */
public static final String NOTIFICATION_VIBRATION = "notification_vibration";
/* Conversation recipients include email address */
public static final String INCLUDE_EMAIL_ADDRESS = "include_email_addr";
@@ -184,9 +175,6 @@ public class DatabaseHelper extends SQLiteOpenHelper {
+ ConversationColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION + " TEXT, "
+ ConversationColumns.CURRENT_SELF_ID + " TEXT, "
+ ConversationColumns.PARTICIPANT_COUNT + " INT DEFAULT(0), "
+ ConversationColumns.NOTIFICATION_ENABLED + " INT DEFAULT(1), "
+ ConversationColumns.NOTIFICATION_SOUND_URI + " TEXT, "
+ ConversationColumns.NOTIFICATION_VIBRATION + " INT DEFAULT(1), "
+ ConversationColumns.INCLUDE_EMAIL_ADDRESS + " INT DEFAULT(0), "
+ ConversationColumns.SMS_SERVICE_CENTER + " TEXT ,"
+ ConversationColumns.IS_ENTERPRISE + " INT DEFAULT(0)"
@@ -197,15 +197,6 @@ public abstract class MessageNotificationState extends NotificationState {
// line infos is capped.
int mTotalMessageCount;
// Custom ringtone if set
final String mRingtoneUri;
// Should notification be enabled for this conversation?
final boolean mNotificationEnabled;
// Should notifications vibrate for this conversation?
final boolean mNotificationVibrate;
// Avatar uri of sender
final Uri mAvatarUri;
@@ -224,9 +215,6 @@ public abstract class MessageNotificationState extends NotificationState {
final boolean includeEmailAddress,
final long receivedTimestamp,
final String selfParticipantId,
final String ringtoneUri,
final boolean notificationEnabled,
final boolean notificationVibrate,
final Uri avatarUri,
final Uri contactUri,
final int subId,
@@ -239,11 +227,8 @@ public abstract class MessageNotificationState extends NotificationState {
mSelfParticipantId = selfParticipantId;
mLineInfos = new ArrayList<NotificationLineInfo>();
mTotalMessageCount = 0;
mRingtoneUri = ringtoneUri;
mAvatarUri = avatarUri;
mContactUri = contactUri;
mNotificationEnabled = notificationEnabled;
mNotificationVibrate = notificationVibrate;
mSubId = subId;
mParticipantCount = participantCount;
}
@@ -870,10 +855,6 @@ public abstract class MessageNotificationState extends NotificationState {
if (currConvInfo == null) {
final ConversationListItemData convData =
ConversationListItemData.getExistingConversation(db, convId);
if (!convData.getNotificationEnabled()) {
// Skip conversations that have notifications disabled.
continue;
}
final int subId = BugleDatabaseOperations.getSelfSubscriptionId(db,
convData.getSelfId());
groupConversationName = convData.getName();
@@ -888,9 +869,6 @@ public abstract class MessageNotificationState extends NotificationState {
convData.getIncludeEmailAddress(),
convMessageData.getReceivedTimeStamp(),
convData.getSelfId(),
convData.getNotificationSoundUri(),
convData.getNotificationEnabled(),
convData.getNotifiationVibrate(),
avatarUri,
convMessageData.getSenderContactLookupUri(),
subId,
@@ -1103,22 +1081,6 @@ public abstract class MessageNotificationState extends NotificationState {
return BugleNotifications.LOCAL_SMS_NOTIFICATION;
}
@Override
public String getRingtoneUri() {
if (mConvList.mConvInfos.size() > 0) {
return mConvList.mConvInfos.get(0).mRingtoneUri;
}
return null;
}
@Override
public boolean getNotificationVibrate() {
if (mConvList.mConvInfos.size() > 0) {
return mConvList.mConvInfos.get(0).mNotificationVibrate;
}
return false;
}
protected CharSequence getTicker() {
return BugleNotifications.buildColonSeparatedMessage(
mTickerSender != null ? mTickerSender : mTitle,
@@ -134,15 +134,6 @@ public abstract class NotificationState {
*/
public abstract int getPriority();
/** @return custom ringtone URI or null if not set */
public String getRingtoneUri() {
return null;
}
public boolean getNotificationVibrate() {
return false;
}
public long getLatestReceivedTimestamp() {
return Long.MIN_VALUE;
}
@@ -51,33 +51,14 @@ public class SyncManager {
*/
public static class ConversationCustomization {
private final boolean mArchived;
private final boolean mMuted;
private final boolean mNoVibrate;
private final String mNotificationSoundUri;
public ConversationCustomization(final boolean archived, final boolean muted,
final boolean noVibrate, final String notificationSoundUri) {
public ConversationCustomization(final boolean archived) {
mArchived = archived;
mMuted = muted;
mNoVibrate = noVibrate;
mNotificationSoundUri = notificationSoundUri;
}
public boolean isArchived() {
return mArchived;
}
public boolean isMuted() {
return mMuted;
}
public boolean noVibrate() {
return mNoVibrate;
}
public String getNotificationSoundUri() {
return mNotificationSoundUri;
}
}
SyncManager() {
@@ -436,15 +417,13 @@ public class SyncManager {
if (customization != null) {
// There is user customization we need to recover
conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId,
customization.isArchived(), participants, customization.isMuted(),
customization.noVibrate(), customization.getNotificationSoundUri());
customization.isArchived(), participants);
if (customization.isArchived()) {
mArchivedConversations.add(conversationId);
}
} else {
conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId,
false/*archived*/, participants, false/*noNotification*/,
false/*noVibrate*/, null/*soundUri*/);
false/*archived*/, participants);
}
if (conversationId != null) {
@@ -35,6 +35,7 @@ import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.sms.MmsUtils;
import com.android.messaging.util.Assert;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.NotificationsUtil;
import com.android.messaging.widget.WidgetConversationProvider;
import java.util.ArrayList;
@@ -118,6 +119,10 @@ public class DeleteConversationAction extends Action implements Parcelable {
+ " has an invalid telephony thread id; will delete messages individually");
deleteConversationMessagesFromTelephony();
}
// Finally, remove the conversation specific notification channel
NotificationsUtil.deleteNotificationChannel(Factory.get().getApplicationContext(),
conversationId);
} else {
LogUtil.e(TAG, "DeleteConversationAction: conversationId is empty");
}
@@ -110,7 +110,7 @@ public class GetOrCreateConversationAction extends Action implements Parcelable
}
final String conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId,
false, participants, false, false, null);
false, participants);
return conversationId;
}
@@ -274,7 +274,7 @@ public class InsertNewMessageAction extends Action implements Parcelable {
}
final String conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId,
false, participants, false, false, null);
false, participants);
final ParticipantData self = BugleDatabaseOperations.getOrCreateSelf(db, subId);
@@ -1,156 +0,0 @@
/*
* Copyright (C) 2015 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.messaging.datamodel.action;
import android.content.ContentValues;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.messaging.datamodel.BugleDatabaseOperations;
import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.DatabaseHelper.ConversationColumns;
import com.android.messaging.datamodel.DatabaseWrapper;
import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.util.Assert;
/**
* Action used to update conversation options such as notification settings.
*/
public class UpdateConversationOptionsAction extends Action
implements Parcelable {
/**
* Enable/disable conversation notifications.
*/
public static void enableConversationNotifications(final String conversationId,
final boolean enableNotification) {
Assert.notNull(conversationId);
final UpdateConversationOptionsAction action = new UpdateConversationOptionsAction(
conversationId, enableNotification, null, null);
action.start();
}
/**
* Sets conversation notification sound.
*/
public static void setConversationNotificationSound(final String conversationId,
final String ringtoneUri) {
Assert.notNull(conversationId);
final UpdateConversationOptionsAction action = new UpdateConversationOptionsAction(
conversationId, null, ringtoneUri, null);
action.start();
}
/**
* Enable/disable vibrations for conversation notification.
*/
public static void enableVibrationForConversationNotification(final String conversationId,
final boolean enableVibration) {
Assert.notNull(conversationId);
final UpdateConversationOptionsAction action = new UpdateConversationOptionsAction(
conversationId, null, null, enableVibration);
action.start();
}
private static final String KEY_CONVERSATION_ID = "conversation_id";
// Keys for all settable settings.
private static final String KEY_ENABLE_NOTIFICATION = "enable_notification";
private static final String KEY_RINGTONE_URI = "ringtone_uri";
private static final String KEY_ENABLE_VIBRATION = "enable_vibration";
protected UpdateConversationOptionsAction(final String conversationId,
final Boolean enableNotification, final String ringtoneUri,
final Boolean enableVibration) {
Assert.notNull(conversationId);
actionParameters.putString(KEY_CONVERSATION_ID, conversationId);
if (enableNotification != null) {
actionParameters.putBoolean(KEY_ENABLE_NOTIFICATION, enableNotification);
}
if (ringtoneUri != null) {
actionParameters.putString(KEY_RINGTONE_URI, ringtoneUri);
}
if (enableVibration != null) {
actionParameters.putBoolean(KEY_ENABLE_VIBRATION, enableVibration);
}
}
protected void putOptionValuesInTransaction(final ContentValues values,
final DatabaseWrapper dbWrapper) {
Assert.isTrue(dbWrapper.getDatabase().inTransaction());
if (actionParameters.containsKey(KEY_ENABLE_NOTIFICATION)) {
values.put(ConversationColumns.NOTIFICATION_ENABLED,
actionParameters.getBoolean(KEY_ENABLE_NOTIFICATION));
}
if (actionParameters.containsKey(KEY_RINGTONE_URI)) {
values.put(ConversationColumns.NOTIFICATION_SOUND_URI,
actionParameters.getString(KEY_RINGTONE_URI));
}
if (actionParameters.containsKey(KEY_ENABLE_VIBRATION)) {
values.put(ConversationColumns.NOTIFICATION_VIBRATION,
actionParameters.getBoolean(KEY_ENABLE_VIBRATION));
}
}
@Override
protected Object executeAction() {
final String conversationId = actionParameters.getString(KEY_CONVERSATION_ID);
final DatabaseWrapper db = DataModel.get().getDatabase();
db.beginTransaction();
try {
final ContentValues values = new ContentValues();
putOptionValuesInTransaction(values, db);
BugleDatabaseOperations.updateConversationRowIfExists(db, conversationId, values);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
MessagingContentProvider.notifyConversationMetadataChanged(conversationId);
return null;
}
protected UpdateConversationOptionsAction(final Parcel in) {
super(in);
}
public static final Parcelable.Creator<UpdateConversationOptionsAction> CREATOR
= new Parcelable.Creator<UpdateConversationOptionsAction>() {
@Override
public UpdateConversationOptionsAction createFromParcel(final Parcel in) {
return new UpdateConversationOptionsAction(in);
}
@Override
public UpdateConversationOptionsAction[] newArray(final int size) {
return new UpdateConversationOptionsAction[size];
}
};
@Override
public void writeToParcel(final Parcel parcel, final int flags) {
writeActionToParcel(parcel, flags);
}
}
@@ -52,9 +52,6 @@ public class ConversationListItemData {
private String mOtherParticipantNormalizedDestination;
private String mSelfId;
private int mParticipantCount;
private boolean mNotificationEnabled;
private String mNotificationSoundUri;
private boolean mNotificationVibrate;
private boolean mIncludeEmailAddress;
private int mMessageStatus;
private int mMessageRawTelephonyStatus;
@@ -92,9 +89,6 @@ public class ConversationListItemData {
INDEX_OTHER_PARTICIPANT_NORMALIZED_DESTINATION);
mSelfId = cursor.getString(INDEX_SELF_ID);
mParticipantCount = cursor.getInt(INDEX_PARTICIPANT_COUNT);
mNotificationEnabled = cursor.getInt(INDEX_NOTIFICATION_ENABLED) == 1;
mNotificationSoundUri = cursor.getString(INDEX_NOTIFICATION_SOUND_URI);
mNotificationVibrate = cursor.getInt(INDEX_NOTIFICATION_VIBRATION) == 1;
mIncludeEmailAddress = cursor.getInt(INDEX_INCLUDE_EMAIL_ADDRESS) == 1;
mMessageStatus = cursor.getInt(INDEX_MESSAGE_STATUS);
mMessageRawTelephonyStatus = cursor.getInt(INDEX_MESSAGE_RAW_TELEPHONY_STATUS);
@@ -199,18 +193,6 @@ public class ConversationListItemData {
return mIncludeEmailAddress;
}
public boolean getNotificationEnabled() {
return mNotificationEnabled;
}
public String getNotificationSoundUri() {
return mNotificationSoundUri;
}
public boolean getNotifiationVibrate() {
return mNotificationVibrate;
}
public final boolean getIsFailedStatus() {
return (mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_FAILED ||
mMessageStatus == MessageData.BUGLE_STATUS_OUTGOING_FAILED_EMERGENCY_NUMBER ||
@@ -328,12 +310,6 @@ public class ConversationListItemData {
+ " as " + ConversationListViewColumns.PREVIEW_CONTENT_TYPE + ", "
+ DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.PARTICIPANT_COUNT
+ " as " + ConversationListViewColumns.PARTICIPANT_COUNT + ", "
+ DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.NOTIFICATION_ENABLED
+ " as " + ConversationListViewColumns.NOTIFICATION_ENABLED + ", "
+ DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.NOTIFICATION_SOUND_URI
+ " as " + ConversationListViewColumns.NOTIFICATION_SOUND_URI + ", "
+ DatabaseHelper.CONVERSATIONS_TABLE + '.' + ConversationColumns.NOTIFICATION_VIBRATION
+ " as " + ConversationListViewColumns.NOTIFICATION_VIBRATION + ", "
+ DatabaseHelper.CONVERSATIONS_TABLE + '.' +
ConversationColumns.INCLUDE_EMAIL_ADDRESS
+ " as " + ConversationListViewColumns.INCLUDE_EMAIL_ADDRESS + ", "
@@ -396,9 +372,6 @@ public class ConversationListItemData {
ConversationColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION;
static final String CURRENT_SELF_ID = ConversationColumns.CURRENT_SELF_ID;
static final String PARTICIPANT_COUNT = ConversationColumns.PARTICIPANT_COUNT;
static final String NOTIFICATION_ENABLED = ConversationColumns.NOTIFICATION_ENABLED;
static final String NOTIFICATION_SOUND_URI = ConversationColumns.NOTIFICATION_SOUND_URI;
static final String NOTIFICATION_VIBRATION = ConversationColumns.NOTIFICATION_VIBRATION;
static final String INCLUDE_EMAIL_ADDRESS =
ConversationColumns.INCLUDE_EMAIL_ADDRESS;
static final String MESSAGE_STATUS = MessageColumns.STATUS;
@@ -424,9 +397,6 @@ public class ConversationListItemData {
ConversationListViewColumns.OTHER_PARTICIPANT_NORMALIZED_DESTINATION,
ConversationListViewColumns.PARTICIPANT_COUNT,
ConversationListViewColumns.CURRENT_SELF_ID,
ConversationListViewColumns.NOTIFICATION_ENABLED,
ConversationListViewColumns.NOTIFICATION_SOUND_URI,
ConversationListViewColumns.NOTIFICATION_VIBRATION,
ConversationListViewColumns.INCLUDE_EMAIL_ADDRESS,
ConversationListViewColumns.MESSAGE_STATUS,
ConversationListViewColumns.SHOW_DRAFT,
@@ -456,23 +426,20 @@ public class ConversationListItemData {
private static final int INDEX_OTHER_PARTICIPANT_NORMALIZED_DESTINATION = 10;
private static final int INDEX_PARTICIPANT_COUNT = 11;
private static final int INDEX_SELF_ID = 12;
private static final int INDEX_NOTIFICATION_ENABLED = 13;
private static final int INDEX_NOTIFICATION_SOUND_URI = 14;
private static final int INDEX_NOTIFICATION_VIBRATION = 15;
private static final int INDEX_INCLUDE_EMAIL_ADDRESS = 16;
private static final int INDEX_MESSAGE_STATUS = 17;
private static final int INDEX_SHOW_DRAFT = 18;
private static final int INDEX_DRAFT_PREVIEW_URI = 19;
private static final int INDEX_DRAFT_PREVIEW_CONTENT_TYPE = 20;
private static final int INDEX_DRAFT_SNIPPET_TEXT = 21;
private static final int INDEX_ARCHIVE_STATUS = 22;
private static final int INDEX_MESSAGE_ID = 23;
private static final int INDEX_SUBJECT_TEXT = 24;
private static final int INDEX_DRAFT_SUBJECT_TEXT = 25;
private static final int INDEX_MESSAGE_RAW_TELEPHONY_STATUS = 26;
private static final int INDEX_SNIPPET_SENDER_FIRST_NAME = 27;
private static final int INDEX_SNIPPET_SENDER_DISPLAY_DESTINATION = 28;
private static final int INDEX_IS_ENTERPRISE = 29;
private static final int INDEX_INCLUDE_EMAIL_ADDRESS = 13;
private static final int INDEX_MESSAGE_STATUS = 14;
private static final int INDEX_SHOW_DRAFT = 15;
private static final int INDEX_DRAFT_PREVIEW_URI = 16;
private static final int INDEX_DRAFT_PREVIEW_CONTENT_TYPE = 17;
private static final int INDEX_DRAFT_SNIPPET_TEXT = 18;
private static final int INDEX_ARCHIVE_STATUS = 19;
private static final int INDEX_MESSAGE_ID = 20;
private static final int INDEX_SUBJECT_TEXT = 21;
private static final int INDEX_DRAFT_SUBJECT_TEXT = 22;
private static final int INDEX_MESSAGE_RAW_TELEPHONY_STATUS = 23;
private static final int INDEX_SNIPPET_SENDER_FIRST_NAME = 24;
private static final int INDEX_SNIPPET_SENDER_DISPLAY_DESTINATION = 25;
private static final int INDEX_IS_ENTERPRISE = 26;
private static final String DIVIDER_TEXT = ", ";
@@ -26,7 +26,6 @@ import android.os.Bundle;
import com.android.messaging.datamodel.BoundCursorLoader;
import com.android.messaging.datamodel.MessagingContentProvider;
import com.android.messaging.datamodel.action.BugleActionToasts;
import com.android.messaging.datamodel.action.UpdateConversationOptionsAction;
import com.android.messaging.datamodel.action.UpdateDestinationBlockedAction;
import com.android.messaging.datamodel.binding.BindableData;
import com.android.messaging.datamodel.binding.BindingBase;
@@ -74,7 +73,7 @@ public class PeopleAndOptionsData extends BindableData implements
final Uri uri =
MessagingContentProvider.buildConversationMetadataUri(mConversationId);
return new BoundCursorLoader(bindingId, mContext, uri,
PeopleOptionsItemData.PROJECTION, null, null, null);
new String[]{}, null, null, null);
}
case PARTICIPANT_LOADER: {
@@ -169,33 +168,6 @@ public class PeopleAndOptionsData extends BindableData implements
}
}
public void enableConversationNotifications(final BindingBase<PeopleAndOptionsData> binding,
final boolean enable) {
final String bindingId = binding.getBindingId();
if (isBound(bindingId)) {
UpdateConversationOptionsAction.enableConversationNotifications(
mConversationId, enable);
}
}
public void setConversationNotificationSound(final BindingBase<PeopleAndOptionsData> binding,
final String ringtoneUri) {
final String bindingId = binding.getBindingId();
if (isBound(bindingId)) {
UpdateConversationOptionsAction.setConversationNotificationSound(mConversationId,
ringtoneUri);
}
}
public void enableConversationNotificationVibration(
final BindingBase<PeopleAndOptionsData> binding, final boolean enable) {
final String bindingId = binding.getBindingId();
if (isBound(bindingId)) {
UpdateConversationOptionsAction.enableVibrationForConversationNotification(
mConversationId, enable);
}
}
public void setDestinationBlocked(final BindingBase<PeopleAndOptionsData> binding,
final boolean blocked) {
final String bindingId = binding.getBindingId();
@@ -207,4 +179,8 @@ public class PeopleAndOptionsData extends BindableData implements
BugleActionToasts.makeUpdateDestinationBlockedActionListener(mContext));
}
}
public String getConversationId() {
return mConversationId;
}
}
@@ -17,44 +17,19 @@ package com.android.messaging.datamodel.data;
import android.content.Context;
import android.database.Cursor;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import com.android.messaging.R;
import com.android.messaging.datamodel.data.ConversationListItemData.ConversationListViewColumns;
import com.android.messaging.util.Assert;
import com.android.messaging.util.RingtoneUtil;
public class PeopleOptionsItemData {
public static final String[] PROJECTION = {
ConversationListViewColumns.NOTIFICATION_ENABLED,
ConversationListViewColumns.NOTIFICATION_SOUND_URI,
ConversationListViewColumns.NOTIFICATION_VIBRATION,
};
// Column index for query projection.
private static final int INDEX_NOTIFICATION_ENABLED = 0;
private static final int INDEX_NOTIFICATION_SOUND_URI = 1;
private static final int INDEX_NOTIFICATION_VIBRATION = 2;
// Identification for each setting that's surfaced to the UI layer.
public static final int SETTING_NOTIFICATION_ENABLED = 0;
public static final int SETTING_NOTIFICATION_SOUND_URI = 1;
public static final int SETTING_NOTIFICATION_VIBRATION = 2;
public static final int SETTING_BLOCKED = 3;
public static final int SETTINGS_COUNT = 4;
// Type of UI switch to show for the toggle button.
public static final int TOGGLE_TYPE_CHECKBOX = 0;
public static final int TOGGLE_TYPE_SWITCH = 1;
public static final int SETTING_NOTIFICATION = 0;
public static final int SETTING_BLOCKED = 1;
public static final int SETTINGS_COUNT = 2;
private String mTitle;
private String mSubtitle;
private Uri mRingtoneUri;
private boolean mCheckable;
private boolean mChecked;
private boolean mEnabled;
private int mItemId;
private ParticipantData mOtherParticipant;
@@ -71,41 +46,12 @@ public class PeopleOptionsItemData {
*/
public void bind(
final Cursor cursor, final ParticipantData otherParticipant, final int settingType) {
mSubtitle = null;
mRingtoneUri = null;
mCheckable = true;
mEnabled = true;
mItemId = settingType;
mOtherParticipant = otherParticipant;
final boolean notificationEnabled = cursor.getInt(INDEX_NOTIFICATION_ENABLED) == 1;
switch (settingType) {
case SETTING_NOTIFICATION_ENABLED:
case SETTING_NOTIFICATION:
mTitle = mContext.getString(R.string.notifications_enabled_conversation_pref_title);
mChecked = notificationEnabled;
break;
case SETTING_NOTIFICATION_SOUND_URI:
mTitle = mContext.getString(R.string.notification_sound_pref_title);
final String ringtoneString = cursor.getString(INDEX_NOTIFICATION_SOUND_URI);
Uri ringtoneUri = RingtoneUtil.getNotificationRingtoneUri(ringtoneString);
mSubtitle = mContext.getString(R.string.silent_ringtone);
if (ringtoneUri != null) {
final Ringtone ringtone = RingtoneManager.getRingtone(mContext, ringtoneUri);
if (ringtone != null) {
mSubtitle = ringtone.getTitle(mContext);
}
}
mCheckable = false;
mRingtoneUri = ringtoneUri;
mEnabled = notificationEnabled;
break;
case SETTING_NOTIFICATION_VIBRATION:
mTitle = mContext.getString(R.string.notification_vibrate_pref_title);
mChecked = cursor.getInt(INDEX_NOTIFICATION_VIBRATION) == 1;
mEnabled = notificationEnabled;
break;
case SETTING_BLOCKED:
@@ -113,7 +59,6 @@ public class PeopleOptionsItemData {
final int resourceId = otherParticipant.isBlocked() ?
R.string.unblock_contact_title : R.string.block_contact_title;
mTitle = mContext.getString(resourceId, otherParticipant.getDisplayDestination());
mCheckable = false;
break;
default:
@@ -125,30 +70,10 @@ public class PeopleOptionsItemData {
return mTitle;
}
public String getSubtitle() {
return mSubtitle;
}
public boolean getCheckable() {
return mCheckable;
}
public boolean getChecked() {
return mChecked;
}
public boolean getEnabled() {
return mEnabled;
}
public int getItemId() {
return mItemId;
}
public Uri getRingtoneUri() {
return mRingtoneUri;
}
public ParticipantData getOtherParticipant() {
return mOtherParticipant;
}
@@ -252,11 +252,6 @@ public final class SmsReceiver extends BroadcastReceiver {
protected Style build(Builder builder) {
return null;
}
@Override
public boolean getNotificationVibrate() {
return true;
}
}
public static void postNewMessageSecondaryUserNotification() {
@@ -282,10 +277,7 @@ public final class SmsReceiver extends BroadcastReceiver {
final NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(Factory.get().getApplicationContext());
int defaults = Notification.DEFAULT_LIGHTS;
if (BugleNotifications.shouldVibrate(new SecondaryUserNotificationState())) {
defaults |= Notification.DEFAULT_VIBRATE;
}
int defaults = Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE;
notification.defaults = defaults;
notificationManager.notify(getNotificationTag(),
@@ -264,16 +264,6 @@ public abstract class UIIntents {
*/
public abstract Intent getViewUrlIntent(final String url);
/**
* Get an intent to launch the ringtone picker
* @param title the title to show in the ringtone picker
* @param existingUri the currently set uri
* @param defaultUri the default uri if none is currently set
* @param toneType type of ringtone to pick, maybe any of RingtoneManager.TYPE_*
*/
public abstract Intent getRingtonePickerIntent(final String title, final Uri existingUri,
final Uri defaultUri, final int toneType);
/**
* Get an intent to launch the wireless alert viewer.
*/
@@ -27,7 +27,6 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
@@ -450,16 +449,6 @@ public class UIIntentsImpl extends UIIntents {
return resultPendingIntent;
}
@Override
public Intent getRingtonePickerIntent(final String title, final Uri existingUri,
final Uri defaultUri, final int toneType) {
return new Intent(RingtoneManager.ACTION_RINGTONE_PICKER)
.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, toneType)
.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, title)
.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, existingUri)
.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI, defaultUri);
}
@Override
public PendingIntent getPendingIntentForLowStorageNotifications(final Context context) {
final TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(context);
@@ -18,18 +18,12 @@ package com.android.messaging.ui.appsettings;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.preference.RingtonePreference;
import android.preference.SwitchPreference;
import android.preference.TwoStatePreference;
import android.provider.Settings;
import androidx.core.app.NavUtils;
import android.text.TextUtils;
@@ -85,14 +79,10 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
return super.onOptionsItemSelected(item);
}
public static class ApplicationSettingsFragment extends PreferenceFragment implements
OnSharedPreferenceChangeListener {
public static class ApplicationSettingsFragment extends PreferenceFragment {
private String mNotificationsEnabledPreferenceKey;
private TwoStatePreference mNotificationsEnabledPreference;
private String mRingtonePreferenceKey;
private RingtonePreference mRingtonePreference;
private Preference mVibratePreference;
private String mNotificationsPreferenceKey;
private Preference mNotificationsPreference;
private String mSmsDisabledPrefKey;
private Preference mSmsDisabledPreference;
private String mSmsEnabledPrefKey;
@@ -112,14 +102,9 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
getPreferenceManager().setSharedPreferencesName(BuglePrefs.SHARED_PREFERENCES_NAME);
addPreferencesFromResource(R.xml.preferences_application);
mNotificationsEnabledPreferenceKey =
getString(R.string.notifications_enabled_pref_key);
mNotificationsEnabledPreference = (TwoStatePreference) findPreference(
mNotificationsEnabledPreferenceKey);
mRingtonePreferenceKey = getString(R.string.notification_sound_pref_key);
mRingtonePreference = (RingtonePreference) findPreference(mRingtonePreferenceKey);
mVibratePreference = findPreference(
getString(R.string.notification_vibration_pref_key));
mNotificationsPreferenceKey =
getString(R.string.notifications_pref_key);
mNotificationsPreference = findPreference(mNotificationsPreferenceKey);
mSmsDisabledPrefKey = getString(R.string.sms_disabled_pref_key);
mSmsDisabledPreference = findPreference(mSmsDisabledPrefKey);
mSmsEnabledPrefKey = getString(R.string.sms_enabled_pref_key);
@@ -130,9 +115,6 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
(SwitchPreference) findPreference(mSwipeRightToDeleteConversationkey);
mIsSmsPreferenceClicked = false;
final SharedPreferences prefs = getPreferenceScreen().getSharedPreferences();
updateSoundSummary(prefs);
if (!DebugUtils.isDebugEnabled()) {
final Preference debugCategory = findPreference(getString(
R.string.debug_pref_key));
@@ -154,8 +136,13 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
}
@Override
public boolean onPreferenceTreeClick (PreferenceScreen preferenceScreen,
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
Preference preference) {
if (preference.getKey() == mNotificationsPreferenceKey) {
Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
startActivity(intent);
}
if (preference.getKey() == mSmsDisabledPrefKey ||
preference.getKey() == mSmsEnabledPrefKey) {
mIsSmsPreferenceClicked = true;
@@ -163,35 +150,6 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
private void updateSoundSummary(final SharedPreferences sharedPreferences) {
// The silent ringtone just returns an empty string
String ringtoneName = mRingtonePreference.getContext().getString(
R.string.silent_ringtone);
String ringtoneString = sharedPreferences.getString(mRingtonePreferenceKey, null);
// Bootstrap the default setting in the preferences so that we have a valid selection
// in the dialog the first time that the user opens it.
if (ringtoneString == null) {
ringtoneString = Settings.System.DEFAULT_NOTIFICATION_URI.toString();
final SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(mRingtonePreferenceKey, ringtoneString);
editor.apply();
}
if (!TextUtils.isEmpty(ringtoneString)) {
final Uri ringtoneUri = Uri.parse(ringtoneString);
final Ringtone tone = RingtoneManager.getRingtone(mRingtonePreference.getContext(),
ringtoneUri);
if (tone != null) {
ringtoneName = tone.getTitle(mRingtonePreference.getContext());
}
}
mRingtonePreference.setSummary(ringtoneName);
}
private void updateSmsEnabledPreferences() {
if (!OsUtil.isAtLeastKLP()) {
getPreferenceScreen().removePreference(mSmsDisabledPreference);
@@ -222,48 +180,14 @@ public class ApplicationSettingsActivity extends BugleActionBarActivity {
getPreferenceScreen().removePreference(mSmsEnabledPreference);
mSmsDisabledPreference.setSummary(defaultSmsAppLabel);
}
updateNotificationsPreferences();
}
mIsSmsPreferenceClicked = false;
}
private void updateNotificationsPreferences() {
final boolean canNotify = !OsUtil.isAtLeastKLP()
|| PhoneUtils.getDefault().isDefaultSmsApp();
mNotificationsEnabledPreference.setEnabled(canNotify);
}
@Override
public void onStart() {
super.onStart();
// We do this on start rather than on resume because the sound picker is in a
// separate activity.
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onResume() {
super.onResume();
updateSmsEnabledPreferences();
updateNotificationsPreferences();
}
@Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,
final String key) {
if (key.equals(mNotificationsEnabledPreferenceKey)) {
updateNotificationsPreferences();
} else if (key.equals(mRingtonePreferenceKey)) {
updateSoundSummary(sharedPreferences);
}
}
@Override
public void onStop() {
super.onStop();
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
}
}
@@ -30,7 +30,6 @@ import android.view.View;
import com.android.messaging.R;
import com.android.messaging.datamodel.action.DeleteConversationAction;
import com.android.messaging.datamodel.action.UpdateConversationArchiveStatusAction;
import com.android.messaging.datamodel.action.UpdateConversationOptionsAction;
import com.android.messaging.datamodel.action.UpdateDestinationBlockedAction;
import com.android.messaging.datamodel.data.ConversationListData;
import com.android.messaging.datamodel.data.ConversationListItemData;
@@ -190,23 +189,6 @@ public abstract class AbstractConversationListActivity extends BugleActionBarAc
exitMultiSelectState();
}
@Override
public void onActionBarNotification(final Iterable<SelectedConversation> conversations,
final boolean isNotificationOn) {
for (final SelectedConversation conversation : conversations) {
UpdateConversationOptionsAction.enableConversationNotifications(
conversation.conversationId, isNotificationOn);
}
final int textId = isNotificationOn ?
R.string.notification_on_toast_message : R.string.notification_off_toast_message;
final String message = getResources().getString(textId, 1);
UiUtils.showSnackBar(this, findViewById(android.R.id.list), message,
null /* undoRunnable */,
SnackBar.Action.SNACK_BAR_UNDO, mConversationListFragment.getSnackBarInteractions());
exitMultiSelectState();
}
@Override
public void onActionBarAddContact(final SelectedConversation conversation) {
final Uri avatarUri;
@@ -127,7 +127,6 @@ public class ConversationListItemView extends FrameLayout implements OnClickList
private TextView mTimestampTextView;
private ContactIconView mContactIconView;
private ImageView mContactCheckmarkView;
private ImageView mNotificationBellView;
private ImageView mFailedStatusIconView;
private ImageView mCrossSwipeArchiveLeftImageView;
private ImageView mCrossSwipeArchiveRightImageView;
@@ -153,7 +152,6 @@ public class ConversationListItemView extends FrameLayout implements OnClickList
mTimestampTextView = (TextView) findViewById(R.id.conversation_timestamp);
mContactIconView = (ContactIconView) findViewById(R.id.conversation_icon);
mContactCheckmarkView = (ImageView) findViewById(R.id.conversation_checkmark);
mNotificationBellView = (ImageView) findViewById(R.id.conversation_notification_bell);
mFailedStatusIconView = (ImageView) findViewById(R.id.conversation_failed_status_icon);
mCrossSwipeArchiveLeftImageView = (ImageView) findViewById(R.id.crossSwipeArchiveIconLeft);
mCrossSwipeArchiveRightImageView =
@@ -503,9 +501,6 @@ public class ConversationListItemView extends FrameLayout implements OnClickList
mAudioAttachmentView.setOnLongClickListener(this);
mAudioAttachmentView.setVisibility(audioPreviewVisiblity);
final int notificationBellVisiblity = mData.getNotificationEnabled() ? GONE : VISIBLE;
mNotificationBellView.setVisibility(notificationBellVisiblity);
if (PrefsUtils.isSwipeRightToDeleteEnabled()) {
mCrossSwipeArchiveLeftImageView.setImageDrawable(getResources()
.getDrawable(R.drawable.ic_delete_small_dark));
@@ -37,8 +37,6 @@ public class MultiSelectActionModeCallback implements Callback {
void onActionBarDelete(Collection<SelectedConversation> conversations);
void onActionBarArchive(Iterable<SelectedConversation> conversations,
boolean isToArchive);
void onActionBarNotification(Iterable<SelectedConversation> conversations,
boolean isNotificationOn);
void onActionBarAddContact(final SelectedConversation conversation);
void onActionBarBlock(final SelectedConversation conversation);
void onActionBarHome();
@@ -52,7 +50,6 @@ public class MultiSelectActionModeCallback implements Callback {
public final CharSequence participantLookupKey;
public final boolean isGroup;
public final boolean isArchived;
public final boolean notificationEnabled;
public SelectedConversation(ConversationListItemData data) {
conversationId = data.getConversationId();
timestamp = data.getTimestamp();
@@ -61,7 +58,6 @@ public class MultiSelectActionModeCallback implements Callback {
participantLookupKey = data.getParticipantLookupKey();
isGroup = data.getIsGroup();
isArchived = data.getIsArchived();
notificationEnabled = data.getNotificationEnabled();
}
}
@@ -72,8 +68,6 @@ public class MultiSelectActionModeCallback implements Callback {
private MenuItem mUnarchiveMenuItem;
private MenuItem mAddContactMenuItem;
private MenuItem mBlockMenuItem;
private MenuItem mNotificationOnMenuItem;
private MenuItem mNotificationOffMenuItem;
private boolean mHasInflated;
public MultiSelectActionModeCallback(final Listener listener) {
@@ -89,8 +83,6 @@ public class MultiSelectActionModeCallback implements Callback {
mUnarchiveMenuItem = menu.findItem(R.id.action_unarchive);
mAddContactMenuItem = menu.findItem(R.id.action_add_contact);
mBlockMenuItem = menu.findItem(R.id.action_block);
mNotificationOffMenuItem = menu.findItem(R.id.action_notification_off);
mNotificationOnMenuItem = menu.findItem(R.id.action_notification_on);
mHasInflated = true;
updateActionIconsVisiblity();
return true;
@@ -113,12 +105,6 @@ public class MultiSelectActionModeCallback implements Callback {
case R.id.action_unarchive:
mListener.onActionBarArchive(mSelectedConversations.values(), false);
return true;
case R.id.action_notification_off:
mListener.onActionBarNotification(mSelectedConversations.values(), false);
return true;
case R.id.action_notification_on:
mListener.onActionBarNotification(mSelectedConversations.values(), true);
return true;
case R.id.action_add_contact:
Assert.isTrue(mSelectedConversations.size() == 1);
mListener.onActionBarAddContact(mSelectedConversations.valueAt(0));
@@ -186,16 +172,8 @@ public class MultiSelectActionModeCallback implements Callback {
boolean hasCurrentlyArchived = false;
boolean hasCurrentlyUnarchived = false;
boolean hasCurrentlyOnNotification = false;
boolean hasCurrentlyOffNotification = false;
final Iterable<SelectedConversation> conversations = mSelectedConversations.values();
for (final SelectedConversation conversation : conversations) {
if (conversation.notificationEnabled) {
hasCurrentlyOnNotification = true;
} else {
hasCurrentlyOffNotification = true;
}
if (conversation.isArchived) {
hasCurrentlyArchived = true;
} else {
@@ -203,15 +181,10 @@ public class MultiSelectActionModeCallback implements Callback {
}
// If we found at least one of each example we don't need to keep looping.
if (hasCurrentlyOffNotification && hasCurrentlyOnNotification &&
hasCurrentlyArchived && hasCurrentlyUnarchived) {
if (hasCurrentlyArchived && hasCurrentlyUnarchived) {
break;
}
}
// If we have notification off conversations we show on button, if we have notification on
// conversation we show off button. We can show both if we have a mixture.
mNotificationOffMenuItem.setVisible(hasCurrentlyOnNotification);
mNotificationOnMenuItem.setVisible(hasCurrentlyOffNotification);
mArchiveMenuItem.setVisible(hasCurrentlyUnarchived);
mUnarchiveMenuItem.setVisible(hasCurrentlyArchived);
@@ -18,12 +18,12 @@ package com.android.messaging.ui.conversationsettings;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.NotificationManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
import android.media.RingtoneManager;
import android.os.Bundle;
import android.os.Parcelable;
import android.provider.Settings;
@@ -49,6 +49,7 @@ import com.android.messaging.ui.CompositeAdapter;
import com.android.messaging.ui.PersonItemView;
import com.android.messaging.ui.UIIntents;
import com.android.messaging.ui.conversation.ConversationActivity;
import com.android.messaging.util.NotificationsUtil;
import com.android.messaging.util.Assert;
import java.util.ArrayList;
@@ -62,11 +63,10 @@ public class PeopleAndOptionsFragment extends Fragment
private ListView mListView;
private OptionsListAdapter mOptionsListAdapter;
private PeopleListAdapter mPeopleListAdapter;
private List<ParticipantData> mOtherParticipants;
private final Binding<PeopleAndOptionsData> mBinding =
BindingBase.createBinding(this);
private static final int REQUEST_CODE_RINGTONE_PICKER = 1000;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -89,17 +89,6 @@ public class PeopleAndOptionsFragment extends Fragment
return view;
}
@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_RINGTONE_PICKER) {
final Parcelable pick = data.getParcelableExtra(
RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
final String pickedUri = pick == null ? "" : pick.toString();
mBinding.getData().setConversationNotificationSound(mBinding, pickedUri);
}
}
@Override
public void onDestroy() {
super.onDestroy();
@@ -125,30 +114,31 @@ public class PeopleAndOptionsFragment extends Fragment
final List<ParticipantData> participants) {
mBinding.ensureBound(data);
mPeopleListAdapter.updateParticipants(participants);
mOtherParticipants = participants;
final ParticipantData otherParticipant = participants.size() == 1 ?
participants.get(0) : null;
mOptionsListAdapter.setOtherParticipant(otherParticipant);
}
@Override
public void onOptionsItemViewClicked(final PeopleOptionsItemData item,
final boolean isChecked) {
public void onOptionsItemViewClicked(final PeopleOptionsItemData item) {
switch (item.getItemId()) {
case PeopleOptionsItemData.SETTING_NOTIFICATION_ENABLED:
mBinding.getData().enableConversationNotifications(mBinding, isChecked);
break;
case PeopleOptionsItemData.SETTING_NOTIFICATION_SOUND_URI:
final Intent ringtonePickerIntent = UIIntents.get().getRingtonePickerIntent(
getString(R.string.notification_sound_pref_title),
item.getRingtoneUri(), Settings.System.DEFAULT_NOTIFICATION_URI,
RingtoneManager.TYPE_NOTIFICATION);
startActivityForResult(ringtonePickerIntent, REQUEST_CODE_RINGTONE_PICKER);
break;
case PeopleOptionsItemData.SETTING_NOTIFICATION_VIBRATION:
mBinding.getData().enableConversationNotificationVibration(mBinding,
isChecked);
case PeopleOptionsItemData.SETTING_NOTIFICATION:
ArrayList<String> participantsNames = new ArrayList<String>();
for (ParticipantData participant : mOtherParticipants) {
participantsNames.add(participant.getDisplayName(true));
}
NotificationsUtil.createNotificationChannelGroup(getActivity(),
NotificationsUtil.CONVERSATION_GROUP_NAME,
R.string.notification_channel_messages_title);
NotificationsUtil.createNotificationChannel(getActivity(),
mBinding.getData().getConversationId(),
String.join(", ", participantsNames),
NotificationManager.IMPORTANCE_DEFAULT,
NotificationsUtil.CONVERSATION_GROUP_NAME);
Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
startActivity(intent);
break;
case PeopleOptionsItemData.SETTING_BLOCKED:
@@ -38,7 +38,7 @@ public class PeopleOptionsItemView extends LinearLayout {
* Implemented by the host of this view that handles options click event.
*/
public interface HostInterface {
void onOptionsItemViewClicked(PeopleOptionsItemData item, boolean isChecked);
void onOptionsItemViewClicked(PeopleOptionsItemData item);
}
private TextView mTitle;
@@ -55,12 +55,10 @@ public class PeopleOptionsItemView extends LinearLayout {
@Override
protected void onFinishInflate () {
mTitle = (TextView) findViewById(R.id.title);
mSubtitle = (TextView) findViewById(R.id.subtitle);
mSwitch = (SwitchCompat) findViewById(R.id.switch_button);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
mHostInterface.onOptionsItemViewClicked(mData, !mData.getChecked());
mHostInterface.onOptionsItemViewClicked(mData);
}
});
}
@@ -72,28 +70,5 @@ public class PeopleOptionsItemView extends LinearLayout {
mHostInterface = hostInterface;
mTitle.setText(mData.getTitle());
final String subtitle = mData.getSubtitle();
if (TextUtils.isEmpty(subtitle)) {
mSubtitle.setVisibility(GONE);
} else {
mSubtitle.setVisibility(VISIBLE);
mSubtitle.setText(subtitle);
}
if (mData.getCheckable()) {
mSwitch.setVisibility(VISIBLE);
mSwitch.setChecked(mData.getChecked());
} else {
mSwitch.setVisibility(GONE);
}
final boolean enabled = mData.getEnabled();
if (enabled != isEnabled()) {
mTitle.setEnabled(enabled);
mSubtitle.setEnabled(enabled);
mSwitch.setEnabled(enabled);
setAlpha(enabled ? 1.0f : 0.5f);
setEnabled(enabled);
}
}
}
@@ -0,0 +1,101 @@
/*
* Copyright (C) 2019 The LineageOS 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.messaging.util;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
public final class NotificationsUtil {
public static final String DEFAULT_CHANNEL_ID = "messaging_channel";
public static final String CONVERSATION_GROUP_NAME = "conversation_group";
private NotificationsUtil() {
}
public static void createNotificationChannel(Context context, String id,
int titleResId, int priority, String groupId) {
String title = context.getString(titleResId);
createNotificationChannel(context, id, title, priority, groupId);
}
public static void createNotificationChannel(Context context, String id,
String title, int priority, String groupId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
NotificationManager manager = context.getSystemService(NotificationManager.class);
NotificationChannel existing = manager.getNotificationChannel(id);
if (existing != null) {
return;
}
NotificationChannel newChannel = new NotificationChannel(id, title, priority);
newChannel.enableLights(true);
if (groupId != null) {
newChannel.setGroup(groupId);
}
manager.createNotificationChannel(newChannel);
}
public static void deleteNotificationChannel(Context context, String id) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
NotificationManager manager = context.getSystemService(NotificationManager.class);
manager.deleteNotificationChannel(id);
}
public static void createNotificationChannelGroup(Context context, String id,
int titleResId) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return;
}
NotificationManager manager = context.getSystemService(NotificationManager.class);
NotificationChannelGroup existing = manager.getNotificationChannelGroup(id);
if (existing != null) {
return;
}
String title = context.getString(titleResId);
NotificationChannelGroup newChannelGroup = new NotificationChannelGroup(id, title);
manager.createNotificationChannelGroup(newChannelGroup);
}
public static NotificationChannel getNotificationChannel(Context context, String id) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return null;
}
NotificationManager manager = context.getSystemService(NotificationManager.class);
return manager.getNotificationChannel(id);
}
public static NotificationChannelGroup getNotificationChannelGroup(Context context, String id) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
return null;
}
NotificationManager manager = context.getSystemService(NotificationManager.class);
return manager.getNotificationChannelGroup(id);
}
}
@@ -1,53 +0,0 @@
/*
* Copyright (C) 2015 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.messaging.util;
import android.content.Context;
import android.net.Uri;
import android.provider.Settings;
import android.text.TextUtils;
import com.android.messaging.Factory;
import com.android.messaging.R;
public class RingtoneUtil {
/**
* Return a ringtone Uri for the string representation passed in. Use the app
* and system defaults as fallbacks
* @param ringtoneString is the ringtone to resolve
* @return the Uri of the ringtone or the fallback ringtone
*/
public static Uri getNotificationRingtoneUri(String ringtoneString) {
if (ringtoneString == null) {
// No override specified, fall back to system-wide setting.
final BuglePrefs prefs = BuglePrefs.getApplicationPrefs();
final Context context = Factory.get().getApplicationContext();
final String prefKey = context.getString(R.string.notification_sound_pref_key);
ringtoneString = prefs.getString(prefKey, null);
}
if (!TextUtils.isEmpty(ringtoneString)) {
// We have set a value, even if it is the default Uri at some point
return Uri.parse(ringtoneString);
} else if (ringtoneString == null) {
// We have no setting specified (== null), so we default to the system default
return Settings.System.DEFAULT_NOTIFICATION_URI;
} else {
// An empty string (== "") here is the result of selecting "None" as the ringtone
return null;
}
}
}
@@ -120,10 +120,6 @@ public class WidgetConversationListService extends RemoteViewsService {
remoteViews.setTextViewText(R.id.from,
boldifyIfUnread(conv.getName(), hasUnreadMessages));
// Notifications turned off mini-bell icon
remoteViews.setViewVisibility(R.id.conversation_notification_bell,
conv.getNotificationEnabled() ? View.GONE : View.VISIBLE);
// On click intent.
final Intent intent = UIIntents.get().getIntentForConversationActivity(mContext,
conv.getConversationId(), null /* draft */);