Continuos Vibrate and Music Alert until Confirmation : update

I had recently thought I solved my issue with creating a continuous vibrate and music alert for the BlackBerry application that I created for work. Unfortunately, when I put the fix on to the phone, the volume level of the music was very low, even though I had the volume set to 100%. So, it was back to the drawing board to try and figure out how to get this working. The main issue was determining if the music had finished and then starting it again until the user pressed a key.

After messing around with NotificationsEngineListener, Event and the Notifications Demo for what seemed like forever and continuing to get thread errors and other issues, it was a simple step back and regroup that helped me see the light. Instead of trying to kick off an event and a listener for the Notification, all I had to do was immediately kick off the Notification.

NotificationsManager.triggerImmediateEvent(bbhAttribute.NOTIFICATION_MESSAGEPAGE, 0, null, null);

I was already checking to see when the music had finished to determine if I needed to stop or continue. So, the simple step was to put the triggerImmediateEvent in the check.

public void audioDone(int reason)
{
	System.out.println("audioDone function");
	
	switch(reason) {
		case REASON_KEY_PRESSED:
			//System.out.println("Key Pressed.  Stopping Alert");
			stopAlert();
			break;
		case  REASON_COMPLETED:
			//System.out.println("Completed Called.  Stopping Alert");
			NotificationsManager.triggerImmediateEvent(bbhAttribute.NOTIFICATION_MESSAGEPAGE, 0, null, null);
			continueAlert();
			break;
		case REASON_STOP_CALLED:
			//System.out.println("Stop Called.  Do Nothing.");
			// Do nothing
			break;
	}
}

Which basically means, the initial music and vibrate will play and then when they are done and the user hasn’t clicked a button to stop it, the Notification will start before the music and vibrate plays again.

So far, this seems to be the working solution. I hope this is the final solution, because it has been a headache. I hope the BB10 method of notify users has something simple for continuous notification.

Continuos Vibrate and Music Alert until Confirmation

The main phone application that I support at work gets data pushed to it. Part of the data is a Message Page to notify the technician that they have a service call assigned or needing attention. Due to the nature of the service calls, the technician needs this alert to continually go off until they acknowledge the alert. A while back I changed the pop up message to a global message, which makes the pop up show on all screens, regardless if the user is in the application.

public static void displayGlobalMessage(final String gMessage)
{
	UiApplication.getUiApplication().invokeLater( new Runnable() {
			public void run() {
				UiEngine uie = Ui.getUiEngine();
				alertDialogListener  closeListener = new alertDialogListener();
				
				Dialog screen = new Dialog(Dialog.D_OK, gMessage, Dialog.OK,
											Bitmap.getPredefinedBitmap(Bitmap.EXCLAMATION),
											Manager.VERTICAL_SCROLL);
				screen.setDialogClosedListener(closeListener);
				uie.pushGlobalScreen(screen, 1, UiEngine.GLOBAL_MODAL);
			}
	});
}

Unfortunately, when I created this option, I some how broke my continuous alert that was previously setup. After many attempts to get it to work again, including using a TimerTask, I finally got the alert to work again.

The first part is creating the AlertListener class that I will use to handled starting the music and vibrate. This was the most important part, because stopping and starting the alert depends on what is to be called. It took a very long time to figure out when to turn off the overall alert (continueAlert) and when to turn on and off the music and vibrate (audioFinished). This is handled in the audioDone function, because the music plays longer than the vibration (which is why nothing is done on vibrateDone and buzzerDone).

public class SDAAlertListener implements AlertListener
{
    private boolean continueAlert;
    private Object continueAlertLock;
    private boolean audioFinished;
    private Object audioFinishedLock;
    
    public SDAAlertListener()
    {
    	//System.out.print("Added Continuos Alert Listener");
        continueAlert = true;
        continueAlertLock = new Object();
        audioFinished = false;
        audioFinishedLock = new Object();
    }
    
    public void startAlert()
    {
    	//System.out.println("Starting Contiuous Alert");
    	SetContinueAlertStatus(true);
    	SetAudioDoneStatus(false); 
    	
    	Alert.startAudio(music.myFogHornTune, 8);
    	Alert.startVibrate(500);
    }
    
