Detect better call states on Android with RINGING, OFFHOOK and IDLE - java

A week ago, for a week I tried to have an app I'm making completely independent detecting call states. I'm on Lollipop 5.1, so I can't use PRECISE_CALL_STATE that exists from Marshmallow upwards. I'm restricted to the usual CALL_STATE_RINGING, OFFHOOK and IDLE.
My extensive research (majority here on StackOverflow) with Google's help made me realize there's possibly no other way of detecting call states without these 3 on Lollipop or lower, or without system permissions on newer Android versions. There are very precise call states on all firmwares. But from a thread here on SO, it seems they can only be used by the current phone app (which I won't replace - will still be the normal Phone app). So no way to use those states in this case, it seems.
I'm also writing this question and answering it right away because I was able to make something which works just fine for me with the app I'm making. If it were a phone app, it would have to be more precise though. But in my case, I don't mind at all.
And as I see so many questions about parts of this question, I decided to put it all in the same question and answer it with something I tried to do and went well enough for me, and hopefully for some others. Feel free to suggest improvements and/or post other solutions!
If I did anything wrong on asking and answering right away please correct me. I never did this before.

With what I wrote on the question in mind, I was forced to use these 3 states, or use the call history. As I wanted the app to be as independent as possible, I tried to make the app detect the PRECISE_CALL_STATEs from itself. Except when there are too many calls. In case they're 3 or more, I must go get the call state of some from the call history (except one case if there are 3 calls, which is explained in the code). I just can't detect calls on hold, sadly.
So far I can detect the following cases:
Incoming call;
Incoming call waiting;
Outgoing call;
Call just lost;
Call lost some time ago;
Call just answered;
Call answered some time ago;
Call just finished;
Call finished some time ago.
And this is the code which I made to make this happen. By the way, as this has some lines already and I found out Stack Exchange network websites have an automatic license, then add this one to it (as I say in my profile), so anyone can do whatever they want with it, including copy-pasting (may or may not take time to understand, since when I did this first without comments, I had a hard time understanding what I had done):
This work marked with CC0 1.0 Universal. To view a copy of this
license, visit https://creativecommons.org/publicdomain/zero/1.0
Hope this is big enough to worry about license issues. Saw about this a week ago and I've began to think when to put license notice of CC0 on code I post. If it's not necessary here, please tell me so I can improve the "detection" next times.
public static final String CALL_PHASE_OUTGOING = "CALL_PHASE_OUTGOING";
public static final String CALL_PHASE_RINGING_NEW = "CALL_PHASE_RINGING_NEW";
public static final String CALL_PHASE_LOST = "CALL_PHASE_LOST";
public static final String CALL_PHASE_LOST_LATE = "CALL_PHASE_LOST_LATE";
public static final String CALL_PHASE_RINGING_WAITING = "CALL_PHASE_RINGING_WAITING";
//public static final String CALL_PHASE_ON_HOLD = "CALL_PHASE_ON_HOLD";
public static final String CALL_PHASE_ANSWERED = "CALL_PHASE_ANSWERED";
public static final String CALL_PHASE_ANSWERED_LATE = "CALL_PHASE_ANSWERED_LATE";
public static final String CALL_PHASE_FINISHED = "CALL_PHASE_FINISHED";
public static final String CALL_PHASE_FINISHED_LATE = "CALL_PHASE_FINISHED_LATE";
public static final String BETTER_CALL_STATE_OUTGOING = "BETTER_CALL_STATE_OUTGOING";
public static final String BETTER_CALL_STATE_INCOMING = "BETTER_CALL_STATE_INCOMING";
public static final String BETTER_CALL_STATE_WAITING = "BETTER_CALL_STATE_WAITING";
public static final String BETTER_CALL_STATE_DISCONNECTED = "BETTER_CALL_STATE_FINISHED";
//public static final String BETTER_CALL_STATE_ON_HOLD = "BETTER_CALL_STATE_ON_HOLD";
public static final String BETTER_CALL_STATE_ACTIVE = "BETTER_CALL_STATE_ACTIVE";
/**
* <p>This gets the phase of the call when a new phone state is detected (RINGING, OFFHOOK, or IDLE).</p>
* <p>There are values ending in "_LATE". Those are so because they're only detected after the end of all calls are over and after the phone gets to IDLE state.
* Which means, they already happened some time ago (1 second, 10 minutes, unpredictable).</p>
* <br>
* <p><b><u>---CONSTANTS---</u></b></p>
* <p>- <u>CALL_PHASE_OUTGOING [int]</u> --> returned in case an outgoing call was just started (whether it is answered or not by the other party - it's not possible to detect that easily).</p>
* <p>- <u>CALL_PHASE_RINGING_NEW [int]</u> --> returned in case it's a new incoming call.</p>
* <p>- <u>CALL_PHASE_LOST [int]</u> --> returned in case the call has just been lost.</p>
* <p>- <u>CALL_PHASE_LOST_LATE [int]</u> --> returned in case the call was lost some time ago already.</p>
* <p>- <u>CALL_PHASE_RINGING_WAITING [int]</u> --> returned in case there's a new call which is waiting to be answered (some call is already active).</p>
* <p>- <u>CALL_PHASE_ANSWERED [int]</u> --> returned in case the call has just been answered.</p>
* <p>- <u>CALL_PHASE_ANSWERED_LATE [int]</u> --> returned in case the call was answered some time ago alreaedy.</p>
* <p>- <u>CALL_PHASE_FINISHED [int]</u> --> returned in case the call was just finished (after having been answered - if it wasn't answered, it was LOST or LOST_LATE).</p>
* <p>- <u>CALL_PHASE_FINISHED_LATE [int]</u> --> returned in case the call was finished some time ago already (the same in parenthesis for FINISHED applies here).</p>
* <p><b><u>---CONSTANTS---</u></b></p>
*
* #param context <u>[Context]</u> --> Context of the application.
* #param state <u>[int]</u> --> One of the CALL_STATE in TelephonyManager.
* #param incomingNumber <u>[String]</u> --> Phone number that came with the state change.
* #param calls_state <u>[ArrayList(ArrayList(String))]</u> --> An ArrayList of the indicated type that will have the list of calls currently in processing
* (put empty in the beginning and keep the object where the array was created in memory, so the contents of the array are kept, or save it somewhere,
* but don't give always an empty one - this method handles cleaning it when needed. Just give it empty in the beginning of the app and let the method
* handle it from there).
* #param map_CallLog_to_CALL_PHASE <u>[LinkedHashMap(Integer, String)]</u> --> A map with the TYPEs in CallLog.Call on its keys, and on its valus, the corresponding CALL_PHASEs.
* Example: <br><br>map_CallLog_to_CALL_PHASE.put(CallLog.Calls.INCOMING_TYPE, CALL_PHASE_ANSWERED);
* <br>map_CallLog_to_CALL_PHASE.put(CallLog.Calls.MISSED_TYPE, CALL_PHASE_LOST).
*
* #return <u>[ String[][] ]</u> --> A double array of Strings in which each element contains the number and the phase call (CALL_PHASE) in which the number is currently in.
* There can be more than one event in a state change. It may be understood that a call had already been finished some time ago, or lost some time ago.
* Though, the events will always be in the actual event order. If a call was lost before another was answered, then the order will be exactly that one and not the opposite.
*/
public static String[][] get_call_phase(Context context, int state, String incomingNumber, ArrayList<ArrayList<String>> calls_state, LinkedHashMap<Integer, String> map_CallLog_to_CALL_PHASE) {
switch (state) {
case (CALL_STATE_RINGING): {
//System.out.println("RINGING - " + incomingNumber);
// New incoming call (there are no calls in the current processing call list).
if (calls_state.size() == 0) { // Which means, was in IDLE.
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(incomingNumber);
arrayList.add(BETTER_CALL_STATE_INCOMING);
calls_state.add(arrayList);
System.out.println(CALL_PHASE_RINGING_NEW + " -> " + incomingNumber);
return new String[][]{new String[]{incomingNumber, CALL_PHASE_RINGING_NEW}};
} else {
// New incoming call waiting
for (int i = 0; i < calls_state.size(); i++) {
if (calls_state.get(i).get(1).equals(BETTER_CALL_STATE_ACTIVE)) {
// If any call was already active and another one came, then that other one is waiting to be answered.
// This also works with 3 calls, even on case 8, since the state of the 1st call only changes on IDLE.
// Until then it remains ACTIVE, even having been already disconnected (don't know a way to detect it was disconnected).
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(incomingNumber);
arrayList.add(BETTER_CALL_STATE_WAITING);
calls_state.add(arrayList);
System.out.println(CALL_PHASE_RINGING_WAITING + " -> " + incomingNumber);
return new String[][]{new String[]{incomingNumber, CALL_PHASE_RINGING_WAITING}};
//break;
}
}
}
break;
}
case (CALL_STATE_OFFHOOK): {
//System.out.println("OFFHOOK - " + incomingNumber);
String[] to_return = null;
/*if (calls_state.size() == 0) {
// If there are no calls in processing (for example, the app was started with at least one call already in course), abort and do nothing at all.
// Can't have this here... Or it won't detect an outgoing call, which puts the phone in this state in the beginning.
break;
}*/
// Check if it's an outgoing call.
if (calls_state.size() == 0) { // Ou seja, estava em IDLE.
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(incomingNumber);
arrayList.add(BETTER_CALL_STATE_OUTGOING);
calls_state.add(arrayList);
System.out.println(CALL_PHASE_OUTGOING + " -> " + incomingNumber);
to_return = new String[]{incomingNumber, CALL_PHASE_OUTGOING};
} else {
// Check if the 1st or only call was answered.
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
if (calls_state.get(i).get(1).equals(BETTER_CALL_STATE_INCOMING)) {
// If the number was in INCOMING (not WAITING, because I don't know how to detect a call waiting that is answered)
// and we are now in the OFFHOOK state, then the call was answered.
calls_state.get(i).set(1, BETTER_CALL_STATE_ACTIVE);
System.out.println(CALL_PHASE_ANSWERED + " -> " + incomingNumber);
return new String[][]{new String[]{incomingNumber, CALL_PHASE_ANSWERED}};
}
}
}
}
// Add the number to the list with the state OFFHOOK, or update the state in case the number is already on the list.
// This is for in case the cases above don't apply --> WAITING to OFFHOOK (don't know what to do with that - can't be rejected or answered).
// Then in that case, I leave the state CALL_STATE_OFFHOOK on the list.
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
calls_state.get(i).set(1, String.valueOf(CALL_STATE_OFFHOOK));
break;
}
}
return new String[][]{to_return};
//break;
}
case (CALL_STATE_IDLE): {
//System.out.println("IDLE - " + incomingNumber);
ArrayList<String[]> final_return = new ArrayList<>();
if (calls_state.size() == 0) {
// If there are no calls in processing (for example, the app was started with at least one call already in course), abort and do nothing at all.
break;
}
System.out.println("Aqui:");
for (int i = 0; i < calls_state.size(); i++) {
System.out.println(calls_state.get(i).get(0) + " | " + calls_state.get(i).get(1));
}
//////////////////////////////////////
// Beginning of the LATE events
// We begin by the LATE events for the correct order to go inthe return array of all events, the closes to reality possible.
// Bellow is the handling of all numbers that didn't came with the state IDLE. Only one can come with the state and is the one for which the call was finished right now or lost right now.
// The other would get no treatment. Therefore, this tried to understand what may have happened. All here will be of the LATE type because of exactly that (what happened --> past).
// If it's more than a call, we can apply a "trick" to know the state of the first and of the last - answered or lost. This is an example of the cases 1 and 6 (2 calls) and 7 to 10 (3 calls).
if (calls_state.size() > 1) {
// If the first call would have been lost, this would have gone to IDLE directly, and the list would have gotten empty. Then the call coming next would
// be the first call again. If there is a 2nd, the 1st must have been answered. And if it was answered, it was finished in some moment.
// In case the 1st call wasn't the one that came in IDLE, then it was finished some time ago already.
if (!calls_state.get(0).get(0).equals(incomingNumber)) {
// In 2 calls, if the 2nd comes on IDLE, then it means the 1st one was already finished a some time ago (because, again, if the 1st wasn't answered,
// there would be no 2nd). And this sets that state in the call and returns it.
// This can be applied for 3 or more calls too. In that case, if any call not the 1st gets on IDLE, the 1st was already finished some time ago.
System.out.println(CALL_PHASE_FINISHED_LATE + " -> " + calls_state.get(0).get(0));
final_return.add(new String[]{calls_state.get(0).get(0), CALL_PHASE_FINISHED_LATE});
calls_state.get(0).set(1, BETTER_CALL_STATE_DISCONNECTED);
}
}
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
if (!(calls_state.get(i).get(1).equals(BETTER_CALL_STATE_INCOMING) || calls_state.get(i).get(1).equals(BETTER_CALL_STATE_WAITING))) {
// In case the call didn't come from INCOMING or WAITING, then check if it was answered some time ago or not.
// Which means, if it wasn't detected the call was answered in the right moment (so it's not in ACTIVE state)...
if (!calls_state.get(i).get(1).equals(BETTER_CALL_STATE_ACTIVE)) {
System.out.println(CALL_PHASE_ANSWERED_LATE + " -> " + calls_state.get(i).get(0));
final_return.add(new String[]{calls_state.get(i).get(0), CALL_PHASE_ANSWERED_LATE}); // ... then it was answered some time ago already.
}
}
break;
}
}
// For 3 or more calls, for all calls in the middle of the 1st and last, it's not possible to know their state without, at least, a way of knowing if any
// ended in the middle or not. There, it would be possible to know that the 2nd one was answered, for example (in the case of 3 calls). But without that,
// there's no way of knowing.
// So, in that case, for the remaining calls, we're forced to go to the phone's call history.
// This unless the case 9 happens. In that case, we can know the state of the 3 calls.
// Sum up: this is done for all calls, except the 1st and last, and the one that got to IDLE. Supposing it's the 2nd in the case of 3 calls, nothing it's done.
// In other cases, the calls that get here, we got get the state from the call history.
// And this is done here to go in the correct order in the return array. After the handling of the 1st call and before the handling of the last call. And on the LATE events.
if (calls_state.size() >= 3) {
for (int i = 1; i < calls_state.size()-1; i++) {
if (calls_state.get(i).get(0).equals(incomingNumber)) {
continue;
}
int tipo_chamada = obter_tipo_ultima_chamada_numero(context, calls_state.get(i).get(0));
if (tipo_chamada == CallLog.Calls.INCOMING_TYPE || tipo_chamada == CallLog.Calls.MISSED_TYPE) {
System.out.println(map_CallLog_to_CALL_PHASE.get(tipo_chamada) + " -> " + calls_state.get(calls_state.size() - 1).get(0));
final_return.add(new String[]{calls_state.get(calls_state.size() - 1).get(0), map_CallLog_to_CALL_PHASE.get(tipo_chamada)});
}
calls_state.get(calls_state.size() - 1).set(1, BETTER_CALL_STATE_DISCONNECTED);
}
// TODO Imagine the same person calls, gives up, calls again, I hang up who I was talking with and answered this person.
// This will detect the state as the last one (answered), when I didn't answered the 1st time.
// This should be in real-time detecting the exact states from the call history if it can't get them directly, but I don't have time to think on that.
// In the app I'm making (an assistant), I don't need all the states when the happen exactly. Only missed calls in the end and inoming calls in the beginning.
}
if (calls_state.size() > 1 && !calls_state.get(calls_state.size() - 1).get(0).equals(incomingNumber)) {
// The detection of the last call, in case it was lost some time ago, works both for 2 as for any other superior number of calls.
// For only one call it's not necessary, because on that case, we know exactly when it's lost.
// PS: The call that got to IDLE is never LOST_LATE - either lost right now or finished right now.
// If the last call on the list got the OFFHOOK state (which means, without knowing if it was answered or lost in the right moment),
// and didn't get here on IDLE, then by the cases 1 and 6 for 2 calls, and by the cases 7 to 10 for 3 calls, it wasn't answered either,
// or it would have been that call getting to IDLE itself.
// Getting the 1st one here on IDLE in the case of 2 calls or any other call in the case of 3 calls, then this last one was lost some time ago.
if (calls_state.get(calls_state.size() - 1).get(1).equals(String.valueOf(CALL_STATE_OFFHOOK))) {
System.out.println(CALL_PHASE_LOST_LATE + " -> " + calls_state.get(calls_state.size() - 1).get(0));
final_return.add(new String[]{calls_state.get(calls_state.size() - 1).get(0), CALL_PHASE_LOST_LATE});
calls_state.get(calls_state.size() - 1).set(1, BETTER_CALL_STATE_DISCONNECTED);
}
}
// End of LATE events
//////////////////////////////////////
// Now processing of the immediate events, so they get all in order (the late ones happened before the immediate ones).
for (int i = 0; i < calls_state.size(); i++) {
if (PhoneNumberUtils.compareStrictly(calls_state.get(i).get(0), incomingNumber)) {
if (calls_state.get(i).get(1).equals(BETTER_CALL_STATE_INCOMING) || calls_state.get(i).get(1).equals(BETTER_CALL_STATE_WAITING)) {
// If it came directly from INCOMING or WAITING states to IDLE, then the call was lost right now.
System.out.println(CALL_PHASE_LOST + " -> " + incomingNumber);
final_return.add(new String[]{incomingNumber, CALL_PHASE_LOST});
} else {
// If the state is not INCOMING or WAITING, this in case will be OFFHOOK or ANSWERED. Which means, the call was finished right now (means was alreaedy answered some ago - or would have been lost).
// In no case where a call goes from OFFHOOK to IDLE means the call was lost some time ago, from the testing. So the only option is the call having been finished, or lost, right now (which is handled on the above IF).
System.out.println(CALL_PHASE_FINISHED + " -> " + incomingNumber);
final_return.add(new String[]{incomingNumber, CALL_PHASE_FINISHED});
}
calls_state.get(i).set(1, BETTER_CALL_STATE_DISCONNECTED);
break;
}
}
calls_state.clear();
return final_return.toArray(new String[0][0]);
//break;
}
}
return null;
}
I call this from inside TelephonyManager.listen() method, when the phone state changes. That method I have in a constructor of a class which is instanciated on a service that never stops and it's the main service of the entire app. Basically, this object is always on memory. Read method description there to know what to do with it. Hopefully I explained decently. If I didn't, please tell me what I can explain better.
If anyone has a better solution, please feel free to share! I'd appreciate it! A better way than this one that I see is on every phone state change, go see if there was a change in the call history, depending on which change it was. But that would take even more time from me and I don't have enough for that. A week for this was already too much, since I had to borrow my mother, father and brother's phones, and sometimes the house phone to get to this hahaha. And they're not home all the time.
I'll leave a GitHub link here for the rest of what is needed to understand the code completely. I talk there about case number X and Y, for example. That's here: https://github.com/DADi590/Detect-better-call-states-on-Android, along with ideas that I had and didn't work or that I had but will not implement for lack of time.

