First experiments with Android

First experiments with Android

During the previous weeks I have modified JPARSEC code to make it as independent as possible from AWT and Swing Java libraries. My intenction was to support Android, but since I have no experience at all in this platform I wasn't sure if it is even possible. Latest release of JPARSEC is in fact independent from those libraries, using an interface (jparsec.graph.chartRendering.Graphics) which is implemented in class AWTGraphics for Java desktop platform. The solution is simple and fine, although code is a little dark in some points due to the requirements of the port. In the last few days I have succesfully implemented an AndroidGraphics version with the same features, though the superscript/subscript (and color/size change) within labels is something more difficult and not useful for sky and planetary rendering.

Starting with the Android SDK

The instructions to install the Android SDK can be found here, and for configuring here. My working PC is a Mepis 11 64-bit Linux desktop running on a Core i5 CPU, 8GB RAM, and NVIDIA GT530. To install the release 18 of the SDK I had to upgrade my 3.5 version of Eclipse to 3.7, and then I installed it manually since the guided mode of Eclipse didn't work. During the installation I found problems when downloading any additional packages besides those corresponding to the Android Virtual Machines (VM). In fact, to install the VMs I had to 'insist' several times (it seems I was able only to install one package at a time, when being lucky). Finally I installed all VMs with the SDK Manager and configured them using the AVD Manager from Eclipse, assigning around 512 MB of RAM to each VM.

Of course, I started with the 'Hellow World' typical case. I found that only Android 1.6 and 4.0.3 VMs were working, those for 2.1 and 3.2 didn't boot (I waited for about 10 minutes). Getting used to the Android emulators was a little tricky. The Android 4 emulator, although worked, showed a 'heap error' when installing a given application more than once, inviting me to switch off Eclipse and start again. Fortunately the 1.6 emulator was more friendly and fast to boot, so I adopted it as my working environment. In fact, I was glad of it, since I own an Android 1.6 phone. Another issue I had is that after installing a given app a few times I was always runnning into another out of memory error. I finally realize that the apps are installed to the device memory instead of the virtual SD card, so I had to use the emulator as a 'phone' with the mouse to manually uninstall the app and get enough memory to continue working. Too realistic, doesn't seem ideal when developing apps.

An annoying problem is the debugging. Fortunately I have a generic JPARSECException class for handling errors and a Logger class for debug messages, because System.out.println will produce exceptions (it took me a while to find that!). However, the biggest issue is the extremely low performance/speed of the emulation. Any code change requires minutes to see the results, it remembers me the time I developed apps with Windows and Fortran. It seems the SDK only emulates one ARM processor, which is extremely slow compared to those present in any phone or tablet nowadays.

Implementing AndroidGraphics

I started a new project in Eclipse for Android 1.6 and simply copied everything from JPARSEC tree inside. Then, I eliminated everything I didn't need and fixed the compilation errors. To my surprise, I found few errors and this step was quite easy. At the same time I wrote a basic Activity class to render the sky and I implemented in the new AndroidGraphics a few methods just to see something in the emulator. Once everything was ready I starting to launch the program. First tries showed continues Java heap errors due to the large number of static content in JPARSEC. I finally decided to reduce the library to the minimum, eliminating JPL and ELP2000 ephemerides, and other things that requires a lot of static or disk memory. The only ephemeris method I left available for planets was the one by S. Moshier. Finally the app agreed to install in the emulator, and after a few memory tweaks I managed to run it succesfully and see a mere rectangle on the screen. But the time required to execute it and see that rectangle (without including the emulator startup time) was 7 minutes!

So the main work was then to complete the graphics implementation. AndroidGraphics is basically an AWT replacement for Android. It uses a Canvas and Paint objects to render into an Android's Bitmap. Everything needed from Java to render sky and planets is available in Canvas and Paint quite straightforward. In fact, the implementation is more clean in Android since it uses enums to help the developer, although other things like drawing text inside the clipped rectangle is more tricky and the result in the screen is worse. There are some minor differences in the pixel level between Android and Java when considering the limits of the rectangle, clipping region, or drawing/filling ovals within a rectangle, but finally each method of the interface was succesfully implemented (after minutes of testing for each and several iterations…). The most tricky part was to draw text with a given rotation angle and a clipping rectangle, without the forums probably I wouldn't found how to do that.

