ActivityMonitor getHits() doesn't work - java

I tried this test but getHits() always return 0. Anyone can help me?
public void testSettingsAboutShazamClickOnLink() {
Instrumentation inst = getInstrumentation();
IntentFilter intentFilter = new IntentFilter(android.content.Intent.ACTION_SENDTO);
intentFilter.addDataScheme("mailto");
ActivityMonitor monitor = inst.addMonitor(intentFilter, null, false);
final Intent emailIntent = new Intent(android.content.Intent.ACTION_SENDTO);
emailIntent.setData(Uri.parse("mailto:"));
emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
assertEquals(0, monitor.getHits());
inst.getContext().startActivity(emailIntent);
monitor.waitForActivityWithTimeout(5000);
assertEquals(1, monitor.getHits());
inst.removeMonitor(monitor);
}

I solved this problem. I used Solo of Robotium and apparently Solo inserts an ActivityMonitor that matches every Filter, so when you call startActivity, it calls execStartActivity in the Instrumentation class, whose code is
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
As soon as there is an ActivityMonitor that matches something the mHits of that monitor is increased end the "cycle for" breaks. Given that calling Solo was the first thing I did, the first ActivityMonitor to check is the one of Solo, given that this matches everything, any other ActivityMonitors added after it is not checked, so no mHits variable is increased for the other monitors. If you want to understand more see the execStartMonitor method of Instrumentation class, remembering that this method is called after startActivity is called.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/app/Instrumentation.java#Instrumentation.execStartActivity%28android.content.Context%2Candroid.os.IBinder%2Candroid.os.IBinder%2Candroid.app.Activity%2Candroid.content.Intent%2Cint%29

Related

How to detect a switch in data and do something?

So i have this code below:
public static String getBSSID(Context context){
WifiManager wifiManager;
List<ScanResult> results;
List<String> ids = new ArrayList<>();
wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
String essidPrefix = MainFragment.configuration.getEssidPrefix();
String bssid = null;
results = wifiManager.getScanResults();
for(ScanResult scanResult : results){
String scanWifi = scanResult.SSID;
if(scanWifi.startsWith(essidPrefix)){
ids.add(scanResult.BSSID);
bssid = TextUtils.join(",",ids);
}
}
return bssid;
When i detect a certain Wifi BSSID, it will return its BSSID, when it doesnt detect a certain Wifi BSSID, it returns null, what i want to do is when i detect a change from null to not null in BSSID , do something, how should i construct this?
EDITS
public static String getBSSID(Context context){
final Configuration configuration = new Configuration();
final String essidPrefix = MainFragment.configuration.getEssidPrefix();
final WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if(wifiManager != null){
wifiManager.startScan();
}
final IntentFilter filter = new IntentFilter();
filter.addAction(wifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
final List<String> ids = new ArrayList<>();
final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
ids.clear();
results = wifiManager.getScanResults();
for(ScanResult scanResult : results){
String scanSSID = scanResult.SSID;
if(scanSSID.startsWith(essidPrefix)){
ids.add(scanResult.BSSID);
configuration.setScanBssid(TextUtils.join(",",ids));
}
}
}
};
if(wifiManager != null){
context.registerReceiver(receiver,filter);
}
return configuration.getScanBssid();
}
First, you have to bear in mind that in order to detect something changed you will have to keep a record of how it was before, to compare.
You can store a previous result from your method and compare. It would help you if you directly returned the list. Also, consider that a Set would help you quickly find what has been added new or removed.
Finally, if you're planning to stick to returning the comma separated String, use TextUtils.join outside of the for loop; you'll see the result is the same, however, more efficient.
I think your approach is wrong. You should use BroadcastReceiver in order to handle updated list of the WiFi access points. Once you call wifiManager.getScanResults(), then you cannot be sure if it's null or not. You should update list of the acces points, once you get it from the device. Check out the code snippet below.
final WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
if(wifiManager != null) {
wifiManager.startScan();
}
final IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
final List<ScanResult> accessPoints = new ArrayList<>();
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override public void onReceive(Context context, Intent intent) {
// you can handle list of the access points here...
accessPoints.clear();
accessPoints.addAll(wifiManager.getScanResults());
// optionally you can call wifiManager.startScan()
// to continuosly receive updated list of the access points
}
}
if(wifiManager != null) {
context.registerReceiver(receiver, filter);
} else {
// optionally log error here...
}
I haven't tested if it code works, but I think it should. At least you have general idea of the approach here. You can adjust it to your needs by playing around with the List depending on the result you want to achieve (e.g. add new APs only, collect everything what your received and then filter it, etc.).
You can also check my library, which helps to solve this problem:
https://github.com/pwittchen/ReactiveWiFi/

