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.app.voicemail.error.VoicemailErrorAlert; 32 import com.android.dialer.app.voicemail.error.VoicemailErrorMessageCreator; 33 import com.android.dialer.app.voicemail.error.VoicemailStatus; 34 import com.android.dialer.app.voicemail.error.VoicemailStatusReader; 35 import com.android.dialer.common.Assert; 36 import com.android.dialer.common.LogUtil; 37 import com.android.dialer.database.CallLogQueryHandler; 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()) { 90 statuses.add(status); 91 addServiceStateListener(status); 92 } 93 } 94 alertItem.updateStatus(statuses, this); 95 // TODO: b/30668323 support error from multiple sources. 96 return; 97 } 98 99 @MainThread addServiceStateListener(VoicemailStatus status)100 private void addServiceStateListener(VoicemailStatus status) { 101 Assert.isMainThread(); 102 if (!VoicemailComponent.get(context).getVoicemailClient().isVoicemailModuleEnabled()) { 103 LogUtil.i("VoicemailErrorManager.addServiceStateListener", "VVM module not enabled"); 104 return; 105 } 106 if (!status.sourcePackage.equals(context.getPackageName())) { 107 LogUtil.i("VoicemailErrorManager.addServiceStateListener", "non-dialer source"); 108 return; 109 } 110 TelephonyManager telephonyManager = 111 context 112 .getSystemService(TelephonyManager.class) 113 .createForPhoneAccountHandle(status.getPhoneAccountHandle()); 114 if (telephonyManager == null) { 115 LogUtil.e("VoicemailErrorManager.addServiceStateListener", "invalid PhoneAccountHandle"); 116 return; 117 } 118 PhoneAccountHandle phoneAccountHandle = status.getPhoneAccountHandle(); 119 if (listeners.containsKey(phoneAccountHandle)) { 120 return; 121 } 122 LogUtil.i( 123 "VoicemailErrorManager.addServiceStateListener", 124 "adding listener for " + phoneAccountHandle); 125 ServiceStateListener serviceStateListener = new ServiceStateListener(); 126 telephonyManager.listen(serviceStateListener, PhoneStateListener.LISTEN_SERVICE_STATE); 127 listeners.put(phoneAccountHandle, serviceStateListener); 128 } 129 130 @Override onVoicemailUnreadCountFetched(Cursor cursor)131 public void onVoicemailUnreadCountFetched(Cursor cursor) { 132 // Do nothing 133 } 134 135 @Override onMissedCallsUnreadCountFetched(Cursor cursor)136 public void onMissedCallsUnreadCountFetched(Cursor cursor) { 137 // Do nothing 138 } 139 140 @Override onCallsFetched(Cursor combinedCursor)141 public boolean onCallsFetched(Cursor combinedCursor) { 142 // Do nothing 143 return false; 144 } 145 onResume()146 public void onResume() { 147 isForeground = true; 148 if (statusInvalidated) { 149 fetchStatus(); 150 } 151 } 152 onPause()153 public void onPause() { 154 isForeground = false; 155 statusInvalidated = false; 156 } 157 onDestroy()158 public void onDestroy() { 159 TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); 160 for (ServiceStateListener listener : listeners.values()) { 161 telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE); 162 } 163 } 164 165 @Override refresh()166 public void refresh() { 167 fetchStatus(); 168 } 169 170 /** 171 * Fetch the status when the dialer is in foreground, or queue a fetch when the dialer resumes. 172 */ fetchStatus()173 private void fetchStatus() { 174 if (!isForeground) { 175 // Dialer is in the background, UI should not be updated. Reload the status when it resumes. 176 statusInvalidated = true; 177 return; 178 } 179 callLogQueryHandler.fetchVoicemailStatus(); 180 } 181 182 private class ServiceStateListener extends PhoneStateListener { 183 184 @Override onServiceStateChanged(ServiceState serviceState)185 public void onServiceStateChanged(ServiceState serviceState) { 186 fetchStatus(); 187 } 188 } 189 } 190