I'm having an issue in trying to run a PhoneStateListener on* callback methods in a backround thread. Here's what I did so far:
First, I wrote my PhoneStateListener implementation. Something on the lines of:
public MyPhoneStateListener extends PhoneStateListener {
private TelephonyManager mTelephonyManager;
public MyPhoneStateListener(Context context) {
mTelephonyManager = (TelephonyManager) context.getSystemServices(Context.TELEPHONY_SERVICE);
}
#Override
public void onCallStateChanged(int state, String incomingNumber) { ... }
public void startListening() {
mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
}
}
Then I created a Handler class:
public PhoneStateHandler extends Handler {
private Context mContext;
private MyPhoneStateListener mListener;
public static final int MESSAGE_START = 0;
public PhoneStateHandler(Context context, Looper looper) {
super(looper);
mContext = context;
}
#Override
public void onHandleMessage(Message message) {
if (mListener == null) {
mListener = new MyPhoneStateListener(mContext);
}
switch (message.what) {
case MESSAGE_START:
mListener.startListening();
break;
}
}
}
Finally, from within a Service I start a new HandlerThread and attach my PhoneStateHandler to its' looper:
HandlerThread mThread = new HandlerThread("PhoneStateListenerThread");
mThread.start();
PhoneStateHandler mHandler = new PhoneStateHandler(this, mThread.getLooper());
mHandler.sendMessage(Message.obtain(mHandler, PhoneStateHandler.MESSAGE_START));
What I expect, is that methods such as MyPhoneStateListener.onCallStateChanged() to be executed on the mThread thread instead of the UI thread. I'm exploiting the fact that when a PhoneStateListener is first created, it is bound to the Looper of its current thread. This is undocumented, but that's what actually happens.
What I observed, however, is that despite the PhoneStateHandler.onHandleMessage() method gets called in the HandlerThread's looper (as I expect), the MyPhoneStateListener.onCallStateChanged() still gets called in the UI thread.
Any clue about what's wrong with my code?
Thanks
Related
I am working on an application where i need to finish the activity from onResume if there is an incoming call. I have created a CallHelper class where i am detecting any incoming call using PhoneStateListener.
public class CallHelper {
/**
* Listener to detect incoming calls.
*/
private static class CallStateListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
System.out.println("RINGING");
OverlayActivity overlayActivity = OverlayActivity.getInstance();
overlayActivity.finish();
System.out.println("Activity has been closed!!");
break;
}
}
}
/**
* Broadcast receiver to detect the outgoing calls.
*/
public class OutgoingReceiver extends BroadcastReceiver {
public OutgoingReceiver() {
}
#Override
public void onReceive(Context context, Intent intent) {
String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(ctx,
"Outgoing: "+number,
Toast.LENGTH_LONG).show();
}
}
private Context ctx;
private TelephonyManager tm;
private CallStateListener callStateListener;
private OutgoingReceiver outgoingReceiver;
public CallHelper(Context ctx) {
this.ctx = ctx;
callStateListener = new CallStateListener();
outgoingReceiver = new OutgoingReceiver();
}
/**
* Start calls detection.
*/
public void start() {
tm = (TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL);
ctx.registerReceiver(outgoingReceiver, intentFilter);
}
/**
* Stop calls detection.
*/
public void stop() {
tm.listen(callStateListener, PhoneStateListener.LISTEN_NONE);
ctx.unregisterReceiver(outgoingReceiver);
}
}
I need to detect inside onResume, if there is any call, i need to finish the activity if not i don't need to do anything.
I haven't found the solution yet, so what i did is, i have added the code for detecting the CallStateChange inside my Activity's onResume and using that code i am executing the task which i want to execute on CallStateChange.
I am not sure if this is a correct solution or not, but would be expecting views on the same from the community.
Here's the code which i am using:
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener callStateListener = new PhoneStateListener() {
public void onCallStateChanged(int state, String incomingNumber)
{
if(state==TelephonyManager.CALL_STATE_RINGING){
// Code i wanted to execute
}
}
};
telephonyManager.listen(callStateListener,PhoneStateListener.LISTEN_CALL_STATE);
I have the launcher activity in my app that should fire an event with otto event bus in onCreate(). The activity itself makes sure that a service is started. The bus is implemented as a singleton via a BusProvider of a derived class of the bus. Whenever I post to the bus from my activity, the activity itself receives the event (I subscribed for debugging purposes) but not the service, although it should both be sent on the main thread.
Because I receive the event in the activity, otto basically seams to work. I assume that the difference between service (background) and activity (ui) may be the cause why I don't receive the event in the service, but why?
Here is the code of My derived Bus Class:
public class MainBus extends Bus {
private final Handler handler = new Handler(Looper.getMainLooper());
public MainBus(ThreadEnforcer any) {
super(any);
}
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
MainBus.super.post(event);
}
});
}
}
}
Here is the BusProvider Singleton
public final class BusProvider {
// Also tried with ThreadEnforcer.MAIN
private static final MainBus BUS = new MainBus(ThreadEnforcer.ANY);
public static MainBus getInstance() {
return BUS;
}
private BusProvider() {
// No instances.
}
}
Here is the relevant code of my Activity
public class Splashscreen extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BusProvider.getInstance().register(this);
// Start the Service if not yet started.
if (!isMyServiceRunning()) {
startMyService();
}
LogProvider.getInstance().d("Splashscreen", "Activity started.");
BusProvider.getInstance().post(new SplashscreenStartupCompletedEvent());
LogProvider.getInstance().d("Splashscreen",
"After Startup Event posted");
setContentView(R.layout.splashscreen);
}
/**
* private function to check if the service is running
*
* #return boolean True if the service is running, false otherwise
*/
private boolean isMyServiceRunning() {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager
.getRunningServices(Integer.MAX_VALUE)) {
if (MyApplicationService.class.getName().equals(
service.service.getClassName())) {
LogProvider.getInstance().i("Splashscreen",
"Service is already running.");
return true;
}
}
LogProvider.getInstance().n("Splashscreen", "Service is not running.");
return false;
}
/**
* private function to start the Backgroundservice
*/
private void startMyService() {
LogProvider.getInstance().i("Splashscreen", "Service will be started.");
Context context = getApplicationContext();
Intent service = new Intent(
context,
com.classpath.MyApplicationService.class);
context.startService(service);
}
#Subscribe
public void afterStartEvent(SplashscreenStartupCompletedEvent event) {
LogProvider.getInstance().d("afterStartEvent", "Event received");
}
}
And the relevant part of the Service
public class MyApplicationService extends Service {
#Override
public void onCreate() {
BusProvider.getInstance().register(this);
super.onCreate();
}
#Subscribe
public void afterStartupCompleted(SplashscreenStartupCompletedEvent event) {
LogProvider.getInstance().d("MyApplicationService",
"StartupCompletedEvent empfangen");
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
I have a service which I start from my Activity.
Now the serivce performs some task by starting a new thread from onStartCommand()
I want to stop the service after the thread has finished its job.
I tried using a Handler like this
public class MainService extends Service{
private Timer myTimer;
private MyHandler mHandler;
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mHandler = new MyHandler();
myTimer = new Timer();
myTimer.schedule(new MyTask(), 120000);
return 0;
}
private class MyTask extends TimerTask{
#Override
public void run() {
Intent intent = new Intent(MainService.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
mHandler.sendEmptyMessage(0);
}
}
private static class MyHandler extends Handler{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("", "INSIDE handleMEssage");
//stopSelf();
}
}
First it was giving me a warning that if handler class is not static it will cause leaks
After I made it static, stopSelf() can not be called, because its non static.
Is my approach correct or is there a simpler way around ?
you should use IntentService rather service. It starts automatically in separate thread and stop itself as task completes.
public class MyService extends IntentService {
public MyService(String name) {
super("");
}
#Override
protected void onHandleIntent(Intent arg0) {
// write your task here no need to create separate thread. And no need to stop.
}
}
Use IntentService its base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
try this,
private static class MyHandler extends Handler{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("", "INSIDE handleMEssage");
MainService.this.stopSelf();;
}
}
I want to detect when the outgoing call has started and ended, and want to start an service when call has started and end the service when call has ended.
Here's the code i got from another question, but don't know where to put the start and stop activity to make it work.
public class CallListener {
private CallStateListener callStateListener = null;
private OutgoingReceiver outgoingReceiver = null;
public CallListener(Context context) {
this.context = context;
callStateListener = new CallStateListener();
outgoingReceiver = new OutgoingReceiver();
}
private class CallStateListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
doSomething1();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
doSomething2();
break;
case TelephonyManager.CALL_STATE_RINGING:
doSomething3();
break;
}
}
}
public class OutgoingReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
doSomething4();
}
}
public void start() {
telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(callStateListener, PhoneStateListener.LISTEN_CALL_STATE);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL);
context.registerReceiver(outgoingReceiver, intentFilter);
}
public void stop() {
telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(callStateListener, PhoneStateListener.LISTEN_NONE);
context.unregisterReceiver(outgoingReceiver);
}}
And here's to add in the service to make it work:
public class MyService extends Service {
private CallListener call = null;
public MyService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
call = new CallListener(getApplicationContext());
call.start();
return(START_STICKY);
}
#Override
public void onDestroy() {
call.stop();
call.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Thanks so much for any help, i may know that this is easy but i just don't know.
Thanks and have a really good day..!! :)
I had already searched how to detect call events like ringing / idle / etc... no success :(
Unfortunately, there is no solution in Android at this time...
This is well explained in this previous question/summary post:
Detecting outgoing call answered on Android
I initialize new Thread in service but when i start service the new one is made and it make my app crash beacause I use camera in it.
How to make that it will be ony one instance of that Thread?
When Thread is closing? If I close service where I made it, it will be also closed?
you could use a lock or a static variable:
private static boolean isThreadRunning;
and then in your service:
if(isThreadRunning)
return;
Thread t=new Thread(new Runnable(){
protected void run(){
isThreadRunning=true;
while(yourcondition){
//your thread code...
}
isThreadRunning=false;
//if you want to start another thread after this one is ended, you should post a message to a handler here and it should start another thread like this
}
});
You can also achieve this using Handler class, which is recommended by Google in thread operations. The code bellow shows generic example how to use it in Service.
public class MyService extends Service{
private final Handler handler = new Handler();
private final static int RUNABLE_WHAT=6558057;
private final static int PEROID=6*1000;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(!handler.hasMessages(RUNABLE_WHAT))
{
handler.sendMessageDelayed(new Worker().extractMessage(), PEROID);
}
return START_STICKY;
}
private class Worker implements Runnable{
#Override
public void run() {
//DO WORK HERE
handler.sendMessageDelayed(new Worker().extractMessage(), PEROID);
}
private Message extractMessage()
{
Message message = Message.obtain(handler, this);
message.what=RUNABLE_WHAT;
return message;
}
}
}