Related

read mutiple characteristic from BLE and reading a string

So I have two question.
Let's start with the first one, how do you make two readCharacteristic after eachothers? the code I've showed is what I was thinking you could do it. But because onCharacteristicRead isn't called yet in the first readCharacteristic call the next readCharacteristic isn't triggered. Here i solved it by calling the second readCharacteristic in the if-statement for the first readCharacteristic in the onCharacteristicRead, but i don't know it this is normal/stupid solution?
public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService mBluetoothGattService = gatt.getService(UUID.fromString(CSUuid));
if (mBluetoothGattService != null) {
Log.i(TAG, "Connection State: Service characteristic UUID found: " + mBluetoothGattService.getUuid().toString());
mCharacterisitc = mBluetoothGattService.getCharacteristic(UUID.fromString(UuidRead));
mCharacterisitc2 = mBluetoothGattService.getCharacteristic(UUID.fromString(UuidRead2));
Log.w(TAG, "Connection State 1: mCharacterisitc " + mCharacterisitc + " " + mCharacterisitc2);
readCharacteristic(gatt, mCharacterisitc);
//I know I have to wait for the above is done, but can I do it here instead of
//calling the line under in onCharacteristicRead?
readCharacteristic(gatt, mCharacterisitc2);
} else {
Log.i(TAG, "Connection State: Service characteristic not found for UUID: " + UuidRead);
}
}
}
Next question is a bit hard I think?
the code is made in PSoC creator 4.3
So at the moment I read a single int from my PSoC 6 BLE device, and another letter 'M' converted to a integer and back to a 'M' on the app-side. The reason I only read a SIGNLE 'M' is because I don't know how to send a whole string like 'Made it'. I think the issue I'm having is on the PSoC side where I don't know how to read a whole string.
for(;;)
{
/* Place your application code here. https://www.youtube.com/watch?v=Aeip0hkc4YE*/
cy_stc_ble_gatt_handle_value_pair_t serviceHandle;
cy_stc_ble_gatt_value_t serviceData;
//this is the variables I've declared earlier in the code
//static uint8 data[1] = {0};
//static char * ValStr;
//here I just have a simple Integer which count up every sec
serviceData.val = (uint8*)data;
serviceData.len = 1;
serviceHandle.attrHandle = CY_BLE_CUSTOM_SERVICE_DEVICE_OUTBOUND_CHAR_HANDLE;
serviceHandle.value = serviceData;
Cy_BLE_GATTS_WriteAttributeValueLocal(&serviceHandle); //sending the data to -> OUTBOUND
//this part should probably not be in a for-loop, but for now it is.
ValStr = "Mads Sander Hoegstrup"; //I want read whole string on my android APP
serviceData.val = (uint8*) ValStr; //this only takes the 'M' and thats the only variable I can read from my APP not the rest of the string
serviceData.len = 1; //Does not help to increase, if it's more than 1 I read 0 and not a letter
serviceHandle.attrHandle = CY_BLE_CUSTOM_SERVICE_DEVICE_OUTBOUND_2_CHAR_HANDLE;
serviceHandle.value = serviceData;
Cy_BLE_GATTS_WriteAttributeValueLocal(&serviceHandle); //sending the data to -> OUTBOUND_2
data[0]++;
CyDelay(1000);
}
Here you can see that I revice the right values, a Integer and a String, but only the letter 'M' and not the string 'Mads Sander Hoegstrup'
Just ask if you want more information
You'd better ask two separate questions, since they have nothing to do with each other.
I'll answer the first question. You cannot wait inside the onServicesDiscovered method between the two reads. Even if you wait for 30 seconds it will not work. The reason is that only one thread can run a callback on each BluetoothGatt object at the same time, and it's the caller of onCharacteristicRead that clears the internal gatt busy flag which otherwise prevents you from submitting another request. You'd better implement some queue mechanism to keep the code cleaner if you like.