Dial multiple numbers one by one programmatically | Android

I am trying to develop a feature for an app where you have a list of "codes" that you enter and that are dialed one by one. I have looked over TelephonyManager and followed a tutorial on developing a broadcast receiver with a listener for responses but it does not always work as it should.
One idea was to store all the numbers necessary in SharedPref. If the Activity (only created for the intent towards dialer) went into onStop() (meaning that above, the dialer screen was on) and then into onResume() (call ended and activity resumed), I would remove the number last dialed from the SharedPref and then, if any remained, open the dialer again. The broadcast made sure than once the state flow of the TelephonyManager was "OFFHOOK => IDLE", it would return the user to the Activity . Short story, it did not always perform as it should have.
How should I tackle the problem ?
EDIT
My curent solution was to
Create a doPhoneCall() function that would handle the intent creation and deployment itself.
#Override
protected void doPhoneCall(){
super.onResume();
wentIntoCall = false;
/** More code here for dialing */
}
Place this function into the onResume(). Even if the onResume will be called multiple times, the wentIntoCall boolean will make sure that the function won't be called multiple times.
#Override
protected void onResume() {
super.onResume();
if(wentIntoCall)
doPhoneCall();
}
Having in mind that after a call, the phone should return to its previous state, so it would return to the Activity in which we are doing are call, we will add to the activity a CallListener, and in the case of IDLE, based on the tutorial linked above, we make the wentIntoCall be true. ( The activity will go into onResume() and, upon seeing that the boolean is true, it will initialize the next call ).
case TelephonyManager.CALL_STATE_IDLE:
Log.e(TAG, "CALL_STATE_IDLE==>"+incoming_number);
if((prev_state == TelephonyManager.CALL_STATE_OFFHOOK)){
prev_state=state;
wentIntoCall = true;
//Answered Call which is ended
}
if((prev_state == TelephonyManager.CALL_STATE_RINGING)){
prev_state=state;
wentIntoCall = true;
//Rejected or Missed call
}
My final question : is this the right way to handle this functionality, or should I try to come up with another implementation of it ?
EDIT 2
Looks like my "codes", being USSD codes, are not behaving like normal phone calls.. So for normal phone calls the code above seems to work, but for dialing codes, not that much. I have "downgraded" my solution to a simple for-loop. Seems to be working fine now.
I dont know for android O , but for android 6.0 > You cant detect answer in direct way . Call no exist number and see PhoneStateListener what will trigger in one case and track successed call also.
Make public static array , add all your numbers intro array .
I made services . Insert permissions in manifest make your own action also ( NEXT_CALL for example ) .
Than easy make intent for startServices :
SharedPreferences settings;
SharedPreferences.Editor SAVES;
Intent serviceIntent = new Intent(MainActivity.this, ServiceForCalls.class);
serviceIntent.setAction("xxx.xxx.NEXT_CALL");
startService(serviceIntent);
isCalling = true;
SAVES.putBoolean( "isCalling" , isCalling );
SAVES.commit();
SAVES.apply();
You must use timeout interval about 10 sec for next call.
Heres little help func - end call and phoneState handler :
void END_CALL () throws InvocationTargetException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException {
tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
Class c = null;
try {
c = Class.forName(tm.getClass().getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method m = null;
try {
m = c.getDeclaredMethod("getITelephony");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
m.setAccessible(true);
Object telephonyService = m.invoke(tm); // Get the internal ITelephony object
c = Class.forName(telephonyService.getClass().getName()); // Get its class
m = c.getDeclaredMethod("endCall"); // Get the "endCall()" method
m.setAccessible(true); // Make it accessible
m.invoke(telephonyService); // invoke endCall()
if ( SIGNAL_STOP == false ) {
timerHandlerServicesStartNewNumber.postDelayed(timerRunnableServicesStartNewNumber, 1000);
}
}
private class PhoneStateChangeListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch(state){
case TelephonyManager.CALL_STATE_RINGING:
Log.println( Log.INFO , "RINGING" , "SERVICES%%%%%%%%%%%%%%%%RINGING%%%%%%%%%%%%%%%%%%");
wasRinging = true;
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.println( Log.INFO , "OFFHOOK BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%%%%%%%%");
if (!wasRinging) {
// Start your new activity
Log.println( Log.INFO , "OFFHOOK BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%%%%%%%");
if (SIGNAL_STOP == false) {
timerHandlerServices.postDelayed(timerRunnableServices, 10000);
}
} else {
// Cancel your old activity
Log.println( Log.INFO , "OFFHOOK BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
}
// this should be the last piece of code before the break
wasRinging = true;
break;
case TelephonyManager.CALL_STATE_IDLE:
Log.println( Log.INFO , "IDLE BROADCAST" , "SERVICES%%%%%%%%%%%%%%%%%IDLE%%%%%%%%%%%%%%%%%%%");
// this should be the last piece of code before the break
wasRinging = false;
break;
}
}
}
Thanks for : "meaning that above, the dialer screen was on" nice catch.
As a side note, in the Android O developer preview there is a new API which allows you to send a USSD request and register a callback to receive its results. For newer versions of Android this might meet your needs better.

boolean value not update in Call Receiver

I have call receiver which I want to display dialog on incoming call only. For that I have created a global Boolean variable and trying to changes its value to true in ringing state. But when call disconnects, code always picks default value of Boolean not the updated value given in ringing state. The variable is num. Why it always give false value though its value getting true in ringing state only. Here is the code:
public class phonerece extends BroadcastReceiver{
private Boolean num = false;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
//some task here
}
} else if (extraState != null) {
if (extraState.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
//task
} else if (extraState
.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
if (num) {
phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
//call dialog }
}
} else if (extraState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
if (checknumber() != null) {
Log.e("Nummber", "found");
} else {
Log.e("Number", "Not Found");
num = true;
}
}
}
}
public String checknumber() {
String res = null;
try {
ContentResolver resolver = context.getContentResolver();
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor c = resolver.query(uri, new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
if (c != null) { // cursor not null means number is found contactsTable
if (c.moveToFirst()) { // so now find the contact Name
res = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
}
c.close();
}
} catch (Exception ex) {
/* Ignore */
}
return res;
}
}
You should use static variables (private static num = false) or save your variable in SharedPreferences (it's better), because BroadcastReceivers are not saved between broadcasts. Every broadcast will create a new instance of the BroadcastReceiver, at least if registered automatically via the manifest.
(Your code snippet looks broken, the num variable is missing its type? This answer assumes its type is boolean.)
This sounds like a multithreading problem. Threads in java may cache values of variables, because synchronizing through the main memory is more expensive. You can force the synchronization by flagging the field in question as volatile. This keyword is explained here.
When a field is flagged as volatile, Threads may not cache its value, and all modifications to the variable become visible to all other Threads.
private volatile boolean num = false;

Android Wait until Text to Speech OnInit is called

I had an issue where Text to Speech would not speak anything. I realised this was due to the fact that I was attempting to call 'Speak()' before TTS had initialised.
I need to wait until TTS has initialised, so that I can call 'Speak()' successfully. I thought doing something along the lines of this would work:
#Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
mTTSInitialised = true;
} else {
Log.e("TTS", "Initialisation Failed!");
}
}
...
while(!mTTSInitialised){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
But this fails to initialise at all. Is there a way to do this effectively?
The initialisation of the Text to Speech engine is asynchronous, which is why you realised you have to 'wait' for it to complete, before requesting that it processes an utterance.
Even when it eventually initialises successfully, it can be subsequently killed by the system, or it can of course fail to initialise, so you always need to be ready to handle a request to speak, where the engine isn't prepared.
Add the following helper class
public class PendingTTS {
private String pendingUtterance;
private int pendingQueueType;
public String getPendingUtterance() {
return this.pendingUtterance;
}
public void setPendingUtterance(#NonNull final String pendingUtterance) {
this.pendingUtterance = pendingUtterance;
}
public int getPendingQueueType() {
return this.pendingQueueType;
}
public void setPendingQueueType(final int pendingQueueType) {
this.pendingQueueType = pendingQueueType;
}
}
Assuming you're using an Activity, you need to declare the following variables:
private volatile PendingTTS pendingTTS;
private static final int MAX_INIT_ATTEMPTS = 4;
private volatile int initCount;
and initialise the Text to Speech object in onCreate()
tts = new TextToSpeech(YOURActivity.this, YOURonInitListener);
In your onInitListener you would check if there is any pending speech:
#Override
public void onInit(final int status) {
switch (status) {
case TextToSpeech.SUCCESS:
initCount = 0;
// Set up tts stuff
tts.setOnUtteranceProgressListener(YOURprogressListener);
if (pendingTTS != null) {
// We have pending speech, process it and check the result
int speechResult = tts.speak(pendingTTS.getPendingUtterance(),pendingTTS.getPendingQueueType(),
// remaining tts variables here)
switch (speechResult){
case TextToSpeech.SUCCESS:
// Result was successful
pendingTTS = null;
break;
case TextToSpeech.ERROR:
// Speech failed
// Check if it has repeatedly failed up to the max attempts
if(initCount < MAX_INIT_ATTEMPTS){
initCount ++;
tts = new TextToSpeech(YOURActivity.this, YOURonInitListener);
} else {
// Totally broken - let the user know it's not working
}
break;
}
} else {
// there was nothing to process
}
break;
case TextToSpeech.ERROR:
// Check if it has repeatedly failed up to the max attempts
if(initCount < MAX_INIT_ATTEMPTS){
initCount ++;
tts = new TextToSpeech(YOURActivity.this, YOURonInitListener);
} else {
// Totally broken - let the user know it's not working
}
break;
}
I've glued the above together from my code - where the speech and initialisation methods are all separated, but I tried to give you an overview above of everything you need to handle.
Elsewhere in your code, when you make a tts.speak(//stuff here) request, you need to check the result as demonstrated above, to make sure it was successful. Again, in my code, this is separated into one single method. If it does fail, you need to set the PendingTTS parameters prior to attempting to initialise again:
pendingTTS = new PendingTTS();
pendingTTS.setPendingQueueType(// your queue type);
pendingTTS.setPendingUtterance(// your utterance);
It is is successful, make sure pendingTTS is set to null.
The overall design is that if the initialisation failed, it will attempt to initialise again, up to the maximum allowed attempts. If the speech fails, it will attempt to initialise the engine again, firstly setting the PendingTTS parameters.
Hope you managed to follow that.
Hmm..
Not a very good idea.
You can try to add the text to the TTS queue and let it do it's work. This snippet can be inside button click, etc as:
tts.speak(toSpeak, TextToSpeech.QUEUE_ADD, null);
Small tutorial that would help.

Sms ContentObserver onChange() fires multiple times

I know this question has been asked multiple times, but nobody has been able to come up with a working answer from what I have seen.
Im working on an app to intercept text messages and depending on the sending #, pop up with a custom alert. I have it working beautifully with a broadcast receiver, however if the user has goSms installed the onReceive() method is never called as goSms aborts it before it ever reaches my app.
To get around this, Im trying a content observer on content://sms/
Its working just fine, however the onChange() is called twice, with exactly the same parameters. Ive tried to check the time stamps, but they are the same, as is the type and every other parameter I have set.
From what I've seen, this is a common issue, but not one that I've seen answered anywhere.
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
querySMS();
}
protected void querySMS() {
Cursor cur = getContentResolver().query(u, null, null, null, null);
cur.moveToNext(); // this will make it point to the first record, which is the last SMS sent
String type = cur.getString(cur.getColumnIndex("type"));
String body = cur.getString(cur.getColumnIndex("body")); //content of sms
String add = cur.getString(cur.getColumnIndex("address")); //phone num
if (type.equals("1")) {
if (add.equals(Test.SENDER)) {
String[] bodys = body.split(" ", 7);
if (bodys[0].equals("test")) {
test = true;
}
cat = bodys[1];
level = bodys[2];
urgency = bodys[3];
certainty = bodys[4];
carrier = bodys[5];
message = bodys[6];
final Intent intent = new Intent(context, AlertActivity.class);
Bundle b = new Bundle();
b.putString("title", cat);
b.putString("certainty", certainty);
b.putString("urgency", urgency);
b.putString("level", level);
b.putString("message", message);
b.putBoolean("test", test);
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
carrierName = manager.getNetworkOperatorName();
if (carrierName.replaceAll(" ", "").equals(carrier)) {
context.startActivity(intent);
} else {
//testing
Toast.makeText(context, carrierName.replaceAll(" ", ""), Toast.LENGTH_LONG).show();
}
}
}
}
Because of the onChange() being fired twice, Im getting two alerts as well. I cannot for the life of me figure out a way around this.
If the two are identical:
store each message recv'd
compare it to previous messages recv'd
if not found, process
if found, discard the message
The life of the messages stored should be infinitesimal, a little circular buffer of 5 messages should be fine.
here is my code, it works fine for me
public class SmsObserver extends ContentObserver {
private Context context;
private static int initialPos;
private static final String TAG = "SMSContentObserver";
private static final Uri uriSMS = Uri.parse("content://sms/sent");
public SmsObserver(Handler handler, Context ctx) {
super(handler);
context = ctx;
initialPos = getLastMsgId();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
queryLastSentSMS();
}
public int getLastMsgId() {
Cursor cur = context.getContentResolver().query(uriSMS, null, null, null, null);
cur.moveToFirst();
int lastMsgId = cur.getInt(cur.getColumnIndex("_id"));
Log.i(TAG, "Last sent message id: " + String.valueOf(lastMsgId));
return lastMsgId;
}
protected void queryLastSentSMS() {
new Thread(new Runnable() {
#Override
public void run() {
Cursor cur =
context.getContentResolver().query(uriSMS, null, null, null, null);
if (cur.moveToNext()) {
try {
if (initialPos != getLastMsgId()) {
// Here you get the last sms. Do what you want.
String receiver = cur.getString(cur.getColumnIndex("address"));
System.out.println(" Receiver Ph no :"+receiver);
// Then, set initialPos to the current position.
initialPos = getLastMsgId();
}
} catch (Exception e) {
// Treat exception here
}
}
cur.close();
}
}).start();
}
}//End of class SmsObserver
You can save last message's id and compare it to the id of the message that is returned by cur in onChange. you then can simply disregard the message if ids are the same.
// might contain mistakes, but you'll get the idea:
protected void querySMS() {
Cursor cur = getContentResolver().query(u, null, null, null, null);
cur.moveToNext();
if (lastId == cur.getLong(cur.getColumnIndex("_id")))
return;
lastId = cur.getLong(cur.getColumnIndex("_id"));
... //continue as it was
}
However - GO SMS only prevents other app's from recieving Broadcast if the user selected this option (Recieve Settings - Disable other message notification) - so if the user does not want other apps to disturb him - I think it's good idea not to do so.
I just use SharedPreference to remark last SMS info (like: id\type ...). if it is the same, I will return.

Categories

Resources