    public void continueAlert()
    {
    	//System.out.println("Contiuous Alert to Continue");
    	SetContinueAlertStatus(true);
    	SetAudioDoneStatus(false); 
    	
    	Alert.startAudio(music.myFogHornTune, 8);
    	Alert.startVibrate(500);
    }
    
    public void stopAlert()
    {
    	//System.out.println("Stopping Contiuous Alert");
    	SetAudioDoneStatus(true); 
    	SetContinueAlertStatus(false);
    	
    	Alert.stopAudio();
        Alert.stopBuzzer();
    }
    
    public boolean GetContinueAlertStatus()
    {
        synchronized(continueAlertLock)
        {
            //System.out.println("Getting continueAlertStatus.");
            return continueAlert;
        }
    }
    
    public boolean GetAudioDoneStatus()
    {
        synchronized(audioFinishedLock)
        {
            //System.out.println("Getting buzzerFinished.");
            return audioFinished;
        }
    }
    
    public void SetContinueAlertStatus(boolean value)
    {
    	synchronized(continueAlertLock)
        {
        	//System.out.println("Setting ContinueAlert to " + value);
        	continueAlert = value;
        }
    }
    
    public void SetAudioDoneStatus(boolean value)
    {
        synchronized(audioFinishedLock)
        {
            //System.out.println("Setting audioFinished to " + value);
            audioFinished = value;
        }
    }
    
    public void audioDone(int reason)
    {
    	System.out.println("audioDone function");
    	
    	switch(reason) {
	    	case REASON_KEY_PRESSED:
	    		//System.out.println("Key Pressed.  Stopping Alert");
	    		stopAlert();
	    		break;
	    	case  REASON_COMPLETED:
	    		//System.out.println("Completed Called.  Stopping Alert");
	    		continueAlert();
	    		break;
	    	case REASON_STOP_CALLED:
	    		//System.out.println("Stop Called.  Do Nothing.");
	    		// Do nothing
	    		break;
    	}
    }
    
    public void buzzerDone(int reason)
    {
        // Do nothing.
    }
    
    public void vibrateDone(int reason)
    {
        // Do nothing.
    }
}

The next step is to create an instance of the listener on the main screen, so it is always there. This was one of the issues I was having, because I was adding the listener when the Message Page came in and then removing it after the alert was done. By creating the single listener, I only worry about starting and stopping the music and vibrate.

// Before the main screen is created
public static SDAAlertListener _sdaAlertListener = new SDAAlertListener();
// After the application is created and is only called once
((Application) UiApplication.getUiApplication()).addAlertListener(StartScreen._sdaAlertListener);

Next, I handled the Message Page by sending it to the GlobalMessage display function and creating a loop to check for when the Alert should be played again. The loop checks the Continue Alert Status to see if the button click hasn’t turned it off. Then it checks to see if the audio is done playing. If the Continue Alert is true and the Audio Done is true, then the startAlert function is called again to replay the music and vibration. Otherwise a break stops the loop.

/* Global Dialog display instead of screen pop */
bbhUtil.displayGlobalMessage("New Message Page " + MessagePagingInfo.getCurrentMessage());     
// Start Continuous paging section 
boolean alertStatus = true;
StartScreen._sdaAlertListener.startAlert();
// Start loop 
while (true)
{ 
	alertStatus = StartScreen._sdaAlertListener.GetContinueAlertStatus();
	if(alertStatus)
	{
		if(StartScreen._sdaAlertListener.GetAudioDoneStatus()) {
			//System.out.println("Audio Done Status is True");
			StartScreen._sdaAlertListener.startAlert();
		} else {
			break;
		}
	}
	else
	{ 
		System.out.println("Paging Alert Stop.");
		break;
	}                               
}

Finally, the Global Message display has a close listener attached to it. This listener calls the Alert stop function when the user clicks the “OK” button.

public class alertDialogListener implements DialogClosedListener {
	public void dialogClosed(Dialog dialog, int choice) {
		// Check for OK click and then stop Timer
		//System.out.println("Output Choice: " + choice);
   		 switch (choice) {
   		 	case Dialog.OK : 
   		 		synchronized(UiApplication.getEventLock())
   		 		{
   		 			//System.out.println("Calling Alert Listener Stop");
   		 			StartScreen._sdaAlertListener.stopAlert();
                }
        		break;
            case Dialog.CANCEL : 
            	Status.show("Cancel msg"); 
            	break;
        }
	}
}