Recursion! I'm creating a method that counts back up and then down to a certain number, but I keep getting stuck in an infinite loop

So, I am currently creating a method for an assignment using recursion. I need to take an int, then print going down until it hits 0. After that, I need to print going up until it hits the original number, then stopping. Here's my code so far.
public static void recursivePrinter(int levels)
{
final int start = levels;
if (levels < start ) {
System.out.println("Going up! " + levels);
recursivePrinter(levels + 1);
}
else {
System.out.println("Going down! " + levels);
recursivePrinter(levels - 1);
}
return;
}
You don't reach the return; statement. the code always go in the else statement. to keep track of the starting number you could use a global variable . also you need to add a condition where the recursion should finish. so you can try some thing like this :
static int start = 10;
public static void recursivePrinter(int levels)
{
if (levels < start ) {
System.out.println("Going up! " + levels);
recursivePrinter(levels + 1);
}
else {
System.out.println("Going down! " + levels);
// recursivePrinter(levels - 1);
start-- ;
}
return;
}
In an attempt to provide a meaningful answer to help future visitors (as opposed to the comment thread on the question above)...
The initial problem was two-fold:
The method had no condition in which it doesn't recursively call itself. Which results in an infinite recursion. There must always be some condition by which the method stops recursion.
The method was locally storing a value that it doesn't need, and the logic was incorrectly assuming that value won't be different for each call to the method.
Essentially, a recursive method almost always follows a basic structure:
method(argument) {
terminating condition;
state change or method action;
recursive call;
}
Depending on the state change or the method action, this can be a bit more complex. But the basic components are generally always there in one form or another.
In your case, the argument is an integer, the terminating condition is testing whether that integer is a known value, the state change is changing the integer, the method action is printing the integer, and the recursive call is invoking the method with the new integer.
Based on your comment above:
It's supposed to count down from 3 (3, 2, 1) and then back up to 3 (1, 2, 3).
Consider the following pseudo-code (so as to not do your homework for you) structure:
myMethod(level) {
// terminating condition
if level is 0
return
// method action
print level
// recurse
myMethod(level - 1)
}
This would be a great time to step through the code in your debugger and see what a recursive method call actually does. Each time the method is invoked, it's an isolated action unaware of any other invocations of the method. It's "building a stack" of calls to itself. When the terminating condition is reached, that stack will "unwind" and those calls will all return to each other in reverse order.
Given this, printing the numbers "counting back up" is a natural result of just printing it again in the method:
myMethod(level) {
// terminating condition
if level is 0
return
// method action
print level
// recurse
myMethod(level - 1)
// more method action
print level
}
That last operation simply prints the value a second time. But it does so after the recursive call, therefore after all printing of lower numbers done within that recursive call, regardless of how many there are.

