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