package anywheresoftware.b4a.objects;
import java.util.ArrayList;
import android.app.WallpaperManager;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.Events;
import anywheresoftware.b4a.BA.Hide;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.keywords.Common;
import anywheresoftware.b4a.objects.drawable.CanvasWrapper;
import anywheresoftware.b4a.objects.drawable.CanvasWrapper.BitmapWrapper;
import anywheresoftware.b4a.objects.drawable.CanvasWrapper.RectWrapper;
@Hide
public class WallpaperInternalService extends WallpaperService{
@Override
public void onCreate() {
super.onCreate();
try {
Intent i = new Intent(this, Class.forName(getPackageName() + ".wallpaperservice"));
startService(i);
} catch (ClassNotFoundException e) {
Common.Log("WallpaperService not found.");
throw new RuntimeException(e);
}
}
@Override
public Engine onCreateEngine() {
LWEngine l = new LWEngine();
B4AEngine b = new B4AEngine(l);
l.engine = b;
b.setTouchEventsEnabled(LWManager.acceptTouch);
LWManager.engines.add(l);
return b;
}
/**
* Manages the wallpaper events and the timer.
*A tutorial is available <link>here|http://www.b4x.com/forum/basic4android-getting-started-tutorials/12605-android-live-wallpaper-tutorial.html</link>.
*/
@Events(values={
"SizeChanged (Engine As LWEngine)",
"Touch (Engine As LWEngine, Action As Int, X As Float, Y As Float)",
"VisibilityChanged (Engine As LWEngine, Visible As Boolean)",
"EngineDestroyed (Engine As LWEngine)",
"Tick (Engine As LWEngine)",
"OffsetChanged (Engine As LWEngine)"
})
@Version(1.01f)
@ShortName("LWManager")
public static class LWManager {
private static String eventName;
private static BA ba;
static ArrayList<LWEngine> engines = new ArrayList<LWEngine>();
static int interval;
static boolean atLeastOneVisible = false;
static boolean acceptTouch;
/**
* Initializes the object.
*EventName - Sets the Subs that will handle the events.
*TouchEventsEnabled - Whether the wallpaper should raise the Touch event when the user touches the screen.
*/
public static void Initialize(String EventName, boolean TouchEventsEnabled, BA ba) {
if (ba.sharedProcessBA == null || ba.sharedProcessBA.isService == false) {
throw new RuntimeException("LWManager can only be added to a service.");
}
acceptTouch = TouchEventsEnabled;
LWManager.ba = ba;
LWManager.eventName = EventName.toLowerCase(BA.cul);
}
/**
* Starts the internal timer.
*IntervalMs - Interval in milliseconds.
*/
public static void StartTicking(int IntervalMs) {
interval = IntervalMs;
if (TickTack.isQueued == false) {
TickTack tt = new TickTack();
BA.handler.postDelayed(tt, interval);
}
}
/**
* Stops the internal timer.
*/
public static void StopTicking() {
interval = 0;
}
static void raiseEvent(String event, Object... params) {
ba.raiseEvent(null, eventName + "_" + event, params);
}
static void tick() {
for (LWEngine e : engines) {
if (e.engine.isVisible() == true) {
ba.raiseEvent(null, eventName + "_tick", e);
}
}
}
static void checkIfThereIsAtLeastOneVisible() {
if (interval == 0)
return;
atLeastOneVisible = false;
for (LWEngine e : engines) {
if (e.engine.isVisible() == true) {
atLeastOneVisible = true;
break;
}
}
if (atLeastOneVisible) {
StartTicking(interval);
}
}
static class TickTack implements Runnable {
static boolean isQueued;
@Override
public void run() {
if (interval == 0 || atLeastOneVisible == false) {
isQueued = false;
return;
}
isQueued = true;
BA.handler.postDelayed(this,interval);
tick();
}
}
}
/**
* Represents a wallpaper instance.
*A tutorial is available <link>here|http://www.b4x.com/forum/basic4android-getting-started-tutorials/12605-android-live-wallpaper-tutorial.html</link>.
*/
@ShortName("LWEngine")
public static class LWEngine {
B4AEngine engine;
/**
* Gets or sets the Tag value. This is a place holder which can used to store additional data.
*/
public Object Tag;
/**
* A convenient Rect object which you can use. This object is not used internally.
*/
public RectWrapper Rect = new RectWrapper();
/**
* Tests whether this wallpaper is running in "preview mode".
*/
public boolean getIsPreview() {
return engine.isPreview();
}
/**
* Tests whether this wallpaper is visible.
*/
public boolean getIsVisible() {
return engine.isVisible();
}
public LWEngine() {
Rect.Initialize(0, 0, 0, 0);
}
/**
* Returns the canvas which is used to draw on the wallpaper.
*Changes will not be visible till you call Refresh or RefreshAll.
*/
public CanvasWrapper getCanvas() {
return engine.cw;
}
/**
* Refreshes the complete screen.
*/
public void RefreshAll() {
Refresh(engine.all);
}
/**
* Refreshes the given region.
*/
public void Refresh(Rect DirtyRect) {
SurfaceHolder sh = engine.getSurfaceHolder();
Canvas c = sh.lockCanvas(DirtyRect);
if (c == null) {
Common.Log("Null canvas returned.");
return;
}
try {
c.drawBitmap(engine.bg, DirtyRect, DirtyRect, null);
}
finally {
sh.unlockCanvasAndPost(c);
}
}
/**
* Tests whether this object is initialized.
*/
public boolean IsInitialized() {
return engine != null;
}
/**
* Returns the current horizontal offset related to the full wallpaper width.
*/
public int getCurrentOffsetX() {
return engine.offsetX;
}
/**
* Returns the current vertical offset related to the full wallpaper height.
*/
public int getCurrentOffsetY() {
return engine.offsetY;
}
/**
* Returns the screen width.
*/
public int getScreenWidth() {
return engine.width;
}
/**
* Returns the screen height.
*/
public int getScreenHeight() {
return engine.height;
}
/**
* Returns the full wallpaper width. A wallpaper can be made of several screens.
*/
public int getFullWallpaperWidth() {
return engine.fullWidth;
}
/**
* Returns the full wallpaper height.
*/
public int getFullWallpaperHeight() {
return engine.fullHeight;
}
}
@Hide
public class B4AEngine extends Engine {
private LWEngine lw;
Rect all = new Rect(0, 0, 0, 0);
Bitmap bg;
CanvasWrapper cw = new CanvasWrapper();
int offsetX, offsetY;
int fullWidth, fullHeight;
int width, height;
public B4AEngine(LWEngine l) {
lw = l;
}
@Override
public void onVisibilityChanged(boolean visible) {
LWManager.checkIfThereIsAtLeastOneVisible();
LWManager.raiseEvent("visibilitychanged", lw, visible);
}
@Override
public void onTouchEvent(MotionEvent event) {
LWManager.raiseEvent("touch", lw, event.getAction(), event.getX(), event.getY());
}
@Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep,
int xPixelOffset, int yPixelOffset) {
offsetX = -xPixelOffset;
offsetY = -yPixelOffset;
LWManager.raiseEvent("offsetchanged", lw);
}
@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (bg != null)
bg.recycle();
BitmapWrapper bw = new BitmapWrapper();
bw.InitializeMutable(width, height);
bg = bw.getObject();
cw.Initialize2(bg);
this.width = width;
this.height = height;
all.right = width;
all.bottom = height;
fullWidth = WallpaperManager.getInstance(BA.applicationContext).getDesiredMinimumWidth();
fullHeight = WallpaperManager.getInstance(BA.applicationContext).getDesiredMinimumHeight();
LWManager.raiseEvent("sizechanged", lw);
}
@Override
public void onDestroy() {
LWManager.raiseEvent("enginedestroyed", lw);
LWManager.engines.remove(lw);
}
}
}