package jparsec.graph.chartRendering;
 
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Path.FillType;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region.Op;
import android.graphics.Typeface;
import jparsec.graph.DataSet;
import jparsec.graph.JPARSECStroke;
import jparsec.math.Constant;
 
/**
 * Implements the {@linkplain Graphics} interface for Android platform.
 * 
 * @author T. Alonso Albi - OAN (Spain)
 * @version 1.0
 */
public class AndroidGraphics implements Graphics {
 
	/**
	 * The color mode.
	 */
	private ANAGLYPH_COLOR_MODE colorMode = ANAGLYPH_COLOR_MODE.GREEN_RED;
 
	private Bitmap image;
	private Paint paint;
	private Canvas g;
	private FillType criteria = Path.FillType.WINDING;
	private FONT font = FONT.DIALOG_BOLD_12;
 
	/**
	 * Creates a new Graphics provider for Android platform,
	 * with no anaglyph mode. Antialiasing is enabled by default.
	 * @param w The width of the image to render.
	 * @param h The height.
	 */
	public AndroidGraphics(int w, int h) {
		image = Bitmap.createBitmap(w, h, Config.ARGB_8888);
		g = new Canvas(image);
		paint = new Paint();
		this.colorMode = Graphics.ANAGLYPH_COLOR_MODE.NO_ANAGLYPH;
		this.enableAntialiasing();
	}
 
	/**
	 * Creates a new Graphics provider for Android platform.
	 * Antialiasing is enabled by default.
	 * @param w The width of the image to render.
	 * @param h The height.
	 * @param mode The color mode, analyph or normal.
	 */
	public AndroidGraphics(int w, int h, ANAGLYPH_COLOR_MODE mode) {
		image = Bitmap.createBitmap(w, h, Config.ARGB_8888);
		g = new Canvas(image);
		paint = new Paint();
		this.colorMode = mode;
		this.enableAntialiasing();
	}
 
	/**
	 * Creates a new Graphics provider for Android platform.
	 * Antialiasing is enabled by default.
	 * @param g The Graphics object from AWT to obtain default color, font, and clip region.
	 * @param w The width of the image to render.
	 * @param h The height.
	 * @param mode The color mode, analyph or normal.
	 */
	public AndroidGraphics(AndroidGraphics ag, int w, int h, ANAGLYPH_COLOR_MODE mode) {
		this.colorMode = mode;
		image = Bitmap.createBitmap(w, h, Config.ARGB_8888);
		g = new Canvas(image);
		this.paint = ag.paint;
		this.g.clipRect(g.getClipBounds(), Op.REPLACE);
		this.enableAntialiasing();
	}
 
	@Override
	public void drawLine(int i, int j, int k, int l) {
		g.drawLine(i, j, k, l, paint);
	}
 
	@Override
	public void setColor(int r, int g, int b, int a) {
		paint.setARGB(a, r, g, b);
	}
 
	@Override
	public void drawImage(Object img, int x, int y) {
		Bitmap bm = (Bitmap) img;
		g.drawBitmap(bm, x - x, y, null);
	}
 
...
 
	@Override
	public void drawRotatedString(String label, double i, double j, double k) {
		label = fix(label);
 
		g.save();
		int c[] = this.getClip();
		Rectangle r = this.getStringBounds(label);
		float hw = (float) (r.getWidth()/2.0);
		float hh = (float) (r.getHeight()/2.0);
		float xc = (float) (i + hw);
		float yc = (float) (j + hh);
		if ((c[0] == 0 && c[1] == 0 && c[2] == image.getWidth() && c[3] == image.getHeight()) ||
				((xc-c[0]) > hw && (c[0]+c[2]-xc) > hw && (yc-c[1]) > hh && (c[1]+c[3]-yc) > hh)) {
			if (k != 0.0) g.rotate((float) (-k * Constant.RAD_TO_DEG), (float) i, (float) j);
			this.g.drawText(label, (float)i, (float)j, paint);
			g.restore();
			return;
		}
		if (k != 0.0) g.rotate((float) (-k * Constant.RAD_TO_DEG), (float) i, (float) j);
		Path p = new Path();
		paint.getTextPath(label, 0, label.length(), (float) i, (float) j, p);
		g.clipPath(p, Op.INTERSECT);
		Style s = paint.getStyle();
		paint.setStyle(Style.FILL);
		g.drawPath(p, paint);
		g.restore();
		paint.setStyle(s);
	}
 
...
}

