How to navigate back to the previous screen in Blackberry? - user-interface

In Blackberry I can navigate from one screen to the next screen, but I can't navigate back to the previous screen. Pressing the escape key in the emulator terminates the entire application. Is there any other key in the emulator to go to the previous screen or any code to navigate back? If you know please help me.

As Andrey said, there is a display stack, so if you push screens without popping them, they will stay in stack, so closing current screen, previous screen will be shown automattically, and if there is no prev. screen, application will close.
However it's not good to hold many screens in display stack, so you can implement kind of stack inside of screens, to handle navigation manually.
Abstract screen class for screen stack implementation:
public abstract class AScreen extends MainScreen {
Screen prevScreen = null;
void openScreen(AScreen nextScreen) {
nextScreen.prevScreen = this;
UiApplication.getUiApplication().popScreen(this);
UiApplication.getUiApplication().pushScreen(nextScreen);
}
void openPrevScreen() {
UiApplication.getUiApplication().popScreen(this);
if (null != prevScreen)
UiApplication.getUiApplication().pushScreen(prevScreen);
}
}
Sample first screen:
public class FirstScreen extends AScreen implements FieldChangeListener {
ButtonField mButton = null;
public FirstScreen() {
super();
mButton = new ButtonField("Go second screen", ButtonField.CONSUME_CLICK);
mButton.setChangeListener(this);
add(mButton);
}
public void fieldChanged(Field field, int context) {
if (mButton == field) {
openScreen(new SecondScreen());
}
}
}
Sample second screen:
public class SecondScreen extends AScreen implements FieldChangeListener {
ButtonField mButton = null;
public SecondScreen() {
super();
mButton = new ButtonField("Go first screen", ButtonField.CONSUME_CLICK);
mButton.setChangeListener(this);
add(mButton);
}
public void fieldChanged(Field field, int context) {
if (mButton == field) {
openPrevScreen();
}
}
public boolean onClose() {
openPrevScreen();
return true;
}
}

The BlackBerry maintains a stack of screens; the display stack.
Screens are popped onto the stack, and popped off the stack through the UiApplication in charge of them. Popping the last screen off the stack closes the application by default.
If you are running a UiApplication, named MyUiApplication, you can add the screen to the stack by calling pushScreen(new SomeScreen());
The screen, if derived from MainScreen, as most BlackBerry screens are, is created with the DEFAULT_CLOSE flag, meaning that the ESCAPE button on the BlackBerry will naturally close the screen, causing popScreen() to be called. You can, of course, call popScreen() following any keypress or trackwheel/trackball click. The screen can also call close() on itself, which has the same result; the screen is popped off the stack, returning the application to the previous screen, or terminating the application if the last screen is popped off the display stack.
If the application is not created as a UiApplication, or if the screen was initially pushed onto the display stack from a non-UI thread (such as a background thread), then one must make sure that the call to close the screen is also done from the UI thread. This can be done by making sure that the eventLock is taken on the Application class prior to performing any UI operation (one would typically call invokeLater as well, in this situation).
If the original screen was popped onto the stack as a global screen (modeless, on top of all other screens), then it must be popped off the stack using something like:
Ui.getUiEngine().dismissStatus(this);
In any case, overriding onClose() and close() of the derived Screen will allow you to trap the occurring exception for debugging and further analysis.

Related

Start specific view of Gluon App from a notification

I set up an alarm to show a corresponding Notification. The PendingIntent of the Notification is used to start the Gluon App main class. To show a View other than the homeView, I call switchView(otherView) in the postInit method. OtherView is shown, but without AppBar. While it's possible to make the AppBar appear, I wonder if this is the right approach.
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
switchView(READING_VIEW);
}
}
When triggering anything related to the JavaFX thread from another thread, we have to use Platform.runLater().
Yours is a clear case of this situation: the Android thread is calling some pending intent, and as a result, the app is started again.
This should be done:
#Override
public void postInit(Scene scene) {
// additional setUp logic
boolean showReadingView = (boolean) PlatformProvider.getPlatform().getLaunchIntentExtra("showReadingView", false);
if (showReadingView) {
Platform.runLater(() -> switchView(READING_VIEW));
}
}

