• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 package com.android.dialer.util;
17 
18 import android.app.AlertDialog;
19 import android.content.ActivityNotFoundException;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.content.DialogInterface.OnClickListener;
23 import android.content.Intent;
24 import android.graphics.Point;
25 import android.os.Build.VERSION;
26 import android.os.Build.VERSION_CODES;
27 import android.os.Bundle;
28 import android.telecom.TelecomManager;
29 import android.telephony.TelephonyManager;
30 import android.text.BidiFormatter;
31 import android.text.TextDirectionHeuristics;
32 import android.view.View;
33 import android.view.inputmethod.InputMethodManager;
34 import android.widget.Toast;
35 import com.android.dialer.common.LogUtil;
36 import com.android.dialer.telecom.TelecomUtil;
37 import java.io.File;
38 import java.util.Iterator;
39 import java.util.Random;
40 
41 /** General purpose utility methods for the Dialer. */
42 public class DialerUtils {
43 
44   /**
45    * Prefix on a dialed number that indicates that the call should be placed through the Wireless
46    * Priority Service.
47    */
48   private static final String WPS_PREFIX = "*272";
49 
50   public static final String FILE_PROVIDER_CACHE_DIR = "my_cache";
51 
52   private static final Random RANDOM = new Random();
53 
54   /**
55    * Attempts to start an activity and displays a toast with the default error message if the
56    * activity is not found, instead of throwing an exception.
57    *
58    * @param context to start the activity with.
59    * @param intent to start the activity with.
60    */
startActivityWithErrorToast(Context context, Intent intent)61   public static void startActivityWithErrorToast(Context context, Intent intent) {
62     startActivityWithErrorToast(context, intent, R.string.activity_not_available);
63   }
64 
65   /**
66    * Attempts to start an activity and displays a toast with a provided error message if the
67    * activity is not found, instead of throwing an exception.
68    *
69    * @param context to start the activity with.
70    * @param intent to start the activity with.
71    * @param msgId Resource ID of the string to display in an error message if the activity is not
72    *     found.
73    */
startActivityWithErrorToast( final Context context, final Intent intent, int msgId)74   public static void startActivityWithErrorToast(
75       final Context context, final Intent intent, int msgId) {
76     try {
77       if ((Intent.ACTION_CALL.equals(intent.getAction()))) {
78         // All dialer-initiated calls should pass the touch point to the InCallUI
79         Point touchPoint = TouchPointManager.getInstance().getPoint();
80         if (touchPoint.x != 0 || touchPoint.y != 0) {
81           Bundle extras;
82           // Make sure to not accidentally clobber any existing extras
83           if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
84             extras = intent.getParcelableExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
85           } else {
86             extras = new Bundle();
87           }
88           extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
89           intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
90         }
91 
92         if (shouldWarnForOutgoingWps(context, intent.getData().getSchemeSpecificPart())) {
93           LogUtil.i(
94               "DialUtils.startActivityWithErrorToast",
95               "showing outgoing WPS dialog before placing call");
96           AlertDialog.Builder builder = new AlertDialog.Builder(context);
97           builder.setMessage(R.string.outgoing_wps_warning);
98           builder.setPositiveButton(
99               R.string.dialog_continue,
100               new OnClickListener() {
101                 @Override
102                 public void onClick(DialogInterface dialog, int which) {
103                   placeCallOrMakeToast(context, intent);
104                 }
105               });
106           builder.setNegativeButton(android.R.string.cancel, null);
107           builder.create().show();
108         } else {
109           placeCallOrMakeToast(context, intent);
110         }
111       } else {
112         context.startActivity(intent);
113       }
114     } catch (ActivityNotFoundException e) {
115       Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
116     }
117   }
118 
placeCallOrMakeToast(Context context, Intent intent)119   private static void placeCallOrMakeToast(Context context, Intent intent) {
120     final boolean hasCallPermission = TelecomUtil.placeCall(context, intent);
121     if (!hasCallPermission) {
122       // TODO: Make calling activity show request permission dialog and handle
123       // callback results appropriately.
124       Toast.makeText(context, "Cannot place call without Phone permission", Toast.LENGTH_SHORT)
125           .show();
126     }
127   }
128 
129   /**
130    * Returns whether the user should be warned about an outgoing WPS call. This checks if there is a
131    * currently active call over LTE. Regardless of the country or carrier, the radio will drop an
132    * active LTE call if a WPS number is dialed, so this warning is necessary.
133    */
shouldWarnForOutgoingWps(Context context, String number)134   private static boolean shouldWarnForOutgoingWps(Context context, String number) {
135     if (number != null && number.startsWith(WPS_PREFIX)) {
136       TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class);
137       boolean isOnVolte =
138           VERSION.SDK_INT >= VERSION_CODES.N
139               && telephonyManager.getVoiceNetworkType() == TelephonyManager.NETWORK_TYPE_LTE;
140       boolean hasCurrentActiveCall =
141           telephonyManager.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK;
142       return isOnVolte && hasCurrentActiveCall;
143     }
144     return false;
145   }
146 
147   /**
148    * Closes an {@link AutoCloseable}, silently ignoring any checked exceptions. Does nothing if
149    * null.
150    *
151    * @param closeable to close.
152    */
closeQuietly(AutoCloseable closeable)153   public static void closeQuietly(AutoCloseable closeable) {
154     if (closeable != null) {
155       try {
156         closeable.close();
157       } catch (RuntimeException rethrown) {
158         throw rethrown;
159       } catch (Exception ignored) {
160       }
161     }
162   }
163 
164   /**
165    * Joins a list of {@link CharSequence} into a single {@link CharSequence} seperated by ", ".
166    *
167    * @param list List of char sequences to join.
168    * @return Joined char sequences.
169    */
join(Iterable<CharSequence> list)170   public static CharSequence join(Iterable<CharSequence> list) {
171     StringBuilder sb = new StringBuilder();
172     final BidiFormatter formatter = BidiFormatter.getInstance();
173     final CharSequence separator = ", ";
174 
175     Iterator<CharSequence> itr = list.iterator();
176     boolean firstTime = true;
177     while (itr.hasNext()) {
178       if (firstTime) {
179         firstTime = false;
180       } else {
181         sb.append(separator);
182       }
183       // Unicode wrap the elements of the list to respect RTL for individual strings.
184       sb.append(
185           formatter.unicodeWrap(itr.next().toString(), TextDirectionHeuristics.FIRSTSTRONG_LTR));
186     }
187 
188     // Unicode wrap the joined value, to respect locale's RTL ordering for the whole list.
189     return formatter.unicodeWrap(sb.toString());
190   }
191 
showInputMethod(View view)192   public static void showInputMethod(View view) {
193     final InputMethodManager imm =
194         (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
195     if (imm != null) {
196       imm.showSoftInput(view, 0);
197     }
198   }
199 
hideInputMethod(View view)200   public static void hideInputMethod(View view) {
201     final InputMethodManager imm =
202         (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
203     if (imm != null) {
204       imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
205     }
206   }
207 
208   /**
209    * Create a File in the cache directory that Dialer's FileProvider knows about so they can be
210    * shared to other apps.
211    */
createShareableFile(Context context)212   public static File createShareableFile(Context context) {
213     long fileId = Math.abs(RANDOM.nextLong());
214     File parentDir = new File(context.getCacheDir(), FILE_PROVIDER_CACHE_DIR);
215     if (!parentDir.exists()) {
216       parentDir.mkdirs();
217     }
218     return new File(parentDir, String.valueOf(fileId));
219   }
220 }
221