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