Get EditText data on swipe to next Fragment

I have three fragments in a view pager:
A -> B -> C
I would like to get the strings of my two edittexts in Fragment A on swipe to Fragment B to show them in Fragment B. The edittext data may be changed up until the swipe.
Someone has suggested listening for typing and sending data after each one, but the callbacks I know for that change state EVERY key click (which can be expensive). How do I this without using buttons, since their right next to each other, for a more delightful experience?
You can check the data of the EditText on swipe ; if it's not null, then you can send it to any other fragment using Bundle since you are dealing with fragments
With help from #I. Leonard I found a solution here.
It was deprecated so I used the newer version. I put the below code in my fragment class because I needed access to the data without complicating things. It works like a charm!
On the page listener callback, I suggest, calling an interface for inter-fragment communication to do your actions on your home activity or to call the appropriate fragment that can do the work, from the activity.
// set content to next page on scroll start
vPager = (ViewPager) getActivity().findViewById(R.id.viewpager);
vPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_SETTLING) {
// ViewPager is slowing down to settle on a page
if (vPager.getCurrentItem() == 1) {
// The page to be settled on is the second (Preview) page
if (!areFieldsNull(boxOne.getText().toString(), boxTwo.getText().toString()))
// call interface method in view pager activity using interface reference
communicator.preview(boxOne.getText().toString(), boxTwo.getText().toString());
}
}
}
});

How do I handle double click on a GWT web application running on a touch device?

I have built a GWT (2.5) web application that, among other things, uses a DataGrid. I have used addDomHandler to add a DoubleClickEvent to select a row and perform an action, and it works great on the desktop. However, when I run the web application on a touch device, the double click zooms the screen instead. Is there are proper way to handle that? I would prefer to override the default behavior of zooming, but I have no idea where to begin. I suppose a long press might be more appropriate, but I have no idea where to begin with that either.
The code:
_dataGrid.addDomHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
// Do something exciting here!
}
}, DoubleClickEvent.getType());
The only idea I have it that stopping the propagation of the DOM event may prevent the default zoom behavior.
Although I'd be curious is it registers as a double-click event at all on a touchscreen device. Would be worth trying just putting the handler on Root and seeing if you can even catch the event.
Also try this: just have your application catch -any- DOM event and simply write the name out somehow. That way you, should find out what event is triggering (might be one for long touch!) and can write a handler for that.
OK, I found a solution, but it's probably pretty unique to my situation. What I did was keep the double click handler and I also implemented a slow double click. In other words, if you select the same row of the DataGrid twice in sequence, no matter how fast you do it, then the application interprets that as a double click. Here is the code:
result.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
Scheduler.get().scheduleDeferred(new Command() {
public void execute () {
if (result.isDoubleTap()) {
// Do the same thing a a double click.
}
}
});
}});
I had a problem with the click handler being fired before the selection change handler, so I had to defer the processing of the click event with the Scheduler. The "result" is a Composite that contains the DataGrid and some other stuff, and in each "result" I store the last selected item in a private variable. Then in IsDoubleTap() all I do is see if the current selection is the same as the last one:
public boolean isDoubleTap() {
boolean result = false;
String selected = getSelected();
if (_lastSelect != null && selected != null && selected.equals(_lastSelect))
result = true;
_lastSelect = selected;
return result;
}
So effectively if you do a normal double click or a slow double click you get the same action. I'm just glad that while I use this result object many places, it is the only place that I use a double click. And I would REALLY like to have a conversion with the committee that decided overriding standard double click behavior with a touch device was a GOOD thing.

Touch event handling in Blackberry

