How to know Phone State as a service? - java

The Problem:
What I want the app to do is checking a phone's current call state in the background. Now, for making an app that works in background we need a service(source codes in the next section), but the only way I have managed to do this is by using a receiver. The issue with the receiver is that it is not always running if you kill the app in the background. Now this seems a fairly simple question, but I am new to JAVA.
Source Codes
This is my main service's OnStartCommand file:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("asdaff").setContentText("asdas").setSmallIcon(R.drawable.ic_android_black_24dp).build();
startForeground(1, notification);
TelephonyManager telephonyManager =
(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener callStateListener;
callStateListener = new PhoneStateListener() {
public void onCallStateChanged(int state, String incomingNumber) {
String stateString = "N/A";
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
stateString = "Idle";
Toast toast = Toast.makeText(getApplicationContext(), stateString, Toast.LENGTH_LONG);
toast.setGravity(Gravity.CENTER, 0,0);
toast.show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
stateString = "Off Hook";
Toast toast1 = Toast.makeText(getApplicationContext(), stateString, Toast.LENGTH_LONG);
toast1.setGravity(Gravity.CENTER, 0,0);
toast1.show();
break;
case TelephonyManager.CALL_STATE_RINGING:
stateString = "Ringing";
Toast toast2 = Toast.makeText(getApplicationContext(), stateString, Toast.LENGTH_LONG);
toast2.setGravity(Gravity.CENTER, 0,0);
toast2.show();
break;
}
}
};
return super.onStartCommand(intent, flags, startId);
}
Edit
Used startForegroundservice() and it still does not work. I have the phone permission. Please tell if you need more of the source code to tell the problem.

So I figured it out. The issue is in the onStartCommand method itself. When using a phone state listener, you need to have the listener registered to the telephony manager.
telephonyManager.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);
Now, I thought this is one of those "Google-able" Things, that have a proper beginner mistake warning that may help anyone who is going to commit such a stupid mistake(i.e, me).
I am Writing this answer so that anyone who comes across this may no what not to do.
Cheers.

Related

Notification transport controls dont appear to be doing anything