This also allows for multiple Message Page alerts, since it turns on and off the same listener. I just wish I hadn’t taken so long in figuring it out.

Cursing the cursor

It would seem that every step to upgrade to OS 6 and use SQLite for my BlackBerry java application comes with its own little headaches. I’m sure there is some sort of documentation somewhere that would make it a little easier to do this coding. But, until then, I’ll just muddle along.

The key reason for using SQLite is so that I can populate the device-side information easily.  Unfortunately, the data sent to the device is not in the best format at the moment.  So, I have to take sudo HTML tables and parse them out into data to be stored in the SQLite table.  But, before I can do that, I needed to figure out which table is being sent.  When the application initializes the database, it adds values to a configuration table of the table name and the table id.  When the data is loaded, I grab the table name and then look in the configuration table to determine the id.  Then with the id, I can use a switch statement to process the data using the parser associated for that table.  Simple enough.  But, when the code was retrieving the table name, it would error and crash trying to determine the id.  It took a bunch of break points and stepping to finally figure out the issue.

Here is the code to get an Value while passing in an Attribute

       Statement statement = myDataBase.createStatement("SELECT value FROM myTable WHERE attribute = ?");
            statement.prepare();
            statement.bind(1, attribute);
            Cursor cursor = statement.getCursor();
            Row row;
            //Use first cursor spot
            cursor.first();
            // Get the row from the cursor
            row = cursor.getRow();
            thisValue = row.getString(0);

            statement.close();
            cursor.close();

The issue I had was on the getString(0) part. Instead of the first position being at 1, the first position is at 0. Sort of obvious, but I guess I was looking at the bind statement, which has the first position at 1.

Another simple thing I determined today, not being the expert Java developer, is that I can call the static instance of the database connection without having to pass it through to a different class function.  In other words, I created the appDB connection when the application opens up and I initialize the database connection.  I do this on a class called SQLManager.  Therefor, when I want to use the database connection, I can just use SQLManager.appDB anywhere in application.  I wasn’t sure this would work, but after a little test, I found that it did work and will help cut down on code when calling functions to update tables.

Upgrading from 4.5 to 6.0

At work I have been attempting to get our 800+ BlackBerry devices updated. This all began when we provided our service application to a site to use with their own technicians. The devices that we gave them were the BlackBerry Bold 9930. Luckily for me, I also got one of these devices and handed in an old 8800 BlackBerry. The Bold’s came with OS 7.0, but I updated mine to 7.1 in order to take advantage of the Hot Spot feature. Also, in OS 6 and 7, there is a feature for using Near Field Communication NFC tags. While most of the focus on NFC is payment, I see that my company can use the data storage and macros features for use with the service application.

But, the main reason for the upgrade to OS 6 or better, is the availability of SQLite on the phone. Previously, storing data on the BlackBerry devices was a little limited. It was similar to creating a very long text file and finding a particular item at a certain spot in order to get the data. In a word, it was clunky. Even though SQLite isn’t a full featured database, it is enough of a database to make inserting, updating and deleting data on the device much easier. So, I decided to delve in an update the old client application to version 6, which first required changing to Eclipse, more specifically, BlackBerry plugin for Eclipse. And changing to a new IDE and when you do that, you have re-setup your BlackBerry connections on the simulator to get things to work correctly.

With BlackBerry, you have a MDS that routes the pushes from a server to the appropriate device. On a simulator, you modify a rimpublic.properties file to make sure the data is okay to go to the device. An issue I had was that the push was failing. Because the push server had been already setup and I was just trying to get the simulator to receive the pushes, I didn’t have a direct look at the push command. One of the things you have to provide is the port of the application. When you setup up the device side listener to the wrong port, then your pushes never get to the application. D’oh! After I fixed that simple error, the data being pushed wasn’t displaying the entire data stream. The demo had the output example happen before the stream was repackaged back into a database, so only a portion was being shown. Oops. After I figured out to step to the parsing function after the push stream was finished. This was also an issue with opening and closing the database. The demo showed that every time you do a transaction to the database, you should open it and then close it. Well, that worked great in the simulator, but not so on the device. In the end, I found out that the best thing to do was open the database when the application was open and then not close it until I wanted to close the application for good.

The upgrade has been a slow start, but now it is moving along. Hopefully I can get the application converted and work on the new features that I want the application to do without too much more trouble.