I try to implement simple touch event handling on Blackberry 9550 emulator, but it doesn't work. Actually, touchEvent never gets called, 'cos no text ever appears in the console. Also, I get an annoying "Full Menu" which appears on touching the screen.
Here's the code:
package mypackage;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.EventInjector.TouchEvent;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.VirtualKeyboard;
import net.rim.device.api.ui.container.MainScreen;
public class MyScreen extends MainScreen
{
public MyScreen()
{
super(NO_SYSTEM_MENU_ITEMS);
getScreen().getVirtualKeyboard().setVisibility(VirtualKeyboard.HIDE_FORCE);
add(new HandleTouch());
}
class HandleTouch extends Field {
protected void layout(int width, int height) {
setExtent(width, height);
}
public void paint(Graphics graphics) {
graphics.drawBitmap(0, 0, this.getWidth(), this.getHeight(), Bitmap.getBitmapResource("bg.png"), 0, 0);
}
public boolean isFocusable() { return true;}
protected boolean touchEvent(TouchEvent message) {
switch( message.getEvent() ) {
case TouchEvent.CLICK:
System.out.println("----------------------------->CLICK");
return true;
case TouchEvent.DOWN:
System.out.println("----------------------------->DOWN");
return true;
case TouchEvent.MOVE:
System.out.println("----------------------------->MOVE");
return true;
}
System.out.println("PRINT ME SOMETHING IN ANY CASE");
return false;
}
public HandleTouch() {
}
}
}
1). First of all, with this code
protected void layout(int width, int height) {
setExtent(width, height);
}
you are actually setting a VERY large size of the field. This because the BB UI framework passes max available/possible dimentions to layout(int width, int height) so the field should use some part within the passed values. In this specific case the width will be the width of the display (360 px) and the height is the max possible height of the VerticalFieldManager (the one your are adding screen fields to, it is implicitly present in the screen's internals) (1073741823 px). So, finally this may result in a very large Bitmap object that is required with the field in order to be painted and you can get an uncaught error "Bitmap is too large" (I did on Storm 9530).
So, the layout() should use some relatively small values, e.g.:
protected void layout(int width, int height) {
setExtent(Math.min(width, 360), Math.min(height, 480));
}
2).
Actually, touchEvent never gets called
Well, actually it does get called. To see that you should simply touch (versus click). Left button of the mouse simulates clicks (a sequence of TouchEvent.DOWN > TouchEvent.CLICK > TouchEvent.UNCLICK > TouchEvent.UP), right button simulates touches (a sequence of TouchEvent.DOWN > TouchEvent.UP).
3).
Also, I get an annoying "Full Menu" which appears on touching the screen.
This is because your field does not consume TouchEvent.UNCLICK event. For instance, with this code your field will not show the popup:
protected boolean touchEvent(TouchEvent message) {
return true;
}
But, that is a bad solution for the popup. It is better to understand what really causes the popup. If TouchEvent.UNCLICK event is not consumed then BB UI framework calls getContextMenu(int instance) and makeContextMenu(ContextMenu contextMenu, int instance) methods of the field. So in order to disable the popup (which is actually a ContextMenu created by the getContextMenu(int instance) you should override the getContextMenu(int instance) to be smth like this:
public ContextMenu getContextMenu(int instance) {
// just in case check if a context menu is requested
// in order not to disable other types of menu
boolean isContextMenu = (Menu.INSTANCE_CONTEXT == instance);
return isContextMenu ? null : super.getContextMenu(instance);
}
4). Finally I'd recommend to not change native/default behavior of touchEvent(TouchEvent message) method. You can just watch/log it, but don't change (always call its super version). This is because touch events handling is more complicated than it may look at first. It is very easy to get a tricky bug here. I do believe most programmers should not change the native behavior of touchEvent(TouchEvent message) unless they really want to create some custom UI component to work with touch gestures. Normally they just want to react on a click (to behave as a ButtonField), however for that you can simply override navigationClick(int status, int time) or navigationUnclick(int status, int time). The BB UI framework will call those methods when user clicks your field on a touch screen.
I would like to add extra info to Arhimed's answer, since this seems to be a landing page for googling touch events...
My experiences are not to contradict him, but to add possible solutions for future readers. I am using BB OS 5.0. My experiences have been with the Storm simulator, and a Torch device. My app was originally written for OS 4.5, so it might be running in some sort of compatibility mode.
1) As explained in his point 4, a Touch Event gets passed along to a Navigation Click event, if touchEvent(TouchEvent) returns false. If navigationClick(int, int) returns false, this prompts the system to display a ContextMenu.
2) On my system, I could not find a method getContextMenu(int). So I could not test his point 3. I presume this gets added in BB6 or later.
3) I did find getContextMenu() - i.e. it takes no parameters. I tried to override that method to return null.
The strange thing is that this method only gets called after the initial context menu popup is shown! The initial context menu popup (?) gets shown, with a button on it for "Full Menu". When that button is pressed, this method gets called, and can be used to populate the MainMenu that appears. ... strange...
However, it means that overriding that method did not solve my problem.
4) I was unable to get a solution by returning true in touchEvent(TouchEvent). I agree that this would have been bad form (hack), but I have learnt to hack a lot on the BB platform. However, scrolling list fields need to have the touch event passed up, so that the scroll works.
5) Eventually I found something similar to the OP's problem with TouchEvent.UNCLICK. It has taken me 18 months to find the method navigationUnClick(int, int). Similar to my point 1 above, an unhandled UNCLICK becomes a navigationUnClick(int, int) call, which can also lead to the context menu being shown.
So by adding similar logic to both navigationClick(int, int) & navigationUnClick(int, int), I was able to get my lists & touches to interact nicely.
This is just supplemental info, that may add to the accepted anser.

