Problem:
I have an existing application that I would like to use to run applications on Android 6.0. In Runtime Permissions, I read a lot of different things, but I just can't turn my head about all the different fragments. Nothing I found actually shows how to implement this in an existing Activity.
Other points
When I run an existing SDK v23 targeting application, I get a permission error as expected, but the permission error that I get is not even the permission that I request. I have SEND_SMS permission in the manifest file, but the error I get is for READ_SMS. My application works fine on pre 6.0 without READ_SMS.
I want my application to request permission as soon as the application starts, because the only purpose of the application is to send an SMS message, so without this permission there is no other use for the application.
Questions:
How do I implement Runtime permissions for SEND_SMS in my existing action right after launching the application?
Do these permissions need to be processed in the background thread?
I also need permissions for READ_SMS, since this is a permission error that it grants (although this permission has never been used in my application)?
My Existing Activity:
public class MainActivity extends Activity implements OnClickListener { SimpleCursorAdapter mAdapter; AutoCompleteTextView txtContract; EditText txtTrip; EditText txtDate; Button btnSend; Button btnUpdate; String today; String SENT = "SMS_SENT"; String DELIVERED = "SMS_DELIVERED"; private static final String API_KEY = "abcxyz"; private static final String CONTRACT_REGEX = "^([a-zA-Z0-9_-]){5}$"; private static final String TRIP_REGEX = "^([a-zA-Z0-9_-]){1,10}$"; private static final String DATE_REGEX = "^\\d{2}\\/\\d{2}\\/\\d{4}$"; private static final String PHONE_NUMBER = "1234567890"; private static final String DATE_FORMAT = "MM/dd/yyyy"; private BroadcastReceiver sendBroadcastReceiver; private BroadcastReceiver deliveryBroadcastReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // TODO - IMPLEMENT RUNTIME PERMISSIONS FOR ANDROID >= 6.0 try { // Initialize Views txtContract = (AutoCompleteTextView) findViewById(R.id.txtContract); txtTrip = (EditText) findViewById(R.id.txtTrip); txtDate = (EditText) findViewById(R.id.txtDate); btnSend = (Button) findViewById(R.id.btnSend); btnUpdate = (Button) findViewById(R.id.btnUpdate); // Set Listeners txtDate.setOnClickListener(this); btnSend.setOnClickListener(this); btnUpdate.setOnClickListener(this); // Set Date To Today And Format final Calendar td = Calendar.getInstance(); int tYear = td.get(Calendar.YEAR); int tMonth = td.get(Calendar.MONTH); int tDay = td.get(Calendar.DAY_OF_MONTH); SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH); td.set(tYear, tMonth, tDay); today = sdf.format(td.getTime()); txtDate.setText(today); // Check If Device Is Capable Of Sending SMS PackageManager pm = this.getPackageManager(); if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) && !pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) { Toast.makeText(this, "Sorry, your device probably can't send SMS...", Toast.LENGTH_SHORT).show(); } // Send Receiver sendBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context arg0, Intent arg1) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "Requesting trip...", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: Toast.makeText(getBaseContext(), "Generic failure", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NO_SERVICE: Toast.makeText(getBaseContext(), "No service", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_NULL_PDU: Toast.makeText(getBaseContext(), "Null PDU", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_RADIO_OFF: Toast.makeText(getBaseContext(), "Radio off", Toast.LENGTH_SHORT).show(); break; } } }; // Delivery Receiver deliveryBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context arg0, Intent arg1) { switch (getResultCode()) { case Activity.RESULT_OK: Toast.makeText(getBaseContext(), "Trip request successful.", Toast.LENGTH_SHORT).show(); break; case Activity.RESULT_CANCELED: Toast.makeText(getBaseContext(), "Trip request failed.", Toast.LENGTH_SHORT).show(); break; } } }; // Register Receivers registerReceiver(deliveryBroadcastReceiver, new IntentFilter(DELIVERED)); registerReceiver(sendBroadcastReceiver , new IntentFilter(SENT)); // Set Up Adapter For Autocomplete initializeAutoCompleteAdapter(); } catch (Exception ex) { Toast.makeText(this, "Error in MainActivity.onCreate: " + ex.getMessage(), Toast.LENGTH_SHORT).show(); } } @Override protected void onDestroy() { unregisterReceiver(sendBroadcastReceiver); unregisterReceiver(deliveryBroadcastReceiver); super.onDestroy(); } // Auto Complete Adapter public void initializeAutoCompleteAdapter() { // Set Database Handler final DBHelper DBHelper = new DBHelper(getBaseContext()); // Set Up Adapter For Autocomplete (This does not run on the main UI thread) mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null, new String[] { "contract" }, new int[] {android.R.id.text1}, 0); txtContract.setAdapter(mAdapter); mAdapter.setCursorToStringConverter(new SimpleCursorAdapter.CursorToStringConverter() { @Override public CharSequence convertToString(Cursor cursor) { final int colIndex = cursor.getColumnIndexOrThrow("contract"); return cursor.getString(colIndex); } }); mAdapter.setFilterQueryProvider(new FilterQueryProvider() { @Override public Cursor runQuery(CharSequence description) { String strContract = txtContract.getText().toString(); return DBHelper.getContract(strContract); } }); } // OnClickListener Handler @Override public void onClick(View v) { // Handle Clicked View switch (v.getId()) { // Date Field case R.id.txtDate: // Get Current Date final Calendar c = Calendar.getInstance(); c.set(c.get(Calendar.YEAR),c.get(Calendar.MONTH),c.get(Calendar.DAY_OF_MONTH),0,0,0); int mYear = c.get(Calendar.YEAR); int mMonth = c.get(Calendar.MONTH); int mDay = c.get(Calendar.DAY_OF_MONTH); // Set Up DatePicker Dialog DatePickerDialog datePickerDialog = new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() { @Override public void onDateSet(DatePicker view, int year, int month, int day) { // Define A New Calendar For Formatting final Calendar cf = Calendar.getInstance(); // Format Selected Date SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH); cf.set(year,month,day); String selectedDate = sdf.format(cf.getTime()); // Add Selected Date To EditText Field txtDate.setText(selectedDate); } }, mYear, mMonth, mDay); // Set Max Date c.add(Calendar.DATE, 2); c.add(Calendar.SECOND, -1); datePickerDialog.getDatePicker().setMaxDate(c.getTimeInMillis()); // Set Min Date c.add(Calendar.DAY_OF_MONTH,-5); c.add(Calendar.SECOND, 1); datePickerDialog.getDatePicker().setMinDate(c.getTimeInMillis()); // Display DatePicker datePickerDialog.show(); break; // Submit Button case R.id.btnSend: Boolean rval = true; if (!Validation.isValid(txtContract, CONTRACT_REGEX, "Invalid Contract #", true)) rval = false; if (!Validation.isValid(txtTrip, TRIP_REGEX, "Invalid Trip #", true)) rval = false; if (!Validation.isValid(txtDate, DATE_REGEX, "Invalid Date", true)) rval = false; if(rval) { new ValidateAndSend(this).execute(); } break; // Update Contract DB case R.id.btnUpdate: TelephonyManager tMgr = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); String mPhoneNumber = tMgr.getLine1Number(); new POSTAsync(this).execute(API_KEY, mPhoneNumber); break; } } // Validate And Send class ValidateAndSend extends AsyncTask<String, String, Boolean>{ private final WeakReference<MainActivity> MainActivityWeakRef; public ValidateAndSend(MainActivity mainActivity) { super(); this.MainActivityWeakRef = new WeakReference<>(mainActivity); } // Define Variables String strContract = txtContract.getText().toString(); String strTrip = txtTrip.getText().toString(); String strDate = txtDate.getText().toString(); String strMessage = strContract.concat("|").concat(strTrip).concat("|").concat(strDate); Boolean rval = true; @Override protected void onPreExecute() { } @Override protected Boolean doInBackground(String... contract) { DBHelper DBHelper = new DBHelper(MainActivity.this); if (DBHelper.validateContract(strContract) < 1) rval = false; return rval; } @Override protected void onPostExecute(Boolean rval){ if(rval){ // Hide Keyboard View view = MainActivity.this.getCurrentFocus(); if(view != null){ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } if (MainActivityWeakRef.get() != null && !MainActivityWeakRef.get().isFinishing()) { // Confirm Details AlertDialog.Builder alert = new AlertDialog.Builder(MainActivity.this); alert.setTitle("Confirm Trip"); alert.setMessage("CONTRACT: " + strContract + "\nTRIP: " + strTrip + "\nDATE: " + strDate); alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Send SMS sendSMS(PHONE_NUMBER, strMessage); // Clear Fields txtContract.setText(""); txtTrip.setText(""); txtDate.setText(today); } }); alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // Cancelled } }); // Show Alert alert.show(); } } else{ txtContract.setError("Invalid contract #"); Toast.makeText(MainActivity.this, "You may need to update contracts.", Toast.LENGTH_LONG).show(); } } } // Send SMS private void sendSMS(String phoneNumber, String message) { String SENT = "SMS_SENT"; String DELIVERED = "SMS_DELIVERED"; PendingIntent sentPI = PendingIntent.getBroadcast(this, 0, new Intent(SENT), 0); PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0, new Intent(DELIVERED), 0); SmsManager sms = SmsManager.getDefault(); sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI); }
}