1 /* 2 * Copyright (C) 2015 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.messaging.datamodel.action; 18 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.text.TextUtils; 22 23 import com.android.messaging.Factory; 24 import com.android.messaging.datamodel.BugleDatabaseOperations; 25 import com.android.messaging.datamodel.DataModel; 26 import com.android.messaging.datamodel.DatabaseWrapper; 27 import com.android.messaging.datamodel.action.ActionMonitor.ActionCompletedListener; 28 import com.android.messaging.datamodel.data.LaunchConversationData; 29 import com.android.messaging.datamodel.data.ParticipantData; 30 import com.android.messaging.sms.MmsUtils; 31 import com.android.messaging.util.Assert; 32 import com.android.messaging.util.Assert.RunsOnMainThread; 33 import com.android.messaging.util.LogUtil; 34 35 import java.util.ArrayList; 36 37 /** 38 * Action used to get or create a conversation for a list of conversation participants. 39 */ 40 public class GetOrCreateConversationAction extends Action implements Parcelable { 41 /** 42 * Interface for GetOrCreateConversationAction listeners 43 */ 44 public interface GetOrCreateConversationActionListener { 45 @RunsOnMainThread onGetOrCreateConversationSucceeded(final ActionMonitor monitor, final Object data, final String conversationId)46 abstract void onGetOrCreateConversationSucceeded(final ActionMonitor monitor, 47 final Object data, final String conversationId); 48 49 @RunsOnMainThread onGetOrCreateConversationFailed(final ActionMonitor monitor, final Object data)50 abstract void onGetOrCreateConversationFailed(final ActionMonitor monitor, 51 final Object data); 52 } 53 getOrCreateConversation( final ArrayList<ParticipantData> participants, final Object data, final GetOrCreateConversationActionListener listener)54 public static GetOrCreateConversationActionMonitor getOrCreateConversation( 55 final ArrayList<ParticipantData> participants, final Object data, 56 final GetOrCreateConversationActionListener listener) { 57 final GetOrCreateConversationActionMonitor monitor = new 58 GetOrCreateConversationActionMonitor(data, listener); 59 final GetOrCreateConversationAction action = new GetOrCreateConversationAction(participants, 60 monitor.getActionKey()); 61 action.start(monitor); 62 return monitor; 63 } 64 65 getOrCreateConversation( final String[] recipients, final Object data, final LaunchConversationData listener)66 public static GetOrCreateConversationActionMonitor getOrCreateConversation( 67 final String[] recipients, final Object data, final LaunchConversationData listener) { 68 final ArrayList<ParticipantData> participants = new ArrayList<>(); 69 for (String recipient : recipients) { 70 recipient = recipient.trim(); 71 if (!TextUtils.isEmpty(recipient)) { 72 participants.add(ParticipantData.getFromRawPhoneBySystemLocale(recipient)); 73 } else { 74 LogUtil.w(LogUtil.BUGLE_TAG, "getOrCreateConversation hit empty recipient"); 75 } 76 } 77 return getOrCreateConversation(participants, data, listener); 78 } 79 80 private static final String KEY_PARTICIPANTS_LIST = "participants_list"; 81 GetOrCreateConversationAction(final ArrayList<ParticipantData> participants, final String actionKey)82 private GetOrCreateConversationAction(final ArrayList<ParticipantData> participants, 83 final String actionKey) { 84 super(actionKey); 85 actionParameters.putParcelableArrayList(KEY_PARTICIPANTS_LIST, participants); 86 } 87 88 /** 89 * Lookup the conversation or create a new one. 90 */ 91 @Override executeAction()92 protected Object executeAction() { 93 final DatabaseWrapper db = DataModel.get().getDatabase(); 94 95 // First find the thread id for this list of participants. 96 final ArrayList<ParticipantData> participants = 97 actionParameters.getParcelableArrayList(KEY_PARTICIPANTS_LIST); 98 BugleDatabaseOperations.sanitizeConversationParticipants(participants); 99 final ArrayList<String> recipients = 100 BugleDatabaseOperations.getRecipientsFromConversationParticipants(participants); 101 102 final long threadId = MmsUtils.getOrCreateThreadId(Factory.get().getApplicationContext(), 103 recipients); 104 105 if (threadId < 0) { 106 LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't create a threadId in SMS db for numbers : " + 107 LogUtil.sanitizePII(recipients.toString())); 108 // TODO: Add a better way to indicate an error from executeAction. 109 return null; 110 } 111 112 final String conversationId = BugleDatabaseOperations.getOrCreateConversation(db, threadId, 113 false, participants, false, false, null); 114 115 return conversationId; 116 } 117 118 /** 119 * A monitor that notifies a listener upon completion 120 */ 121 public static class GetOrCreateConversationActionMonitor extends ActionMonitor 122 implements ActionCompletedListener { 123 private final GetOrCreateConversationActionListener mListener; 124 GetOrCreateConversationActionMonitor(final Object data, final GetOrCreateConversationActionListener listener)125 GetOrCreateConversationActionMonitor(final Object data, 126 final GetOrCreateConversationActionListener listener) { 127 super(STATE_CREATED, generateUniqueActionKey("GetOrCreateConversationAction"), data); 128 setCompletedListener(this); 129 mListener = listener; 130 } 131 132 @Override onActionSucceeded(final ActionMonitor monitor, final Action action, final Object data, final Object result)133 public void onActionSucceeded(final ActionMonitor monitor, 134 final Action action, final Object data, final Object result) { 135 if (result == null) { 136 mListener.onGetOrCreateConversationFailed(monitor, data); 137 } else { 138 mListener.onGetOrCreateConversationSucceeded(monitor, data, (String) result); 139 } 140 } 141 142 @Override onActionFailed(final ActionMonitor monitor, final Action action, final Object data, final Object result)143 public void onActionFailed(final ActionMonitor monitor, 144 final Action action, final Object data, final Object result) { 145 // TODO: Currently onActionFailed is only called if there is an error in 146 // processing requests, not for errors in the local processing. 147 Assert.fail("Unreachable"); 148 mListener.onGetOrCreateConversationFailed(monitor, data); 149 } 150 } 151 GetOrCreateConversationAction(final Parcel in)152 private GetOrCreateConversationAction(final Parcel in) { 153 super(in); 154 } 155 156 public static final Parcelable.Creator<GetOrCreateConversationAction> CREATOR 157 = new Parcelable.Creator<GetOrCreateConversationAction>() { 158 @Override 159 public GetOrCreateConversationAction createFromParcel(final Parcel in) { 160 return new GetOrCreateConversationAction(in); 161 } 162 163 @Override 164 public GetOrCreateConversationAction[] newArray(final int size) { 165 return new GetOrCreateConversationAction[size]; 166 } 167 }; 168 169 @Override writeToParcel(final Parcel parcel, final int flags)170 public void writeToParcel(final Parcel parcel, final int flags) { 171 writeActionToParcel(parcel, flags); 172 } 173 } 174