The ANDROIDSkyRenderActivity activity

With everything ready I finally polished the activity class, adding basic drag/zoom support and even taking user's location from GPS. My app simulates the sky at current time, from Madrid by default, and in Spanish language, drawing basically the constellations visible. It runs close to the limit of available memory, so I still cannot show planets, but it works!

When running on the Android 1.6 emulator at 480×800 px of resolution, initial startup time of the app is 7 minutes, and, when dragging, each image is rendered in 10s (that's slow!). When executing it again once installed (from the emulator, not from Eclipse), startup time is now 36s, and rendering time 2s, around 200 times slower than my desktop PC. On my HTC Tattoo phone at 240×320 px, the program starts after 2 minutes and requires also 2s of rendering time. Despite my phone is old, the app runs smooth with apparently no memory issues. On the emulators (specially the 1.6) the app crashes easily due to a 16 MB memory limitation build internally to preserve the stability of the Android system, it doesn't matter how much RAM I put on the VM.



 

Previous figures show the great results, very close in quality to the desktop version of JPARSEC. There are still big issues with memory. Although the port took me only 4 days of work, it seems to me that Android SDK needs an urgent development to make it faster and more stable, and memory limits should be reduced since current tablets (specially those running Android 4) are powerful, and even old phones run apps fluently. Current limitations make me think I will not develope, at least now, anything serious for Android, I will just complete the port of the library and the graphics core. It has no sense to develope something for Android 4 in an emulation environment which is around 10 times slower than any real Android 4 device, and stops you continuously with heap memory errors.

This Android port is still in experimental phase, I plan to release the complete source code for AndroidGraphics and JPARSEC soon, after reducing the memory problems to allow also planetary rendering on the sky. Below I show the code of the activity class. If you want to test the app, you can download the apk install package here.

ANDROIDSkyRenderActivity.java
package jparsec.android;
 
import jparsec.astronomy.Constellation;
import jparsec.astronomy.TelescopeElement;
import jparsec.astronomy.CoordinateSystem.COORDINATE_SYSTEM;
import jparsec.ephem.EphemerisElement;
import jparsec.ephem.Target.TARGET;
import jparsec.graph.chartRendering.AndroidGraphics;
import jparsec.graph.chartRendering.Graphics;
import jparsec.graph.chartRendering.PlanetRenderElement;
import jparsec.graph.chartRendering.Projection;
import jparsec.graph.chartRendering.Projection.PROJECTION;
import jparsec.graph.chartRendering.RenderSky;
import jparsec.graph.chartRendering.SkyRenderElement;
import jparsec.math.Constant;
import jparsec.math.FastMath;
import jparsec.observer.City;
import jparsec.observer.CityElement;
import jparsec.observer.ObserverElement;
import jparsec.time.AstroDate;
import jparsec.time.TimeElement;
import jparsec.time.TimeElement.SCALE;
import jparsec.util.Configuration;
import jparsec.util.JPARSECException;
import jparsec.util.Logger;
import jparsec.util.Logger.LEVEL;
import jparsec.util.Translate;
 
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
 
/**
 * A simple test to show sky rendering on Android platform, with drag/zoom support.
 * 
 * @author T. Alonso Albi - OAN (Spain)
 * @version 1.0
 */
public class ANDROIDSkyRenderActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
                                WindowManager.LayoutParams.FLAG_FULLSCREEN);
 
        setContentView(new myView(this));
    }
 
    private class myView extends View implements LocationListener {	
    	private Bitmap myBitmap = null;
    	private boolean drawn = false, drawing = false;
    	private RenderSky skyRender = null;
        private float lastMouseClickX = 0, lastMouseClickY = 0;
        private double fastField;
        private boolean textures;
        private ObserverElement observer;
        private TimeElement time;
        private EphemerisElement eph;
 
        public myView(Context context) {
        	super(context);
        	this.setFocusable(true);
        	this.setFocusableInTouchMode(true);
        }
 
    	 @Override
    	 protected void onDraw(Canvas canvas) {
    		 drawing = true;
    		 myBitmap = getSkyRendering(canvas.getWidth(), canvas.getHeight());
 
    		 if (myBitmap != null) {
    			 canvas.drawBitmap(myBitmap, 0, 0, null);
    			 drawn = true;
    			 Logger.log(LEVEL.INFO, "EVERYTHING FINE, SKY RENDERING DRAWN !!!");
    		 } else {
    			 Logger.log(LEVEL.INFO, "Everything ends, but image was null. Nothing was drawn ...");
    		 }
    		 drawing = false;
    	 }
 
    	 @Override
    	 public boolean onTouchEvent(MotionEvent event) {
    		 if (!drawn || drawing) return false;
 
    		 int action = event.getAction();
			 float x = event.getX();
			 float y = event.getY();
//    		 if (action == MotionEvent.ACTION_MOVE || action == MotionEvent.ACTION_DOWN ||
//   				 action == MotionEvent.ACTION_UP)  // Not fast enough ...
			 if (action == MotionEvent.ACTION_UP) 
    		 {
 				double v = 2 * fastField / skyRender.render.width;
 				float dx = x - lastMouseClickX;
 				float dy = y - lastMouseClickY;
 				double moveAngleX = v * dx;
 				double moveAngleY = v * dy;
   				if (skyRender.render.coordinateSystem == COORDINATE_SYSTEM.HORIZONTAL) {
   					moveAngleX = -moveAngleX;
   					int ycenter = 0;
   	  				if ((skyRender.render.projection == Projection.PROJECTION.STEREOGRAPHICAL ||
   	  					skyRender.render.projection == Projection.PROJECTION.SPHERICAL) &&
   	  					skyRender.render.centralLatitude < 15.0 * Constant.DEG_TO_RAD && 
 	  					skyRender.render.coordinateSystem == COORDINATE_SYSTEM.HORIZONTAL && 
   	  					fastField > 120 * Constant.DEG_TO_RAD) // Horizon-view mode 
   	  					ycenter = (skyRender.render.height-100)/2;
   	  				try {
 						skyRender.setYCenterOffset(ycenter);
 					} catch (Exception e1) {
          				Logger.log(LEVEL.ERROR, "Error setting center y value. Message was: "+e1.getLocalizedMessage()+". Trace: "+JPARSECException.getTrace(e1.getStackTrace()));
 					}
   				}
 				skyRender.render.centralLongitude += moveAngleX;
 				skyRender.render.centralLatitude += moveAngleY;
 				if (skyRender.render.centralLatitude > Constant.PI_OVER_TWO)
 					skyRender.render.centralLatitude = Constant.PI_OVER_TWO;
 				if (skyRender.render.centralLatitude < -Constant.PI_OVER_TWO)
 					skyRender.render.centralLatitude = -Constant.PI_OVER_TWO;
   				if (skyRender.render.centralLongitude > Constant.TWO_PI)
   					skyRender.render.centralLongitude -= Constant.TWO_PI;
   				if (skyRender.render.centralLongitude < 0)
   					skyRender.render.centralLongitude += Constant.TWO_PI;
   				lastMouseClickX = x;
   				lastMouseClickY = y;
 				skyRender.render.planetRender.textures = false;
 				if (action == MotionEvent.ACTION_UP) skyRender.render.planetRender.textures = textures;
 				invalidate();
 				return true;
    		 }
    		 if (action == MotionEvent.ACTION_DOWN) {
				 lastMouseClickX = x;
				 lastMouseClickY = y;
    		 }
    		 return true;
    	 }
 
    	 @Override
    	 public boolean onKeyDown(int keyCode, KeyEvent event) {
    		 if (!drawn || drawing) return false;
 
			 int key = event.getKeyCode();
    		 if (key == KeyEvent.KEYCODE_DPAD_UP ||
    				 key == KeyEvent.KEYCODE_DPAD_DOWN) 
    		 {
    			 if (key == KeyEvent.KEYCODE_DPAD_DOWN) {
    				 skyRender.render.telescope.ocular.focalLength *= 1.2f;
    			 } else {
    				 skyRender.render.telescope.ocular.focalLength /= 1.2f;					
    			 }
    			 try { 
    				 fastField = skyRender.render.telescope.getField();
    				 double min = 30 * Constant.ARCSEC_TO_RAD, max = 360 * Constant.DEG_TO_RAD;
    				 if (fastField < min) {
    					 skyRender.render.telescope.ocular.focalLength *= min / fastField;
    					 fastField = skyRender.render.telescope.getField();						
    				 }
    				 if (fastField > max) {
    					 skyRender.render.telescope.ocular.focalLength *= max / fastField;
    					 fastField = skyRender.render.telescope.getField();						
    				 }
    			 } catch (Exception exc) {
    				 Logger.log(LEVEL.ERROR, "Error processing mouse wheel. Message was: "+exc.getLocalizedMessage()+". Trace: "+JPARSECException.getTrace(exc.getStackTrace()));
    			 }
    			 int ycenter = 0;
    			 if ((skyRender.render.projection == Projection.PROJECTION.STEREOGRAPHICAL ||
  					skyRender.render.projection == Projection.PROJECTION.SPHERICAL) &&
  					skyRender.render.coordinateSystem == COORDINATE_SYSTEM.HORIZONTAL && 
  					skyRender.render.centralLatitude < 15.0 * Constant.DEG_TO_RAD && 
  					fastField > 120 * Constant.DEG_TO_RAD) // Horizon-view mode 
  					ycenter = (skyRender.render.height-100)/2;
    			 try {
    				 skyRender.setYCenterOffset(ycenter);
    			 } catch (Exception e1) {
    				 Logger.log(LEVEL.ERROR, "Error setting center y value. Message was: "+e1.getLocalizedMessage()+". Trace: "+JPARSECException.getTrace(e1.getStackTrace()));
    			 }
    			 skyRender.render.planetRender.textures = false;
    			 invalidate();
    			 return true;
    		 }
    		 return false;
    	 }
 
    	 private Bitmap getSkyRendering(int w, int h) 
    	 {
    		 try
    		 {
    			 if (skyRender != null) {
    				 Graphics g = new AndroidGraphics(skyRender.render.width, skyRender.render.height);
					 skyRender.renderize(g);
 
					 return (Bitmap) skyRender.getImage();
	    		 }
 
	    		 Translate.setDefaultLanguage(Translate.LANGUAGE.SPANISH); // To render in Spanish
	    		 Configuration.PREFER_PRECISION_IN_EPHEMERIDES = false; // JPL not available so it doesn't matter
	    		 FastMath.setMaximumNumberOfAngles(36000/5); // To save additional memory without reducing visible quality
 
	    		 int yMargin = 0;
 
	    		 AstroDate astro = new AstroDate();
	    		 time = new TimeElement(astro, SCALE.LOCAL_TIME);
	    		 eph = new EphemerisElement(TARGET.NOT_A_PLANET, EphemerisElement.COORDINATES_TYPE.APPARENT,
	    				 EphemerisElement.EQUINOX_OF_DATE, EphemerisElement.TOPOCENTRIC, EphemerisElement.REDUCTION_METHOD.IAU_2006,
	    				 EphemerisElement.FRAME.ICRF);
 
	    		 PlanetRenderElement render = new PlanetRenderElement(false, true, true, false, false);
	    		 TelescopeElement telescope = TelescopeElement.OBJECTIVE_50mm_f1_4;
	    		 telescope.ocular.focalLength = 60;
 
	    		 SkyRenderElement sky = new SkyRenderElement(jparsec.astronomy.CoordinateSystem.COORDINATE_SYSTEM.HORIZONTAL,
	    				 PROJECTION.STEREOGRAPHICAL, 0, 0.0, w, h, render, telescope);
 
	    		 sky.drawConstellationNamesType = Constellation.CONSTELLATION_NAME.SPANISH;
	    		 sky.drawDeepSkyObjectsColor = Color.argb(255, 0, 255, 0); // Green
	    		 sky.drawObjectsLimitingMagnitude = 5.5f;
	    		 sky.drawPlanetsMoonSun = true;
	    		 sky.drawSpaceProbes = false;
	    		 sky.drawStarsLabels = SkyRenderElement.STAR_LABELS.ONLY_PROPER_NAME_SPANISH;
	    		 sky.drawStarsGreekSymbols = true;
	    		 sky.drawStarsGreekSymbolsOnlyIfHasProperName = true;
	    		 sky.drawTransNeptunianObjects = false;
	    		 sky.drawStarsLimitingMagnitude = 5.5f; // must be < 6.5 and >= 5.5 (unless you select not to show constellations)
	    		 //sky.drawStarsLabelsLimitingMagnitude = sky.drawStarsLimitingMagnitude;
	    		 sky.drawArtificialSatellites = false;
	    		 sky.drawAsteroids = false;
	    		 sky.drawComets = false;
	    		 sky.drawLeyend = true;
	    		 sky.drawFaintStars = false;
	    		 sky.drawSunSpots = false;
	    		 sky.drawSuperNovaEvents = false;
	    		 sky.drawDeepSkyObjects = false;
	    		 sky.drawCoordinateGridEcliptic = false;
	    		 sky.drawPlanetsMoonSun = false;
	    		 if (sky.width < 800) sky.drawLeyend = false;
 
	    		 sky.drawConstellationLimits = false;
	    		 sky.drawSkyCorrectingLocalHorizon = false;
	    		 sky.drawSkyBelowHorizon = true;
	    		 sky.drawFastLabels = true;
	    		 sky.drawFastLabelsInWideFields = true;
	    		 sky.fillMilkyWay = true;
	    		 sky.useMultiThread = true;
	    		 sky.useSuperScriptForRA = false; // For Android 1.6, and when using Equatorial coordinates instead of horizontal
	    		 sky.maxAllowedMovementPixels = 10; // More speed !
	    		 Logger.reportJPARSECLogs = false; // More speed !
 
    			 CityElement city = City.findCity("Madrid");
    			 observer = ObserverElement.parseCity(city);
    			 try {
    				 LocationManager m_locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
 
    				 String provider = null;
 
    				 if (m_locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER)) {
    					 provider = LocationManager.GPS_PROVIDER;
    					 m_locationManager.requestLocationUpdates(provider, 3600000, 10000, this);
    				 } else if (m_locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
    					 provider = LocationManager.NETWORK_PROVIDER;
    					 m_locationManager.requestLocationUpdates(provider, 3600000, 10000, this);
    				 } else { }
 
    				 if (provider != null) {
    					 Location loc = m_locationManager.getLastKnownLocation(provider);
    					 if (loc != null) {
    						 observer.longitude = loc.getLongitude() * Constant.DEG_TO_RAD;
    						 observer.latitude = loc.getLatitude() * Constant.DEG_TO_RAD;
    						 observer.height = (int) loc.getAltitude();    
    						 Logger.log(LEVEL.INFO, "User location set to "+loc.getLongitude()+"/"+loc.getLatitude()+"/"+loc.getAltitude());
    					 }
    				 }
    			 } catch (Exception ex) { }
 
    			 // Initial center at cenit // 12h RA, +40 deg of DEC
    			 sky.centralLongitude = 0; //18 * 15.0 * Constant.DEG_TO_RAD;
    			 sky.centralLatitude = Constant.PI_OVER_TWO; //40 * Constant.DEG_TO_RAD;
    			 fastField = sky.telescope.getField();
    			 textures = sky.planetRender.textures;
 
    			 skyRender = new RenderSky(time, observer, eph, sky);
    			 if (yMargin > 0) skyRender.setYMargin(yMargin);
    			 Graphics g = new AndroidGraphics(sky.width, sky.height);
    			 skyRender.renderize(g);
 
    			 return (Bitmap) skyRender.getImage();   				
    		 } catch (Exception ve)
    		 {
    			 Logger.log(LEVEL.INFO, "Error found. Details: "+ve.toString());
    			 Logger.log(LEVEL.INFO, "Error found. Details: "+JPARSECException.getTrace(ve.getStackTrace()));
    			 return null;
    		 }
    	 }
 
    	 public void onLocationChanged(Location location) {
    		 observer.longitude = location.getLongitude() * Constant.DEG_TO_RAD;
    		 observer.latitude = location.getLatitude() * Constant.DEG_TO_RAD;
    		 observer.height = (int) location.getAltitude();
    		 try {
				skyRender.setSkyRenderElement(skyRender.render, time, observer, eph);
        		Logger.log(LEVEL.INFO, "User location set to "+location.getLongitude()+"/"+location.getLatitude()+"/"+location.getAltitude());
    		 } catch (Exception e) { }
    	 }
    	 public void onStatusChanged(String provider, int status, Bundle extras) {}
    	 public void onProviderEnabled(String provider) {}
    	 public void onProviderDisabled(String provider) {}
    }
}
 
blog/android_experiment.txt · created: 2012/04/30 14:33 (Last modified 2016/05/17 12:41) by Tomás Alonso Albi
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki