Problem:
I have an existing app that I would like to implement Android 6.0's Runtime Permissions on. I have read a lot of different things on Runtime Permissions, but I just can't seem to wrap my head around all the different snippets. Nothing I have found actually shows how to implement this into an existing Activity.
Other Points
When I run my existing application targeting SDK v23 I get the permission error as expected, but the permission error I get is not even a permission that I am requesting. I have the SEND_SMS permission in the Manifest file, but the error I am getting is for READ_SMS. My app runs fine on pre 6.0 without READ_SMS.
I would like the my app to ask for permission as soon as the app is launched because the sole purpose of the app is the send an SMS message, so without that permission there is no other use for the app.
Questions:
How would I implement Runtime Permissions for SEND_SMS into my existing Activity as soon as the app is launched?
Does the handling of these permission need to run in a background thread?
Do I also need permissions for READ_SMS since that is the permission error that it is giving (even though that permission has never been used on my app)?
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);
}
}
The runtime permission model for Android 6.0 is mainly divided into part
1. Checking Permission
2. Requesting Permission
you can create two method for this thing in your activity, As follow
Check Permission
private boolean checkPermission(){
int result = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_SMS);
if (result == PackageManager.PERMISSION_GRANTED){
return true;
} else {
return false;
}
}
Request Permission
private void requestPermission(){
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,Manifest.permission.READ_SMS)){
Toast.makeText(context,"Read Sms Allowed.",Toast.LENGTH_LONG).show();
} else {
ActivityCompat.requestPermissions(activity,new String[]{Manifest.permission.READ_SMS},PERMISSION_REQUEST_CODE);
}
}
Last but not least you need to override the onRequestPermissionsResult method
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Snackbar.make(view,"Permission Granted, Now you can access SMS.",Snackbar.LENGTH_LONG).show();
} else {
Snackbar.make(view,"Permission Denied, You cannot access SMS.",Snackbar.LENGTH_LONG).show();
}
break;
}
}
as you asked do i need to run this in thread .. answer is No Just do this in main thread
If you want to do less code then, please use Dexter is an Android library that simplifies the process of requesting permissions at runtime.
Related
I am trying to make a phone number verification on android studio using java. I followed the instructions from the documentation here https://developers.google.com/identity/sms-retriever/user-consent/overview but sadly it isn't sending me an SMS code, and I am not getting any error. Below is my code:
public class OTPSMSActivity extends AppCompatActivity {
private ImageView blur;
private TextView resend;
private CustomEditText editText;
private FirebaseFirestore db = FirebaseFirestore.getInstance();
private KProgressHUD loadingBar;
private static final int SMS_CONSENT_REQUEST = 2;
// Set to an unused request code
private final BroadcastReceiver smsVerificationReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status smsRetrieverStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
switch (smsRetrieverStatus.getStatusCode()) {
case CommonStatusCodes.SUCCESS:
// Get consent intent
Intent consentIntent = extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT);
try {
/*Start activity to show consent dialog to user within
*5 minutes, otherwise you'll receive another TIMEOUT intent
*/
startActivityForResult(consentIntent, SMS_CONSENT_REQUEST);
Log.d("life", "Intent to send image");
} catch (ActivityNotFoundException e) {
Log.e("life", "Exception: " + e.toString());
}
break;
case CommonStatusCodes.TIMEOUT:
Log.d("life", "Timeout!");
break;
}
} else {
Log.d("life", "SmsRetriever don't matched");
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_otpsms);
blur = findViewById(R.id.blur);
editText = findViewById(R.id.number);
Button verify = findViewById(R.id.verify);
TextView change = findViewById(R.id.textView42);
resend = findViewById(R.id.resend);
Paper.init(this);
getBackgroundImage();
change.setOnClickListener(view -> {
finish();
});
String phoneNumber = getIntent().getStringExtra("phone");
loadingBar = KProgressHUD.create(OTPSMSActivity.this)
.setStyle(KProgressHUD.Style.SPIN_INDETERMINATE)
.setLabel("Please wait")
.setDetailsLabel("Sending sms code to your phone number.")
.setCancellable(true)
.setAnimationSpeed(2)
.setDimAmount(0.5f)
.show();
verify.setOnClickListener(view -> {
loadingBar = KProgressHUD.create(OTPSMSActivity.this)
.setStyle(KProgressHUD.Style.SPIN_INDETERMINATE)
.setLabel("Loading")
.setDetailsLabel("Verifying code")
.setCancellable(true)
.setAnimationSpeed(2)
.setDimAmount(0.5f)
.show();
String theCode = editText.getText().toString();
if (theCode.length() != 6){
new StyleableToast
.Builder(OTPSMSActivity.this)
.text("Invalid code.")
.iconStart(R.drawable.error)
.textColor(Color.WHITE)
.backgroundColor(getResources().getColor(R.color.error))
.show();
editText.setError("Invalid phone number.");
editText.requestFocus();
loadingBar.dismiss();
return;
}
verifyCode(theCode);
});
resend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
}
});
String phone = "+63" + phoneNumber.substring(1);
Log.d("life", phone);
Task<Void> task = SmsRetriever.getClient(this).startSmsUserConsent(phone);
task.addOnCompleteListener(listener -> {
if (listener.isSuccessful()) {
Log.d("life", "Success");
loadingBar.dismiss();
IntentFilter intentFilter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
registerReceiver(smsVerificationReceiver, intentFilter);
} else {
Exception exception = listener.getException();
exception.printStackTrace();
}
});
}
private void verifyCode(String code) {
if (code.equals(editText.getText().toString())) {
String phoneNumber = getIntent().getStringExtra("phone");
String userID = Paper.book().read("userID");
loadingBar.setDetailsLabel("Uploading number to database");
db.collection("Buyers").document(userID)
.update("phone", "+63" + phoneNumber.substring(1))
.addOnCompleteListener(task11 -> {
if (task11.isSuccessful()){
loadingBar.dismiss();
StyleableToast.makeText(OTPSMSActivity.this, "Success! Phone number updated.", Toast.LENGTH_LONG, R.style.successtoast).show();
finish();
}
});
} else {
new StyleableToast
.Builder(OTPSMSActivity.this)
.text("Code does not matched.")
.iconStart(R.drawable.error)
.textColor(Color.WHITE)
.backgroundColor(getResources().getColor(R.color.error))
.show();
loadingBar.dismiss();
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SMS_CONSENT_REQUEST) {
if (resultCode == RESULT_OK) {
// Get SMS message content
String message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE);
// Extract one-time code from the message and complete verification
String oneTimeCode = parseOneTimeCode(message);
Log.d("life", "oneTimeCode: " + oneTimeCode);
//for this demo we will display it instead
editText.setText(oneTimeCode);
} else {
Log.d("life", "Error2");
}
} else {
Log.d("life", "Error1");
}
}
private String parseOneTimeCode(String message) {
//simple number extractor
return message.replaceAll("[^0-9]", "");
}
#Override
protected void onDestroy() {
super.onDestroy();
//to prevent IntentReceiver leakage unregister
unregisterReceiver(smsVerificationReceiver);
}
I want to know what am I doing wrong here.
Calling this API won't be sending you an SMS. This API listens to the SMS that you will receive on your device, ask for your permission and then retrieve it.
You should not wait for the task to complete. It is there to listen to the SMS you receive. So the first thing that you need to correct in your onCreate function:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_otpsms);
...
Task<Void> task = SmsRetriever.getClient(this).startSmsUserConsent(null);
IntentFilter intentFilter = new IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION);
registerReceiver(smsVerificationReceiver, intentFilter);
...
}
The phone number you are passing to SmsRetriever.getClient(this).startSmsUserConsent is the phone number of the sender. So if you know which number will send you an SMS, then pass it to this function. But if you don't know the number of the sender, keep it null.
And note that the sender phone number should not be in the phone's contacts list as mentioned in the documentation: https://developers.google.com/identity/sms-retriever/choose-an-api
So first call create this task instruction above, have it wait for your sms and then request an SMS. You could use third party platforms to send SMS messages. To test that you can send an SMS using your emulator to the emulator device.
I'm creating an app where part of it will send a message if it meet a certain condition but whenever it is being sent, the receiver does not call onReceive method. I tried all the suggestions from the answered SO questions but nothing helped me fix it. I want to get the report if my SMS is sent with these codes:
BroadcastReceiver class:
public class SendSMS extends BroadcastReceiver {
public SendSMS() {
super();
}
#Override
public void onReceive(Context context, Intent intent) {
int resultCode = getResultCode();
switch (resultCode) {
case AppCompatActivity.RESULT_OK:
result = "Message sent.";
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
result = "Error. Message not sent.";
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
result = "Error: No service.";
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
result = "Error: Null PDU.";
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
result = "Error: Radio off.";
break;
}
}
}
Sending text class:
public class SMSSender {
private Context context;
private final int MAX_SMS_MESSAGE_LENGTH = 160;
private final String SMS_SENT = "sent";
private final String SMS_DELIVERED = "delivered";
public SMSSender(Context context) {
this.context = context;
}
public void sendSMS(String phoneNumber, String message) {
SendSMS brSendSMS = new SendSMS();
try {
//##### Also tried all kinds of context ###### //
PendingIntent piSend = PendingIntent.getBroadcast(context.getApplicationContext(), 0, new Intent(SMS_SENT), 0);
int length = message.length();
context.getApplicationContext().registerReceiver(brSendSMS, new IntentFilter(SMS_SENT));
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, piSend, null);
} catch (Exception ex) {
Toast.makeText(context, ex.getMessage(), Toast.LENGTH_LONG).show();
} finally {
context.getApplicationContext().unregisterReceiver(brSendSMS);
}
}
}
And to test this, I placed it in button clicked method.
SMSSender smsSender = new SMSSender(this);
smsSender.sendSMS("000000", "TESTING ONLY");
What am I doing wrong here? I also learned that it uses my default sms app stating "Not sent. Tap to try again".
My project is a checklist of phone numbers in a recyclerView/cardView. The phone numbers/businesses can be added or subtracted by a checkBox to make individual groups. I want to be able to send a group multi-text to the selected individuals.
My problem is that only the first phone number (recipient) in a group receives the message while the rest receive nothing, but the numbers still display in the edit text (the first is the only functioning number).
I have tried a lot of different ways but nothing has worked, I am about to give up.
No one seems to know how to fix this problem. If this problem can be solved please let me know.
I don't want to loop the numbers and text individually, that was a suggested fix.
This is the phone activity:
public class ACPhone extends AppCompatActivity {
private static final String SEPARATOR = ";";
EditText txtPhoneNo;
EditText txtMessage;
TextView txtView;
Button btnsend;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_acphone);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
txtPhoneNo = (EditText) findViewById(R.id.txtPhoneNo);
txtMessage = (EditText) findViewById(R.id.txtMessage);
txtView = (TextView)findViewById(R.id.txtMessageMass);
btnsend = (Button) findViewById(R.id.btnSend);
Intent intent = getIntent();
if (intent != null){
ArrayList<CharSequence> selectedNumbers =
intent.getCharSequenceArrayListExtra(SELECTED_NUMBERS);
StringBuffer sb = new StringBuffer();
for (int i = 0; i < selectedNumbers.size(); i++) {
sb.append(selectedNumbers.get(i));
if (i != selectedNumbers.size() - 1){
sb.append(SEPARATOR);
}
}
txtPhoneNo.setText(sb.toString());
}
btnsend.setOnClickListener(new View.OnClickListener() {
#Override public void onClick(View v) {
String phoneNo = txtPhoneNo.getText().toString();
String message = txtMessage.getText().toString();
String messageView = txtView.getText().toString();
if (phoneNo.length() > 0 && message.length() > 0) {
sendMessage(phoneNo, message, messageView);
} else {
Toast.makeText(getBaseContext(), "Please enter message",
Toast.LENGTH_SHORT).show();
}
}
});
}
private void sendMessage(String phoneNo,String message, String staticMessage){
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNo,null,message + "\n" +
staticMessage,null,null);
Toast.makeText(getApplicationContext(), "Message Sent",
Toast.LENGTH_SHORT).show();
}
catch (Exception e){
Toast.makeText(getApplicationContext(), "Unable to send. Please try again", Toast.LENGTH_SHORT).show();
}
}
}
You could create a list of all the numbers and do a for loop through the list in your onclick or in a method and call it in onclick. That's how I would do it anyway.
Following are the some steps to send one single message to multiple contact when it is checked.
Step 1 : In your MainActivity.class like this,
public class MainActivity extends AppCompatActivity {
ListView listView;
EditText editMessage;
ProgressDialog progressDialog;
Handler progresshandler;
boolean isThreadRunning;
int i;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.contactsView);
editMessage = (EditText) findViewById(R.id.editMessage);
listView.setAdapter(new ContactAdapter(this, contacts));
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Sending Messages.. Please wait!");
progresshandler = new Handler() {
public void handleMessage(Message msg) {
progressDialog.dismiss();
Toast.makeText(MainActivity.this, "Messages Sent",
Toast.LENGTH_LONG).show();
}
};
}
}
Step 2 : Create one class within this MainActivity.class(Put this class below onCreate() method)
class SendMessagesThread extends Thread {
Handler handler;
public SendMessagesThread(Handler handler) {
this.handler = handler;
}
public void run() {
SmsManager smsManager = SmsManager.getDefault();
// Find out which contacts are selected
for (int i = 0; i < listView.getCount(); i++) {
View item = (View) listView.getChildAt(i);
boolean selected = ((CheckBox) item.findViewById(R.id.selected)).isChecked();
if (selected) {
String mobile = ((TextView) item.findViewById(R.id.mobile)).getText().toString();
try {
smsManager.sendTextMessage(mobile, null, editMessage.getText().toString(), null, null);
} catch (Exception ex) {
Log.d("Mobile", "Could not send message to " + mobile);
}
}
}
Message m = handler.obtainMessage();
handler.sendMessage(m);
} // run
} // Thread
Step 3: Create one method(put this method below step - 2)
public void sendMessages(View v) {
if (editMessage.getText().toString().length() > 0) {
SendMessagesThread thread = new SendMessagesThread(progresshandler);
thread.start();
progressDialog.show();
} else {
Toast.makeText(this, "Please enter message!", Toast.LENGTH_LONG)
.show();
}
}
Note : According to my project, I am not using any SQLite database or webservice.Basically, I am fetching all the contact from device contact book and displaying that contact to listview. So, Try to understand and modify.
public class TextActivity extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ArrayList<CharSequence> selectedNumbers
=getIntent().getCharSequenceArrayListExtra(SELECTED_NUMBERS);;
String phNumbers = "";
for (CharSequence s: selectedNumbers) {
phNumbers += s + ";";
}
// for (int i = 0; i < selectedNumbers.size(); i++) {
// phNumbers += selectedNumbers.get(i);
// if (i != selectedNumbers.size()-1){
// phNumbers += ";";
// }
// }
phNumbers = phNumbers.substring(0, phNumbers.length() - 1);
String message = "";
Intent smsIntent = new Intent(Intent.ACTION_VIEW);
smsIntent.setType("vnd.android-dir/mms-sms");
smsIntent.putExtra("address", phNumbers);
smsIntent.putExtra("sms_body",message);
startActivity(smsIntent);
}
}
I have a written a receiver for a NEW_OUTGOING_CALL intent (static receiver). In order not to hold the system, I do the lengthy part of the process in a AsyncTask.
Based on the number dialed, I may or may not start the AsyncTask (and proceed with regular processing). However, the tasks starts on its own, with the right param passed, and I cant figure out how !!
I've grep'ed the project, and there are no other calls to LongOperation other than the one in the CallOneShot function - but the traces surrounding the 'new' statement do not appear.
How can this happen ?
Please find the code attached, sorry for the length, I've tried to cut it down a bit
Thanks for the help
J.
package com.iper.phoneeco;
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "XXBroadcastReceiver";
FileWriter fDevLog;
MyPrefs myprefs=null;
public final static String EXTRA_MESSAGE = "com.iper.phoneeco.msg1";
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("android.intent.action.NEW_OUTGOING_CALL"))
{
Log.d(TAG,"OUTGOING CALL RECEIVED");
String phoneNumber = getResultData();
if (phoneNumber == null) {
// No reformatted number, use the original
phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
Log.d(TAG,"phone number:"+phoneNumber);
if (IsToProcess(phoneNumber)) {
Log.d (TAG,"Trapping the call");
// Lets Roll
CallOneShot(phoneNumber);
// and prevent other apps from calling as well
setResultData(null);
// abortBroadcast();
}
else {
Log.d (TAG,"Standard processing");
Toast.makeText(context, "standard processing" , Toast.LENGTH_LONG).show();
}
Log.d (TAG,"Finished processing intent");
}
//
// check is number against a list of exceptions, that we dont handle
//
private boolean IsToProcess(String num){
String[] excluded = {"15","17","18","112","911","991","08.*","^\\*.*","^#.*"};
for (String ex : excluded){
Log.d(TAG,"Exclusion test: "+ex + "versus: "+num);
if (num.matches(ex)) {
Log.d(TAG,"Exclusion FOUND: "+ex);
return false;
}
}
if (num.length() < myprefs.minLen) {
Log.d(TAG,"Exclusion FOUND: Numero trop court");
return false;
}
Log.d(TAG,"Exclusion not found: ");
return true;
}
//
// Displays a toast
//
void MyToast(String s, int col, int dur ) {
Toast toast=Toast.makeText(myprefs.ctx, s, dur);
toast.setGravity(Gravity.CENTER_HORIZONTAL, 0, 0);
toast.getView().setBackgroundColor(col );
LinearLayout toastLayout = (LinearLayout) toast.getView();
TextView toastTV = (TextView) toastLayout.getChildAt(0);
toastTV.setTextSize(20);
toast.show();
}
void MyToast(String s, int col) {
MyToast(s,col,Toast.LENGTH_LONG);
}
public void CallOneShot(String phoneNumber) {
Log.d (TAG,"CallOneShot");
MyToast (myprefs.ctx.getResources().getString(R.string.callbackipg)+" "+phoneNumber,Color.BLUE);
new LongOperation().execute(phoneNumber);
}
//
// the meat....
//
public class LongOperation extends AsyncTask<String, Void, String> {
String numToCall;
#Override
protected String doInBackground(String... params) {
int bytesRead;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
String msgres;
String response;
Log.d(TAG, "Clientthread started");
numToCall=params[0];
Log.d(TAG, "numTocall"+numToCall);
// and add to the call log
ContentValues values = new ContentValues();
values.put(CallLog.Calls.NUMBER, numToCall);
values.put(CallLog.Calls.DATE, System.currentTimeMillis());
values.put(CallLog.Calls.DURATION, 0);
values.put(CallLog.Calls.TYPE, CallLog.Calls.OUTGOING_TYPE);
values.put(CallLog.Calls.NEW, 1);
values.put(CallLog.Calls.CACHED_NAME, "");
values.put(CallLog.Calls.CACHED_NUMBER_TYPE, 0);
values.put(CallLog.Calls.CACHED_NUMBER_LABEL, "");
Log.d(TAG, "Inserting call log placeholder for " + numToCall);
ContentResolver resolver = myprefs.ctx.getContentResolver();
resolver.insert(CallLog.Calls.CONTENT_URI, values);
response=myprefs.ctx.getResources().getString(R.string.errundef);
return response;
}
protected void onPostExecute (String s) {
if (!s.equals("ok")) {
Log.d(TAG,"OnPostExecute - failed: "+s);
MyToast (myprefs.ctx.getResources().getString(R.string.errcallback)+"\n"+s,Color.RED);
}
}
}
}
Have you assign value to myprefs. It seems that you have initialized it to null and never assign it to any value
ok - stupid me is the answer - I had changed the name of the package, and an old version of the package was still on the emulator, trapping the intent ! once I removed it, it all went back to normal...
Many thanks for your help anyway
Edit: I have figured out that when you set the alarm for a time before the current time it imediately goes off
I am creating an alarm app that has an activity that you use to create the alarm, and then it registers it with the android system. The problem I am having is that as soon as you create the alarm it goes off. I have also seen some odd behaviour with further testing, if you change the time in the time picker before you change the name it goes off at the correct time if not it just goes off as soon as it is created. Here is my alarm class which is used to create the pending-intent for the alarm(at least the important parts):
public class Alarm {
private String name;
private int id;
private Calendar time;
private boolean on = true;
public Alarm(String name, int id, Calendar time) {
this.setName(name);
this.setId(id);
this.setTime(time);
}
public Alarm() {
}
...
public PendingIntent createPendingIntent(Context context) {
Intent intent = new Intent(context, MathAlarmReceiverActivity.class);
intent.putExtra("name", getName());
PendingIntent pendingIntent = PendingIntent.getActivity(context,
id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
return pendingIntent;
}
...
}
I thought that the problem might be where I created the intent, but when I changed the time in the time picker before I changed the name, it went off at the correct time so I was able to rule that out.
Next I have my Activity that actually has the time-picker:
public class EditActivity extends Activity {
public static int CODE_EDIT = 100;
public static int CODE_ADD = 101;
Button doneButton;
TimePicker tp;
EditText editName;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_edit);
doneButton = (Button) findViewById(R.id.buttonDone);
doneButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
done();
}
});
tp = (TimePicker) findViewById(R.id.timePicker);
editName = (EditText) findViewById(R.id.editTextAlarmName);
if (getIntent().getExtras().getInt("requestCode") == CODE_ADD) {
setTitle("Add Alarm");
doneButton.setText("Add Alarm");
} else {
doneButton.setText("Edit Alarm");
editName.setText(getIntent().getExtras().getString("name"));
Calendar c = TimeUtils.stringToCalendar(getIntent().getExtras().getString("time"));
tp.setCurrentHour(c.get(Calendar.HOUR_OF_DAY));
tp.setCurrentMinute(c.get(Calendar.MINUTE));
setTitle("Edit Alarm");
}
if (android.os.Build.VERSION.SDK_INT > 10) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
...
private void done() {
String name = editName.getText().toString().trim();
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, tp.getCurrentHour());
c.set(Calendar.MINUTE, tp.getCurrentMinute());
String time = TimeUtils.calendarToString(c);
if (name.length() == 0) {
AlertDialog.Builder b = new AlertDialog.Builder(this);
b.setMessage("Please enter an alarm name!");
b.setNeutralButton("Ok", null);
b.show();
} else {
Intent returnIntent = new Intent();
returnIntent.putExtra("name", name);
returnIntent.putExtra("time", time);
if (getIntent().getExtras().getInt("requestCode") == CODE_EDIT) {
returnIntent.putExtra("id", getIntent().getExtras()
.getInt("id"));
returnIntent.putExtra("on",
getIntent().getExtras().getBoolean("on"));
}
setResult(RESULT_OK, returnIntent);
finish();
}
}
...
}
Then I thought well maybe when I didn't do it that specific way it somehow changed what was in the time-picker, but I wasn't sure how that could happen! Here I have my method for adding the alarm to the android system:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
if (requestCode == EditActivity.CODE_EDIT) {
dataSource.update_byID(
data.getExtras().getInt("id"),
TimeUtils.stringToCalendar(data.getExtras().getString(
"time")), data.getExtras().getString("name"),
data.getExtras().getBoolean("on"));
AlarmManager am = (AlarmManager) getSystemService(Activity.ALARM_SERVICE);
for(int i = 0; i < dataSource.getAllAlarms().size(); i++) {
if(dataSource.getAllAlarms().get(i).getId() == data.getExtras().getInt("id")) {
Alarm tempAlarm = dataSource.getAllAlarms().get(i);
am.setRepeating(AlarmManager.RTC_WAKEUP, tempAlarm.getTime()
.getTimeInMillis(), AlarmManager.INTERVAL_DAY, tempAlarm
.createPendingIntent(this));
break;
}
}
}
if (requestCode == EditActivity.CODE_ADD) {
Alarm tempAlarm = dataSource.createAlarm(
data.getExtras().getString("name"),
TimeUtils.stringToCalendar(data.getExtras().getString(
"time")));
AlarmManager am = (AlarmManager) getSystemService(Activity.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, tempAlarm.getTime()
.getTimeInMillis(), AlarmManager.INTERVAL_DAY, tempAlarm
.createPendingIntent(this));
}
}
refreshList();
}
And also these are the methods I used to convert a Calendar to string and back, I thought maybe I made an error here, but I tested the methods and they work:
public static String calendarToString(Calendar c) {
return String.valueOf(c.get(Calendar.HOUR)) + ":"
+ getMinuteInReadableFormat(c.get(Calendar.MINUTE))
+ (((c.get(Calendar.AM_PM)) == Calendar.AM) ? "AM" : "PM");
}
public static Calendar stringToCalendar(String s) {
String[] t = s.split(":");
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR, Integer.valueOf(t[0]));
c.set(Calendar.MINUTE, minuteToInt(t[1]));
c.set(Calendar.AM_PM, (t[1].endsWith("AM") ? Calendar.AM : Calendar.PM));
return c;
}
public static String convert12(String s) {
String newString = null;
if (s.startsWith("0")) {
newString = s.replaceFirst("0", "12");
}
if (newString == null) {
return s;
} else {
return newString;
}
}
private static String getMinuteInReadableFormat(int m) {
if (m < 10) {
return "0" + String.valueOf(m);
} else {
return String.valueOf(m);
}
}
private static int minuteToInt(String m) {
String x = m;
if (m.startsWith("0")) {
x = m.replaceFirst("0", "");
}
String y = x.replace("AM", "");
String z = y.replace("PM", "");
return Integer.valueOf(z.trim());
}
My problem is that as soon as you create the alarm it goes off. I have also seen some odd behaviour with further testing, if you change the time in the time picker before you change the name it goes off at the correct time if not it just goes off as soon as it is created. And I am not sure what to do know because I don't know what the problem is.
Also Thank You for taking the time to read my question.
Well I've solved the problem. For anyone that has a similar problem here is the solution. When I was creating an alarm for a time earlier than the current time it would go off immediately. The solution is to add the equivalent of 24 hours to the time you want when setting the alarm.