/** * Navit, a modular navigation system. * Copyright (C) 2005-2008 Navit Team * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include #include #include #include "config.h" #include "window.h" #include "point.h" #include "graphics.h" #include "color.h" #include "item.h" #include "xmlconfig.h" #include "plugin.h" #include "event.h" #include "debug.h" #include "callback.h" #include "android.h" #include "command.h" struct graphics_priv { jclass NavitGraphicsClass; jmethodID NavitGraphics_draw_polyline, NavitGraphics_draw_polygon, NavitGraphics_draw_rectangle, NavitGraphics_draw_circle, NavitGraphics_draw_text, NavitGraphics_draw_image, NavitGraphics_draw_image_warp, NavitGraphics_draw_mode, NavitGraphics_draw_drag, NavitGraphics_overlay_disable, NavitGraphics_overlay_resize, NavitGraphics_SetCamera, NavitGraphics_setBackgroundColor, NavitGraphics_draw_polygon_with_holes; jclass PaintClass; jmethodID Paint_init,Paint_setStrokeWidth,Paint_setARGB; jobject NavitGraphics; jobject Paint; jclass BitmapFactoryClass; jmethodID BitmapFactory_decodeFile, BitmapFactory_decodeResource; jclass BitmapClass; jmethodID Bitmap_getHeight, Bitmap_getWidth, Bitmap_createScaledBitmap; jclass ContextClass; jmethodID Context_getResources; jclass ResourcesClass; jobject Resources; jmethodID Resources_getIdentifier; jobject packageName; struct callback_list *cbl; struct window win; struct padding *padding; jint bgcolor; }; struct graphics_font_priv { int size; }; struct graphics_gc_priv { struct graphics_priv *gra; int linewidth; enum draw_mode_num mode; int a,r,g,b; unsigned char *dashes; int ndashes; }; struct graphics_image_priv { jobject Bitmap; int width; int height; struct point hot; }; static GHashTable *image_cache_hash = NULL; static int find_class_global(char *name, jclass *ret) { *ret=(*jnienv)->FindClass(jnienv, name); if (! *ret) { dbg(lvl_error,"Failed to get Class %s",name); return 0; } *ret = (*jnienv)->NewGlobalRef(jnienv, *ret); return 1; } static int find_method(jclass class, char *name, char *args, jmethodID *ret) { *ret = (*jnienv)->GetMethodID(jnienv, class, name, args); if (*ret == NULL) { dbg(lvl_error,"Failed to get Method %s with signature %s",name,args); return 0; } return 1; } static int find_static_method(jclass class, char *name, char *args, jmethodID *ret) { *ret = (*jnienv)->GetStaticMethodID(jnienv, class, name, args); if (*ret == NULL) { dbg(lvl_error,"Failed to get static Method %s with signature %s",name,args); return 0; } return 1; } static void graphics_destroy(struct graphics_priv *gr) { } static void font_destroy(struct graphics_font_priv *font) { g_free(font); } static struct graphics_font_methods font_methods = { font_destroy }; static struct graphics_font_priv *font_new(struct graphics_priv *gr, struct graphics_font_methods *meth, char *font, int size, int flags) { struct graphics_font_priv *ret=g_new0(struct graphics_font_priv, 1); *meth=font_methods; ret->size=size; return ret; } static void gc_destroy(struct graphics_gc_priv *gc) { g_free(gc->dashes); g_free(gc); } static void gc_set_linewidth(struct graphics_gc_priv *gc, int w) { gc->linewidth = w; } static void gc_set_dashes(struct graphics_gc_priv *gc, int w, int offset, unsigned char *dash_list, int n) { g_free(gc->dashes); gc->ndashes=n; if(n) { gc->dashes=g_malloc(n); memcpy(gc->dashes, dash_list, n); } else { gc->dashes=NULL; } } static void gc_set_foreground(struct graphics_gc_priv *gc, struct color *c) { gc->r = c->r >> 8; gc->g = c->g >> 8; gc->b = c->b >> 8; gc->a = c->a >> 8; } static void gc_set_background(struct graphics_gc_priv *gc, struct color *c) { } static struct graphics_gc_methods gc_methods = { gc_destroy, gc_set_linewidth, gc_set_dashes, gc_set_foreground, gc_set_background }; static struct graphics_gc_priv *gc_new(struct graphics_priv *gr, struct graphics_gc_methods *meth) { struct graphics_gc_priv *ret=g_new0(struct graphics_gc_priv, 1); *meth=gc_methods; ret->gra = gr; ret->a = ret->r = ret->g = ret->b = 255; ret->linewidth=1; return ret; } static void image_destroy(struct graphics_image_priv *img) { // unused? } static struct graphics_image_methods image_methods = { image_destroy }; static struct graphics_image_priv *image_new(struct graphics_priv *gra, struct graphics_image_methods *meth, char *path, int *w, int *h, struct point *hot, int rotation) { struct graphics_image_priv* ret = NULL; ret=g_new0(struct graphics_image_priv, 1); jstring string; jclass localBitmap = NULL; int id; dbg(lvl_debug,"enter %s",path); if (!strncmp(path,"res/drawable/",13)) { jstring a=(*jnienv)->NewStringUTF(jnienv, "drawable"); char *path_noext=g_strdup(path+13); char *pos=strrchr(path_noext, '.'); if (pos) *pos='\0'; dbg(lvl_debug,"path_noext=%s",path_noext); string = (*jnienv)->NewStringUTF(jnienv, path_noext); g_free(path_noext); id=(*jnienv)->CallIntMethod(jnienv, gra->Resources, gra->Resources_getIdentifier, string, a, gra->packageName); dbg(lvl_debug,"id=%d",id); if (id) localBitmap=(*jnienv)->CallStaticObjectMethod(jnienv, gra->BitmapFactoryClass, gra->BitmapFactory_decodeResource, gra->Resources, id); (*jnienv)->DeleteLocalRef(jnienv, a); } else { string = (*jnienv)->NewStringUTF(jnienv, path); localBitmap=(*jnienv)->CallStaticObjectMethod(jnienv, gra->BitmapFactoryClass, gra->BitmapFactory_decodeFile, string); } if (localBitmap) { ret->width=(*jnienv)->CallIntMethod(jnienv, localBitmap, gra->Bitmap_getWidth); ret->height=(*jnienv)->CallIntMethod(jnienv, localBitmap, gra->Bitmap_getHeight); if((*w!=IMAGE_W_H_UNSET && *w!=ret->width) || (*h!=IMAGE_W_H_UNSET && *w!=ret->height)) { jclass scaledBitmap=(*jnienv)->CallStaticObjectMethod(jnienv, gra->BitmapClass, gra->Bitmap_createScaledBitmap, localBitmap, (*w==IMAGE_W_H_UNSET)?ret->width:*w, (*h==IMAGE_W_H_UNSET)?ret->height:*h, JNI_TRUE); if(!scaledBitmap) { dbg(lvl_error,"Bitmap scaling to %dx%d failed for %s",*w,*h,path); } else { (*jnienv)->DeleteLocalRef(jnienv, localBitmap); localBitmap=scaledBitmap; ret->width=(*jnienv)->CallIntMethod(jnienv, localBitmap, gra->Bitmap_getWidth); ret->height=(*jnienv)->CallIntMethod(jnienv, localBitmap, gra->Bitmap_getHeight); } } ret->Bitmap = (*jnienv)->NewGlobalRef(jnienv, localBitmap); (*jnienv)->DeleteLocalRef(jnienv, localBitmap); dbg(lvl_debug,"w=%d h=%d for %s",ret->width,ret->height,path); ret->hot.x=ret->width/2; ret->hot.y=ret->height/2; } else { g_free(ret); ret=NULL; dbg(lvl_warning,"Failed to open %s",path); } (*jnienv)->DeleteLocalRef(jnienv, string); if (ret) { *w=ret->width; *h=ret->height; if (hot) *hot=ret->hot; } return ret; } static void initPaint(struct graphics_priv *gra, struct graphics_gc_priv *gc) { float wf = gc->linewidth; (*jnienv)->CallVoidMethod(jnienv, gc->gra->Paint, gra->Paint_setStrokeWidth, wf); (*jnienv)->CallVoidMethod(jnienv, gc->gra->Paint, gra->Paint_setARGB, gc->a, gc->r, gc->g, gc->b); } static void draw_lines(struct graphics_priv *gra, struct graphics_gc_priv *gc, struct point *p, int count) { int arrsize=1+4+1+gc->ndashes+count*2; jint pc[arrsize]; int i; jintArray points; if (count <= 0) return; points = (*jnienv)->NewIntArray(jnienv,arrsize); pc[0]=gc->linewidth; pc[1]=gc->a; pc[2]=gc->r; pc[3]=gc->g; pc[4]=gc->b; pc[5]=gc->ndashes; for (i = 0 ; i < gc->ndashes ; i++) { pc[6+i] = gc->dashes[i]; } for (i = 0 ; i < count ; i++) { pc[6+gc->ndashes+i*2]=p[i].x; pc[6+gc->ndashes+i*2+1]=p[i].y; } (*jnienv)->SetIntArrayRegion(jnienv, points, 0, arrsize, pc); (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_polyline, gc->gra->Paint, points); (*jnienv)->DeleteLocalRef(jnienv, points); } /* calculate the area a polygon covers in pixels. * * Bonus: Positive area indicates clockwise, negative counter clockwise */ static int polygon_area(struct point* p, int count) { int area =0; int i; /* initialize j with the last point */ int j = count -1; for(i=0; i < count; i ++) { area += (p[j].x + p[i].x) * (p[j].y - p[i].y); j = i; /* j is previous vertex to i */ } return area/2; } /* returns if vertexes of given polygon are clockwise * * @returns: > o if clockwise, otherwise counter clockwise. * * Note: On self intersecting polygons, it will return if it is * "mostly" clockwise. Be warned. */ static inline int is_clockwise (struct point * p, int count) { return polygon_area(p, count); } /* copy over vertexes to the jni array. Allows to reverse the polygon */ static int add_vertex_to_java_array(struct point * p, int count, jint * j_p, int reverse) { int i; if(reverse > 0) { for(i=0; i < count; i ++) { j_p[i*2]=p[(count -1) -i].x; j_p[i*2+1]=p[(count -1) -i].y; } } else { for(i=0; i < count; i ++) { j_p[i*2]=p[i].x; j_p[i*2+1]=p[i].y; } } return count * 2; } /* prepare the java call to draw a polygon with holes. As Java drawing code assumes polygons are directed * clockwise for outer and counter clockwise for inner, all polygons are checked and reversed if required. * While the reversing does not add significant overhead, the calculating adds some overhead. * * TODO: Find a more performant way of doing this probably NOT requiring the java code to get the polygons * directd. */ static void draw_polygon_with_holes (struct graphics_priv *gra, struct graphics_gc_priv *gc, struct point *p, int count, int hole_count, int* ccount, struct point **holes) { int i; /* need to get us some arrays for java */ int java_p_size; jintArray java_p; jint * j_p=NULL; int java_ccount_size; jintArray java_ccount; jint * j_ccount=NULL; int java_holes_size; jintArray java_holes; jint * j_holes=NULL; /* Don't even try to draw a polygon with less than 3 points */ if(count < 3) return; /* get java array for coordinates */ java_p_size=count*2; java_p = (*jnienv)->NewIntArray(jnienv,java_p_size); j_p = g_malloc(sizeof(jint) * java_p_size); /* add outer polygon to java array. Ensure it's clockwise */ add_vertex_to_java_array(p, count, j_p, (is_clockwise(p, count) > 0)? 0: 1); /* get java array for ccount */ java_ccount_size = hole_count; java_ccount=(*jnienv)->NewIntArray(jnienv,java_ccount_size); j_ccount=g_malloc(sizeof(jint) * java_ccount_size); /* get java array for hole coordinates */ java_holes_size = 0; for(i=0; i < hole_count; i ++) { java_holes_size += ccount[i] * 2; } java_holes=(*jnienv)->NewIntArray(jnienv,java_holes_size); j_holes=g_malloc(sizeof(jint) * java_holes_size); /* copy over the holes to the jint coordinate array */ int j_holes_used=0; for(i=0; i < hole_count; i ++) { /* remember this holes ccount */ j_ccount[i] = ccount[i] * 2; /* add inner polygon. ensure its counter clockwise */ j_holes_used += add_vertex_to_java_array(holes[i], ccount[i], &(j_holes[j_holes_used]), (is_clockwise(holes[i], ccount[i]) <= 0)? 0: 1); } /* attach the arrays with their storage to the JVM */ (*jnienv)->SetIntArrayRegion(jnienv, java_p, 0, java_p_size, j_p); (*jnienv)->SetIntArrayRegion(jnienv, java_ccount, 0, java_ccount_size, j_ccount); (*jnienv)->SetIntArrayRegion(jnienv, java_holes, 0, java_holes_size, j_holes); /* call the java function */ (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_polygon_with_holes, gc->gra->Paint, gc->linewidth, gc->r, gc->g, gc->b, gc->a, java_p, java_ccount, java_holes); /* clean up */ (*jnienv)->DeleteLocalRef(jnienv, java_holes); (*jnienv)->DeleteLocalRef(jnienv, java_ccount); (*jnienv)->DeleteLocalRef(jnienv, java_p); g_free(j_p); g_free(j_ccount); g_free(j_holes); } static void draw_polygon(struct graphics_priv *gra, struct graphics_gc_priv *gc, struct point *p, int count) { int arrsize=1+4+count*2; jint pc[arrsize]; int i; jintArray points; if (count <= 0) return; points = (*jnienv)->NewIntArray(jnienv,arrsize); for (i = 0 ; i < count ; i++) { pc[5+i*2]=p[i].x; pc[5+i*2+1]=p[i].y; } pc[0]=gc->linewidth; pc[1]=gc->a; pc[2]=gc->r; pc[3]=gc->g; pc[4]=gc->b; (*jnienv)->SetIntArrayRegion(jnienv, points, 0, arrsize, pc); (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_polygon, gc->gra->Paint, points); (*jnienv)->DeleteLocalRef(jnienv, points); } static void draw_rectangle(struct graphics_priv *gra, struct graphics_gc_priv *gc, struct point *p, int w, int h) { initPaint(gra, gc); (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_rectangle, gc->gra->Paint, p->x, p->y, w, h); } static void draw_circle(struct graphics_priv *gra, struct graphics_gc_priv *gc, struct point *p, int r) { initPaint(gra, gc); (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_circle, gc->gra->Paint, p->x, p->y, r); } static void draw_text(struct graphics_priv *gra, struct graphics_gc_priv *fg, struct graphics_gc_priv *bg, struct graphics_font_priv *font, char *text, struct point *p, int dx, int dy) { int bgcolor=0; dbg(lvl_debug,"enter %s", text); initPaint(gra, fg); if(bg) bgcolor=(bg->a<<24)| (bg->r<<16) | (bg->g<<8) | bg->b; jstring string = (*jnienv)->NewStringUTF(jnienv, text); (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_text, fg->gra->Paint, p->x, p->y, string, font->size, dx, dy, bgcolor); (*jnienv)->DeleteLocalRef(jnienv, string); } static void draw_image(struct graphics_priv *gra, struct graphics_gc_priv *fg, struct point *p, struct graphics_image_priv *img) { dbg(lvl_debug,"enter %p",img); initPaint(gra, fg); (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_image, fg->gra->Paint, p->x, p->y, img->Bitmap); } static void draw_image_warp (struct graphics_priv *gr, struct graphics_gc_priv *fg, struct point *p, int count, struct graphics_image_priv *img) { /* * * * if coord count==3 then top.left top.right bottom.left * */ if (count==3) { initPaint(gr, fg); (*jnienv)->CallVoidMethod(jnienv, gr->NavitGraphics, gr->NavitGraphics_draw_image_warp, fg->gra->Paint, count, p[0].x, p[0].y,p[1].x, p[1].y, p[2].x, p[2].y, img->Bitmap); } else dbg(lvl_debug,"draw_image_warp is called with unsupported count parameter value %d", count); } static void draw_drag(struct graphics_priv *gra, struct point *p) { (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_drag, p ? p->x : 0, p ? p->y : 0); } static void background_gc(struct graphics_priv *gr, struct graphics_gc_priv *gc) { } static void draw_mode(struct graphics_priv *gra, enum draw_mode_num mode) { (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_draw_mode, (int)mode); } static struct graphics_priv * overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int wraparound); static void *get_data(struct graphics_priv *this, const char *type) { if (!strcmp(type,"padding")) return this->padding; if (!strcmp(type,"window")) return &this->win; return NULL; } static void image_free(struct graphics_priv *gr, struct graphics_image_priv *priv) { } static void get_text_bbox(struct graphics_priv *gr, struct graphics_font_priv *font, char *text, int dx, int dy, struct point *ret, int estimate) { int len = g_utf8_strlen(text, -1); int xMin = 0; int yMin = 0; int yMax = 13*font->size/256; int xMax = 9*font->size*len/256; ret[0].x = xMin; ret[0].y = -yMin; ret[1].x = xMin; ret[1].y = -yMax; ret[2].x = xMax; ret[2].y = -yMax; ret[3].x = xMax; ret[3].y = -yMin; } static void overlay_disable(struct graphics_priv *gra, int disable) { (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_overlay_disable, disable); } static void overlay_resize(struct graphics_priv *gra, struct point *pnt, int w, int h, int wraparound) { (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_overlay_resize, pnt ? pnt->x:0, pnt ? pnt->y:0, w, h, wraparound); } static int set_attr(struct graphics_priv *gra, struct attr *attr) { switch (attr->type) { case attr_use_camera: (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_SetCamera, attr->u.num); return 1; case attr_background_color: gra->bgcolor = (attr->u.color->a / 0x101) << 24 | (attr->u.color->r / 0x101) << 16 | (attr->u.color->g / 0x101) << 8 | (attr->u.color->b / 0x101); dbg(lvl_debug, "set attr_background_color %04x %04x %04x %04x (%08x)", attr->u.color->r, attr->u.color->g, attr->u.color->b, attr->u.color->a, gra->bgcolor); if (gra->NavitGraphics_setBackgroundColor != NULL) (*jnienv)->CallVoidMethod(jnienv, gra->NavitGraphics, gra->NavitGraphics_setBackgroundColor, gra->bgcolor); else dbg(lvl_error, "NavitGraphics.setBackgroundColor not found, cannot set background color"); return 1; default: return 0; } } int show_native_keyboard (struct graphics_keyboard *kbd); void hide_native_keyboard (struct graphics_keyboard *kbd); static struct graphics_methods graphics_methods = { graphics_destroy, draw_mode, draw_lines, draw_polygon, draw_rectangle, draw_circle, draw_text, draw_image, draw_image_warp, draw_drag, font_new, gc_new, background_gc, overlay_new, image_new, get_data, image_free, get_text_bbox, overlay_disable, overlay_resize, set_attr, show_native_keyboard, hide_native_keyboard, NULL, /*get_dpi*/ draw_polygon_with_holes }; static void resize_callback(struct graphics_priv *gra, int w, int h) { dbg(lvl_debug,"w=%d h=%d ok",w,h); dbg(lvl_debug,"gra=%p, gra->cbl=%p", gra, gra->cbl); callback_list_call_attr_2(gra->cbl, attr_resize, (void *)w, (void *)h); } static void padding_changed_callback(struct graphics_priv *gra, int left, int top, int right, int bottom) { dbg(lvl_debug, "win.padding left=%d top=%d right=%d bottom=%d", left, top, right, bottom); gra->padding->left = left; gra->padding->top = top; gra->padding->right = right; gra->padding->bottom = bottom; } static void motion_callback(struct graphics_priv *gra, int x, int y) { struct point p; p.x=x; p.y=y; callback_list_call_attr_1(gra->cbl, attr_motion, (void *)&p); } static void keypress_callback(struct graphics_priv *gra, char *s) { dbg(lvl_debug,"enter %s",s); callback_list_call_attr_1(gra->cbl, attr_keypress, s); } static void button_callback(struct graphics_priv *gra, int pressed, int button, int x, int y) { struct point p; p.x=x; p.y=y; callback_list_call_attr_3(gra->cbl, attr_button, (void *)pressed, (void *)button, (void *)&p); } static int set_activity(jobject graphics) { jclass ActivityClass; jmethodID cid; ActivityClass = (*jnienv)->GetObjectClass(jnienv, android_activity); dbg(lvl_debug,"at 5"); if (ActivityClass == NULL) { dbg(lvl_debug,"no activity class found"); return 0; } dbg(lvl_debug,"at 6"); cid = (*jnienv)->GetMethodID(jnienv, ActivityClass, "setContentView", "(Landroid/view/View;)V"); if (cid == NULL) { dbg(lvl_error,"no setContentView method found"); return 0; } dbg(lvl_debug,"at 7"); (*jnienv)->CallVoidMethod(jnienv, android_activity, cid, graphics); dbg(lvl_debug,"at 8"); return 1; } /** * @brief Initializes a new Android graphics instance. * * This initializes a new Android graphics instance, which can either be the main view or an overlay. * * @param ret The new graphics instance * @param parent The graphics instance that contains the new instance ({@code NULL} for the main view) * @param p The position of the overlay in its parent ({@code NULL} for the main view) * @param w The width of the overlay (0 for the main view) * @param h The height of the overlay (0 for the main view) * @param wraparound (0 for the main view) * @param use_camera Whether to use the camera (0 for overlays) */ static int graphics_android_init(struct graphics_priv *ret, struct graphics_priv *parent, struct point *pnt, int w, int h, int wraparound, int use_camera) { struct callback *cb; jmethodID cid, Context_getPackageName; dbg(lvl_debug,"at 2 jnienv=%p",jnienv); if (parent) ret->padding = parent->padding; if (!find_class_global("android/graphics/Paint", &ret->PaintClass)) return 0; if (!find_method(ret->PaintClass, "", "(I)V", &ret->Paint_init)) return 0; if (!find_method(ret->PaintClass, "setARGB", "(IIII)V", &ret->Paint_setARGB)) return 0; if (!find_method(ret->PaintClass, "setStrokeWidth", "(F)V", &ret->Paint_setStrokeWidth)) return 0; if (!find_class_global("android/graphics/BitmapFactory", &ret->BitmapFactoryClass)) return 0; if (!find_static_method(ret->BitmapFactoryClass, "decodeFile", "(Ljava/lang/String;)Landroid/graphics/Bitmap;", &ret->BitmapFactory_decodeFile)) return 0; if (!find_static_method(ret->BitmapFactoryClass, "decodeResource", "(Landroid/content/res/Resources;I)Landroid/graphics/Bitmap;", &ret->BitmapFactory_decodeResource)) return 0; if (!find_class_global("android/graphics/Bitmap", &ret->BitmapClass)) return 0; if (!find_method(ret->BitmapClass, "getHeight", "()I", &ret->Bitmap_getHeight)) return 0; if (!find_method(ret->BitmapClass, "getWidth", "()I", &ret->Bitmap_getWidth)) return 0; if (!find_static_method(ret->BitmapClass, "createScaledBitmap", "(Landroid/graphics/Bitmap;IIZ)Landroid/graphics/Bitmap;", &ret->Bitmap_createScaledBitmap)) return 0; if (!find_class_global("android/content/Context", &ret->ContextClass)) return 0; if (!find_method(ret->ContextClass, "getResources", "()Landroid/content/res/Resources;", &ret->Context_getResources)) return 0; ret->Resources=(*jnienv)->CallObjectMethod(jnienv, android_activity, ret->Context_getResources); if (ret->Resources) ret->Resources = (*jnienv)->NewGlobalRef(jnienv, ret->Resources); if (!find_class_global("android/content/res/Resources", &ret->ResourcesClass)) return 0; if (!find_method(ret->ResourcesClass, "getIdentifier", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", &ret->Resources_getIdentifier)) return 0; if (!find_method(ret->ContextClass, "getPackageName", "()Ljava/lang/String;", &Context_getPackageName)) return 0; ret->packageName=(*jnienv)->CallObjectMethod(jnienv, android_activity, Context_getPackageName); ret->packageName=(*jnienv)->NewGlobalRef(jnienv, ret->packageName); if (!find_class_global("org/navitproject/navit/NavitGraphics", &ret->NavitGraphicsClass)) return 0; dbg(lvl_debug,"at 3"); cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "", "(Landroid/app/Activity;Lorg/navitproject/navit/NavitGraphics;IIIIII)V"); if (cid == NULL) { dbg(lvl_error,"no method found"); return 0; /* exception thrown */ } dbg(lvl_debug,"at 4 android_activity=%p",android_activity); ret->NavitGraphics=(*jnienv)->NewObject(jnienv, ret->NavitGraphicsClass, cid, android_activity, parent ? parent->NavitGraphics : NULL, pnt ? pnt->x:0, pnt ? pnt->y:0, w, h, wraparound, use_camera); dbg(lvl_debug,"result=%p",ret->NavitGraphics); if (ret->NavitGraphics) ret->NavitGraphics = (*jnienv)->NewGlobalRef(jnienv, ret->NavitGraphics); /* Create a single global Paint, otherwise android will quickly run out * of global refs.*/ /* 0x101 = text kerning (default), antialiasing */ ret->Paint=(*jnienv)->NewObject(jnienv, ret->PaintClass, ret->Paint_init, 0x101); dbg(lvl_debug,"result=%p",ret->Paint); if (ret->Paint) ret->Paint = (*jnienv)->NewGlobalRef(jnienv, ret->Paint); cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setSizeChangedCallback", "(J)V"); if (cid == NULL) { dbg(lvl_error,"no setResizeCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(resize_callback), ret); (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setPaddingChangedCallback", "(J)V"); if (cid == NULL) { dbg(lvl_error,"no setPaddingCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(padding_changed_callback), ret); (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setButtonCallback", "(J)V"); if (cid == NULL) { dbg(lvl_error,"no setButtonCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(button_callback), ret); (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setMotionCallback", "(J)V"); if (cid == NULL) { dbg(lvl_error,"no setMotionCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(motion_callback), ret); (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setKeypressCallback", "(J)V"); if (cid == NULL) { dbg(lvl_error,"no setKeypressCallback method found"); return 0; /* exception thrown */ } cb=callback_new_1(callback_cast(keypress_callback), ret); (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, cid, (jlong)cb); if (!find_method(ret->NavitGraphicsClass, "draw_polyline", "(Landroid/graphics/Paint;[I)V", &ret->NavitGraphics_draw_polyline)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_polygon_with_holes", "(Landroid/graphics/Paint;IIIII[I[I[I)V", &ret->NavitGraphics_draw_polygon_with_holes)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_polygon", "(Landroid/graphics/Paint;[I)V", &ret->NavitGraphics_draw_polygon)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_rectangle", "(Landroid/graphics/Paint;IIII)V", &ret->NavitGraphics_draw_rectangle)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_circle", "(Landroid/graphics/Paint;III)V", &ret->NavitGraphics_draw_circle)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_text", "(Landroid/graphics/Paint;IILjava/lang/String;IIII)V", &ret->NavitGraphics_draw_text)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_image", "(Landroid/graphics/Paint;IILandroid/graphics/Bitmap;)V", &ret->NavitGraphics_draw_image)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_image_warp", "(Landroid/graphics/Paint;IIIIIIILandroid/graphics/Bitmap;)V", &ret->NavitGraphics_draw_image_warp)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_mode", "(I)V", &ret->NavitGraphics_draw_mode)) return 0; if (!find_method(ret->NavitGraphicsClass, "draw_drag", "(II)V", &ret->NavitGraphics_draw_drag)) return 0; if (!find_method(ret->NavitGraphicsClass, "overlay_disable", "(I)V", &ret->NavitGraphics_overlay_disable)) return 0; if (!find_method(ret->NavitGraphicsClass, "overlay_resize", "(IIIII)V", &ret->NavitGraphics_overlay_resize)) return 0; if (!find_method(ret->NavitGraphicsClass, "setCamera", "(I)V", &ret->NavitGraphics_SetCamera)) return 0; #if 0 set_activity(ret->NavitGraphics); #endif return 1; } static jclass NavitClass; static jmethodID Navit_disableSuspend, Navit_exit, Navit_fullscreen, Navit_runOptionsItem, Navit_showMenu, Navit_showNativeKeyboard, Navit_hideNativeKeyboard; static int graphics_android_fullscreen(struct window *win, int on) { (*jnienv)->CallVoidMethod(jnienv, android_activity, Navit_fullscreen, on); return 1; } static void graphics_android_disable_suspend(struct window *win) { dbg(lvl_debug,"enter"); (*jnienv)->CallVoidMethod(jnienv, android_activity, Navit_disableSuspend); } /** * @brief Runs an item from the Android menu. * * This is a callback function which implements multiple API functions. * * @param this The {@code graohics_prov} structure * @param function The API function which was called * @param in Parameters to pass to the API function * @param out Points to a buffer which will receive a pointer to the output of the command * @param valid */ static void graphics_android_cmd_runMenuItem(struct graphics_priv *this, char *function, struct attr **in, struct attr ***out, int *valid) { int ncmd=0; dbg(lvl_debug,"Running %s",function); if(!strcmp(function,"map_download_dialog")) { ncmd=3; } else if(!strcmp(function,"backup_restore_dialog")) { ncmd=7; } else if(!strcmp(function,"set_map_location")) { ncmd=10; } (*jnienv)->CallVoidMethod(jnienv, android_activity, Navit_runOptionsItem, ncmd); } /** * @brief Shows the Android menu. * * This is the callback function associated with the {@code menu()} API function. * * @param this The {@code graohics_prov} structure * @param function The API function which was called * @param in Parameters to pass to the API function * @param out Points to a buffer which will receive a pointer to the output of the command * @param valid */ static void graphics_android_cmd_menu(struct graphics_priv *this, char *function, struct attr **in, struct attr ***out, int *valid) { dbg(lvl_debug, "enter"); (*jnienv)->CallVoidMethod(jnienv, android_activity, Navit_showMenu); } /** * The command table. Each entry consists of an API function name and the callback function which implements * this command. */ static struct command_table commands[] = { {"map_download_dialog",command_cast(graphics_android_cmd_runMenuItem)}, {"set_map_location",command_cast(graphics_android_cmd_runMenuItem)}, {"backup_restore_dialog",command_cast(graphics_android_cmd_runMenuItem)}, {"menu", command_cast(graphics_android_cmd_menu)}, }; /** * @brief Creates a new Android graphics instance. * * This method is called when the graphics plugin is initialized. It creates the main view, i.e. the map view. * Unless overlay mode is enabled, it also holds any OSD items. * * @param nav The navit instance. * @param meth The methods for the new graphics instance * @param attrs The attributes for the new graphics instance * @param cbl The callback list for the new graphics instance * * @return The new graphics instance */ static struct graphics_priv *graphics_android_new(struct navit *nav, struct graphics_methods *meth, struct attr **attrs, struct callback_list *cbl) { struct graphics_priv *ret; struct attr *attr; int use_camera=0; jmethodID cid; jint android_bgcolor; dbg(lvl_debug, "enter"); if (!event_request_system("android","graphics_android")) return NULL; ret=g_new0(struct graphics_priv, 1); ret->cbl=cbl; *meth=graphics_methods; ret->win.priv=ret; ret->win.fullscreen=graphics_android_fullscreen; ret->win.disable_suspend=graphics_android_disable_suspend; ret->padding = g_new0(struct padding, 1); ret->padding->left = 0; ret->padding->top = 0; ret->padding->right = 0; ret->padding->bottom = 0; /* attr_background_color is the background color for system bars (API 17+ only) */ if ((attr=attr_search(attrs, attr_background_color))) { ret->bgcolor = (attr->u.color->a / 0x101) << 24 | (attr->u.color->r / 0x101) << 16 | (attr->u.color->g / 0x101) << 8 | (attr->u.color->b / 0x101); dbg(lvl_debug, "attr_background_color %04x %04x %04x %04x (%08x)", attr->u.color->r, attr->u.color->g, attr->u.color->b, attr->u.color->a, ret->bgcolor); } else { /* default is the same as for OSD */ ret->bgcolor = 0xa0000000; } if ((attr=attr_search(attrs, attr_use_camera))) { use_camera=attr->u.num; } if ((attr=attr_search(attrs, attr_callback_list))) { command_add_table(attr->u.callback_list, commands, sizeof(commands)/sizeof(struct command_table), ret); } image_cache_hash = g_hash_table_new(g_str_hash, g_str_equal); if (graphics_android_init(ret, NULL, NULL, 0, 0, 0, use_camera)) { cid = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "hasMenuButton", "()Z"); if (cid != NULL) { attr = g_new0(struct attr, 1); attr->type = attr_has_menu_button; attr->u.num = (*jnienv)->CallBooleanMethod(jnienv, ret->NavitGraphics, cid); /* * Although the attribute refers to information obtained by the graphics plugin, we are storing it * with the navit object: the object is easier to access from anywhere in the program, and ultimately * it refers to a configuration value affecting all of Navit, thus users are likely to look for it in * the navit object (as the fact that graphics also handles input devices is not immedately obvious). */ navit_object_set_attr((struct navit_object *) nav, attr); dbg(lvl_debug, "attr_has_menu_button=%ld", attr->u.num); g_free(attr); } ret->NavitGraphics_setBackgroundColor = (*jnienv)->GetMethodID(jnienv, ret->NavitGraphicsClass, "setBackgroundColor", "(I)V"); if (ret->NavitGraphics_setBackgroundColor != NULL) { (*jnienv)->CallVoidMethod(jnienv, ret->NavitGraphics, ret->NavitGraphics_setBackgroundColor, ret->bgcolor); } dbg(lvl_debug,"returning %p",ret); return ret; } else { g_free(ret); return NULL; } } /** * @brief Creates a new overlay * * This method creates a graphics instance for a new overlay. If overlay mode is enabled, a separate overlay is * created for each OSD item. * * @param gr The parent graphics instance, i.e. the one which will contain the overlay. * @param meth The methods for the new graphics instance * @param p The position of the overlay in its parent * @param w The width of the overlay * @param h The height of the overlay * @param wraparound * * @return The graphics instance for the new overlay */ static struct graphics_priv *overlay_new(struct graphics_priv *gr, struct graphics_methods *meth, struct point *p, int w, int h, int wraparound) { struct graphics_priv *ret=g_new0(struct graphics_priv, 1); *meth=graphics_methods; if (graphics_android_init(ret, gr, p, w, h, wraparound, 0)) { dbg(lvl_debug,"returning %p",ret); return ret; } else { g_free(ret); return NULL; } } static void event_android_main_loop_run(void) { dbg(lvl_debug,"enter"); } static void event_android_main_loop_quit(void) { dbg(lvl_debug,"enter"); (*jnienv)->CallVoidMethod(jnienv, android_activity, Navit_exit); } static jclass NavitTimeoutClass; static jmethodID NavitTimeout_init; static jmethodID NavitTimeout_remove; static jclass NavitIdleClass; static jmethodID NavitIdle_init; static jmethodID NavitIdle_remove; static jclass NavitWatchClass; static jmethodID NavitWatch_init; static jmethodID NavitWatch_remove; static void do_poll(JNIEnv *env, int fd, int cond) { struct pollfd pfd; pfd.fd=fd; dbg(lvl_debug,"poll called for %d %d", fd, cond); switch ((enum event_watch_cond)cond) { case event_watch_cond_read: pfd.events=POLLIN; break; case event_watch_cond_write: pfd.events=POLLOUT; break; case event_watch_cond_except: pfd.events=POLLERR; break; default: pfd.events=0; } pfd.revents=0; poll(&pfd, 1, -1); } static struct event_watch *event_android_add_watch(int h, enum event_watch_cond cond, struct callback *cb) { jobject ret; ret=(*jnienv)->NewObject(jnienv, NavitWatchClass, NavitWatch_init, (jlong)do_poll, h, (jint) cond, (jlong)cb); dbg(lvl_debug,"result for %d,%d,%p = %p",h,cond,cb,ret); if (ret) ret = (*jnienv)->NewGlobalRef(jnienv, ret); return (struct event_watch *)ret; } static void event_android_remove_watch(struct event_watch *ev) { dbg(lvl_debug,"enter %p",ev); if (ev) { jobject obj=(jobject )ev; (*jnienv)->CallVoidMethod(jnienv, obj, NavitWatch_remove); (*jnienv)->DeleteGlobalRef(jnienv, obj); } } struct event_timeout { void (*handle_timeout)(struct event_timeout *priv); jobject jni_timeout; int multi; struct callback *cb; }; static void event_android_remove_timeout(struct event_timeout *priv) { if (priv && priv->jni_timeout) { (*jnienv)->CallVoidMethod(jnienv, priv->jni_timeout, NavitTimeout_remove); (*jnienv)->DeleteGlobalRef(jnienv, priv->jni_timeout); priv->jni_timeout = NULL; } } static void event_android_handle_timeout(struct event_timeout *priv) { callback_call_0(priv->cb); if (!priv->multi) event_android_remove_timeout(priv); } static struct event_timeout *event_android_add_timeout(int timeout, int multi, struct callback *cb) { struct event_timeout *ret = g_new0(struct event_timeout, 1); ret->cb = cb; ret->multi = multi; ret->handle_timeout = event_android_handle_timeout; ret->jni_timeout = (*jnienv)->NewObject(jnienv, NavitTimeoutClass, NavitTimeout_init, timeout, multi, (jlong)(ret)); dbg(lvl_debug,"result for %d,%d,%p = %p",timeout,multi,cb,ret); if (ret->jni_timeout) ret->jni_timeout = (*jnienv)->NewGlobalRef(jnienv, ret->jni_timeout); return ret; } static struct event_idle *event_android_add_idle(int priority, struct callback *cb) { #if 0 jobject ret; dbg(lvl_debug,"enter"); ret=(*jnienv)->NewObject(jnienv, NavitIdleClass, NavitIdle_init, (int)cb); dbg(lvl_debug,"result for %p=%p",cb,ret); if (ret) ret = (*jnienv)->NewGlobalRef(jnienv, ret); return (struct event_idle *)ret; #endif return (struct event_idle *)event_android_add_timeout(1, 1, cb); } static void event_android_remove_idle(struct event_idle *ev) { #if 0 dbg(lvl_debug,"enter %p",ev); if (ev) { jobject obj=(jobject )ev; (*jnienv)->CallVoidMethod(jnienv, obj, NavitIdle_remove); (*jnienv)->DeleteGlobalRef(jnienv, obj); } #endif event_android_remove_timeout((struct event_timeout *)ev); } static void event_android_call_callback(struct callback_list *cb) { dbg(lvl_debug,"enter"); } static struct event_methods event_android_methods = { event_android_main_loop_run, event_android_main_loop_quit, event_android_add_watch, event_android_remove_watch, event_android_add_timeout, event_android_remove_timeout, event_android_add_idle, event_android_remove_idle, event_android_call_callback, }; static struct event_priv *event_android_new(struct event_methods *meth) { dbg(lvl_debug,"enter"); if (!find_class_global("org/navitproject/navit/NavitTimeout", &NavitTimeoutClass)) return NULL; NavitTimeout_init = (*jnienv)->GetMethodID(jnienv, NavitTimeoutClass, "", "(IZJ)V"); if (NavitTimeout_init == NULL) return NULL; NavitTimeout_remove = (*jnienv)->GetMethodID(jnienv, NavitTimeoutClass, "remove", "()V"); if (NavitTimeout_remove == NULL) return NULL; #if 0 if (!find_class_global("org/navitproject/navit/NavitIdle", &NavitIdleClass)) return NULL; NavitIdle_init = (*jnienv)->GetMethodID(jnienv, NavitIdleClass, "", "(I)V"); if (NavitIdle_init == NULL) return NULL; NavitIdle_remove = (*jnienv)->GetMethodID(jnienv, NavitIdleClass, "remove", "()V"); if (NavitIdle_remove == NULL) return NULL; #endif if (!find_class_global("org/navitproject/navit/NavitWatch", &NavitWatchClass)) return NULL; NavitWatch_init = (*jnienv)->GetMethodID(jnienv, NavitWatchClass, "", "(JIIJ)V"); if (NavitWatch_init == NULL) return NULL; NavitWatch_remove = (*jnienv)->GetMethodID(jnienv, NavitWatchClass, "remove", "()V"); if (NavitWatch_remove == NULL) return NULL; if (!find_class_global("org/navitproject/navit/Navit", &NavitClass)) return NULL; Navit_disableSuspend = (*jnienv)->GetMethodID(jnienv, NavitClass, "disableSuspend", "()V"); if (Navit_disableSuspend == NULL) return NULL; Navit_exit = (*jnienv)->GetMethodID(jnienv, NavitClass, "onDestroy", "()V"); if (Navit_exit == NULL) return NULL; Navit_fullscreen = (*jnienv)->GetMethodID(jnienv, NavitClass, "fullscreen", "(I)V"); if (Navit_fullscreen == NULL) return NULL; Navit_runOptionsItem = (*jnienv)->GetMethodID(jnienv, NavitClass, "runOptionsItem", "(I)V"); if (Navit_runOptionsItem == NULL) return NULL; Navit_showMenu = (*jnienv)->GetMethodID(jnienv, NavitClass, "showMenu", "()V"); if (Navit_showMenu == NULL) return NULL; Navit_showNativeKeyboard = (*jnienv)->GetMethodID(jnienv, NavitClass, "showNativeKeyboard", "()I"); Navit_hideNativeKeyboard = (*jnienv)->GetMethodID(jnienv, NavitClass, "hideNativeKeyboard", "()V"); dbg(lvl_debug,"ok"); *meth=event_android_methods; return NULL; } /* below needs review, android resizes the view and the actual height of the keyboard is not * passed down here and is irrelevant, only wether it is onscreen matters, so * android returns a height of 1 px in the case of an onscreen keyboard just * to keep the logic to remove the keyboard from the screen afterwards working untill * the logic in native code is reviewed. */ /** * @brief Displays the native input method. * * This method decides whether a native on-screen input method, such as a virtual keyboard, needs to be * displayed. A typical case in which there is no need for an on-screen input method is if a hardware * keyboard is present. * * Note that the Android platform lacks reliable means of determining whether an on-screen input method * is going to be displayed or how much screen space it is going to occupy. Therefore this method tries * to guess these values. They have been tested and found to work correctly with the AOSP keyboard, and * are thus expected to be compatible with most on-screen keyboards found on the market, but results may * be unexpected with other input methods. * * @param kbd A {@code struct graphics_keyboard} which describes the requirements for the input method * and will be populated with the data of the input method before the function returns. * * @return True if the input method is going to be displayed, false if not. */ int show_native_keyboard (struct graphics_keyboard *kbd) { if (Navit_showNativeKeyboard == NULL) { dbg(lvl_error, "method Navit.showNativeKeyboard() not found, cannot display keyboard"); return 0; } kbd->h = (*jnienv)->CallIntMethod(jnienv, android_activity, Navit_showNativeKeyboard); dbg(lvl_error, "keyboard height is %d px is a lie", kbd->h); /* zero height means we're not showing a keyboard, therefore normalize height to boolean */ return !!(kbd->h); } /** * @brief Hides the native input method and frees associated private data. * * @param kbd The {@code struct graphics_keyboard} which was passed to the earlier call to * {@link show_native_keyboard(struct graphics_keyboard *)}. The {@code gra_priv} member of the struct * will be freed by this function. */ void hide_native_keyboard (struct graphics_keyboard *kbd) { if (Navit_hideNativeKeyboard == NULL) { dbg(lvl_error, "method Navit.hideNativeKeyboard() not found, cannot dismiss keyboard"); return; } (*jnienv)->CallVoidMethod(jnienv, android_activity, Navit_hideNativeKeyboard); g_free(kbd->gra_priv); } void plugin_init(void) { dbg(lvl_debug,"enter"); plugin_register_category_graphics("android", graphics_android_new); plugin_register_category_event("android", event_android_new); }