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