I have created an app that can play audio using a MediaBrowserServiceCompat and a MediaSessionCompat. As per the instructions on the android developers website, I have created a notification in the MediaSessionCompat.Callback().onPlay() method that uses MediaStyle to provide transport controls which are supposed to connect to my media session when provided with the appropriate token. The in app controls for playing and pausing work as expected, even when the app is closed and opened again. The service appears to be running as expected.
The problem however, is that although the notification appears as expected, the included pause button is seemingly unable to do anything. And despite the fact that the android developers example indicates that a cancel button should be present, it is not. Furthermore the example also indicated that the service should be stoppable by swiping the notification away, and yet it does not.
Suffice it to say, nothing in the following code snippet is working correctly. Except that the notification does, in fact, appear.
private NotificationCompat.Builder getMediaNotificationBuilder() {
Intent contentIntent = new Intent(mContext, MainActivity.class);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingContentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);
MediaControllerCompat controller = mMediaSession.getController();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, "PODCAST");
builder
.setContentTitle("PODCAST")
.setContentText("THIS IS A PLACE HOLDER.")
.setSubText("Still a place holder.")
// Enable launching the player by clicking the notification
.setContentIntent(pendingContentIntent)
// Stop the service when the notification is swiped away
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext, PlaybackStateCompat.ACTION_STOP))
// Make the transport controls visible on the lockscreen
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
// Add an app icon and set its accent color
// Be careful about the color
.setSmallIcon(R.drawable.ic_launcher_background)
.setColor(ContextCompat.getColor(mContext, R.color.colorPrimaryDark))
// Add a pause button
.addAction(new NotificationCompat.Action(
R.drawable.ic_pause, "Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_PAUSE)))
// Take advantage of MediaStyle features
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mMediaSession.getSessionToken())
.setShowActionsInCompactView(0)
// Add a cancel button
.setShowCancelButton(true)
.setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_STOP)));
return builder;
}
I then go on to pass this notification to
startForground(1, getMediaNotificationBuilder().build())
and then start the service.
I will be happy to share the entire app source code if it is necessary. I am sure that I have missed something very simple here.
As I suspected I was missing something very simple. In order for my MediaBrowserServiceCompat subclass to react to my notification controls, I needed to override onStartCommand from the Service base class and pass the Intent there in to my MediaSessionCompat object. After doing this, the MediaSessionCompat.Callback should handle the command assuming it has been programed to do so. This is what the code for that looks like, inside of my MediaBrowserService class.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(LOG_TAG, "onStartCommand(): received intent " + intent.getAction() + " with flags " + flags + " and startId " + startId);
MediaButtonReceiver.handleIntent(mMediaSession, intent);
return super.onStartCommand(intent, flags, startId);
}
After adding this code you should see the method in logcat as well. Just in case anyone out there is still missing something, you will at least know that the code is responding to your button presses.
EDIT:
As for stopping the Service by swiping the notification, I was misunderstanding the interaction between the notification and the user. The notification CAN be swiped away by the user but only if the media is PAUSED first. This paradigm is further supported by the standard media player app's notification controls. This makes sense as the user might accidentally swipe away the controls while in the middle of listening to something otherwise.
In addition I have decided to include the entire source code for my MediaBrowserServiceCompat class in hopes that this additional information will
provide some context for disscussion
public class MediaPlaybackService extends MediaBrowserServiceCompat {
private static final String LOG_TAG = "MediaPlaybackService";
private static final String MY_MEDIA_ROOT_ID = "media_root_id";
private static final String MY_EMPTY_MEDIA_ROOT_ID = "empty_root_id";
// Volume levels: Normal and Duck
// VOLUME_DUCK is the volume we set the media player to when we lose audio focus, but are allowed to reduce the volume instead of stopping playback.
public static final float VOLUME_DUCK = 0.2f;
public static final float VOLUME_NORMAL = 1.0f;
private MediaSessionCompat mMediaSession;
private MediaPlayer mMediaPlayer;
// Current local media player state
private PlaybackStateCompat.Builder mStateBuilder;
private int mState = PlaybackStateCompat.STATE_NONE;
private final class MediaSessionCallback extends MediaSessionCompat.Callback implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, AudioManager.OnAudioFocusChangeListener{
private Context mContext;
private AudioManager mAudioManager;
// Declare the "SHIT THAT'S LOUD" intent, any broadcast receiver
// that is connected to it will trigger when the headphones come unplugged
private IntentFilter shitThatsLoudIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private BroadcastReceiver shitThatsLoudBroadcastReceiver = new BroadcastReceiver() {
// TODO: Put me in a separate class
#Override
public void onReceive(Context context, Intent intent) {
Log.d(LOG_TAG, "SHIT THATS LOUD! The headphones have come unplugged!");
}
};
private MediaSessionCallback(Context context) {
super();
mContext = context;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
initMediaPlayer();
}
private void initMediaPlayer() {
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource("https://www.blogtalkradio.com/kylekulinski/2018/10/15/the-kyle-kulinski-show.mp3");
mMediaPlayer.setOnPreparedListener (this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnErrorListener (this);
mMediaPlayer.prepare();
} catch (IOException e) {
Log.e(LOG_TAG, ".initMediaPlayer(): IOException: "+e.toString());
}
}
private void mediaPlay() {
registerReceiver(shitThatsLoudBroadcastReceiver, shitThatsLoudIntentFilter);
if (mAudioManager.requestAudioFocus(getAudioFocusRequest()) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(LOG_TAG, "Audio focus request granted.");
mState = PlaybackStateCompat.STATE_PLAYING;
mStateBuilder.setActions(PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP);
mStateBuilder.setState(mState, mMediaPlayer.getCurrentPosition(), 1.0f, SystemClock.elapsedRealtime());
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaSession.setActive(true);
mMediaPlayer.start();
startService(new Intent(mContext, MediaPlaybackService.class));
startForeground(1, getMediaNotificationBuilder().build());
}
}
private void mediaPause() {
unregisterReceiver(shitThatsLoudBroadcastReceiver);
mState = PlaybackStateCompat.STATE_PAUSED;
mStateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP);
mStateBuilder.setState(mState, mMediaPlayer.getCurrentPosition(), 1.0f, SystemClock.elapsedRealtime());
mMediaSession.setPlaybackState(mStateBuilder.build());
mMediaPlayer.pause();
stopForeground(false);
}
private void releaseResources() {
mMediaSession.setActive(false);
mAudioManager.abandonAudioFocusRequest(getAudioFocusRequest());
unregisterReceiver(shitThatsLoudBroadcastReceiver);
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
}
stopSelf();
stopForeground(true);
}
private NotificationCompat.Builder getMediaNotificationBuilder() {
Intent contentIntent = new Intent(mContext, MainActivity.class);
contentIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pendingContentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);
MediaControllerCompat controller = mMediaSession.getController();
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, "PODCAST");
builder
.setContentTitle("PODCAST")
.setContentText("THIS IS A PLACE HOLDER.")
.setSubText("Still a place holder.")
// Enable launching the player by clicking the notification
.setContentIntent(pendingContentIntent)
// Stop the service when the notification is swiped away
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext, PlaybackStateCompat.ACTION_STOP))
// Make the transport controls visible on the lockscreen
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
// Add an app icon and set its accent color
// Be careful about the color
.setSmallIcon(R.drawable.ic_launcher_background)
.setColor(ContextCompat.getColor(mContext, R.color.colorPrimaryDark))
// Add a pause button
.addAction(new NotificationCompat.Action(
R.drawable.ic_pause, "Pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_PLAY_PAUSE)))
// Take advantage of MediaStyle features
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mMediaSession.getSessionToken())
.setShowActionsInCompactView(0)
// Add a cancel button
.setShowCancelButton(true)
.setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(mContext,
PlaybackStateCompat.ACTION_STOP)));
return builder;
}
#Override
public void onPlay() {
super.onPlay();
Log.d(LOG_TAG, "I tried to play music");
mediaPlay();
}
#Override
public void onPause() {
super.onPause();
Log.d(LOG_TAG, "I Tried to pause");
mediaPause();
}
#Override
public void onStop() {
super.onStop();
releaseResources();
}
private AudioFocusRequest getAudioFocusRequest() {
// Request audio focus for playback, this registers the afChangeListener
AudioAttributes attrs = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(this)
.setAudioAttributes(attrs)
.build();
return audioFocusRequest;
}
#Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
Log.d(LOG_TAG, "Audio focus has been restored after it was transiently arrested by and intrusive app. We can now start playing audio normally again.");
mMediaPlayer.setVolume(VOLUME_NORMAL, VOLUME_NORMAL);
mediaPlay();
break;
case AudioManager.AUDIOFOCUS_LOSS:
Log.d(LOG_TAG, "Audio focus was lost flat out. Save what we were doing so we don't forget about it later.");
mediaPause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
Log.d(LOG_TAG, "Audio focus was lost (Transient) but we might get it back later, still stop and save though.");
mediaPause();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
Log.d(LOG_TAG, "Audio focus was lost but was just need to keep it down instead of stopping.");
mMediaPlayer.setVolume(VOLUME_DUCK, VOLUME_DUCK);
break;
default:
Log.d(LOG_TAG, "Ignoring unsupported audio focus change: "+focusChange);
break;
}
}
#Override
public void onPrepared(MediaPlayer mp) {
Log.d(LOG_TAG, "MediaSessionCallback.onPrepared(): MediaPlayer is prepared!");
// The media player is done preparing. That means we can start playing if we
// have audio focus.
}
#Override
public void onCompletion(MediaPlayer mp) {
}
#Override
public boolean onError(MediaPlayer mp, int what, int extra) {
Log.e(LOG_TAG, "Media player error: what=" + what + ", extra=" + extra);
return false; // true indicates we handled the error
}
}
#Override
public void onCreate() {
super.onCreate();
// Create a MediaSessionCompat
mMediaSession = new MediaSessionCompat(this, LOG_TAG);
// Set the session's token so that client activities can communicate with it.
setSessionToken(mMediaSession.getSessionToken());
// MediaSessionCallback() has methods that handle callbacks from a media controller
mMediaSession.setCallback(new MediaSessionCallback(this));
// Enable callbacks from media buttons and transport controls
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
);
// Set initial PlaybackState with ACTION_PLAY, so that media buttons start the player
mStateBuilder = new PlaybackStateCompat.Builder()
.setActions(
PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE
);
mMediaSession.setPlaybackState(mStateBuilder.build());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(LOG_TAG, "onStartCommand(): received intent " + intent.getAction() + " with flags " + flags + " and startId " + startId);
MediaButtonReceiver.handleIntent(mMediaSession, intent);
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public BrowserRoot onGetRoot(#NonNull String clientPackageName, int clientUid, #Nullable Bundle rootHints) {
return new BrowserRoot(MY_EMPTY_MEDIA_ROOT_ID, null);
}
#Override
public void onLoadChildren(#NonNull String parentMediaId, #NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
// Browsing not allowed
if (TextUtils.equals(MY_EMPTY_MEDIA_ROOT_ID, parentMediaId)) {
result.sendResult(null);
return;
}
// TODO: If in the future we decide that we do want this class to handle the podcast metadata
// Then we must adapt what ever data podcastFactory produces into a List of MediaBrowserCompat.MediaItem objects
// The constructor of MediaItem requires that a MediaDescription object be passed to it.
// MediaDescription has a builder class which contains methods for setting Title, Artist, Uri, etc...
// MediaDescription.Builder mMediaDescriptionBuilder = new MediaDescription.Builder();
// mMediaDescriptionBuilder.setTitle(String);
// mMediaDescriptionBuilder.setMediaUri(String);
// MediaDescription mMediaDescription = mMediaDescriptionBuilder.build()
// MediaBrowserCompat.MediaItem mMediaItem =
// new MediaBrowserCompat.MediaItem(
// mMediaDescription,
// int flags -> FLAG_BROWSABLE and/or FLAG_PLAYABLE
// );
// add MediaItem to SomeList
// result.sendResult(SomeList);
}

