Home | History | Annotate | Download | only in util
      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    */
     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    */
     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 
    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    */
    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    */
    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    */
    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 
    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 
    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    */
    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