1 /*
2  * Copyright (C) 2016 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.app.voicemail;
18 
19 import android.content.Context;
20 import android.database.ContentObserver;
21 import android.database.Cursor;
22 import android.os.Handler;
23 import android.support.annotation.MainThread;
24 import android.telecom.PhoneAccountHandle;
25 import android.telephony.PhoneStateListener;
26 import android.telephony.ServiceState;
27 import android.telephony.TelephonyManager;
28 import android.util.ArrayMap;
29 import com.android.dialer.app.calllog.CallLogAlertManager;
30 import com.android.dialer.app.calllog.CallLogModalAlertManager;
31 import com.android.dialer.common.Assert;
32 import com.android.dialer.common.LogUtil;
33 import com.android.dialer.database.CallLogQueryHandler;
34 import com.android.dialer.voicemail.listui.error.VoicemailErrorAlert;
35 import com.android.dialer.voicemail.listui.error.VoicemailErrorMessageCreator;
36 import com.android.dialer.voicemail.listui.error.VoicemailStatus;
37 import com.android.dialer.voicemail.listui.error.VoicemailStatusReader;
38 import com.android.voicemail.VoicemailComponent;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.Map;
42 
43 /**
44  * Fetches voicemail status and generate {@link VoicemailStatus} for {@link VoicemailErrorAlert} to
45  * show.
46  */
47 public class VoicemailErrorManager implements CallLogQueryHandler.Listener, VoicemailStatusReader {
48 
49   private final Context context;
50   private final CallLogQueryHandler callLogQueryHandler;
51   private final VoicemailErrorAlert alertItem;
52 
53   private final Map<PhoneAccountHandle, ServiceStateListener> listeners = new ArrayMap<>();
54 
55   private final ContentObserver statusObserver =
56       new ContentObserver(new Handler()) {
57         @Override
58         public void onChange(boolean selfChange) {
59           super.onChange(selfChange);
60           fetchStatus();
61         }
62       };
63 
64   private boolean isForeground;
65   private boolean statusInvalidated;
66 
VoicemailErrorManager( Context context, CallLogAlertManager alertManager, CallLogModalAlertManager modalAlertManager)67   public VoicemailErrorManager(
68       Context context,
69       CallLogAlertManager alertManager,
70       CallLogModalAlertManager modalAlertManager) {
71     this.context = context;
72     alertItem =
73         new VoicemailErrorAlert(
74             context, alertManager, modalAlertManager, new VoicemailErrorMessageCreator());
75     callLogQueryHandler = new CallLogQueryHandler(context, context.getContentResolver(), this);
76     fetchStatus();
77   }
78 
getContentObserver()79   public ContentObserver getContentObserver() {
80     return statusObserver;
81   }
82 
83   @MainThread
84   @Override
onVoicemailStatusFetched(Cursor statusCursor)85   public void onVoicemailStatusFetched(Cursor statusCursor) {
86     List<VoicemailStatus> statuses = new ArrayList<>();
87     while (statusCursor.moveToNext()) {
88       VoicemailStatus status = new VoicemailStatus(context, statusCursor);
89       if (status.isActive(context)) {
90         statuses.add(status);
91         addServiceStateListener(status);
92       } else {
93         LogUtil.i("VisualVoicemailCallLogFragment.shouldAutoSync", "inactive source ignored");
94       }
95     }
96     alertItem.updateStatus(statuses, this);
97     // TODO(twyen): a bug support error from multiple sources.
98     return;
99   }
100 
101   @MainThread
addServiceStateListener(VoicemailStatus status)102   private void addServiceStateListener(VoicemailStatus status) {
103     Assert.isMainThread();
104     if (!VoicemailComponent.get(context).getVoicemailClient().isVoicemailModuleEnabled()) {
105       LogUtil.i("VoicemailErrorManager.addServiceStateListener", "VVM module not enabled");
106       return;
107     }
108     if (!status.sourcePackage.equals(context.getPackageName())) {
109       LogUtil.i("VoicemailErrorManager.addServiceStateListener", "non-dialer source");
110       return;
111     }
112     TelephonyManager telephonyManager =
113         context
114             .getSystemService(TelephonyManager.class)
115             .createForPhoneAccountHandle(status.getPhoneAccountHandle());
116     if (telephonyManager == null) {
117       LogUtil.e("VoicemailErrorManager.addServiceStateListener", "invalid PhoneAccountHandle");
118       return;
119     }
120     PhoneAccountHandle phoneAccountHandle = status.getPhoneAccountHandle();
121     if (listeners.containsKey(phoneAccountHandle)) {
122       return;
123     }
124     LogUtil.i(
125         "VoicemailErrorManager.addServiceStateListener",
126         "adding listener for " + phoneAccountHandle);
127     ServiceStateListener serviceStateListener = new ServiceStateListener();
128     telephonyManager.listen(serviceStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
129     listeners.put(phoneAccountHandle, serviceStateListener);
130   }
131 
132   @Override
onVoicemailUnreadCountFetched(Cursor cursor)133   public void onVoicemailUnreadCountFetched(Cursor cursor) {
134     // Do nothing
135   }
136 
137   @Override
onMissedCallsUnreadCountFetched(Cursor cursor)138   public void onMissedCallsUnreadCountFetched(Cursor cursor) {
139     // Do nothing
140   }
141 
142   @Override
onCallsFetched(Cursor combinedCursor)143   public boolean onCallsFetched(Cursor combinedCursor) {
144     // Do nothing
145     return false;
146   }
147 
onResume()148   public void onResume() {
149     isForeground = true;
150     if (statusInvalidated) {
151       fetchStatus();
152     }
153   }
154 
onPause()155   public void onPause() {
156     isForeground = false;
157     statusInvalidated = false;
158   }
159 
onDestroy()160   public void onDestroy() {
161     TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
162     for (ServiceStateListener listener : listeners.values()) {
163       telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE);
164     }
165   }
166 
167   @Override
refresh()168   public void refresh() {
169     fetchStatus();
170   }
171 
172   /**
173    * Fetch the status when the dialer is in foreground, or queue a fetch when the dialer resumes.
174    */
fetchStatus()175   private void fetchStatus() {
176     if (!isForeground) {
177       // Dialer is in the background, UI should not be updated. Reload the status when it resumes.
178       statusInvalidated = true;
179       return;
180     }
181     callLogQueryHandler.fetchVoicemailStatus();
182   }
183 
184   private class ServiceStateListener extends PhoneStateListener {
185 
186     @Override
onServiceStateChanged(ServiceState serviceState)187     public void onServiceStateChanged(ServiceState serviceState) {
188       fetchStatus();
189     }
190   }
191 }
192