package org.commcare.activities; import android.Manifest; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentActivity; import android.support.v4.content.ContextCompat; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.view.View; import android.widget.Toast; import org.commcare.interfaces.RuntimePermissionRequester; import org.commcare.views.dialogs.CommCareAlertDialog; import org.commcare.views.dialogs.DialogChoiceItem; import org.commcare.views.dialogs.DialogCreationHelpers; import org.commcare.views.dialogs.PaneledChoiceDialog; import org.javarosa.core.services.locale.Localization; import java.util.Date; /** * @author ctsims */ public class CallOutActivity extends FragmentActivity implements RuntimePermissionRequester { public static final String PHONE_NUMBER = "cos_pn"; public static final String CALL_DURATION = "cos_pd"; public static final String INCOMING_ACTION = "cos_inac"; private static final String CALLOUT_ACTION_KEY = "callout-action-key"; private static final int SMS_RESULT = 0; private static final int CALL_RESULT = 1; private static final int CALL_OR_SMS_PERMISSION_REQUEST = 1; private static String number; private String calloutAction; private TelephonyManager tManager; private CallListener listener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tManager = (TelephonyManager)this.getSystemService(TELEPHONY_SERVICE); listener = new CallListener(); number = getIntent().getStringExtra(PHONE_NUMBER); loadStateFromInstance(savedInstanceState); if (getIntent().hasExtra(INCOMING_ACTION)) { calloutAction = getIntent().getStringExtra(INCOMING_ACTION); dispatchActionWithPermissions(); } else { showChoiceDialog(); } } private void loadStateFromInstance(Bundle savedInstanceState) { if (savedInstanceState != null && savedInstanceState.containsKey(CALLOUT_ACTION_KEY)) { calloutAction = savedInstanceState.getString(CALLOUT_ACTION_KEY); } } @Override protected void onResume() { super.onResume(); if (listener.isFinished()) { long duration = listener.getCallDuration(); if (duration > 0) { Intent i = new Intent(getIntent()); i.putExtra(CALL_DURATION, duration); setResult(RESULT_OK, i); finish(); } else { //TODO: We could also pop up a thing here that said "Phone call in progress" //or something Intent i = new Intent(getIntent()); setResult(RESULT_CANCELED, i); finish(); } } } private void showChoiceDialog() { final PaneledChoiceDialog dialog = new PaneledChoiceDialog(this, Localization.get("select.detail.callout.title")); View.OnClickListener callListener = new View.OnClickListener() { @Override public void onClick(View v) { calloutAction = Intent.ACTION_CALL; dispatchActionWithPermissions(); dialog.dismiss(); } }; DialogChoiceItem item1 = new DialogChoiceItem(Localization.get("select.detail.callout.call"), -1, callListener); View.OnClickListener smsListener = new View.OnClickListener() { @Override public void onClick(View v) { calloutAction = Intent.ACTION_SENDTO; dispatchActionWithPermissions(); dialog.dismiss(); } }; DialogChoiceItem item2 = new DialogChoiceItem(Localization.get("select.detail.callout.sms"), -1, smsListener); dialog.setChoiceItems(new DialogChoiceItem[]{item1, item2}); dialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { Intent i = new Intent(getIntent()); setResult(RESULT_CANCELED, i); finish(); } }); dialog.showNonPersistentDialog(); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(CALLOUT_ACTION_KEY, calloutAction); } private void dispatchActionWithPermissions() { if (missingPhonePermission()) { if (shouldShowPhonePermissionRationale()) { CommCareAlertDialog dialog = DialogCreationHelpers.buildPermissionRequestDialog(this, this, CALL_OR_SMS_PERMISSION_REQUEST, Localization.get("permission.case.callout.title"), Localization.get("permission.case.callout.message")); dialog.showNonPersistentDialog(); } else { requestNeededPermissions(CALL_OR_SMS_PERMISSION_REQUEST); } } else { dispatchAction(); } } private boolean missingPhonePermission() { return calloutAction.equals(Intent.ACTION_CALL) && ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED; } private boolean shouldShowPhonePermissionRationale() { return ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE); } @Override public void requestNeededPermissions(int requestCode) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, requestCode); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == CALL_OR_SMS_PERMISSION_REQUEST) { for (int i = 0; i < permissions.length; i++) { if (Manifest.permission.CALL_PHONE.equals(permissions[i]) && grantResults[i] == PackageManager.PERMISSION_GRANTED) { dispatchAction(); return; } } } Toast.makeText(this, Localization.get("permission.case.callout.denied"), Toast.LENGTH_LONG).show(); finish(); } private void dispatchAction() { if (Intent.ACTION_CALL.equals(calloutAction)) { tManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE); Intent call = new Intent(Intent.ACTION_CALL); call.setData(Uri.parse("tel:" + number)); if (call.resolveActivity(getPackageManager()) != null) { startActivityForResult(call, CALL_RESULT); } else { Toast.makeText(this, Localization.get("callout.failure.dialer"), Toast.LENGTH_SHORT).show(); finish(); } } else { Intent sms = new Intent(Intent.ACTION_SENDTO); sms.setData(Uri.parse("smsto:" + number)); if (sms.resolveActivity(getPackageManager()) != null) { startActivityForResult(sms, SMS_RESULT); } else { Toast.makeText(this, Localization.get("callout.failure.sms"), Toast.LENGTH_SHORT).show(); finish(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (requestCode == SMS_RESULT || requestCode == CALL_RESULT) { //we're done here Intent i = new Intent(getIntent()); setResult(RESULT_CANCELED, i); finish(); } } public class CallListener extends PhoneStateListener { boolean called = false; long started; long duration; boolean finished = false; @Override public void onCallStateChanged(int state, String incomingNumber) { super.onCallStateChanged(state, incomingNumber); // Don't fire before the call was made if (state == TelephonyManager.CALL_STATE_OFFHOOK) { called = true; started = new Date().getTime(); } // Call has ended -- now bring the activity back to front if (called && state == TelephonyManager.CALL_STATE_IDLE) { called = false; tManager.listen(this, PhoneStateListener.LISTEN_NONE); duration = new Date().getTime() - started; finished = true; //TODO: Any way to skip the stupid Call Log? if (duration > 0) { Intent i = new Intent(getIntent()); i.putExtra(CALL_DURATION, duration); setResult(RESULT_OK, i); finish(); } else { Intent i = new Intent(getIntent()); setResult(RESULT_CANCELED, i); finish(); } } } public long getCallDuration() { return duration; } public boolean isFinished() { return finished; } } }