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.Activity; 19 import android.content.ActivityNotFoundException; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.content.res.Resources; 26 import android.graphics.Point; 27 import android.net.Uri; 28 import android.os.Bundle; 29 import android.provider.Telephony; 30 import android.telecom.TelecomManager; 31 import android.text.BidiFormatter; 32 import android.text.TextDirectionHeuristics; 33 import android.text.TextUtils; 34 import android.view.View; 35 import android.view.inputmethod.InputMethodManager; 36 import android.widget.Toast; 37 38 import com.android.contacts.common.ContactsUtils; 39 import com.android.contacts.common.interactions.TouchPointManager; 40 import com.android.dialer.R; 41 42 import java.util.Iterator; 43 import java.util.List; 44 import java.util.Locale; 45 46 /** 47 * General purpose utility methods for the Dialer. 48 */ 49 public class DialerUtils { 50 51 /** 52 * Attempts to start an activity and displays a toast with the default error message if the 53 * activity is not found, instead of throwing an exception. 54 * 55 * @param context to start the activity with. 56 * @param intent to start the activity with. 57 */ startActivityWithErrorToast(Context context, Intent intent)58 public static void startActivityWithErrorToast(Context context, Intent intent) { 59 startActivityWithErrorToast(context, intent, R.string.activity_not_available); 60 } 61 62 /** 63 * Attempts to start an activity and displays a toast with a provided error message if the 64 * activity is not found, instead of throwing an exception. 65 * 66 * @param context to start the activity with. 67 * @param intent to start the activity with. 68 * @param msgId Resource ID of the string to display in an error message if the activity is 69 * not found. 70 */ startActivityWithErrorToast(Context context, Intent intent, int msgId)71 public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) { 72 try { 73 if ((IntentUtil.CALL_ACTION.equals(intent.getAction()) 74 && context instanceof Activity)) { 75 // All dialer-initiated calls should pass the touch point to the InCallUI 76 Point touchPoint = TouchPointManager.getInstance().getPoint(); 77 if (touchPoint.x != 0 || touchPoint.y != 0) { 78 Bundle extras; 79 // Make sure to not accidentally clobber any existing extras 80 if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) { 81 extras = intent.getParcelableExtra( 82 TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS); 83 } else { 84 extras = new Bundle(); 85 } 86 extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint); 87 intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras); 88 } 89 90 final boolean hasCallPermission = TelecomUtil.placeCall((Activity) context, intent); 91 if (!hasCallPermission) { 92 // TODO: Make calling activity show request permission dialog and handle 93 // callback results appropriately. 94 Toast.makeText(context, "Cannot place call without Phone permission", 95 Toast.LENGTH_SHORT); 96 } 97 } else { 98 context.startActivity(intent); 99 } 100 } catch (ActivityNotFoundException e) { 101 Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show(); 102 } 103 } 104 105 /** 106 * Returns the component name to use in order to send an SMS using the default SMS application, 107 * or null if none exists. 108 */ getSmsComponent(Context context)109 public static ComponentName getSmsComponent(Context context) { 110 String smsPackage = Telephony.Sms.getDefaultSmsPackage(context); 111 if (smsPackage != null) { 112 final PackageManager packageManager = context.getPackageManager(); 113 final Intent intent = new Intent(Intent.ACTION_SENDTO, 114 Uri.fromParts(ContactsUtils.SCHEME_SMSTO, "", null)); 115 final List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0); 116 for (ResolveInfo resolveInfo : resolveInfos) { 117 if (smsPackage.equals(resolveInfo.activityInfo.packageName)) { 118 return new ComponentName(smsPackage, resolveInfo.activityInfo.name); 119 } 120 } 121 } 122 return null; 123 } 124 125 /** 126 * Closes an {@link AutoCloseable}, silently ignoring any checked exceptions. Does nothing if 127 * null. 128 * 129 * @param closeable to close. 130 */ closeQuietly(AutoCloseable closeable)131 public static void closeQuietly(AutoCloseable closeable) { 132 if (closeable != null) { 133 try { 134 closeable.close(); 135 } catch (RuntimeException rethrown) { 136 throw rethrown; 137 } catch (Exception ignored) { 138 } 139 } 140 } 141 142 /** 143 * Joins a list of {@link CharSequence} into a single {@link CharSequence} seperated by a 144 * localized delimiter such as ", ". 145 * 146 * @param resources Resources used to get list delimiter. 147 * @param list List of char sequences to join. 148 * @return Joined char sequences. 149 */ join(Resources resources, Iterable<CharSequence> list)150 public static CharSequence join(Resources resources, Iterable<CharSequence> list) { 151 StringBuilder sb = new StringBuilder(); 152 final BidiFormatter formatter = BidiFormatter.getInstance(); 153 final CharSequence separator = resources.getString(R.string.list_delimeter); 154 155 Iterator<CharSequence> itr = list.iterator(); 156 boolean firstTime = true; 157 while (itr.hasNext()) { 158 if (firstTime) { 159 firstTime = false; 160 } else { 161 sb.append(separator); 162 } 163 // Unicode wrap the elements of the list to respect RTL for individual strings. 164 sb.append(formatter.unicodeWrap( 165 itr.next().toString(), TextDirectionHeuristics.FIRSTSTRONG_LTR)); 166 } 167 168 // Unicode wrap the joined value, to respect locale's RTL ordering for the whole list. 169 return formatter.unicodeWrap(sb.toString()); 170 } 171 172 /** 173 * @return True if the application is currently in RTL mode. 174 */ isRtl()175 public static boolean isRtl() { 176 return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == 177 View.LAYOUT_DIRECTION_RTL; 178 } 179 showInputMethod(View view)180 public static void showInputMethod(View view) { 181 final InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService( 182 Context.INPUT_METHOD_SERVICE); 183 if (imm != null) { 184 imm.showSoftInput(view, 0); 185 } 186 } 187 hideInputMethod(View view)188 public static void hideInputMethod(View view) { 189 final InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService( 190 Context.INPUT_METHOD_SERVICE); 191 if (imm != null) { 192 imm.hideSoftInputFromWindow(view.getWindowToken(), 0); 193 } 194 } 195 } 196