This is going to be a long post! (grab a cup of coffee/popcorn)
I am using AltBeacon Xamarin sample in my code to show the beacons.
I have come across this example in creating Notifications in Xamarin.
Here there's an Application class where the core logic goes.
public class AltBeaconSampleApplication : Application, IBootstrapNotifier
{
private const string TAG = "AltBeaconSampleApplication";
BeaconManager _beaconManager;
private RegionBootstrap regionBootstrap;
private Region _backgroundRegion;
private BackgroundPowerSaver backgroundPowerSaver;
private bool haveDetectedBeaconsSinceBoot = false;
private string nearbyMessageString = "A beacon is nearby.";
private string nearbyTitleString = "AltBeacon Reference Application";
private MainActivity mainActivity = null;
public MainActivity MainActivity
{
get { return mainActivity; }
set { mainActivity = value; }
}
private NotificationActivity notificationActivity = null;
public NotificationActivity NotificationActivity
{
get { return notificationActivity; }
set { notificationActivity = value; }
}
public AltBeaconSampleApplication() : base() { }
public AltBeaconSampleApplication(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) : base(javaReference, transfer) { }
public override void OnCreate()
{
base.OnCreate();
_beaconManager = BeaconManager.GetInstanceForApplication(this);
var iBeaconParser = new BeaconParser();
// Estimote > 2013
iBeaconParser.SetBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
_beaconManager.BeaconParsers.Add(iBeaconParser);
Log.Debug(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
_backgroundRegion = new Region("backgroundRegion", null, null, null);
regionBootstrap = new RegionBootstrap(this, _backgroundRegion);
// simply constructing this class and holding a reference to it in your custom Application
// class will automatically cause the BeaconLibrary to save battery whenever the application
// is not visible. This reduces bluetooth power usage by about 60%
backgroundPowerSaver = new BackgroundPowerSaver(this);
PerformHttpRequest();
}
public void DidDetermineStateForRegion(int state, AltBeaconOrg.BoundBeacon.Region region)
{
}
public async void PerformHttpRequest()
{
try
{
using (var client = new HttpClient())
{
var uri = "http://exampleuri";
var result = await client.GetStringAsync(uri);
var response = JsonConvert.DeserializeObject<BeaconURL>(result);
SendNotificationFromBeacon(response);
}
}
catch(Exception ex)
{
throw ex;
}
}
private void SendNotificationFromBeacon(BeaconURL receivedNotification)
{
// Setup an intent for SecondActivity:
Intent notificationIntent = new Intent(this, typeof(NotificationActivity));
// Pass some information to SecondActivity:
notificationIntent.PutExtra("CompaignUrl", receivedNotification.CompaignUrl);
notificationIntent.PutExtra("MediaUrl", receivedNotification.MediaUrl);
notificationIntent.PutExtra("titleText", receivedNotification.Title);
notificationIntent.SetFlags(ActivityFlags.NewTask);
// Create a task stack builder to manage the back stack:
Android.App.TaskStackBuilder stackBuilder = Android.App.TaskStackBuilder.Create(this);
// Add all parents of SecondActivity to the stack:
stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(NotificationActivity)));
// Push the intent that starts SecondActivity onto the stack:
stackBuilder.AddNextIntent(notificationIntent);
// Obtain the PendingIntent for launching the task constructed by
// stackbuilder. The pending intent can be used only once (one shot):
const int pendingIntentId = 0;
PendingIntent pendingIntent =
stackBuilder.GetPendingIntent(pendingIntentId, PendingIntentFlags.OneShot);
// Instantiate the builder and set notification elements, including
// the pending intent:
var builder =
new NotificationCompat.Builder(this)
.SetContentTitle(receivedNotification.Title)
.SetContentText(receivedNotification.Text)
.SetSmallIcon(Android.Resource.Drawable.IcDialogInfo);
// Build the notification:
Notification notification = builder.Build();
// Get the notification manager:
NotificationManager notificationManager =
GetSystemService(Context.NotificationService) as NotificationManager;
// Publish the notification:
const int notificationId = 0;
notificationManager.Notify(notificationId, notification);
}
}
BeaconURL is a POCO class
NotificationActivity is a basic Activity class.
I perform the HttpClient request and get data. I create a notification and present it on my screen. It goes like this
Now when I tap on the notification, I dont go to the NotificationActivity. I am trying to invoke an activity from an ApplicationClass. Is this the right way to perform such stuff. Kindly provide details.
Thanks.
Edit: Added NotificationActivity Class
[Activity(Label = "NotificationActivity")]
public class NotificationActivity : MainActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
SetContentView(Resource.Layout.NotificationLayout);
TextView titleTextView = FindViewById<TextView>(Resource.Id.txtTitle);
titleTextView.Text = Intent.Extras.GetString("titleText", "");
ImageView mediaImage = FindViewById<ImageView>(Resource.Id.imgViewMedia);
mediaImage.SetImageBitmap(GetImageBitmapFromUrl(Intent.Extras.GetString("MediaUrl", "")));
}
private Bitmap GetImageBitmapFromUrl(string url)
{
Bitmap imageBitmap = null;
using (var webClient = new WebClient())
{
var imageBytes = webClient.DownloadData(url);
if (imageBytes != null && imageBytes.Length > 0)
{
imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
}
}
return imageBitmap;
}
}
First thing you need to do is to set the your pending intent within the notification builder, it will get your NotificationActivity launching:
var builder =
new NotificationCompat.Builder(this)
.SetContentTitle("receivedNotification.Title")
.SetContentText("receivedNotification.Text")
.SetSmallIcon(Android.Resource.Drawable.IcDialogInfo)
.SetContentIntent(pendingIntent);
The second will be to get your back stack setup, from what you posted I'm not sure what the flow should be as the user will exit the app if they use the back button.
If you want the user to go back to the MainActivity when that press the back button, then you can add a ParentActivity to your NotificationActivity activity attribute, i.e.:
[Activity(Label = "NotificationActivity", ParentActivity = typeof(MainActivity))]
And thus the line:
stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(NotificationActivity)));
Would add the MainActivity to the back stack.
Related
I am using a Dependency service to pick a photo from the gallery. and I want to show the path when the user selects an image from their phone in a Label.
I have read too many logs but not getting the proper results.
I want it like this:
Now the selected image is displayed properly but what I don't get is how to display the path of the selected image.
Please suggest me how to do it for both android and ios.
Note: I'm using Dependency service for it so I don't want third-party plugins.
I hope I will get a better solution for this.
Thanks in advance.
Creating the interface in forms
namespace xxx
{
public interface IPhotoPickerService
{
Task<Dictionary<string,Stream>> GetImageStreamAsync();
}
}
in iOS
[assembly: Dependency (typeof (PhotoPickerService))]
namespace xxx.iOS
{
public class PhotoPickerService : IPhotoPickerService
{
TaskCompletionSource<Dictionary<string, Stream>> taskCompletionSource;
UIImagePickerController imagePicker;
Task<Dictionary<string, Stream>> IPhotoPickerService.GetImageStreamAsync()
{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
};
// Set event handlers
imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
imagePicker.Canceled += OnImagePickerCancelled;
// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);
// Return Task object
taskCompletionSource = new TaskCompletionSource<Dictionary<string, Stream>>();
return taskCompletionSource.Task;
}
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data;
if (args.ReferenceUrl.PathExtension.Equals("PNG") || args.ReferenceUrl.PathExtension.Equals("png"))
{
data = image.AsPNG();
}
else
{
data = image.AsJPEG(1);
}
Stream stream = data.AsStream();
UnregisterEventHandlers();
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(args.ImageUrl.ToString(), stream);
// Set the Stream as the completion of the Task
taskCompletionSource.SetResult(dic);
}
else
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
void OnImagePickerCancelled(object sender, EventArgs args)
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}
void UnregisterEventHandlers()
{
imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
imagePicker.Canceled -= OnImagePickerCancelled;
}
}
}
in Android
in MainActivity
public class MainActivity : FormsAppCompatActivity
{
...
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;
public TaskCompletionSource<Dictionary<string,Stream>> PickImageTaskCompletionSource { set; get; }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);
if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (intent != null))
{
Android.Net.Uri uri = intent.Data;
Stream stream = ContentResolver.OpenInputStream(uri);
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.ToString(), stream);
// Set the Stream as the completion of the Task
PickImageTaskCompletionSource.SetResult(dic);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}
[assembly: Dependency(typeof(PhotoPickerService))]
namespace xxx.Droid
{
public class PhotoPickerService : IPhotoPickerService
{
public Task<Dictionary<string,Stream>> GetImageStreamAsync()
{
// Define the Intent for getting images
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
// Start the picture-picker activity (resumes in MainActivity.cs)
MainActivity.Instance.StartActivityForResult(
Intent.CreateChooser(intent, "Select Picture"),
MainActivity.PickImageId);
// Save the TaskCompletionSource object as a MainActivity property
MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Dictionary<string,Stream>>();
// Return Task object
return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
}
invoke it
Dictionary<string, Stream> dic = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
Stream stream;
string path;
foreach ( KeyValuePair<string, Stream> currentImage in dic )
{
stream = currentImage.Value;
path = currentImage.Key;
label.Text = path;
if (stream != null)
{
image.Source = ImageSource.FromStream(() => stream);
}
}
Update
If you want to get the path , you could invoke
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.Path, stream);
Help me find the error in the code
My MainActivity:
string CHANNEL_ID = "MyChannel";
protected override void OnCreate(Bundle savedInstanceState)
{
....
...
CreateNotificationChannel();
LoadApplication(new App());
}
public void CreateNotification(string SetContentTitle, string SetContentText)
{
var builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.SetContentTitle(SetContentTitle)
.SetContentText(SetContentText)
.SetChannelId(CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.icon);
// Finally, publish the notification:
var notificationManager = NotificationManagerCompat.From(this);
// Get the notification manager:
// NotificationManager notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;
// Publish the notification:
int notificationId = 1000;
notificationManager.Notify(notificationId, builder.Build());
}
void CreateNotificationChannel(){
if (Build.VERSION.SdkInt<BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
string name = "MyName";
var description = "desc";
var channel = new NotificationChannel(CHANNEL_ID, name, NotificationImportance.High)
{
Description = description
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
and I have a class in which when I need I start displaying local notifications using the code below
var urlOb = new MainActivity();
urlOb.CreateNotification(titleback, textback);
I think I did everything right, but local notifications are not shown.
You can call the method in other activity
public static MainActivity instance;
In the method onCreate
//...
base.OnCreate(savedInstanceState);
instance = this;
And you can call it in other method
MainActivity.instance.CreateNotification(titleback, textback);
I already implement FCM notification on xamarin forms android.so I want,when user click on notification then it will open a Xamarin forms content page.
here is my code for receiving FCM notification:
Inside MyFireMessagingService class-
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
SendNotificatios(message.GetNotification().Body, message.GetNotification().Title);
}
public void SendNotificatios(string body, string Header)
{
Notification.Builder builder = new Notification.Builder(this);
builder.SetSmallIcon(Resource.Drawable.AppLauncher);
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, intent, 0);
builder.SetContentIntent(pendingIntent);
builder.SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.AppLauncher));
builder.SetContentTitle(Header);
builder.SetContentText(body);
builder.SetDefaults(NotificationDefaults.Sound);
builder.SetAutoCancel(true);
NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(1, builder.Build());
}
so How to open a content page after click on notification?
As the first thing I have replaced
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
SendNotificatios(message.GetNotification().Body, message.GetNotification().Title);
}
With
public override void HandleIntent(Intent intent)
{
CreateNotification(intent);
}
Then created a new method CreateNotification as follows,
private void CreateNotification(Object e)
{
try
{
string title = "";
string body = "";
var intent = new Intent(this, typeof(MainActivity));
var i = e as Intent;
var bundle = i.Extras;
title = bundle.GetString("gcm.notification.title");
body = bundle.GetString("gcm.notification.body");
intent.PutExtra("title", title);
intent.PutExtra("body", body);
intent.AddFlags(ActivityFlags.ClearTop | ActivityFlags.SingleTop );
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.CancelCurrent | PendingIntentFlags.UpdateCurrent);
Notification.Builder builder = new Notification.Builder(this);
builder.SetSmallIcon(Resource.Drawable.AppLauncher);
builder.SetContentIntent(pendingIntent);
builder.SetLargeIcon(BitmapFactory.DecodeResource(Resources, Resource.Drawable.AppLauncher));
builder.SetContentTitle(Header);
builder.SetContentText(body);
builder.SetDefaults(NotificationDefaults.Sound);
builder.SetAutoCancel(true);
NotificationManager notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.Notify(1, builder.Build());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Now we have to update the MainActivity.cs to handle the push notification click by overriding OnNewIntent method as follows
protected async override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
var title = intent.GetStringExtra("title");
//This method will get called while the app is launching from the app icon or from the notification
if (title != null)
{
//Means new Intent from push notification
//Code to open the page
}
}
Make sure you have kept LaunchMode = LaunchMode.SingleTask in the activity
I am trying to use Xamarin.Auth for a Facebook signin. It's all set up, but I am missing the last part. The Facebook signin is not my MainActivity, one has to click on a button in order to sign in. I don't know how to start the page for the signin. I have trying to follow this approach, but as my MainActivity isn't the Facebook signin page, it won't work. I have binded (I am following the MVVM pattern) the signin button and have implemented an interface in order to use DependencyService. My problem is when I have to implement the code of the platforms.
This is what I have tried on Android:
class LoginFBImpl : Activity, ILoginFB, IFacebookAuthenticationDelegate
{
public void LoginFB() //the method in the interface
{
var auth = new FacebookAuthenticator(FacebookAuthenticator.ClientId, FacebookAuthenticator.Scope, this);
var authenticator = auth.GetAuthenticator();
var intent = authenticator.GetUI(this);
StartActivity(intent); //Problem occurs here
}
public async void OnAuthenticationCompletedAsync(UserModel token)
{
var facebookService = new FacebookService();
var name = await facebookService.GetNameAsync(token.AccessToken);
var id = await facebookService.GetIdAsync(token.AccessToken);
var picture = await facebookService.GetPictureAsync(token.AccessToken);
}
public void OnAuthenticationCancelled()
{
}
public void OnAuthenticationFailed(string message, Exception exception)
{
}
}
iOS:
public class LoginFBImpl : UIViewController, ILoginFB, IFacebookAuthenticationDelegate
{
public void LoginFB()
{
var auth = new FacebookAuthenticator(FacebookAuthenticator.ClientId, FacebookAuthenticator.Scope, this);
var authenticator = auth.GetAuthenticator();
var viewController = authenticator.GetUI();
PresentViewController(viewController, true, null);
}
public async void OnAuthenticationCompletedAsync(UserModel token)
{
DismissViewController(true, null);
var facebookService = new FacebookService();
var name = await facebookService.GetNameAsync(token.AccessToken);
var id = await facebookService.GetIdAsync(token.AccessToken);
var picture = await facebookService.GetPictureAsync(token.AccessToken);
}
public void OnAuthenticationFailed(string message, Exception exception)
{
DismissViewController(true, null);
var alertController = new UIAlertController
{
Title = message,
Message = exception?.ToString()
};
PresentViewController(alertController, true, null);
}
public void OnAuthenticationCancelled()
{
DismissViewController(true, null);
var alertController = new UIAlertController
{
Title = "Authentication cancelled",
Message = "You didn't complete the authentication process"
};
PresentViewController(alertController, true, null);
}
}
I think it has something to do with the Activity/ViewController, but I don't know how to do it properly. When I run this I get: java.lang.NullPointerException: Attempt to invoke virtual method 'android.app.ActivityThread$ApplicationThread android.app.ActivityThread.getApplicationThread()' on a null object reference on Android, and I am expecting something similar on iOS - Haven't tested it yet as I am working on Windows.
Is there a way to show "Loading" screen with animation in blackberry?
Options:
PME animation content
multithreading + set of images + timer/counter
standard rim api
some other way
Any of this?
Thanks!
Fermin, Anthony +1. Thanks to all, you gave me the part of answer.
My final solution:
1.Create or generate (free Ajax loading gif generator) animation and add it to project.
2.Create ResponseCallback interface (see Coderholic - Blackberry WebBitmapField) to receive thread execution result:
public interface ResponseCallback {
public void callback(String data);
}
3.Create a class to handle your background thread job. In my case it was http request:
public class HttpConnector
{
static public void HttpGetStream(final String fileToGet,
final ResponseCallback msgs) {
Thread t = new Thread(new Runnable() {
public void run() {
HttpConnection hc = null;
DataInputStream din = null;
try {
hc = (HttpConnection) Connector.open("http://" + fileToGet);
hc.setRequestMethod(HttpsConnection.GET);
din = hc.openDataInputStream();
ByteVector bv = new ByteVector();
int i = din.read();
while (-1 != i) {
bv.addElement((byte) i);
i = din.read();
}
final String response = new String(bv.toArray(), "UTF-8");
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback(response);
}
});
}
catch (final Exception e) {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
msgs.callback("Exception (" + e.getClass() + "): "
+ e.getMessage());
}
});
}
finally {
try {
din.close();
din = null;
hc.close();
hc = null;
}
catch (Exception e) {
}
}
}
});
t.start();
}
}
4.Create WaitScreen (a hybrid of FullScreen and AnimatedGIFField with ResponseCallback interface):
public class WaitScreen extends FullScreen implements ResponseCallback
{
StartScreen startScreen;
private GIFEncodedImage _image;
private int _currentFrame;
private int _width, _height, _xPos, _yPos;
private AnimatorThread _animatorThread;
public WaitScreen(StartScreen startScreen) {
super(new VerticalFieldManager(), Field.NON_FOCUSABLE);
setBackground(
BackgroundFactory.createSolidTransparentBackground(
Color.WHITE, 100));
this.startScreen = startScreen;
EncodedImage encImg =
GIFEncodedImage.getEncodedImageResource("ajax-loader.gif");
GIFEncodedImage img = (GIFEncodedImage) encImg;
// Store the image and it's dimensions.
_image = img;
_width = img.getWidth();
_height = img.getHeight();
_xPos = (Display.getWidth() - _width) >> 1;
_yPos = (Display.getHeight() - _height) >> 1;
// Start the animation thread.
_animatorThread = new AnimatorThread(this);
_animatorThread.start();
UiApplication.getUiApplication().pushScreen(this);
}
protected void paint(Graphics graphics) {
super.paint(graphics);
// Draw the animation frame.
graphics
.drawImage(_xPos, _yPos, _image
.getFrameWidth(_currentFrame), _image
.getFrameHeight(_currentFrame), _image,
_currentFrame, 0, 0);
}
protected void onUndisplay() {
_animatorThread.stop();
}
private class AnimatorThread extends Thread {
private WaitScreen _theField;
private boolean _keepGoing = true;
private int _totalFrames, _loopCount, _totalLoops;
public AnimatorThread(WaitScreen _theScreen) {
_theField = _theScreen;
_totalFrames = _image.getFrameCount();
_totalLoops = _image.getIterations();
}
public synchronized void stop() {
_keepGoing = false;
}
public void run() {
while (_keepGoing) {
// Invalidate the field so that it is redrawn.
UiApplication.getUiApplication().invokeAndWait(
new Runnable() {
public void run() {
_theField.invalidate();
}
});
try {
// Sleep for the current frame delay before
// the next frame is drawn.
sleep(_image.getFrameDelay(_currentFrame) * 10);
} catch (InterruptedException iex) {
} // Couldn't sleep.
// Increment the frame.
++_currentFrame;
if (_currentFrame == _totalFrames) {
// Reset back to frame 0
// if we have reached the end.
_currentFrame = 0;
++_loopCount;
// Check if the animation should continue.
if (_loopCount == _totalLoops) {
_keepGoing = false;
}
}
}
}
}
public void callback(String data) {
startScreen.updateScreen(data);
UiApplication.getUiApplication().popScreen(this);
}
}
5.In the end, create Start screen to call HttpConnector.HttpGetStream and to show WaitScreen:
public class StartScreen extends MainScreen
{
public RichTextField text;
WaitScreen msgs;
public StartScreen() {
text = new RichTextField();
this.add(text);
}
protected void makeMenu(Menu menu, int instance) {
menu.add(runWait);
super.makeMenu(menu, instance);
}
MenuItem runWait = new MenuItem("wait", 1, 1) {
public void run() {
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
getFile();
}
});
}
};
public void getFile() {
msgs = new WaitScreen(this);
HttpConnector.HttpGetStream(
"stackoverflow.com/faq", msgs);
}
//you should implement this method to use callback data on the screen.
public void updateScreen(String data)
{
text.setText(data);
}
}
UPDATE: another solution naviina.eu: A Web2.0/Ajax-style loading popup in a native BlackBerry application
The basic pattern for this kind of thing is:
Have a thread running a loop that updates a variable (such as the frame index of the animated image) and then calls invalidate on a Field which draws the image (and then sleeps for a period of time). The invalidate will queue a repaint of the field.
In the field's paint method, read the variable and draw the appropriate frame of the image.
Pseudo code (not totally complete, but to give you the idea):
public class AnimatedImageField extends Field implements Runnable {
private int currentFrame;
private Bitmap[] animationFrames;
public void run() {
while(true) {
currentFrame = (currentFrame + 1) % animationFrames.length;
invalidate();
Thread.sleep(100);
}
}
protected void paint(Graphics g) {
g.drawBitmap(0, 0, imageWidth, imageHeight, animationFrames[currentFrame], 0, 0);
}
}
Note also here I used an array of Bitmaps, but EncodedImage lets you treat an animated gif as one object, and includes methods to get specific frames.
EDIT: For completeness: Add this to a PopupScreen (as in Fermin's answer) or create your own dialog by overriding Screen directly. The separate thread is necessary because the RIM API is not thread-safe: you need to do everything UI related on the event thread (or while holding the event lock, see BlackBerry UI Threading - The Very Basics
This is simple code for loading screen ....
HorizontalFieldManager popHF = new HorizontalFieldManager();
popHF.add(new CustomLabelField("Pls wait..."));
final PopupScreen waitScreen = new PopupScreen(popHF);
new Thread()
{
public void run()
{
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().pushScreen(waitScreen);
}
//Here Some Network Call
synchronized (UiApplication.getEventLock())
{
UiApplication.getUiApplication().popScreen(waitScreen);
}
}
}.start();
If it's just an animation could you show an animated gif on a popup and close it when loading operation is complete?
Easiest way is probably to use the standard GaugeField, setting style GaugeField.PERCENT. This will give you a progress bar. Add this to a PopupScreen and it will sit on top of your content. Something like..
private GaugeField _gaugeField;
private PopupScreen _popup;
public ProgressBar() {
DialogFieldManager manager = new DialogFieldManager();
_popup = new PopupScreen(manager);
_gaugeField = new GaugeField(null, 0, 100, 0, GaugeField.PERCENT);
manager.addCustomField(_gaugeField);
}
Then have an update method which will use _gaugeField.setValue(newValue); to update the progress bar.
I normally have this called from whichever thread is doing the work (loading in your case, everytime an operation is complete the progress bar is updated.
I would suggest to take a look at this simple implementation. I liked this but never used it. May be helpful to you.
link text
ActivityIndicator is a good option if you are working with at least BB OS 6.0.
http://www.brighthub.com/mobile/blackberry-platform/articles/94258.aspx
http://docs.blackberry.com/en/developers/deliverables/17966/Screen_APIs_1245069_11.jsp