I have a problem about scan-period when i'm using android-beacon-library. And here is my problem:
I have three main class: MainActivity, BaseService and BeaconService.
MainActivity: Just do startForeground and stopForeground operation.
BaseService: Do some parameters initialization, BeaconManager and so on.
BeaconService: Beacon operation.
I describe my problems first. I’m using a foreground service to do scan operation and the backgroundScanPeriod is 20l. And i also have a MainActivity with two buttons, startService and stopService. The scan-period is 10s when First time i open the app and click startService.
And then i click HOME and kill this app the service is running normal and the scan-period is 10s also. But when i re-open MainActivity by click the notification on the picture.
The scan-period will become 1s. It's fast for me. But the scan-period would become normal if i click HOME again. That means, the scan-period will become very fast every time except the first time i open the MainActivity.
I wanna know why. And here is my important code below:
MainActivity.class
#OnClick(R.id.start_service)
void start_Service() {
if (Utils.isServiceRunning(MainActivity.this, Constants.CLASSNAME)) {
Toast.makeText(this, "service is running, don't start again", Toast.LENGTH_SHORT).show();
} else {
Intent intent = new Intent(MainActivity.this, BeaconService.class);
intent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
startService(intent);
setInfo();
}
}
#OnClick(R.id.stop_service)
void stop_Service() {
if (Utils.isServiceRunning(MainActivity.this, Constants.CLASSNAME)) {
Intent intent = new Intent(MainActivity.this, BeaconService.class);
intent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
startService(intent);
setInfo();
} else {
Toast.makeText(this, "service is dead, don't kill again", Toast.LENGTH_SHORT).show();
}
}
BaseService.class
private void setBeaconManager() {
beaconManager.setBackgroundBetweenScanPeriod(20l);
beaconManager.setBackgroundMode(true);
beaconManager.getBeaconParsers().clear();
beaconManager.getBeaconParsers().add(new BeaconParser().setBeaconLayout(Constants.BEACON_LAYOUT.COMMON_LAYOUT));
}
BeaconService.class
public class BeaconService extends BaseService implements BootstrapNotifier, BeaconConsumer {
private static final int NOTIFICATION = R.string.notify_service_started;
private static final String TAG = "BeaconService";
private int size = -1;
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
private Beacon beacon;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
regionBootstrap = new RegionBootstrap(this, region);
beaconManager.bind(this);
backgroundPowerSaver = new BackgroundPowerSaver(getApplicationContext());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && intent.getAction() != null) {
switch (intent.getAction()) {
case Constants.ACTION.STARTFOREGROUND_ACTION:
startForeground(NOTIFICATION, getNotification());
break;
case Constants.ACTION.STOPFOREGROUND_ACTION:
Log.d(TAG, "Received stop foreground request");
stopForeground(true);
stopSelf();
break;
}
}
return START_STICKY;
}
#Override
public void onDestroy() {
beaconManager.unbind(this);
regionBootstrap.disable();
Log.d(TAG, "service onDestroy");
}
/**
* Called when at least one beacon in a Region is visible.
*
* #param region region
*/
#Override
public void didEnterRegion(Region region) {
// TODO: 3/8/16 reload all the resource
Log.d(TAG, "didEnterRegion called");
L.object(region);
try {
beaconManager.startRangingBeaconsInRegion(region);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* Called when no beacons in a Region are visible.
*
* #param region region
*/
#Override
public void didExitRegion(Region region) {
// TODO: 3/8/16 close all the resource
Log.d(TAG, "didExitRegion called");
try {
beaconManager.stopRangingBeaconsInRegion(region);
} catch (RemoteException e) {
e.printStackTrace();
}
beaconManager.unbind(this);
regionBootstrap.disable();
L.object(region);
}
/**
* Called with a state value of MonitorNotifier.INSIDE when at least one beacon in a Region is visible
*
* #param region region
*/
#Override
public void didDetermineStateForRegion(int i, Region region) {
Log.d(TAG, "switch from seeing/not seeing beacons");
L.object(region);
}
#Override
public void onBeaconServiceConnect() {
Log.d(TAG, "onBeaconServiceConnect");
if (null == beaconManager.getRangingNotifier()) {
beaconManager.setRangeNotifier(new RangeNotifier() {
#Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
Log.d(TAG, "beacons.size():" + beacons.size() + "," + this);
if (beacons.size() != 0) {
Iterator<Beacon> iterator = beacons.iterator();
if (beacons.size() != size) {
saveBeacon(iterator);
size = beacons.size();
}
}
}
});
}
}
/**
* Save beacon p-o-j-o to SQLite.
*/
private void saveBeacon(Iterator<Beacon> iterator) {
while (iterator.hasNext()) {
beacon = iterator.next();
L.object(beacon);
entity.setId(null);
entity.setUuid(beacon.getId1().toString());
entity.setMajor(beacon.getId2().toString());
entity.setMinor(beacon.getId3().toString());
entity.setTxpower(beacon.getTxPower());
entity.setTime(Utils.getCurrentTime());
dbHelper.provideNinjaDao().insert(entity);
Log.d(TAG, "sql save success");
}
}
private Notification getNotification() {
CharSequence text = getText(R.string.notify_service_started);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
Notification notification = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ninja_turtle)
.setTicker(text)
.setWhen(System.currentTimeMillis())
.setContentTitle(getText(R.string.info_service))
.setContentText(text)
.setContentIntent(contentIntent)
.build();
return notification;
}
}
Hope you guys can help me. Thanks in advance.
A few points:
There are two different sets of settings for scan period using the Android Beacon Library, foreground and background. When using the BackgroundPowerSaver as shown in the code, the Android Beacon Library automatically switches back and forth between the foreground scan period and the background scan period.
When using the BackgroundPowerSaver, manually setting beaconManager.setBackgroundMode(true) will only have an effect until the next time the app cycles to the foreground -- the BackgroundPowerSaver will change the value of this setting automatically.
The units of the scan periods is milliseconds. So setting beaconManager.setBackgroundBetweenScanPeriod(20l); sets the scan period to be 20 milliseconds. This is way too short to pick up beacons reliably. I recommend a minimum scan period of 1100 ms. The longer the period, the higher probability of detecting a beacon, but the more battery is used.
If you want to wait 10 seconds between scans, you want to set: beaconManager.setBackgroundBetweenScanPeriod(10000l); // 10000 ms = 10.0 secs
If you want the same scan periods to apply both in the foreground and background, simply set them to be the same:
beaconManager.setBackgroundBetweenScanPeriod(10000l);
beaconManager.setForegroundBetweenScanPeriod(10000l);
beaconManager.setBackgroundScanPeriod(1100l);
beaconManager.setForegroundScanPeriod(1100l);
Related
I am having the Xamarin.Forms application. I want to implement the local push notification for Android platform, through which an application can receive the notification whenever the app is minimized or phone is locked. I also want to wake-up the phone and display the notification on the lock screen whenever the phone is locked.
As I am new in this kindly help me.
Thanks in advance.
Vivek
I think you should consider two things.
1.Listen for the screen lock:
create a ScreenListener.cs :
public class ScreenListener
{
private Context mContext;
private ScreenBroadcastReceiver mScreenReceiver;
private static ScreenStateListener mScreenStateListener;
public ScreenListener(Context context)
{
mContext = context;
mScreenReceiver = new ScreenBroadcastReceiver();
}
/**
* screen BroadcastReceiver
*/
private class ScreenBroadcastReceiver : BroadcastReceiver
{
private String action = null;
public override void OnReceive(Context context, Intent intent)
{
action = intent.Action;
if (Intent.ActionScreenOn == action)
{ // screen on
mScreenStateListener.onScreenOn();
}
else if (Intent.ActionScreenOff == action)
{ // screen off
mScreenStateListener.onScreenOff();
}
else if (Intent.ActionUserPresent == action)
{ // unlock
mScreenStateListener.onUserPresent();
}
}
}
/**
* begin to listen screen state
*
* #param listener
*/
public void begin(ScreenStateListener listener)
{
mScreenStateListener = listener;
registerListener();
getScreenState();
}
/**
* get screen state
*/
private void getScreenState()
{
PowerManager manager = (PowerManager)mContext
.GetSystemService(Context.PowerService);
if (manager.IsScreenOn)
{
if (mScreenStateListener != null)
{
mScreenStateListener.onScreenOn();
}
}
else
{
if (mScreenStateListener != null)
{
mScreenStateListener.onScreenOff();
}
}
}
/**
* stop listen screen state
*/
public void unregisterListener()
{
mContext.UnregisterReceiver(mScreenReceiver);
}
/**
* regist screen state broadcast
*/
private void registerListener()
{
IntentFilter filter = new IntentFilter();
filter.AddAction(Intent.ActionScreenOn);
filter.AddAction(Intent.ActionScreenOff);
filter.AddAction(Intent.ActionUserPresent);
mContext.RegisterReceiver(mScreenReceiver, filter);
}
public interface ScreenStateListener
{// Returns screen status information to the caller
void onScreenOn();
void onScreenOff();
void onUserPresent();
}
}
then in the MainActivity.cs:
public class MainActivity : AppCompatActivity,ScreenStateListener
{
ScreenListener mScreenListener;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
mScreenListener = new ScreenListener(this);
}
protected override void OnDestroy()
{
base.OnDestroy();
mScreenListener.unregisterListener();
}
protected override void OnResume()
{
base.OnResume();
mScreenListener.begin(this);
}
public void onScreenOn()
{
Console.WriteLine("onScreenOn");
}
public void onScreenOff()
{
Console.WriteLine("onScreenOff");
}
public void onUserPresent()
{
Console.WriteLine("onUserPresent");
}
}
2.creat loacal Notification:
finally you just push the notification in the onScreenOff() method.
What you need is push notifications not local notifications. Local notifications only work if your app is running. Push notifications are handled by OS (Android in this case). Xamarin is recommending using Firebase service to handle it. Procedure is pretty straight forward all what you have to do is follow Tutorial from official Xamarin site.
Remote Notifications with Firebase Cloud Messaging
I am developing xamarin.Android app in MvvmCross. I want to call a service even when the App is backgrounded and a user is logged in. The problem is, I want to call this service within every say 2 hours whether the app is in foreground or background, just the user of the App needs to be logged in.
Intent loggedintent = new Intent(this,typeof(DeviceLoginHelper));
loggedintent.PutExtra("LoggedIn", true);
StartService(loggedintent);
I have written an android service:
[Service]
public class DeviceLoginHelper : IntentService
{
protected override void OnHandleIntent(Intent intent)
{
try
{
if(intent.HasExtra("LoggedIn"))
{
}
}
catch(Exception ex) { }
}
}
But how can I implement a timer? Where do I initialise and handle event to the timer. And when timer is elapsed when should I call ?
public override void OnDestroy()
{
try
{
base.OnDestroy();
}
catch(Exception ex){}
}
and when a user loges out i want to stop this service. Where do I put the call StopService() in MvvmCross
I would not use a Timer. Instead you should configure the AlarmManager.
[BroadcastReceiver]
public class AlarmReceiver : BroadcastReceiver
{
private static AlarmManager alarmMgr;
private static PendingIntent alarmIntent;
public const int NOTIFICATION_ID = 1;
public const int IDLE_TIME_MS = 30 * 1000; // 30-seconds (update here)
private NotificationManager mNotificationManager;
Notification.Builder builder;
public override void OnReceive(Context context, Intent intent)
{
// Do something when alarm triggers (here I'm building notification)
BuildNotification(context);
// reschedule alarm
ScheduleAlarm(IDLE_TIME_MS);
}
public static Context ApplicationContext { get; set; }
public static void ScheduleAlarm(int milliseconds)
{
if (milliseconds == 0) return;
alarmMgr = (AlarmManager)ApplicationContext.GetSystemService(Context.AlarmService);
var intent = new Intent(ApplicationContext, typeof(AlarmReceiver));
alarmIntent = PendingIntent.GetBroadcast(ApplicationContext, 0, intent, 0);
alarmMgr.Set(AlarmType.ElapsedRealtimeWakeup,
SystemClock.ElapsedRealtime() + milliseconds, alarmIntent);
}
private void BuildNotification(Context context)
{
mNotificationManager = (NotificationManager)context.GetSystemService(Context.NotificationService);
var contentIntent = PendingIntent.GetActivity(context, 0, new Intent(context, typeof(MainView)), 0);
var message = $"Time is up";
var mBuilder = new Notification.Builder(context)
.SetAutoCancel(true)
.SetPriority(NotificationCompat.PriorityMax)
.SetDefaults(NotificationDefaults.All)
.SetContentTitle("Time is up")
.SetStyle(new Notification.BigTextStyle()
.BigText(message))
.SetContentText(message)
.SetSmallIcon(Resource.Drawable.ic_launcher);
mBuilder.SetContentIntent(contentIntent);
mNotificationManager.Notify(NOTIFICATION_ID, mBuilder.Build());
}
}
In your startup code, simply call:
AlarmReceiver.ApplicationContext = context;
AlarmReceiver.ScheduleAlarm(timeInMs);
I am trying to run a simple Android Thing project that simply captures and renders the captured image in the display. I took the sample code from (https://github.com/googlecodelabs/androidthings-imageclassifier/tree/master/imageclassifier-add-camera) without the image recognition part. But I'm getting the following error-
I/InstantRun: starting instant run server: is main process
I/CameraManagerGlobal: Connecting to camera service
D/CameraHandler: Using camera id 0
W/CameraHandler: Cannot capture image. Camera not initialized.
D/CameraHandler: Opened camera.
So it seems it detects the camera but it can't capture images from the camera. Anyone faced similar issues on AndroidThings platform?
Main Camera Handler code provided below-
public class CameraHandler {
private static final String TAG = CameraHandler.class.getSimpleName();
public static final int IMAGE_WIDTH = 320;
public static final int IMAGE_HEIGHT = 240;
private static final int MAX_IMAGES = 1;
private CameraDevice mCameraDevice;
private CameraCaptureSession mCaptureSession;
/**
* An {#link android.media.ImageReader} that handles still image capture.
*/
private ImageReader mImageReader;
// Lazy-loaded singleton, so only one instance of the camera is created.
private CameraHandler() {
}
private static class InstanceHolder {
private static CameraHandler mCamera = new CameraHandler();
}
public static CameraHandler getInstance() {
return InstanceHolder.mCamera;
}
/**
* Initialize the camera device
*/
public void initializeCamera(Context context,
Handler backgroundHandler,
ImageReader.OnImageAvailableListener imageAvailableListener) {
// Discover the camera instance
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
String[] camIds = {};
try {
camIds = manager.getCameraIdList();
} catch (CameraAccessException e) {
Log.d(TAG, "Cam access exception getting IDs", e);
}
if (camIds.length < 1) {
Log.d(TAG, "No cameras found");
return;
}
String id = camIds[0];
Log.d(TAG, "Using camera id " + id);
// Initialize the image processor
mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT,
ImageFormat.JPEG, MAX_IMAGES);
mImageReader.setOnImageAvailableListener(
imageAvailableListener, backgroundHandler);
// Open the camera resource
try {
manager.openCamera(id, mStateCallback, backgroundHandler);
} catch (CameraAccessException cae) {
Log.d(TAG, "Camera access exception", cae);
}
}
/**
* Callback handling device state changes
*/
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(#NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Opened camera.");
mCameraDevice = cameraDevice;
}
#Override
public void onDisconnected(#NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Camera disconnected, closing.");
closeCaptureSession();
cameraDevice.close();
}
#Override
public void onError(#NonNull CameraDevice cameraDevice, int i) {
Log.d(TAG, "Camera device error, closing.");
closeCaptureSession();
cameraDevice.close();
}
#Override
public void onClosed(#NonNull CameraDevice cameraDevice) {
Log.d(TAG, "Closed camera, releasing");
mCameraDevice = null;
}
};
/**
* Begin a still image capture
*/
public void takePicture() {
if (mCameraDevice == null) {
Log.w(TAG, "Cannot capture image. Camera not initialized.");
return;
}
// Here, we create a CameraCaptureSession for capturing still images.
try {
mCameraDevice.createCaptureSession(
Collections.singletonList(mImageReader.getSurface()),
mSessionCallback,
null);
} catch (CameraAccessException cae) {
Log.d(TAG, "access exception while preparing pic", cae);
}
}
/**
* Callback handling session state changes
*/
private CameraCaptureSession.StateCallback mSessionCallback =
new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(#NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (mCameraDevice == null) {
return;
}
// When the session is ready, we start capture.
mCaptureSession = cameraCaptureSession;
triggerImageCapture();
}
#Override
public void onConfigureFailed(#NonNull CameraCaptureSession cameraCaptureSession) {
Log.w(TAG, "Failed to configure camera");
}
};
/**
* Execute a new capture request within the active session
*/
private void triggerImageCapture() {
try {
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
//captureBuilder.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_AUTO);
Log.d(TAG, "Capture request created.");
mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, null);
} catch (CameraAccessException cae) {
Log.d(TAG, "camera capture exception");
}
}
/**
* Callback handling capture session events
*/
private final CameraCaptureSession.CaptureCallback mCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
#Override
public void onCaptureProgressed(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull CaptureResult partialResult) {
Log.d(TAG, "Partial result");
}
#Override
public void onCaptureCompleted(#NonNull CameraCaptureSession session,
#NonNull CaptureRequest request,
#NonNull TotalCaptureResult result) {
session.close();
mCaptureSession = null;
Log.d(TAG, "CaptureSession closed");
}
};
private void closeCaptureSession() {
if (mCaptureSession != null) {
try {
mCaptureSession.close();
} catch (Exception ex) {
Log.e(TAG, "Could not close capture session", ex);
}
mCaptureSession = null;
}
}
/**
* Close the camera resources
*/
public void shutDown() {
closeCaptureSession();
if (mCameraDevice != null) {
mCameraDevice.close();
}
}
}
The pitfall with camera:
Check the permissions in Manifest file, and restart the device.
The camera-permission is granted not after installing the application, but first after install and RESTART of device.
see https://developer.android.com/things/sdk/index.html
I am trying to use AsyncTask and the activity is working perfectly but I am facing a problem. The problem comes after I have pressed back button, because pressing this button my app crashes. I have seen other posts saying that I must cancel the task in onStop() and onDestroy() but still crashes. Does anyone have any idea how can I solve that?
private class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> {
#Override
protected Boolean doInBackground(Void... args0) {
for(int i=1; i<=10; i++) {
try{
if(isCancelled())
break;
publishProgress(i*10);
Thread.sleep(1000);
}catch (InterruptedException e){}
}
return null;
}
/*
* it will update the publishProgress method and it will
* update the interface (if it's necessary)
*/
#Override
protected void onProgressUpdate(Integer... values) {
int progreso = values[0].intValue();
pbarProgreso.setProgress(progreso); // showing progress
pbarProgreso.setSecondaryProgress(progreso + 5);
}
/*
* Initializing progressBar
*/
#Override
protected void onPreExecute() {
pbarProgreso.setMax(100); // maximum value for process bar
pbarProgreso.setProgress(0); // minimum value to start
}
#Override
protected void onPostExecute(Boolean result) {
if(!this.isCancelled())
Toast.makeText(getApplicationContext(), "Task finished!",
Toast.LENGTH_SHORT).show();
}
#Override
protected void onCancelled() {
Toast.makeText(getApplicationContext(), "Task cancelled!",
Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onStop() {
super.onStop();
if(task != null && task.getStatus() == Status.RUNNING){
task.cancel(true);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if(task != null && task.getStatus() == Status.RUNNING) {
task.cancel(true);
}
}
It's because when you press back button, AsyncTask continues to work, and when it do context related work, that context no longer exists, and a crash happens, it's better to use a "isResumed" boolean indicator variable inside an activity, and set it to false in onPause and set to true inside onResume, and inside an AsyncTask do context related things, inside an if condition.
Or if this code is inside a fragment, can use isAdded() method, to check if fragment is active or not. Cancel is also important, but there may be a delay, between an Activity pause and AsyncTask cancel, so keeping that variable is important.
I have created a widget to display the slideshow.In firefox,everything is fine but in chrome nothing happens. After I refresh with many times, the slideshow is displayed. I don't know Why. Can you give me some ideas? Tks
This is my GWT client:
public SlideClient() {
super();
setStyleName("flexslider");
setHeight("100%");
setWidth("100%");
}
#Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
this.client = client;
this.paintableId = uidl.getId();
listImage = Arrays.asList(uidl.getStringArrayAttribute("listImage"));
listUrl = Arrays.asList(uidl.getStringArrayAttribute("listUrl"));
loadImage();
checkImagesLoadedTimer.run();
}
public void display() {
m.setStyleName("slides");
m.setHeight("100%");
m.setWidth("100%");
add(m);
}
public native void slideshow() /*-{
$wnd.$('.flexslider').flexslider({slideshowSpeed: 2000});
}-*/;
public native String getURL(String url)/*-{
return $wnd.open(url,
'target=_blank')
}-*/;
private Timer checkImagesLoadedTimer = new Timer() {
#Override
public void run() {
if (loadedImageElements.size() == toLoad) {
display();
} else {
add(new Label("đang load "+loadedImageElements.size()));
checkImagesLoadedTimer.schedule(2000);
}
}
};
private void loadImage() {
for (String tmp : listImage) {
AbsolutePanel panel = new AbsolutePanel();
final Image ima = new Image(tmp);
add(new Label("before put"));
ima.addLoadHandler(new LoadHandler() {
#Override
public void onLoad(LoadEvent event) {
loadedImageElements.put(toLoad+"", ima);
slideshow();
add(new Label("đang put "+loadedImageElements.size()));
}
});
add(new Label("after put"));
panel.add(ima);
m.add(panel);
if (toLoad != 0) {
panel.setVisible(false);
}
toLoad++;
}
}
}
Did you implement an Image Loader to prepare your images before they are displayed? A clean solution would be to add the image elements to your page root as an invisible istance, wait for them to load and then use them elsewhere.
You should check out the tutorials about ImageBundling as well: ImageResource
Here's a little extract from one of my image loader classes as you requested, altough there are different ways to realize that:
private HashMap<String,ImageElement> loadedImageElements = new HashMap<String,ImageElement>();
private int toLoad = 0;
private void loadImage(final String name, String url){
final Image tempImage = new Image(url);
RootPanel.get().add(tempImage);
++toLoad;
tempImage.addLoadHandler(new LoadHandler(){
public void onLoad(LoadEvent event) {
loadedImageElements.put(name,ImageElement.as(tempImage.getElement()));
tempImage.setVisible(false);
}
});
}
The image url is retrieved via a ClientBundle-Interface pointing towards the real positions of the images.
I also implemented a timer running in the background to check if all the images have been loaded:
private Timer checkImagesLoadedTimer = new Timer(){
public void run() {
System.out.println("Loaded " + loadedImageElements.size() + "/" + toLoad + " Images.");
if(loadedImageElements.size() == toLoad){
buildWidget();
}else{
checkImagesLoadedTimer.schedule(50);
}
}
};
After everythign is ready, the original widget/page is created.
But as I said there are many ways to implement image loaders. Try out different implementations and select one that suits your needs best.