sleep in Java web application

I want to use Thread.sleep() in my java application. But does not work. Program works after removing sleep.
In my program I am running multiple threads and want that each move forward at a variable speed. Some may get executed more some less. So I am using sleep in each with a random number as argument.
If there another way to do this. Without using sleep.
Here is the part where I am using the sleep function.
public void run()
{
Random r = new Random();
int t;
while(true)
{
if(total == 1)
{
// win();
break;
}
if(doa == 1)
break;
// Player x = e[r.nextInt(20)%2];
Player x = choose();
x.attack(this, 10 + (power==1?5:0));
if(r.nextInt(100)%(5 - (power==2?2:0)) == 0)
System.out.println(" " + name + " used Potion effect (" + potionno++ + ") .. now " + name + "'s Health is " + (h+= 10 + r.nextInt(20)));
try
{
sleep(50 + r.nextInt(1000));
}
catch(InterruptedException c)
{ ; }
}
if(doa == 1)) {
// and so on
.
.
and here is my doGet function used for initiation
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String s;
s = request.getParameter("name");
try{
Player.out = out;
Player.e[0] = new Player("Kartik",2);
Player.e[1] = new Player(s,1);
Player.e[2] = new Player("Anirudh",3);
Player.e[3] = new Player("Vinita");
Player.e[4] = new Player("Shivank");
for(Player p: Player. e)
p.start();
}
catch(Exception e)
{
out.print("WRONG");
}
}
You shouldn't do that since, in general, Java EE/servlet containers work on the assumption their applications do not spawn threads (or modify the execution or configuration of existing ones, which you are doing with Thread.sleep()) of their own all nilly willy.
It is possible, but generally frowned upon unless you know what you are doing. See this answer that succinctly but excellently explains why: https://stackoverflow.com/a/533847/201722
As of why your call to Thread.sleep() doesn't work, it is because your servlet container is multi-threaded. Your call to Thread.sleep() is simply putting the thread that is handling your current HTTP request to sleep. But the container is still alive and kicking. If you send another HTTP request, it will grab another thread distinct from the one you put to sleep to handle it.
So, from your POV, it looks like it is not working. But it is working, you put the poor thread to sleep, and the container goes ok, here is another one for you. It just so happens you don't know what the heck is going on.
I would suggest you take the time to go through both the Java and the Java EE tutorials made available by Oracle (former Sun.) Google it and you will find it.
== EDIT ==
I would also recommend the OP to read the following succinct explanation against indiscriminately meddling with threads in a container.
http://www.psionicwave.com/blog/2012/12/15/threading-in-web-containers/
Many things are wrong with your current approach, I'll try to point out some.
Your Player apparently extends Thread. That's an antipattern; you should only implements Runnable and pass the instance of your class to new Thread();
the basic flavor of a Servlet-based Web application is based on a strict request-response paradigm, where the respone happens as soon as possible. What you (possibly) are looking for is a "long response", asynchronous style. This can be achieved with new features in Servlet 3.0, but is well beyond the scope of this answer;
assuming for a moment that you just want a go at it, a quick patch is to append
for (Player p : Player.e) p.join();
to your existing doGet method. This will postpone the returning of doGet until all your subthreads die. You will also need to routinely flush the writer to force the immediate sending of the data to the client side (or use PrintWriter#println, which has auto-flush semantics).

wait and notifyAll mechanism is not working as expected when the threads waiting on the Vector object is greater than 6

There is a requirement in my application wherein 'N' number of Products could be associated with a Quote. The screen layout will have two portions. Top portion has a form that contains quote related information and the bottom portion is meant to hold multiple products. I implemented this functionality by having iframes in the bottom portion. The product will be added/deleted on click of buttons (using javascript). The content to be displayed inside every Product window will be rendered by the same Action (ProductLinesAction.java), JSP (ProductLines.jsp) and other associated resources. The point here is multiple instance of that Action class will be created whenever a new Product window is loaded on the screen. I have no issues in loading the window as it just prepares the form to be displayed. While the quote is to be saved, all these product forms will be submitted and the logic that I incorporated is that 1 to N-1 action instances will put the form values in a VO which is added to a Vector object and saved in session (so that other action instance can take it from session and add on top of it). The N th action instance is meant to save the all these product values collectively. The business rule validation is also performed just before saving and hence the Nth action instance will be made available with the errors which should be displayed in each and every Product window.
Just to make sure that all other action instances also avail the errors corresponding to their window, I implemented the wait and notifyAll mechanism wherein things go awry when more than 6 products are tried to be saved. The code is given below. This piece of code works just fine for products less that or equal to 6 (I mean upto 6 action instances). When the 7th product is loaded and saved, the seventh instance is not at all visible or traceable in debug mode (instance does not reach the intended method upon form submission).
Could anyone throw some light on the mistake that is committed here which is responsible for this issue.
public String submitProducts()
throws Exception {
String resultValue = "";
/* Algorithm: */
// 1. Read the Vector object from Session.
// 2. Check whether the size of the Vector matches the Total Product windows count.
// 3. If yes, call the Save operation and remove the list from session.
// 4. If not, copy the values from current Action instance to VO.
// 5. Add to List object and place in session.
synchronized (productVOsInVector) {
productVOsInVector = getProductVOVectorFromSession();
if (productVOsInVector == null) {
productVOsInVector = new Vector <ProductVO>();
}
log.info("Window Number is " + activeWindowNumber + ". List size is " + productVOsInVector.size());
if (productVOsInVector.size() == (prodWindowCount - 1)) {
productVOsInVector = mapActionToVO(productVOsInVector);
resultValue = saveOperation(productVOsInVector);
if (resultValue.equalsIgnoreCase(SUCCESS)) {
session.put("OperationStatus", SUCCESS);
}
session.remove("productVOMapData");
}
else {
if (quoteSaveStatus) {
quoteSaveStatus = false;
}
session.put("OperationStatus", "");
productVOsInVector = mapActionToVO(productVOsInVector);
session.put("productVOMapData", productVOsInVector);
}
waitForOperationStatus();
}
System.out.println("Came out of sync block");
System.out.println("Action Instance" + activeWindowNumber + " is resuming.");
// Code to display the Error messages
return resultValue;
}
public void waitForOperationStatus() {
String opStatus = getOperationStatusFromSession();
synchronized (productVOsInVector) {
if (!opStatus.equalsIgnoreCase(SUCCESS)) {
try {
System.out.println("Window # " + activeWindowNumber + " Waiting");
productVOsInVector.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
opStatus = getOperationStatusFromSession();
}
productVOsInVector.notifyAll();
}
}
The following is already, by itself, a serious problem:
synchronized (productVOsInVector) {
productVOsInVector = getProductVOVectorFromSession();
if (productVOsInVector == null) {
productVOsInVector = new Vector <ProductVO>();
}
...
}
You're synchronizing on an object referenced by productVOsInVector, and immediately make the reference point to another object. The next thread will thus synchronize on a different object than the first one.
Then you're waiting on this object, and hope that someone will notify you.
I haven't analyzed more than that, but you have a serious design problem. You shouldn't synchronize between threads of the servlet container in the first place. If you have only 6 threads in the pool, and they're all waiting for the 7th one to complete, you have a deadlock. If you have 12 threads in the pool, and 2 clients do that at the same time, you have a deadlock as well. And even if you don't have a deadlock, you make several threads unavailable, doing nothing but wait, hoping that a subsequent HTTP request will notify them. If the last request doesn't come for a reason or another (the user kills his browser, for example, you have 6 threads blocked forever.
My advice is thus: don't mess with threads. Find another way.

Text file validation

I am trying to validate some text files. In the front end i am using JTextarea, The below method is called on every time the user enter 'Enter' key. If the file is too big,say 5000 lines and if the user enters many times 'Enter' key then, i am getting unexpected results, like even if the line is valid, it shows it as invalid.
Is there any thing to do with sleep, should i have to increase the sleep time or something else has to be done? Any ideas will be helpful
private TreeSet validate(int curLine, TreeSet errorSet) {
int increment = 0;
int nextLine = 0;
if (curLine == lines.length || errorSet.size() != 0) {
return errorSet;
} else {
String line = lines[curLine];
//validation starts. After validation, line is incremented as per the requirements
increment = 1 //As per requirement. Depends on validation results of the line
if (increment > 0) {
try{
Thread.currentThread().sleep(100);
}catch(Exception ex){
System.out.println(ex);
}
nextLine = (curLine + increment);
validate(nextLine, errorSet);
}
}
return errorSet;
}
I wouldnt look at making the sleep time any longer/shorter. Instead, I would consider doing a better time at marshalling the trigger to validate. Is there any reason to allow the input of a validation request while one is in progress? if not, i would look at blocking the call to validate while a current process is still not complete.
If you think that multiple validations should be able to occur in tandem, I would then look to the creation of a thread pool for these actions. Testing my determine how many threads can concurrently run, and therefore determine the size of your threadpool. At this point, system memory may also play an important point, so you may want to look at those statistics while testing as well.

Categories

Resources