1 /*
2  * Copyright (C) 2018 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.phonelookup.cequint;
18 
19 import android.content.Context;
20 import android.telecom.Call;
21 import android.text.TextUtils;
22 import com.android.dialer.DialerPhoneNumber;
23 import com.android.dialer.common.Assert;
24 import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
25 import com.android.dialer.common.concurrent.Annotations.LightweightExecutor;
26 import com.android.dialer.inject.ApplicationContext;
27 import com.android.dialer.location.GeoUtil;
28 import com.android.dialer.oem.CequintCallerIdManager;
29 import com.android.dialer.oem.CequintCallerIdManager.CequintCallerIdContact;
30 import com.android.dialer.phonelookup.PhoneLookup;
31 import com.android.dialer.phonelookup.PhoneLookupInfo;
32 import com.android.dialer.phonelookup.PhoneLookupInfo.CequintInfo;
33 import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
34 import com.android.dialer.telecom.TelecomCallUtil;
35 import com.google.common.collect.ImmutableMap;
36 import com.google.common.collect.ImmutableSet;
37 import com.google.common.util.concurrent.Futures;
38 import com.google.common.util.concurrent.ListenableFuture;
39 import com.google.common.util.concurrent.ListeningExecutorService;
40 import javax.inject.Inject;
41 
42 /** PhoneLookup implementation for Cequint. */
43 public class CequintPhoneLookup implements PhoneLookup<CequintInfo> {
44 
45   private final Context appContext;
46   private final ListeningExecutorService backgroundExecutorService;
47   private final ListeningExecutorService lightweightExecutorService;
48 
49   @Inject
CequintPhoneLookup( @pplicationContext Context appContext, @BackgroundExecutor ListeningExecutorService backgroundExecutorService, @LightweightExecutor ListeningExecutorService lightweightExecutorService)50   CequintPhoneLookup(
51       @ApplicationContext Context appContext,
52       @BackgroundExecutor ListeningExecutorService backgroundExecutorService,
53       @LightweightExecutor ListeningExecutorService lightweightExecutorService) {
54     this.appContext = appContext;
55     this.backgroundExecutorService = backgroundExecutorService;
56     this.lightweightExecutorService = lightweightExecutorService;
57   }
58 
59   @Override
lookup(Context appContext, Call call)60   public ListenableFuture<CequintInfo> lookup(Context appContext, Call call) {
61     if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) {
62       return Futures.immediateFuture(CequintInfo.getDefaultInstance());
63     }
64 
65     ListenableFuture<DialerPhoneNumber> dialerPhoneNumberFuture =
66         backgroundExecutorService.submit(
67             () -> {
68               DialerPhoneNumberUtil dialerPhoneNumberUtil = new DialerPhoneNumberUtil();
69               return dialerPhoneNumberUtil.parse(
70                   TelecomCallUtil.getNumber(call), GeoUtil.getCurrentCountryIso(appContext));
71             });
72     String callerDisplayName = call.getDetails().getCallerDisplayName();
73     boolean isIncomingCall = (call.getState() == Call.STATE_RINGING);
74 
75     return Futures.transformAsync(
76         dialerPhoneNumberFuture,
77         dialerPhoneNumber ->
78             backgroundExecutorService.submit(
79                 () ->
80                     buildCequintInfo(
81                         CequintCallerIdManager.getCequintCallerIdContactForCall(
82                             appContext,
83                             Assert.isNotNull(dialerPhoneNumber).getNormalizedNumber(),
84                             callerDisplayName,
85                             isIncomingCall))),
86         lightweightExecutorService);
87   }
88 
89   @Override
lookup(DialerPhoneNumber dialerPhoneNumber)90   public ListenableFuture<CequintInfo> lookup(DialerPhoneNumber dialerPhoneNumber) {
91     if (!CequintCallerIdManager.isCequintCallerIdEnabled(appContext)) {
92       return Futures.immediateFuture(CequintInfo.getDefaultInstance());
93     }
94 
95     return backgroundExecutorService.submit(
96         () ->
97             buildCequintInfo(
98                 CequintCallerIdManager.getCequintCallerIdContactForNumber(
99                     appContext, dialerPhoneNumber.getNormalizedNumber())));
100   }
101 
102   @Override
isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers)103   public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
104     return Futures.immediateFuture(false);
105   }
106 
107   @Override
getMostRecentInfo( ImmutableMap<DialerPhoneNumber, CequintInfo> existingInfoMap)108   public ListenableFuture<ImmutableMap<DialerPhoneNumber, CequintInfo>> getMostRecentInfo(
109       ImmutableMap<DialerPhoneNumber, CequintInfo> existingInfoMap) {
110     return Futures.immediateFuture(existingInfoMap);
111   }
112 
113   @Override
setSubMessage(PhoneLookupInfo.Builder destination, CequintInfo subMessage)114   public void setSubMessage(PhoneLookupInfo.Builder destination, CequintInfo subMessage) {
115     destination.setCequintInfo(subMessage);
116   }
117 
118   @Override
getSubMessage(PhoneLookupInfo phoneLookupInfo)119   public CequintInfo getSubMessage(PhoneLookupInfo phoneLookupInfo) {
120     return phoneLookupInfo.getCequintInfo();
121   }
122 
123   @Override
onSuccessfulBulkUpdate()124   public ListenableFuture<Void> onSuccessfulBulkUpdate() {
125     return Futures.immediateFuture(null);
126   }
127 
128   @Override
registerContentObservers()129   public void registerContentObservers() {
130     // No need to register a content observer as the Cequint content provider doesn't support batch
131     // queries.
132   }
133 
134   @Override
unregisterContentObservers()135   public void unregisterContentObservers() {
136     // Nothing to be done as no content observer is registered.
137   }
138 
139   @Override
clearData()140   public ListenableFuture<Void> clearData() {
141     return Futures.immediateFuture(null);
142   }
143 
144   @Override
getLoggingName()145   public String getLoggingName() {
146     return "CequintPhoneLookup";
147   }
148 
149   /**
150    * Builds a {@link CequintInfo} proto based on the given {@link CequintCallerIdContact} returned
151    * by {@link CequintCallerIdManager}.
152    */
buildCequintInfo(CequintCallerIdContact cequintCallerIdContact)153   private static CequintInfo buildCequintInfo(CequintCallerIdContact cequintCallerIdContact) {
154     CequintInfo.Builder cequintInfoBuilder = CequintInfo.newBuilder();
155 
156     // Every field in CequintCallerIdContact can be null.
157     if (!TextUtils.isEmpty(cequintCallerIdContact.name())) {
158       cequintInfoBuilder.setName(cequintCallerIdContact.name());
159     }
160     if (!TextUtils.isEmpty(cequintCallerIdContact.geolocation())) {
161       cequintInfoBuilder.setGeolocation(cequintCallerIdContact.geolocation());
162     }
163     if (!TextUtils.isEmpty(cequintCallerIdContact.photoUri())) {
164       cequintInfoBuilder.setPhotoUri(cequintCallerIdContact.photoUri());
165     }
166 
167     return cequintInfoBuilder.build();
168   }
169 }
170