Catch activity not found exceptions

Instead of crashing when an ActivityNotFoundException is thrown,
display an error toast.

Bug: 13882939
Change-Id: I5cda14ba8a404ff0f5196fdbc382201c47f4b4c8
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ce08ae5..c608273 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -612,6 +612,14 @@
     <!-- Message displayed when there is no application available to handle voice search. [CHAR LIMIT=NONE] -->
     <string name="voice_search_not_available">Voice search is not available.</string>
 
+    <!-- Message displayed when the Phone application has been disabled and a phone call cannot
+         be made. [CHAR LIMIT=NONE] -->
+    <string name="call_not_available">Cannot make a phone call because the Phone application has been disabled.</string>
+
+    <!-- Message displayed when there is no application available to handle a particular action.
+         [CHAR LIMIT=NONE] -->
+    <string name="activity_not_available">No installed activity available to handle selected action.</string>
+
     <!-- Hint displayed in dialer search box when there is no query that is currently typed.
          [CHAR LIMIT=30] -->
     <string name="dialer_hint_find_contact">Type a name or phone number</string>
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index e09e6f3..470c0b6 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -72,6 +72,7 @@
 import com.android.dialer.calllog.PhoneNumberUtilsWrapper;
 import com.android.dialer.util.AsyncTaskExecutor;
 import com.android.dialer.util.AsyncTaskExecutors;
+import com.android.dialer.util.DialerUtils;
 import com.android.dialer.voicemail.VoicemailPlaybackFragment;
 import com.android.dialer.voicemail.VoicemailStatusHelper;
 import com.android.dialer.voicemail.VoicemailStatusHelper.StatusMessage;
@@ -237,7 +238,8 @@
             if (finishPhoneNumerSelectedActionModeIfShown()) {
                 return;
             }
-            startActivity(((ViewEntry) view.getTag()).primaryIntent);
+            DialerUtils.startActivityWithErrorToast(CallDetailActivity.this,
+                    ((ViewEntry) view.getTag()).primaryIntent);
         }
     };
 
@@ -247,7 +249,8 @@
             if (finishPhoneNumerSelectedActionModeIfShown()) {
                 return;
             }
-            startActivity(((ViewEntry) view.getTag()).secondaryIntent);
+            DialerUtils.startActivityWithErrorToast(CallDetailActivity.this,
+                    ((ViewEntry) view.getTag()).secondaryIntent);
         }
     };
 
@@ -416,8 +419,10 @@
                 TelephonyManager tm = (TelephonyManager)
                         getSystemService(Context.TELEPHONY_SERVICE);
                 if (tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
-                    startActivity(CallUtil.getCallIntent(
-                            Uri.fromParts(CallUtil.SCHEME_TEL, mNumber, null)));
+                    DialerUtils.startActivityWithErrorToast(this,
+                            CallUtil.getCallIntent(Uri.fromParts(CallUtil.SCHEME_TEL, mNumber,
+                                    null)),
+                            R.string.call_not_available);
                     return true;
                 }
             }
@@ -659,13 +664,8 @@
             mMainActionPushLayerView.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    try {
-                        startActivity(actionIntent);
-                    } catch (ActivityNotFoundException e) {
-                        final Toast toast = Toast.makeText(CallDetailActivity.this,
-                                R.string.add_contact_not_available, Toast.LENGTH_SHORT);
-                        toast.show();
-                    }
+                    DialerUtils.startActivityWithErrorToast(CallDetailActivity.this, actionIntent,
+                            R.string.add_contact_not_available);
                 }
             });
             mMainActionPushLayerView.setContentDescription(actionDescription);
@@ -840,7 +840,8 @@
             mStatusMessageAction.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
-                    startActivity(new Intent(Intent.ACTION_VIEW, message.actionUri));
+                    DialerUtils.startActivityWithErrorToast(CallDetailActivity.this,
+                            new Intent(Intent.ACTION_VIEW, message.actionUri));
                 }
             });
         } else {
diff --git a/src/com/android/dialer/util/DialerUtils.java b/src/com/android/dialer/util/DialerUtils.java
new file mode 100644
index 0000000..8b0c4c6
--- /dev/null
+++ b/src/com/android/dialer/util/DialerUtils.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dialer.util;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+import com.android.dialer.R;
+
+/**
+ * General purpose utility methods for the Dialer.
+ */
+public class DialerUtils {
+
+    /**
+     * Attempts to start an activity and displays a toast with the default error message if the
+     * activity is not found, instead of throwing an exception.
+     *
+     * @param context to start the activity with.
+     * @param intent to start the activity with.
+     */
+    public static void startActivityWithErrorToast(Context context, Intent intent) {
+        startActivityWithErrorToast(context, intent, R.string.activity_not_available);
+    }
+
+    /**
+     * Attempts to start an activity and displays a toast with a provided error message if the
+     * activity is not found, instead of throwing an exception.
+     *
+     * @param context to start the activity with.
+     * @param intent to start the activity with.
+     * @param msgId Resource ID of the string to display in an error message if the activity is
+     *              not found.
+     */
+    public static void startActivityWithErrorToast(Context context, Intent intent, int msgId) {
+        try {
+          context.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Toast.makeText(context, msgId, Toast.LENGTH_SHORT).show();
+        }
+    }
+}