How do I mask the current page behind a modal dialog box in vanilla GWT?

I've built a log-in composite that I am displaying in my application entry-point to the user. Upon entry of the username and password, I am sending the username and password to the server via a RemoteService and will receive back an object containing the ClientSession. If the ClientSession is a valid object (recognised username and password), I wish to display the main application panel otherwise I want to display the login dialog again (with an error message).
My question is, that during the async call to the server, how to I mask the screen so that the user cannot click anything whilst the Session is obtained from the server?
I know that the login should be fast, but the Session object contains a lot of Client Side cached values for the current user that is used to generate the main panel. This may take a fraction of a second or up to 5 seconds (I can't control the speed of the underlying infrastructure unfortunately) so I want to mask the screen until a timeout is reached then allow the user to try again.
I have done this exact operation before using GWT Ext, but vanilla GWT seems to have a lot less samples unfortunately.
Thanks
Chris
The GWT class PopupPanel has an optional "glass panel" that blocks interaction with the page underneath.
final PopupPanel popup = new PopupPanel(false, true); // Create a modal dialog box that will not auto-hide
popup.add(new Label("Please wait"));
popup.setGlassEnabled(true); // Enable the glass panel
popup.center(); // Center the popup and make it visible
You might want to check out GlassPanel from the GWT Incubator project. AFAICT it's not perfect, but should be of some help nevertheless ;)
You can also use a dialog box for this purpose.
Here is the code how to use it.
public class NTMaskAlert extends DialogBox {
private String displayText;
private String message;
private static NTMaskAlert alert;
Label lable;
private NTMaskAlert(String text) {
setText(text);
setWidget(new Image(GWT.getModuleBaseURL()
+ "/images/ajax-loader_1.gif"));
setGlassEnabled(true);
setAnimationEnabled(true);
super.show();
super.center();
WorkFlowSessionFactory.putValue(WorkFlowSesisonKey.MASKING_PANEL, this);
}
public static void mask(String text) {
if (text != null)
new NTMaskAlert(text);
else
new NTMaskAlert("Processing");
}
public static void unMask() {
NTMaskAlert alert = (NTMaskAlert) WorkFlowSessionFactory
.getValue(WorkFlowSesisonKey.MASKING_PANEL);
if (alert != null) {
alert.hide();
alert = null;
}
}
public void setDisplayText(String displayText) {
this.displayText = displayText;
alert.setText(displayText);
}
public String getDisplayText() {
return displayText;
}
public void setMessage(String message) {
this.message = message;
lable.setText(message);
}
public String getMessage() {
return message;
}
}
Use static mask and unmask method for operations.
This is my solution:
public class CustomPopupPanel extends PopupPanel {
private Label label = new Label();
public CustomPopupPanel() {
super(false, true); // Create a modal dialog box that will not auto-hide
super.setGlassEnabled(true); // Enable the glass panel
super.add(label); // Add the widget label into the panel
}
public CustomPopupPanel(String text) {
this();
this.mask(text);
}
public final void mask(String text) {
label.setText(text);
super.center(); // Center the popup and make it visible
}
public void unmask() {
super.hide(); // Hide the popup
}
}

Resources