1 /*
2  * Copyright (C) 2015 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 
17 package com.android.messaging.util;
18 
19 import android.app.Activity;
20 import android.app.Fragment;
21 import android.content.ActivityNotFoundException;
22 import android.content.Intent;
23 import android.view.View;
24 
25 import com.android.messaging.R;
26 import com.android.messaging.ui.SnackBar;
27 import com.android.messaging.ui.UIIntents;
28 
29 public class ChangeDefaultSmsAppHelper {
30     private Runnable mRunAfterMadeDefault;
31     private ChangeSmsAppSettingRunnable mChangeSmsAppSettingRunnable;
32 
33     private static final int REQUEST_SET_DEFAULT_SMS_APP = 1;
34 
35     /**
36      * When there's some condition that prevents an operation, such as sending a message,
37      * call warnOfMissingActionConditions to put up a toast and allow the user to repair
38      * that condition.
39      * @param sending - true if we're called during a sending operation
40      * @param runAfterMadeDefault - a runnable to run after the user responds
41      *                  positively to the condition prompt and resolves the condition. It is
42      *                  preferable to specify the value in {@link #handleChangeDefaultSmsResult}
43      *                  as that handles the case where the process gets restarted.
44      *                  If null, the user will be shown a generic toast message.
45      * @param composeView - compose view that may have the keyboard opened and focused
46      * @param rootView - if non-null, use this to attach a snackBar
47      * @param activity - calling activity
48      * @param fragment - calling fragment, may be null if called directly from an activity
49      */
warnOfMissingActionConditions(final boolean sending, final Runnable runAfterMadeDefault, final View composeView, final View rootView, final Activity activity, final Fragment fragment)50     public void warnOfMissingActionConditions(final boolean sending,
51             final Runnable runAfterMadeDefault,
52             final View composeView, final View rootView,
53             final Activity activity, final Fragment fragment) {
54         final PhoneUtils phoneUtils = PhoneUtils.getDefault();
55         final boolean isSmsCapable = phoneUtils.isSmsCapable();
56         final boolean hasPreferredSmsSim = phoneUtils.getHasPreferredSmsSim();
57         final boolean isDefaultSmsApp = phoneUtils.isDefaultSmsApp();
58 
59         // Supports SMS?
60         if (!isSmsCapable) {
61             UiUtils.showToast(R.string.sms_disabled);
62 
63         // Has a preferred sim?
64         } else if (!hasPreferredSmsSim) {
65             UiUtils.showToast(R.string.no_preferred_sim_selected);
66 
67         // Is the default sms app?
68         } else if (!isDefaultSmsApp) {
69             mChangeSmsAppSettingRunnable = new ChangeSmsAppSettingRunnable(activity, fragment);
70             promptToChangeDefaultSmsApp(sending, runAfterMadeDefault,
71                     composeView, rootView, activity);
72         }
73 
74         LogUtil.w(LogUtil.BUGLE_TAG, "Unsatisfied action condition: "
75                 + "isSmsCapable=" + isSmsCapable + ", "
76                 + "hasPreferredSmsSim=" + hasPreferredSmsSim + ", "
77                 + "isDefaultSmsApp=" + isDefaultSmsApp);
78     }
79 
promptToChangeDefaultSmsApp(final boolean sending, final Runnable runAfterMadeDefault, final View composeView, final View rootView, final Activity activity)80     private void promptToChangeDefaultSmsApp(final boolean sending,
81             final Runnable runAfterMadeDefault,
82             final View composeView, final View rootView,
83             final Activity activity) {
84         if (composeView != null) {
85             // Avoid bug in system which puts soft keyboard over dialog after orientation change
86             ImeUtil.hideSoftInput(activity, composeView);
87         }
88         mRunAfterMadeDefault = runAfterMadeDefault;
89 
90         if (rootView == null) {
91             // Immediately open the system "Change default SMS app?" dialog setting.
92             mChangeSmsAppSettingRunnable.run();
93         } else {
94             UiUtils.showSnackBarWithCustomAction(activity,
95                     rootView,
96                     activity.getString(sending ? R.string.requires_default_sms_app_to_send :
97                         R.string.requires_default_sms_app),
98                         SnackBar.Action.createCustomAction(mChangeSmsAppSettingRunnable,
99                                 activity.getString(R.string.requires_default_sms_change_button)),
100                                 null /* interactions */,
101                                 SnackBar.Placement.above(composeView));
102         }
103     }
104 
105     private class ChangeSmsAppSettingRunnable implements Runnable {
106         private final Activity mActivity;
107         private final Fragment mFragment;
108 
ChangeSmsAppSettingRunnable(final Activity activity, final Fragment fragment)109         public ChangeSmsAppSettingRunnable(final Activity activity, final Fragment fragment) {
110             mActivity = activity;
111             mFragment = fragment;
112         }
113 
114         @Override
run()115         public void run() {
116             try {
117                 final Intent intent = UIIntents.get().getChangeDefaultSmsAppIntent(mActivity);
118                 if (mFragment != null) {
119                     mFragment.startActivityForResult(intent, REQUEST_SET_DEFAULT_SMS_APP);
120                 } else {
121                     mActivity.startActivityForResult(intent, REQUEST_SET_DEFAULT_SMS_APP);
122                 }
123             } catch (final ActivityNotFoundException ex) {
124                 // We shouldn't get here, but the monkey on JB MR0 can trigger it.
125                 LogUtil.w(LogUtil.BUGLE_TAG, "Couldn't find activity:", ex);
126                 UiUtils.showToastAtBottom(R.string.activity_not_found_message);
127             }
128         }
129     }
130 
handleChangeDefaultSmsResult( final int requestCode, final int resultCode, Runnable runAfterMadeDefault)131     public void handleChangeDefaultSmsResult(
132             final int requestCode,
133             final int resultCode,
134             Runnable runAfterMadeDefault) {
135         Assert.isTrue(mRunAfterMadeDefault == null || runAfterMadeDefault == null);
136         if (runAfterMadeDefault == null) {
137             runAfterMadeDefault = mRunAfterMadeDefault;
138         }
139 
140         if (requestCode == REQUEST_SET_DEFAULT_SMS_APP) {
141             if (resultCode == Activity.RESULT_OK) {
142                 // mRunAfterMadeDefault can be null if it was set only in
143                 // promptToChangeDefaultSmsApp, and the process subsequently restarted when the
144                 // user momentarily switched to another app. In that case, we'll simply show a
145                 // generic toast since we do not know what the runnable was supposed to do.
146                 if  (runAfterMadeDefault != null) {
147                     runAfterMadeDefault.run();
148                 } else {
149                     UiUtils.showToast(R.string.toast_after_setting_default_sms_app);
150                 }
151             }
152             mRunAfterMadeDefault = null;    // don't want to accidentally run it again
153         }
154     }
155 }
156 
157 
158