Force stopping LE Scan while BT Adapter is OFF thows java.lang.IllegalStateException (IN NEXUS 5 and Android 6)

I am facing this error while disabling Bluetooth. Googled it but did not find solution. Here is my broadcast receiver which invokes when bluetooth state is changed.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.e(LOG_TAG, "Broadcast receiver - onReceive");
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
h.removeCallbacks(runnable);
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_OFF:
Log.i(LOG_TAG, "***** BLE-Bluetooth is disabled");
Toast.makeText(getApplicationContext(), "Bluetooth is disabled", Toast.LENGTH_LONG).show();
Log.i(TAG, "***** mBeaconScanner "+ mBeaconScanner);
if(null != mBeaconScanner)
mBeaconScanner.scanLeDevice(false);
break;
case BluetoothAdapter.STATE_ON:
Log.i(LOG_TAG, "***** BLE-Bluetooth is enabled");
Toast.makeText(getApplicationContext(), "Bluetooth is enabled", Toast.LENGTH_LONG).show();
if(null != mBeaconScanner)
mBeaconScanner.scanLeDevice(true);
break;
}
}
}
};
In the above code I am trying to stop ble scanning while disabling the Bluetooth. In the above code method "mBeaconScanner.scanLeDevice(false);" redirects to :
Log.i(TAG, "***** Stopping BLE Scan for Android version " + Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (bluetoothLeScanner != null)
{
bluetoothLeScanner.stopScan(mScanCallback);
}
} else {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
In the above snippet I am getting Exception when calling bluetoothLeScanner.stopScan(mScanCallback) method.
NOTE: this issue is not reproducible in all the devices but nexus and its not very frequently reproducible as well for me.
Please suggest me if there is any solution for this.. Thanks in advance
The only solution I can suggest here is comment this code:-
//mBeaconScanner.scanLeDevice(false);
The way BLE scan works is pretty complicated at the radio level. There are tons of factors (like power, efficiency etc..) that are taken into consideration while arriving at the right scan window sleep time etc..
For you in this particular case when the BT adapter itself is turned OFF, trying to stop scan is an invalid operation as far as the radio is concerned. Am glad that Android FW throws this exception !! Just get rid of this code and may be do some internal app specific state transition (if you have to) or else just log and be done in case BluetoothAdapter.STATE_OFF:

BroadCast Receiver Battery Status error

i'm trying to use in my project a broadcast receiver which listens to battery status of charging/not charging and throw a toast in each of the options .
every time i change the charger status in the app ,the app crash.
(if i start the app with the charger connected it's show me the right toast
but when i uncharge the phone the app crashes)
here is the code
thanks in advance
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = this.registerReceiver(null, ifilter);
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
if(isCharging==true){
Toast.makeText(this, "Charging", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(this, "Charger not connected", Toast.LENGTH_SHORT).show();
UPDATE
i'm having an hard time to understand what i suppose to do.
i'm pretty new so be patient with me :)
here is the code i made
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkBatteryState(null);
public void checkBatteryState(View sender) {
IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = registerReceiver(null, filter);
int chargeState = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
String strState;
switch (chargeState) {
case BatteryManager.BATTERY_STATUS_CHARGING:
case BatteryManager.BATTERY_STATUS_FULL:
strState = "charging";
Toast.makeText(this, strState, Toast.LENGTH_LONG).show();
break;
default:
strState = "not charging";
Toast.makeText(this, strState, Toast.LENGTH_LONG).show();
}
}
}
http://www.vogella.com/tutorials/AndroidBroadcastReceiver/article.html
is this what you're using?
Maybe you haven't ensured your Intent is sticky.
My advice would be not to use a registerreceiver with a null argument.
Try this method for creating a broadcastreceiver:
How to send data to another app which is not started
put your Toasts in the onReceive() function.
The code is not actually registering a receiver, just getting a sticky broadcast. If the broadcast has never been sent the this will return null which will cause a NPE in the remaining code.

How to "properly" send sms when call is received?

I am trying to have an Android Service listen for an incoming phone call and when one does occur, grab the incoming number and text it a message.
In my service I have made a PhoneStateListener:
TelephonyManager tManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener listener = new PhoneStateListener()
{
#Override
public void onCallStateChanged(int state, String incomingNumber)
{
switch(state)
{
case TelephonyManager.CALL_STATE_IDLE:
Log.d(TAG, "Phone: Idle");
break;
case TelephonyManager.CALL_STATE_RINGING:
Log.d(TAG, "Phone: Ringing.");
Log.i(TAG, "Incoming call from: " + incomingNumber);
sendSms();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.d(TAG, "Phone: Off Hook");
break;
}
}
};
tManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
My sendSms() function is as follows:
private void sendSms()
{
SmsManager manager = SmsManager.getDefault();
wakeLock.acquire();
Log.d(TAG, "Wake Lock Acquired!");
if (getMessageContent(getInformStatus()).length() > 160)
{
ArrayList<String> messagelist = manager.divideMessage(getMessageContent(getInformStatus()));
manager.sendMultipartTextMessage(getReturnAddress(), null, messagelist, null, null);
Log.i(TAG, "Multipart Text Message Sent!");
}
else
{
manager.sendTextMessage(getReturnAddress(), null, getMessageContent(getInformStatus()), sentPI, null);
Log.i(TAG, "Text Message Sent!");
}
wakeLock.release();
Log.d(TAG, "Wake Lock Released!");
}
I even have an SMS_SENT Broadcast Receiver to check if the text message sent out correctly that will recall the sendSms() function if it did not sent for whatever reason:
resend = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
switch (getResultCode())
{
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
Log.e(TAG, "Text did NOT send (GENERIC_FAILURE)");
Log.i(TAG, "Attempting to resend");
sendSms();
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
Log.e(TAG, "Text did NOT send (NO_SERVICE)");
Log.i(TAG, "Attempting to resend");
sendSms();
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
Log.e(TAG, "Text did NOT send (RADIO_OFF)");
Log.i(TAG, "Attempting to resend");
sendSms();
break;
}
}
};
registerReceiver(resend, new IntentFilter("android.provider.Telephony.SMS_SENT"));
This works on my phone fine, however on other people's phones it likes to sometimes toggle back and forth between RINGING and IDLE in the PhoneStateListener a few times before "sending".. The phone actually says that the application is trying to send multiple text messages and asks the user if that is okay. When they click yes, it just asks again.
To make it better, it never actually sends the text message either..
On another phone, it doesn't do this at all.. The other phone will go through the motions and the logcat is identical to mine.. It actually says it sends, but the recipient never gets the SMS..
It is suppose to be an auto-response service of sorts and I wanted to try and make it work on at least Froyo, Gingerbread, and Ice Cream Sandwich..
By the way, it works 100% on my:
Galaxy Nexus (toro) (Android 4.0.3)
Droid 1 (sholes) (Android 2.3.7)
HTC Thunderbolt (mecha) (Android 2.3.7)
However, if it's my friend's:
Nexus S 4G (crespo4g) (Android 4.0.3)
or my other friend's
Galaxy Nexus (toro) (Android 4.0.3)
It does not work..
What I really don't understand is that it doesn't work with my friend with the exact same phone as me..
I feel like this code implementation is a hit or miss and was wondering if anyone had some insight to help out.
Appreciate it!
I think I figured it out. What I ended up doing is putting a Handler that called a Runnable 30 seconds after the phone call was initially received.. This gave the phone time to finish the phone call (assuming it was in your pocket) and then cleanly send the text message..
I hope this is the only issues I was having, but it seems to work on my friends Galaxy Nexus (toro) now..
Hope this helps anyone else wondering about this..
Cheers!

