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.blockreportspam;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.support.v4.app.FragmentManager;
24 import android.widget.Toast;
25 import com.android.dialer.blocking.Blocking;
26 import com.android.dialer.blocking.Blocking.BlockingFailedException;
27 import com.android.dialer.blockreportspam.BlockReportSpamDialogs.DialogFragmentForBlockingNumber;
28 import com.android.dialer.blockreportspam.BlockReportSpamDialogs.DialogFragmentForBlockingNumberAndOptionallyReportingAsSpam;
29 import com.android.dialer.blockreportspam.BlockReportSpamDialogs.DialogFragmentForReportingNotSpam;
30 import com.android.dialer.blockreportspam.BlockReportSpamDialogs.DialogFragmentForUnblockingNumber;
31 import com.android.dialer.blockreportspam.BlockReportSpamDialogs.OnConfirmListener;
32 import com.android.dialer.blockreportspam.BlockReportSpamDialogs.OnSpamDialogClickListener;
33 import com.android.dialer.common.Assert;
34 import com.android.dialer.common.LogUtil;
35 import com.android.dialer.common.concurrent.DialerExecutorComponent;
36 import com.android.dialer.logging.DialerImpression;
37 import com.android.dialer.logging.DialerImpression.Type;
38 import com.android.dialer.logging.Logger;
39 import com.android.dialer.protos.ProtoParsers;
40 import com.android.dialer.spam.Spam;
41 import com.android.dialer.spam.SpamComponent;
42 import com.android.dialer.spam.SpamSettings;
43 import com.google.common.collect.ImmutableList;
44 import com.google.common.util.concurrent.FutureCallback;
45 import com.google.common.util.concurrent.Futures;
46 
47 /**
48  * A {@link BroadcastReceiver} that shows an appropriate dialog upon receiving notifications from
49  * {@link ShowBlockReportSpamDialogNotifier}.
50  */
51 public final class ShowBlockReportSpamDialogReceiver extends BroadcastReceiver {
52 
53   static final String ACTION_SHOW_DIALOG_TO_BLOCK_NUMBER = "show_dialog_to_block_number";
54   static final String ACTION_SHOW_DIALOG_TO_BLOCK_NUMBER_AND_OPTIONALLY_REPORT_SPAM =
55       "show_dialog_to_block_number_and_optionally_report_spam";
56   static final String ACTION_SHOW_DIALOG_TO_REPORT_NOT_SPAM = "show_dialog_to_report_not_spam";
57   static final String ACTION_SHOW_DIALOG_TO_UNBLOCK_NUMBER = "show_dialog_to_unblock_number";
58   static final String EXTRA_DIALOG_INFO = "dialog_info";
59 
60   /** {@link FragmentManager} needed to show a {@link android.app.DialogFragment}. */
61   private final FragmentManager fragmentManager;
62 
63   /** Returns an {@link IntentFilter} containing all actions accepted by this broadcast receiver. */
getIntentFilter()64   public static IntentFilter getIntentFilter() {
65     IntentFilter intentFilter = new IntentFilter();
66     intentFilter.addAction(ACTION_SHOW_DIALOG_TO_BLOCK_NUMBER_AND_OPTIONALLY_REPORT_SPAM);
67     intentFilter.addAction(ACTION_SHOW_DIALOG_TO_BLOCK_NUMBER);
68     intentFilter.addAction(ACTION_SHOW_DIALOG_TO_REPORT_NOT_SPAM);
69     intentFilter.addAction(ACTION_SHOW_DIALOG_TO_UNBLOCK_NUMBER);
70     return intentFilter;
71   }
72 
ShowBlockReportSpamDialogReceiver(FragmentManager fragmentManager)73   public ShowBlockReportSpamDialogReceiver(FragmentManager fragmentManager) {
74     this.fragmentManager = fragmentManager;
75   }
76 
77   @Override
onReceive(Context context, Intent intent)78   public void onReceive(Context context, Intent intent) {
79     LogUtil.enterBlock("ShowBlockReportSpamDialogReceiver.onReceive");
80 
81     String action = intent.getAction();
82 
83     switch (Assert.isNotNull(action)) {
84       case ACTION_SHOW_DIALOG_TO_BLOCK_NUMBER:
85         showDialogToBlockNumber(context, intent);
86         break;
87       case ACTION_SHOW_DIALOG_TO_BLOCK_NUMBER_AND_OPTIONALLY_REPORT_SPAM:
88         showDialogToBlockNumberAndOptionallyReportSpam(context, intent);
89         break;
90       case ACTION_SHOW_DIALOG_TO_REPORT_NOT_SPAM:
91         showDialogToReportNotSpam(context, intent);
92         break;
93       case ACTION_SHOW_DIALOG_TO_UNBLOCK_NUMBER:
94         showDialogToUnblockNumber(context, intent);
95         break;
96       default:
97         throw new IllegalStateException("Unsupported action: " + action);
98     }
99   }
100 
showDialogToBlockNumberAndOptionallyReportSpam(Context context, Intent intent)101   private void showDialogToBlockNumberAndOptionallyReportSpam(Context context, Intent intent) {
102     LogUtil.enterBlock(
103         "ShowBlockReportSpamDialogReceiver.showDialogToBlockNumberAndOptionallyReportSpam");
104 
105     Assert.checkArgument(intent.hasExtra(EXTRA_DIALOG_INFO));
106     BlockReportSpamDialogInfo dialogInfo =
107         ProtoParsers.getTrusted(
108             intent, EXTRA_DIALOG_INFO, BlockReportSpamDialogInfo.getDefaultInstance());
109 
110     Spam spam = SpamComponent.get(context).spam();
111     SpamSettings spamSettings = SpamComponent.get(context).spamSettings();
112 
113     // Set up the positive listener for the dialog.
114     OnSpamDialogClickListener onSpamDialogClickListener =
115         reportSpam -> {
116           LogUtil.i(
117               "ShowBlockReportSpamDialogReceiver.showDialogToBlockNumberAndOptionallyReportSpam",
118               "confirmed");
119 
120           if (reportSpam && spamSettings.isSpamEnabled()) {
121             LogUtil.i(
122                 "ShowBlockReportSpamDialogReceiver.showDialogToBlockNumberAndOptionallyReportSpam",
123                 "report spam");
124             Logger.get(context)
125                 .logImpression(
126                     DialerImpression.Type
127                         .REPORT_CALL_AS_SPAM_VIA_CALL_LOG_BLOCK_REPORT_SPAM_SENT_VIA_BLOCK_NUMBER_DIALOG);
128             spam.reportSpamFromCallHistory(
129                 dialogInfo.getNormalizedNumber(),
130                 dialogInfo.getCountryIso(),
131                 dialogInfo.getCallType(),
132                 dialogInfo.getReportingLocation(),
133                 dialogInfo.getContactSource());
134           }
135 
136           blockNumber(context, dialogInfo);
137         };
138 
139     // Create and show the dialog.
140     DialogFragmentForBlockingNumberAndOptionallyReportingAsSpam.newInstance(
141             dialogInfo.getNormalizedNumber(),
142             spamSettings.isDialogReportSpamCheckedByDefault(),
143             onSpamDialogClickListener,
144             /* dismissListener = */ null)
145         .show(fragmentManager, BlockReportSpamDialogs.BLOCK_REPORT_SPAM_DIALOG_TAG);
146   }
147 
showDialogToBlockNumber(Context context, Intent intent)148   private void showDialogToBlockNumber(Context context, Intent intent) {
149     LogUtil.enterBlock("ShowBlockReportSpamDialogReceiver.showDialogToBlockNumber");
150 
151     Assert.checkArgument(intent.hasExtra(EXTRA_DIALOG_INFO));
152     BlockReportSpamDialogInfo dialogInfo =
153         ProtoParsers.getTrusted(
154             intent, EXTRA_DIALOG_INFO, BlockReportSpamDialogInfo.getDefaultInstance());
155 
156     // Set up the positive listener for the dialog.
157     OnConfirmListener onConfirmListener =
158         () -> {
159           LogUtil.i("ShowBlockReportSpamDialogReceiver.showDialogToBlockNumber", "block number");
160           blockNumber(context, dialogInfo);
161         };
162 
163     // Create and show the dialog.
164     DialogFragmentForBlockingNumber.newInstance(
165             dialogInfo.getNormalizedNumber(), onConfirmListener, /* dismissListener = */ null)
166         .show(fragmentManager, BlockReportSpamDialogs.BLOCK_DIALOG_TAG);
167   }
168 
showDialogToReportNotSpam(Context context, Intent intent)169   private void showDialogToReportNotSpam(Context context, Intent intent) {
170     LogUtil.enterBlock("ShowBlockReportSpamDialogReceiver.showDialogToReportNotSpam");
171 
172     Assert.checkArgument(intent.hasExtra(EXTRA_DIALOG_INFO));
173     BlockReportSpamDialogInfo dialogInfo =
174         ProtoParsers.getTrusted(
175             intent, EXTRA_DIALOG_INFO, BlockReportSpamDialogInfo.getDefaultInstance());
176 
177     // Set up the positive listener for the dialog.
178     OnConfirmListener onConfirmListener =
179         () -> {
180           LogUtil.i("ShowBlockReportSpamDialogReceiver.showDialogToReportNotSpam", "confirmed");
181 
182           if (SpamComponent.get(context).spamSettings().isSpamEnabled()) {
183             Logger.get(context)
184                 .logImpression(DialerImpression.Type.DIALOG_ACTION_CONFIRM_NUMBER_NOT_SPAM);
185             SpamComponent.get(context)
186                 .spam()
187                 .reportNotSpamFromCallHistory(
188                     dialogInfo.getNormalizedNumber(),
189                     dialogInfo.getCountryIso(),
190                     dialogInfo.getCallType(),
191                     dialogInfo.getReportingLocation(),
192                     dialogInfo.getContactSource());
193           }
194         };
195 
196     // Create & show the dialog.
197     DialogFragmentForReportingNotSpam.newInstance(
198             dialogInfo.getNormalizedNumber(), onConfirmListener, /* dismissListener = */ null)
199         .show(fragmentManager, BlockReportSpamDialogs.NOT_SPAM_DIALOG_TAG);
200   }
201 
showDialogToUnblockNumber(Context context, Intent intent)202   private void showDialogToUnblockNumber(Context context, Intent intent) {
203     LogUtil.enterBlock("ShowBlockReportSpamDialogReceiver.showDialogToUnblockNumber");
204 
205     Assert.checkArgument(intent.hasExtra(EXTRA_DIALOG_INFO));
206     BlockReportSpamDialogInfo dialogInfo =
207         ProtoParsers.getTrusted(
208             intent, EXTRA_DIALOG_INFO, BlockReportSpamDialogInfo.getDefaultInstance());
209 
210     // Set up the positive listener for the dialog.
211     OnConfirmListener onConfirmListener =
212         () -> {
213           LogUtil.i("ShowBlockReportSpamDialogReceiver.showDialogToUnblockNumber", "confirmed");
214 
215           unblockNumber(context, dialogInfo);
216         };
217 
218     // Create & show the dialog.
219     DialogFragmentForUnblockingNumber.newInstance(
220             dialogInfo.getNormalizedNumber(), onConfirmListener, /* dismissListener = */ null)
221         .show(fragmentManager, BlockReportSpamDialogs.UNBLOCK_DIALOG_TAG);
222   }
223 
blockNumber(Context context, BlockReportSpamDialogInfo dialogInfo)224   private static void blockNumber(Context context, BlockReportSpamDialogInfo dialogInfo) {
225     Logger.get(context).logImpression(Type.USER_ACTION_BLOCKED_NUMBER);
226     Futures.addCallback(
227         Blocking.block(
228             context,
229             ImmutableList.of(dialogInfo.getNormalizedNumber()),
230             dialogInfo.getCountryIso()),
231         new FutureCallback<Void>() {
232           @Override
233           public void onSuccess(Void unused) {
234             // Do nothing
235           }
236 
237           @Override
238           public void onFailure(Throwable throwable) {
239             if (throwable instanceof BlockingFailedException) {
240               Logger.get(context).logImpression(Type.USER_ACTION_BLOCK_NUMBER_FAILED);
241               Toast.makeText(context, R.string.block_number_failed_toast, Toast.LENGTH_LONG).show();
242             } else {
243               throw new RuntimeException(throwable);
244             }
245           }
246         },
247         DialerExecutorComponent.get(context).uiExecutor());
248   }
249 
unblockNumber(Context context, BlockReportSpamDialogInfo dialogInfo)250   private static void unblockNumber(Context context, BlockReportSpamDialogInfo dialogInfo) {
251     Logger.get(context).logImpression(Type.USER_ACTION_UNBLOCKED_NUMBER);
252     Futures.addCallback(
253         Blocking.unblock(
254             context,
255             ImmutableList.of(dialogInfo.getNormalizedNumber()),
256             dialogInfo.getCountryIso()),
257         new FutureCallback<Void>() {
258           @Override
259           public void onSuccess(Void unused) {
260             // Do nothing
261           }
262 
263           @Override
264           public void onFailure(Throwable throwable) {
265             if (throwable instanceof BlockingFailedException) {
266               Logger.get(context).logImpression(Type.USER_ACTION_UNBLOCK_NUMBER_FAILED);
267               Toast.makeText(context, R.string.unblock_number_failed_toast, Toast.LENGTH_LONG)
268                   .show();
269             } else {
270               throw new RuntimeException(throwable);
271             }
272           }
273         },
274         DialerExecutorComponent.get(context).uiExecutor());
275   }
276 }
277