Toast showing up twice

I have a program that sends pre-defined text messages to a group of people at a push of a button. I have it working well but the problem I have is that when it sends the messages, it pops up with 2 toasts per message sent. Code:
package com.mfd.alerter;
//imports
public class homeScreen extends Activity {
//buttons
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//vars
// Grab the time
final Date anotherCurDate = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("km");
final String formattedTime = formatter.format(anotherCurDate);
// Contacts
final String[] numbers = getResources().getStringArray(R.array.numbers);
// Start messages. Only 1 is given to shorten post
callStructureFire.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
String msgText = "MFD PAGE OUT:\nStructure Fire\nTimeout:"+formattedTime;
for (int i = 0; i < numbers.length; i++) {
sendSMS(numbers[i], msgText);
}
}
});
//more call types. not important.
}
//---sends a SMS message to another device---
private void sendSMS(String numbers, String message)
{
String SENT = "SMS_SENT";
PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
new Intent(SENT), 0);
//---when the SMS has been sent---
registerReceiver(new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent arg1) {
switch (getResultCode())
{
case Activity.RESULT_OK:
Toast.makeText(getBaseContext(), "SMS sent",
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;
}
}
}, new IntentFilter(SENT));
SmsManager sms = SmsManager.getDefault();
sms.sendTextMessage(numbers, null, message, sentPI, null);
}
//action bar stuff. not important.
}
More in detail: Lets say I send the text to 3 people, 6 toast messages will pop up saying "SMS Sent". How do I make it so only 3 will show up?
Also, Is there a way to maybe add a counter of the messages sent? Ex: "Message 1/10 sent", "Message 2/10 sent", etc?
I didn't really look at your code or asked myself why this happens but here's a trick to stop toasts show up twice:
Create a Toast instance using makeToast(), before showing it you call cancel(), set your text and then call show(). This will dismiss the previous toast. You won't even notice that a toast is displayed twice.
That's a stupid workaround, but it works for me ;-)
Aren't you supposed to register the receiver only once and not every time you call sendSMS.
You get 6 Toasts with three sms messages because you have 3 BroadCastReceivers. So in the first run you get 1 Toast. In the second run you get 2 Toasts (the receiver that was registered in the first run is called, and the one in the second). In the third run all three receivers are called, so you get three more toasts. All in sum - 6 Toasts...
so I guess, you have to register only one receiver before the for loop where you call sendSMS, or if you want the registration in sendSMS, then you have to unregister at the end of the method.
I hope this helps,
cheers!

Categories