\n\u003Cuses-permission android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"/>\n \u003C!-- App receives GCM messages. -->\n\u003Cuses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\"/>\n \u003C!-- GCM connects to Google Services. -->\n\u003Cuses-permission android:name=\"android.permission.INTERNET\"/>\n \u003C!-- GCM requires a Google account. -->\n\u003Cuses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>\n \u003C!-- Keeps the processor from sleeping when a message is received. -->\n\u003Cuses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n",[176,3219,3220,3224,3229,3234,3239,3244,3249,3254,3259,3264,3269,3274,3279],{"__ignoreMap":110},[233,3221,3222],{"class":235,"line":236},[233,3223,894],{"emptyLinePlaceholder":127},[233,3225,3226],{"class":235,"line":111},[233,3227,3228],{},"\u003Cpermission\n",[233,3230,3231],{"class":235,"line":402},[233,3232,3233],{}," android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"\n",[233,3235,3236],{"class":235,"line":408},[233,3237,3238],{}," android:protectionLevel=\"signature\"/>\n",[233,3240,3241],{"class":235,"line":414},[233,3242,3243],{},"\u003Cuses-permission android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"/>\n",[233,3245,3246],{"class":235,"line":420},[233,3247,3248],{}," \u003C!-- App receives GCM messages. -->\n",[233,3250,3251],{"class":235,"line":426},[233,3252,3253],{},"\u003Cuses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\"/>\n",[233,3255,3256],{"class":235,"line":657},[233,3257,3258],{}," \u003C!-- GCM connects to Google Services. -->\n",[233,3260,3261],{"class":235,"line":662},[233,3262,3263],{},"\u003Cuses-permission android:name=\"android.permission.INTERNET\"/>\n",[233,3265,3266],{"class":235,"line":667},[233,3267,3268],{}," \u003C!-- GCM requires a Google account. -->\n",[233,3270,3271],{"class":235,"line":673},[233,3272,3273],{},"\u003Cuses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>\n",[233,3275,3276],{"class":235,"line":679},[233,3277,3278],{}," \u003C!-- Keeps the processor from sleeping when a message is received. -->\n",[233,3280,3281],{"class":235,"line":684},[233,3282,3283],{},"\u003Cuses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n",[19,3285,3286],{},"And declare a GCM broadcast receiver within the application tag:",[225,3288,3290],{"className":1649,"code":3289,"language":1651,"meta":110,"style":110},"\n\u003Creceiver\n android:name=\"com.google.android.gcm.GCMBroadcastReceiver\"\n android:permission=\"com.google.android.c2dm.permission.SEND\">\n \u003Cintent-filter>\n \u003Caction android:name=\"com.google.android.c2dm.intent.RECEIVE\"/>\n \u003Caction android:name=\"com.google.android.c2dm.intent.REGISTRATION\"/>\n \u003Ccategory android:name=\"com.synyx.cloudmessagetest\"/>\n \u003C/intent-filter>\n\u003C/receiver>\n\u003Cservice android:name=\".GCMIntentService\"/>\n",[176,3291,3292,3296,3301,3306,3311,3316,3321,3326,3331,3336,3341],{"__ignoreMap":110},[233,3293,3294],{"class":235,"line":236},[233,3295,894],{"emptyLinePlaceholder":127},[233,3297,3298],{"class":235,"line":111},[233,3299,3300],{},"\u003Creceiver\n",[233,3302,3303],{"class":235,"line":402},[233,3304,3305],{}," android:name=\"com.google.android.gcm.GCMBroadcastReceiver\"\n",[233,3307,3308],{"class":235,"line":408},[233,3309,3310],{}," android:permission=\"com.google.android.c2dm.permission.SEND\">\n",[233,3312,3313],{"class":235,"line":414},[233,3314,3315],{}," \u003Cintent-filter>\n",[233,3317,3318],{"class":235,"line":420},[233,3319,3320],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.RECEIVE\"/>\n",[233,3322,3323],{"class":235,"line":426},[233,3324,3325],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.REGISTRATION\"/>\n",[233,3327,3328],{"class":235,"line":657},[233,3329,3330],{}," \u003Ccategory android:name=\"com.synyx.cloudmessagetest\"/>\n",[233,3332,3333],{"class":235,"line":662},[233,3334,3335],{}," \u003C/intent-filter>\n",[233,3337,3338],{"class":235,"line":667},[233,3339,3340],{},"\u003C/receiver>\n",[233,3342,3343],{"class":235,"line":673},[233,3344,3345],{},"\u003Cservice android:name=\".GCMIntentService\"/>\n",[19,3347,3348],{},"The GCMIntentService has to be created by us. And that is what we’ll do now. First off, the GCMIntentService has to\nextend the class GCMBaseIntentService:",[225,3350,3352],{"className":227,"code":3351,"language":229,"meta":110,"style":110},"public class GCMIntentService extends GCMBaseIntentService {\n",[176,3353,3354],{"__ignoreMap":110},[233,3355,3356],{"class":235,"line":236},[233,3357,3351],{},[19,3359,3360],{},"Now implement all the necessary methods. The only method we will use for this little test is onMessage(). We want to\nquickly see if we get a message for this app, so that we can confirm that it works. So we just create a notification\nwith the Notification Builder.",[19,3362,3363],{},"Because we are on an older minimum version of Android, we need to add the support library to have access to the\nNotification Builder. Add it by right clicking the project -> Android tools -> Add Support Library.",[19,3365,3366],{},"First in the onMessage() method, we need to get access to the Main Thread of our App.",[225,3368,3370],{"className":227,"code":3369,"language":229,"meta":110,"style":110},"Handler h = new Handler(Looper.getMainLooper());\nh.post(new Runnable() {\n public void run() {\n }\n}\n",[176,3371,3372,3377,3382,3387,3391],{"__ignoreMap":110},[233,3373,3374],{"class":235,"line":236},[233,3375,3376],{},"Handler h = new Handler(Looper.getMainLooper());\n",[233,3378,3379],{"class":235,"line":111},[233,3380,3381],{},"h.post(new Runnable() {\n",[233,3383,3384],{"class":235,"line":402},[233,3385,3386],{}," public void run() {\n",[233,3388,3389],{"class":235,"line":408},[233,3390,423],{},[233,3392,3393],{"class":235,"line":414},[233,3394,429],{},[19,3396,3397],{},"In the run() method, we get us an Intent from our MainActivity",[225,3399,3401],{"className":227,"code":3400,"language":229,"meta":110,"style":110},"Intent notificationIntent = new Intent(context, CloudMessageTestActivity.class);\n",[176,3402,3403],{"__ignoreMap":110},[233,3404,3405],{"class":235,"line":236},[233,3406,3400],{},[19,3408,3409],{},"And then wrap it with a PendingIntent for the NotificationBuilder",[225,3411,3413],{"className":227,"code":3412,"language":229,"meta":110,"style":110},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n notificationIntent, 0);\n",[176,3414,3415,3420],{"__ignoreMap":110},[233,3416,3417],{"class":235,"line":236},[233,3418,3419],{},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n",[233,3421,3422],{"class":235,"line":111},[233,3423,3424],{}," notificationIntent, 0);\n",[19,3426,3427],{},"Finally, use the NotificationBuilder to create and send the notification",[225,3429,3431],{"className":227,"code":3430,"language":229,"meta":110,"style":110},"NotificationCompat.Builder builder = new NotificationCompat.Builder(\n context);\nbuilder.setContentIntent(pendingIntent);\nbuilder.setAutoCancel(true);\nbuilder.setSmallIcon(R.drawable.ic_launcher);\n//this is added on the server side\nString text = intent.getStringExtra(\"text\");\nbuilder.setContentText(text);\nbuilder.setContentTitle(\"New message from the cloud!\");\nNotification noti = builder.build();\nNotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n//just set the mId to 1, because we don't care about it in this case\nmNotificationManager.notify(1, noti);\n",[176,3432,3433,3438,3443,3448,3453,3458,3463,3468,3473,3478,3483,3488,3493],{"__ignoreMap":110},[233,3434,3435],{"class":235,"line":236},[233,3436,3437],{},"NotificationCompat.Builder builder = new NotificationCompat.Builder(\n",[233,3439,3440],{"class":235,"line":111},[233,3441,3442],{}," context);\n",[233,3444,3445],{"class":235,"line":402},[233,3446,3447],{},"builder.setContentIntent(pendingIntent);\n",[233,3449,3450],{"class":235,"line":408},[233,3451,3452],{},"builder.setAutoCancel(true);\n",[233,3454,3455],{"class":235,"line":414},[233,3456,3457],{},"builder.setSmallIcon(R.drawable.ic_launcher);\n",[233,3459,3460],{"class":235,"line":420},[233,3461,3462],{},"//this is added on the server side\n",[233,3464,3465],{"class":235,"line":426},[233,3466,3467],{},"String text = intent.getStringExtra(\"text\");\n",[233,3469,3470],{"class":235,"line":657},[233,3471,3472],{},"builder.setContentText(text);\n",[233,3474,3475],{"class":235,"line":662},[233,3476,3477],{},"builder.setContentTitle(\"New message from the cloud!\");\n",[233,3479,3480],{"class":235,"line":667},[233,3481,3482],{},"Notification noti = builder.build();\n",[233,3484,3485],{"class":235,"line":673},[233,3486,3487],{},"NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n",[233,3489,3490],{"class":235,"line":679},[233,3491,3492],{},"//just set the mId to 1, because we don't care about it in this case\n",[233,3494,3495],{"class":235,"line":684},[233,3496,3497],{},"mNotificationManager.notify(1, noti);\n",[19,3499,3500],{},"That’ it with the GCMIntentService.",[19,3502,3503],{},"Now we let the app register itself with GCM in our MainActivity, which is fairly easy:",[225,3505,3507],{"className":227,"code":3506,"language":229,"meta":110,"style":110},"//set your senderId from the API here!\n private static final String SENDER_ID = \"1234567890\";\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n GCMRegistrar.checkDevice(this);\n GCMRegistrar.checkManifest(this);\n final String regId = GCMRegistrar.getRegistrationId(this);\n // if we don't have a regId yet, register at gcm\n if (regId.equals(\"\")) {\n GCMRegistrar.register(this, SENDER_ID);\n Toast.makeText(getApplicationContext(), \"Registered GCM!\", Toast.LENGTH_LONG).show();\n // just log the registrationId for this test case.\n Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n } else {\n Log.i(this.getClass().getName(), \"Already registered\");\n Toast.makeText(getApplicationContext(), \"Already registered at GCM!\", Toast.LENGTH_LONG).show();\n Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n }\n}\n",[176,3508,3509,3514,3519,3523,3528,3533,3538,3543,3548,3553,3558,3563,3568,3573,3578,3583,3588,3592,3597],{"__ignoreMap":110},[233,3510,3511],{"class":235,"line":236},[233,3512,3513],{},"//set your senderId from the API here!\n",[233,3515,3516],{"class":235,"line":111},[233,3517,3518],{}," private static final String SENDER_ID = \"1234567890\";\n",[233,3520,3521],{"class":235,"line":402},[233,3522,3104],{},[233,3524,3525],{"class":235,"line":408},[233,3526,3527],{},"protected void onCreate(Bundle savedInstanceState) {\n",[233,3529,3530],{"class":235,"line":414},[233,3531,3532],{}," GCMRegistrar.checkDevice(this);\n",[233,3534,3535],{"class":235,"line":420},[233,3536,3537],{}," GCMRegistrar.checkManifest(this);\n",[233,3539,3540],{"class":235,"line":426},[233,3541,3542],{}," final String regId = GCMRegistrar.getRegistrationId(this);\n",[233,3544,3545],{"class":235,"line":657},[233,3546,3547],{}," // if we don't have a regId yet, register at gcm\n",[233,3549,3550],{"class":235,"line":662},[233,3551,3552],{}," if (regId.equals(\"\")) {\n",[233,3554,3555],{"class":235,"line":667},[233,3556,3557],{}," GCMRegistrar.register(this, SENDER_ID);\n",[233,3559,3560],{"class":235,"line":673},[233,3561,3562],{}," Toast.makeText(getApplicationContext(), \"Registered GCM!\", Toast.LENGTH_LONG).show();\n",[233,3564,3565],{"class":235,"line":679},[233,3566,3567],{}," // just log the registrationId for this test case.\n",[233,3569,3570],{"class":235,"line":684},[233,3571,3572],{}," Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n",[233,3574,3575],{"class":235,"line":689},[233,3576,3577],{}," } else {\n",[233,3579,3580],{"class":235,"line":695},[233,3581,3582],{}," Log.i(this.getClass().getName(), \"Already registered\");\n",[233,3584,3585],{"class":235,"line":701},[233,3586,3587],{}," Toast.makeText(getApplicationContext(), \"Already registered at GCM!\", Toast.LENGTH_LONG).show();\n",[233,3589,3590],{"class":235,"line":706},[233,3591,3572],{},[233,3593,3594],{"class":235,"line":711},[233,3595,3596],{}," }\n",[233,3598,3599],{"class":235,"line":717},[233,3600,429],{},[19,3602,3603],{},"Don’t forget to replace the SENDER_ID with yours!",[19,3605,3606],{},"I haven’t implemented a way to let the server know the registrationId, because reading it from the log seemed sufficient\nfor me in this case.",[19,3608,3609],{},"With this, we finished our small app and can begin implementing the server part.",[23,3611,3613],{"id":3612},"the-web-application","The Web Application",[19,3615,3616],{},"For the Web Application, I created a maven web project with spring-webmvc and named it ‘GCMTestServer’. I’ll leave out\nthe config stuff for now, as everyone can use his/her favorite stack for this. The full sources with the configs are\nattached at the end of the blogpost.",[19,3618,3619,3620,1690],{},"Instead of just copying the GCM server library into the project, I searched a bit and found someone, who created a\nrepository for\nit. (",[159,3621,3622],{"href":3622,"rel":3623},"https://github.com/slorber/gcm-server-repository",[163],[19,3625,3626],{},"We start with creating the Sender class, which isn’t that hard either.",[225,3628,3630],{"className":227,"code":3629,"language":229,"meta":110,"style":110},"public class GCMSender {\n public String apiKey = null;\n public GCMSender(String apiKey) {\n this.apiKey = apiKey;\n }\n public String send(String text, String id) throws IOException {\n Sender sender = new Sender(apiKey);\n Builder builder = new Message.Builder();\n builder.addData(\"text\", text);\n Result result = sender.send(builder.build(), id, 5);\n if (result.getMessageId() != null) {\n String canonicalRegId = result.getCanonicalRegistrationId();\n if (canonicalRegId != null) {\n // same device has more than on registration ID: update database\n return \"same device has more than on registration ID: update database\";\n }\n } else {\n String error = result.getErrorCodeName();\n if (error.equals(Constants.ERROR_NOT_REGISTERED)) {\n // application has been removed from device - unregister database\n return \"application has been removed from device - unregister database\";\n }\n }\n return null;\n }\n}\n",[176,3631,3632,3637,3642,3647,3652,3657,3662,3667,3672,3677,3682,3687,3692,3697,3702,3707,3712,3717,3722,3727,3732,3737,3741,3745,3750,3754],{"__ignoreMap":110},[233,3633,3634],{"class":235,"line":236},[233,3635,3636],{},"public class GCMSender {\n",[233,3638,3639],{"class":235,"line":111},[233,3640,3641],{}," public String apiKey = null;\n",[233,3643,3644],{"class":235,"line":402},[233,3645,3646],{}," public GCMSender(String apiKey) {\n",[233,3648,3649],{"class":235,"line":408},[233,3650,3651],{}," this.apiKey = apiKey;\n",[233,3653,3654],{"class":235,"line":414},[233,3655,3656],{}," }\n",[233,3658,3659],{"class":235,"line":420},[233,3660,3661],{}," public String send(String text, String id) throws IOException {\n",[233,3663,3664],{"class":235,"line":426},[233,3665,3666],{}," Sender sender = new Sender(apiKey);\n",[233,3668,3669],{"class":235,"line":657},[233,3670,3671],{}," Builder builder = new Message.Builder();\n",[233,3673,3674],{"class":235,"line":662},[233,3675,3676],{}," builder.addData(\"text\", text);\n",[233,3678,3679],{"class":235,"line":667},[233,3680,3681],{}," Result result = sender.send(builder.build(), id, 5);\n",[233,3683,3684],{"class":235,"line":673},[233,3685,3686],{}," if (result.getMessageId() != null) {\n",[233,3688,3689],{"class":235,"line":679},[233,3690,3691],{}," String canonicalRegId = result.getCanonicalRegistrationId();\n",[233,3693,3694],{"class":235,"line":684},[233,3695,3696],{}," if (canonicalRegId != null) {\n",[233,3698,3699],{"class":235,"line":689},[233,3700,3701],{}," // same device has more than on registration ID: update database\n",[233,3703,3704],{"class":235,"line":695},[233,3705,3706],{}," return \"same device has more than on registration ID: update database\";\n",[233,3708,3709],{"class":235,"line":701},[233,3710,3711],{}," }\n",[233,3713,3714],{"class":235,"line":706},[233,3715,3716],{}," } else {\n",[233,3718,3719],{"class":235,"line":711},[233,3720,3721],{}," String error = result.getErrorCodeName();\n",[233,3723,3724],{"class":235,"line":717},[233,3725,3726],{}," if (error.equals(Constants.ERROR_NOT_REGISTERED)) {\n",[233,3728,3729],{"class":235,"line":723},[233,3730,3731],{}," // application has been removed from device - unregister database\n",[233,3733,3734],{"class":235,"line":728},[233,3735,3736],{}," return \"application has been removed from device - unregister database\";\n",[233,3738,3739],{"class":235,"line":733},[233,3740,3711],{},[233,3742,3743],{"class":235,"line":1084},[233,3744,3596],{},[233,3746,3747],{"class":235,"line":1089},[233,3748,3749],{}," return null;\n",[233,3751,3752],{"class":235,"line":1094},[233,3753,3656],{},[233,3755,3756],{"class":235,"line":1099},[233,3757,429],{},[19,3759,3760],{},"To send messages from the server, I created a small jsp page where I can enter the text and the RegistrationId of the\nuser to send the message to:",[225,3762,3764],{"className":1772,"code":3763,"language":1774,"meta":110,"style":110},"\u003C%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> \u003C%@page\ncontentType=\"text/html\" pageEncoding=\"UTF-8\"%>\n\u003C!DOCTYPE html>\n\u003Chtml>\n \u003Chead>\n \u003Cmeta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n \u003Clink\n rel=\"stylesheet\"\n type=\"text/css\"\n href=\"/GCMTestServer/frontend_resources/style.css\"\n media=\"screen\"\n />\n \u003Ctitle>GCM Sender\u003C/title>\n \u003C/head>\n \u003Cbody>\n \u003Ch1>GCM Sender\u003C/h1>\n \u003Cc:if test=\"${success == true}\">\n \u003Cdiv class=\"success\">sending successful!\u003C/div>\n \u003C/c:if>\n \u003Cc:if test=\"${error == true}\">\n \u003Cdiv class=\"error\">\n Error at sending!\n \u003Cp>${errormessage}\u003C/p>\n \u003C/div>\n \u003C/c:if>\n \u003Cform method=\"POST\">\n \u003Clabel for=\"text\">Text\u003C/label\n >\u003Cinput name=\"text\" id=\"text\" type=\"text\" />\u003Cbr />\n \u003Clabel for=\"id\">Registration-Id\u003C/label\n >\u003Cinput name=\"id\" id=\"id\" type=\"text\" />\u003Cbr />\n \u003Cinput type=\"submit\" />\n \u003C/form>\n \u003C/body>\n\u003C/html>\n",[176,3765,3766,3778,3783,3798,3806,3815,3844,3851,3861,3871,3881,3891,3896,3910,3919,3928,3940,3957,3980,3989,4004,4019,4024,4038,4047,4055,4072,4093,4130,4148,4178,4193,4201,4209],{"__ignoreMap":110},[233,3767,3768,3770,3773,3775],{"class":235,"line":236},[233,3769,1782],{"class":1785},[233,3771,3772],{"class":1781},"%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> ",[233,3774,1782],{"class":1785},[233,3776,3777],{"class":1781},"%@page\n",[233,3779,3780],{"class":235,"line":111},[233,3781,3782],{"class":1781},"contentType=\"text/html\" pageEncoding=\"UTF-8\"%>\n",[233,3784,3785,3788,3792,3796],{"class":235,"line":402},[233,3786,3787],{"class":1781},"\u003C!",[233,3789,3791],{"class":3790},"s9eBZ","DOCTYPE",[233,3793,3795],{"class":3794},"sScJk"," html",[233,3797,1789],{"class":1781},[233,3799,3800,3802,3804],{"class":235,"line":408},[233,3801,1782],{"class":1781},[233,3803,1774],{"class":3790},[233,3805,1789],{"class":1781},[233,3807,3808,3810,3813],{"class":235,"line":414},[233,3809,1794],{"class":1781},[233,3811,3812],{"class":3790},"head",[233,3814,1789],{"class":1781},[233,3816,3817,3820,3823,3826,3829,3833,3836,3838,3841],{"class":235,"line":420},[233,3818,3819],{"class":1781}," \u003C",[233,3821,3822],{"class":3790},"meta",[233,3824,3825],{"class":3794}," http-equiv",[233,3827,3828],{"class":1781},"=",[233,3830,3832],{"class":3831},"sZZnC","\"Content-Type\"",[233,3834,3835],{"class":3794}," content",[233,3837,3828],{"class":1781},[233,3839,3840],{"class":3831},"\"text/html; charset=UTF-8\"",[233,3842,3843],{"class":1781}," />\n",[233,3845,3846,3848],{"class":235,"line":426},[233,3847,3819],{"class":1781},[233,3849,3850],{"class":3790},"link\n",[233,3852,3853,3856,3858],{"class":235,"line":657},[233,3854,3855],{"class":3794}," rel",[233,3857,3828],{"class":1781},[233,3859,3860],{"class":3831},"\"stylesheet\"\n",[233,3862,3863,3866,3868],{"class":235,"line":662},[233,3864,3865],{"class":3794}," type",[233,3867,3828],{"class":1781},[233,3869,3870],{"class":3831},"\"text/css\"\n",[233,3872,3873,3876,3878],{"class":235,"line":667},[233,3874,3875],{"class":3794}," href",[233,3877,3828],{"class":1781},[233,3879,3880],{"class":3831},"\"/GCMTestServer/frontend_resources/style.css\"\n",[233,3882,3883,3886,3888],{"class":235,"line":673},[233,3884,3885],{"class":3794}," media",[233,3887,3828],{"class":1781},[233,3889,3890],{"class":3831},"\"screen\"\n",[233,3892,3893],{"class":235,"line":679},[233,3894,3895],{"class":1781}," />\n",[233,3897,3898,3900,3903,3906,3908],{"class":235,"line":684},[233,3899,3819],{"class":1781},[233,3901,3902],{"class":3790},"title",[233,3904,3905],{"class":1781},">GCM Sender\u003C/",[233,3907,3902],{"class":3790},[233,3909,1789],{"class":1781},[233,3911,3912,3915,3917],{"class":235,"line":689},[233,3913,3914],{"class":1781}," \u003C/",[233,3916,3812],{"class":3790},[233,3918,1789],{"class":1781},[233,3920,3921,3923,3926],{"class":235,"line":695},[233,3922,1794],{"class":1781},[233,3924,3925],{"class":3790},"body",[233,3927,1789],{"class":1781},[233,3929,3930,3932,3934,3936,3938],{"class":235,"line":701},[233,3931,3819],{"class":1781},[233,3933,15],{"class":3790},[233,3935,3905],{"class":1781},[233,3937,15],{"class":3790},[233,3939,1789],{"class":1781},[233,3941,3942,3944,3947,3950,3952,3955],{"class":235,"line":706},[233,3943,3819],{"class":1781},[233,3945,3946],{"class":1785},"c:if",[233,3948,3949],{"class":3794}," test",[233,3951,3828],{"class":1781},[233,3953,3954],{"class":3831},"\"${success == true}\"",[233,3956,1789],{"class":1781},[233,3958,3959,3962,3965,3968,3970,3973,3976,3978],{"class":235,"line":711},[233,3960,3961],{"class":1781}," \u003C",[233,3963,3964],{"class":3790},"div",[233,3966,3967],{"class":3794}," class",[233,3969,3828],{"class":1781},[233,3971,3972],{"class":3831},"\"success\"",[233,3974,3975],{"class":1781},">sending successful!\u003C/",[233,3977,3964],{"class":3790},[233,3979,1789],{"class":1781},[233,3981,3982,3985,3987],{"class":235,"line":717},[233,3983,3984],{"class":1781}," \u003C/",[233,3986,3946],{"class":1785},[233,3988,1789],{"class":1781},[233,3990,3991,3993,3995,3997,3999,4002],{"class":235,"line":723},[233,3992,3819],{"class":1781},[233,3994,3946],{"class":1785},[233,3996,3949],{"class":3794},[233,3998,3828],{"class":1781},[233,4000,4001],{"class":3831},"\"${error == true}\"",[233,4003,1789],{"class":1781},[233,4005,4006,4008,4010,4012,4014,4017],{"class":235,"line":728},[233,4007,3961],{"class":1781},[233,4009,3964],{"class":3790},[233,4011,3967],{"class":3794},[233,4013,3828],{"class":1781},[233,4015,4016],{"class":3831},"\"error\"",[233,4018,1789],{"class":1781},[233,4020,4021],{"class":235,"line":733},[233,4022,4023],{"class":1781}," Error at sending!\n",[233,4025,4026,4029,4031,4034,4036],{"class":235,"line":1084},[233,4027,4028],{"class":1781}," \u003C",[233,4030,19],{"class":3790},[233,4032,4033],{"class":1781},">${errormessage}\u003C/",[233,4035,19],{"class":3790},[233,4037,1789],{"class":1781},[233,4039,4040,4043,4045],{"class":235,"line":1089},[233,4041,4042],{"class":1781}," \u003C/",[233,4044,3964],{"class":3790},[233,4046,1789],{"class":1781},[233,4048,4049,4051,4053],{"class":235,"line":1094},[233,4050,3984],{"class":1781},[233,4052,3946],{"class":1785},[233,4054,1789],{"class":1781},[233,4056,4057,4059,4062,4065,4067,4070],{"class":235,"line":1099},[233,4058,3819],{"class":1781},[233,4060,4061],{"class":3790},"form",[233,4063,4064],{"class":3794}," method",[233,4066,3828],{"class":1781},[233,4068,4069],{"class":3831},"\"POST\"",[233,4071,1789],{"class":1781},[233,4073,4074,4076,4079,4082,4084,4087,4090],{"class":235,"line":1105},[233,4075,3961],{"class":1781},[233,4077,4078],{"class":3790},"label",[233,4080,4081],{"class":3794}," for",[233,4083,3828],{"class":1781},[233,4085,4086],{"class":3831},"\"text\"",[233,4088,4089],{"class":1781},">Text\u003C/",[233,4091,4092],{"class":3790},"label\n",[233,4094,4095,4098,4101,4104,4106,4108,4111,4113,4115,4118,4120,4122,4125,4128],{"class":235,"line":1111},[233,4096,4097],{"class":1781}," >\u003C",[233,4099,4100],{"class":3790},"input",[233,4102,4103],{"class":3794}," name",[233,4105,3828],{"class":1781},[233,4107,4086],{"class":3831},[233,4109,4110],{"class":3794}," id",[233,4112,3828],{"class":1781},[233,4114,4086],{"class":3831},[233,4116,4117],{"class":3794}," type",[233,4119,3828],{"class":1781},[233,4121,4086],{"class":3831},[233,4123,4124],{"class":1781}," />\u003C",[233,4126,4127],{"class":3790},"br",[233,4129,3843],{"class":1781},[233,4131,4132,4134,4136,4138,4140,4143,4146],{"class":235,"line":1116},[233,4133,3961],{"class":1781},[233,4135,4078],{"class":3790},[233,4137,4081],{"class":3794},[233,4139,3828],{"class":1781},[233,4141,4142],{"class":3831},"\"id\"",[233,4144,4145],{"class":1781},">Registration-Id\u003C/",[233,4147,4092],{"class":3790},[233,4149,4150,4152,4154,4156,4158,4160,4162,4164,4166,4168,4170,4172,4174,4176],{"class":235,"line":2144},[233,4151,4097],{"class":1781},[233,4153,4100],{"class":3790},[233,4155,4103],{"class":3794},[233,4157,3828],{"class":1781},[233,4159,4142],{"class":3831},[233,4161,4110],{"class":3794},[233,4163,3828],{"class":1781},[233,4165,4142],{"class":3831},[233,4167,4117],{"class":3794},[233,4169,3828],{"class":1781},[233,4171,4086],{"class":3831},[233,4173,4124],{"class":1781},[233,4175,4127],{"class":3790},[233,4177,3843],{"class":1781},[233,4179,4180,4182,4184,4186,4188,4191],{"class":235,"line":2149},[233,4181,3961],{"class":1781},[233,4183,4100],{"class":3790},[233,4185,4117],{"class":3794},[233,4187,3828],{"class":1781},[233,4189,4190],{"class":3831},"\"submit\"",[233,4192,3843],{"class":1781},[233,4194,4195,4197,4199],{"class":235,"line":2154},[233,4196,3984],{"class":1781},[233,4198,4061],{"class":3790},[233,4200,1789],{"class":1781},[233,4202,4203,4205,4207],{"class":235,"line":2160},[233,4204,3914],{"class":1781},[233,4206,3925],{"class":3790},[233,4208,1789],{"class":1781},[233,4210,4211,4213,4215],{"class":235,"line":2165},[233,4212,1828],{"class":1781},[233,4214,1774],{"class":3790},[233,4216,1789],{"class":1781},[19,4218,4219],{},"In the corresponding Controller to process the request, send the message:",[225,4221,4223],{"className":227,"code":4222,"language":229,"meta":110,"style":110}," @RequestMapping(value = \"send\", method = RequestMethod.POST)\n public String send(Model model,\n @RequestParam(\"text\") String text,\n @RequestParam(\"id\") String id) {\n String error = null;\n try {\n error = gcmSender.send(text, id);\n } catch (IOException ex) {\n model.addAttribute(\"error\", true);\n model.addAttribute(\"errormessage\", ex.getMessage());\n }\n if (error == null) {\n model.addAttribute(\"success\", true);\n } else {\n model.addAttribute(\"error\", true);\n model.addAttribute(\"errormessage\", error);\n }\n return \"sender\";\n }\n",[176,4224,4225,4230,4235,4240,4245,4250,4255,4260,4265,4270,4275,4279,4284,4289,4293,4297,4302,4306,4311],{"__ignoreMap":110},[233,4226,4227],{"class":235,"line":236},[233,4228,4229],{}," @RequestMapping(value = \"send\", method = RequestMethod.POST)\n",[233,4231,4232],{"class":235,"line":111},[233,4233,4234],{}," public String send(Model model,\n",[233,4236,4237],{"class":235,"line":402},[233,4238,4239],{}," @RequestParam(\"text\") String text,\n",[233,4241,4242],{"class":235,"line":408},[233,4243,4244],{}," @RequestParam(\"id\") String id) {\n",[233,4246,4247],{"class":235,"line":414},[233,4248,4249],{}," String error = null;\n",[233,4251,4252],{"class":235,"line":420},[233,4253,4254],{}," try {\n",[233,4256,4257],{"class":235,"line":426},[233,4258,4259],{}," error = gcmSender.send(text, id);\n",[233,4261,4262],{"class":235,"line":657},[233,4263,4264],{}," } catch (IOException ex) {\n",[233,4266,4267],{"class":235,"line":662},[233,4268,4269],{}," model.addAttribute(\"error\", true);\n",[233,4271,4272],{"class":235,"line":667},[233,4273,4274],{}," model.addAttribute(\"errormessage\", ex.getMessage());\n",[233,4276,4277],{"class":235,"line":673},[233,4278,417],{},[233,4280,4281],{"class":235,"line":679},[233,4282,4283],{}," if (error == null) {\n",[233,4285,4286],{"class":235,"line":684},[233,4287,4288],{}," model.addAttribute(\"success\", true);\n",[233,4290,4291],{"class":235,"line":689},[233,4292,1261],{},[233,4294,4295],{"class":235,"line":695},[233,4296,4269],{},[233,4298,4299],{"class":235,"line":701},[233,4300,4301],{}," model.addAttribute(\"errormessage\", error);\n",[233,4303,4304],{"class":235,"line":706},[233,4305,417],{},[233,4307,4308],{"class":235,"line":711},[233,4309,4310],{}," return \"sender\";\n",[233,4312,4313],{"class":235,"line":717},[233,4314,423],{},[19,4316,4317],{},"Now we are ready to test it!",[19,4319,4320],{},"Start the app, copy the RegistrationId from the Logs, start the Server, enter the RegistrationId and a small text, and\nthere you go:",[19,4322,4323],{},[33,4324],{"alt":4325,"src":4326},"\"notification\"","https://media.synyx.de/uploads//2012/12/notification-300x221.jpg",[19,4328,4329],{},"To get a better understanding, you can also read the rest of the starting guide and the other documentation.",[19,4331,4332],{},"All together, it’s really easy to use the GCM libraries and the messages are beeing sent to the user very fast (around\none second for me). I haven’t tried to use it in a greater context with more users yet, but I don’t think google will\nfail on this behalf 😛",[19,4334,4335,4336],{},"As promised, here are the full sources:",[159,4337,4340],{"href":4338,"rel":4339},"https://media.synyx.de/uploads//2012/12/CloudMessageTest.zip",[163],"CloudMessageTest",[560,4342,4343],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":110,"searchDepth":111,"depth":111,"links":4345},[4346,4347],{"id":3207,"depth":111,"text":3208},{"id":3612,"depth":111,"text":3613},[120,2314],"2013-01-08T08:21:24","https://synyx.de/blog/a-small-look-into-google-cloud-messages/",{},"/blog/a-small-look-into-google-cloud-messages",{"title":3182,"description":3191},"blog/a-small-look-into-google-cloud-messages",[133,4356,4357,4358,4359,4360,4361],"cloud","gcm","google-cloud","messages","messaging","push-notification","Within the scope of some Android R&D I took a look at Google’s Cloud Message Service, GCM. Well, the starter guide at http://developer.android.com/google/gcm/gs.html is almost all you need to get started,…","Zo3lVe4ZoQawpHyu6_t-mMTQUwODaiJpqsdkWTxts5M",{"id":4365,"title":4366,"author":4367,"body":4368,"category":5017,"date":5018,"description":5019,"extension":124,"link":5020,"meta":5021,"navigation":127,"path":5022,"seo":5023,"slug":4372,"stem":5024,"tags":5025,"teaser":5033,"__hash__":5034},"blog/blog/android-expandable-listview.md","Android: Expandable ListView",[586],{"type":12,"value":4369,"toc":5015},[4370,4373,4376,4379,4566,4569,4634,4637,4698,4701,4704,4713,4716,4719,4756,4759,4774,4777,4825,4828,4899,4902,4950,4953,4978,4981,4984,4995,5005,5013],[15,4371,4366],{"id":4372},"android-expandable-listview",[19,4374,4375],{},"In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries.\nWith a button at the end of the list, the user can load more entries.",[19,4377,4378],{},"To achieve this goal, we first need to implement a basic Adapter that provides our ListView with the entries:",[225,4380,4382],{"className":227,"code":4381,"language":229,"meta":110,"style":110},"private class ExpandableListAdapter extends BaseAdapter {\n private List entries;\n private Context context;\n public ExpandableListAdapter(List entries) {\n this.entries = entries;\n }\n @Override\n public int getCount() {\n return entries.size();\n }\n @Override\n public Object getItem(int position) {\n if (position >= 0 && position \u003C entries.size())\n return this.entries.get(position);\n return null;\n }\n @Override\n public long getItemId(int position) {\n return position;\n }\n @Override\n public View getView(int position, View convertView, ViewGroup parent) {\n View view = null;\n //reuse the convertView\n if (convertView == null) {\n view = getLayoutInflater().inflate(R.layout.row, null);\n } else {\n view = convertView;\n }\n String entry = entries.get(position);\n view.setTag(position);\n fillView(view, entry);\n return view;\n }\n private void fillView(View view, String entry) {\n TextView text = (TextView) view.findViewById(R.id.text);\n text.setText(entry);\n }\n}\n",[176,4383,4384,4389,4394,4399,4404,4409,4413,4417,4422,4427,4431,4435,4440,4445,4450,4455,4459,4463,4468,4473,4477,4481,4486,4491,4496,4501,4506,4510,4515,4519,4524,4529,4534,4539,4543,4548,4553,4558,4562],{"__ignoreMap":110},[233,4385,4386],{"class":235,"line":236},[233,4387,4388],{},"private class ExpandableListAdapter extends BaseAdapter {\n",[233,4390,4391],{"class":235,"line":111},[233,4392,4393],{}," private List entries;\n",[233,4395,4396],{"class":235,"line":402},[233,4397,4398],{}," private Context context;\n",[233,4400,4401],{"class":235,"line":408},[233,4402,4403],{}," public ExpandableListAdapter(List entries) {\n",[233,4405,4406],{"class":235,"line":414},[233,4407,4408],{}," this.entries = entries;\n",[233,4410,4411],{"class":235,"line":420},[233,4412,423],{},[233,4414,4415],{"class":235,"line":426},[233,4416,773],{},[233,4418,4419],{"class":235,"line":657},[233,4420,4421],{}," public int getCount() {\n",[233,4423,4424],{"class":235,"line":662},[233,4425,4426],{}," return entries.size();\n",[233,4428,4429],{"class":235,"line":667},[233,4430,423],{},[233,4432,4433],{"class":235,"line":673},[233,4434,773],{},[233,4436,4437],{"class":235,"line":679},[233,4438,4439],{}," public Object getItem(int position) {\n",[233,4441,4442],{"class":235,"line":684},[233,4443,4444],{}," if (position >= 0 && position \u003C entries.size())\n",[233,4446,4447],{"class":235,"line":689},[233,4448,4449],{}," return this.entries.get(position);\n",[233,4451,4452],{"class":235,"line":695},[233,4453,4454],{}," return null;\n",[233,4456,4457],{"class":235,"line":701},[233,4458,423],{},[233,4460,4461],{"class":235,"line":706},[233,4462,773],{},[233,4464,4465],{"class":235,"line":711},[233,4466,4467],{}," public long getItemId(int position) {\n",[233,4469,4470],{"class":235,"line":717},[233,4471,4472],{}," return position;\n",[233,4474,4475],{"class":235,"line":723},[233,4476,423],{},[233,4478,4479],{"class":235,"line":728},[233,4480,773],{},[233,4482,4483],{"class":235,"line":733},[233,4484,4485],{}," public View getView(int position, View convertView, ViewGroup parent) {\n",[233,4487,4488],{"class":235,"line":1084},[233,4489,4490],{}," View view = null;\n",[233,4492,4493],{"class":235,"line":1089},[233,4494,4495],{}," //reuse the convertView\n",[233,4497,4498],{"class":235,"line":1094},[233,4499,4500],{}," if (convertView == null) {\n",[233,4502,4503],{"class":235,"line":1099},[233,4504,4505],{}," view = getLayoutInflater().inflate(R.layout.row, null);\n",[233,4507,4508],{"class":235,"line":1105},[233,4509,1261],{},[233,4511,4512],{"class":235,"line":1111},[233,4513,4514],{}," view = convertView;\n",[233,4516,4517],{"class":235,"line":1116},[233,4518,417],{},[233,4520,4521],{"class":235,"line":2144},[233,4522,4523],{}," String entry = entries.get(position);\n",[233,4525,4526],{"class":235,"line":2149},[233,4527,4528],{}," view.setTag(position);\n",[233,4530,4531],{"class":235,"line":2154},[233,4532,4533],{}," fillView(view, entry);\n",[233,4535,4536],{"class":235,"line":2160},[233,4537,4538],{}," return view;\n",[233,4540,4541],{"class":235,"line":2165},[233,4542,423],{},[233,4544,4545],{"class":235,"line":2171},[233,4546,4547],{}," private void fillView(View view, String entry) {\n",[233,4549,4550],{"class":235,"line":2176},[233,4551,4552],{}," TextView text = (TextView) view.findViewById(R.id.text);\n",[233,4554,4555],{"class":235,"line":2181},[233,4556,4557],{}," text.setText(entry);\n",[233,4559,4560],{"class":235,"line":2186},[233,4561,423],{},[233,4563,4564],{"class":235,"line":2191},[233,4565,429],{},[19,4567,4568],{},"For this example, I used a simple view for the rows, with just a TextView in it:",[225,4570,4572],{"className":1649,"code":4571,"language":1651,"meta":110,"style":110},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"match_parent\"\n android:orientation=\"vertical\">\n \u003CTextView\n android:id=\"@+id/text\"\n android:layout_width=\"match_parent\"\n android:layout_height=\"30dp\"\n android:gravity=\"center_vertical\"\n android:padding=\"5dp\"/>\n\u003C/LinearLayout>\n",[176,4573,4574,4579,4584,4589,4594,4599,4604,4609,4614,4619,4624,4629],{"__ignoreMap":110},[233,4575,4576],{"class":235,"line":236},[233,4577,4578],{},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n",[233,4580,4581],{"class":235,"line":111},[233,4582,4583],{},"\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n",[233,4585,4586],{"class":235,"line":402},[233,4587,4588],{}," android:layout_width=\"match_parent\"\n",[233,4590,4591],{"class":235,"line":408},[233,4592,4593],{}," android:layout_height=\"match_parent\"\n",[233,4595,4596],{"class":235,"line":414},[233,4597,4598],{}," android:orientation=\"vertical\">\n",[233,4600,4601],{"class":235,"line":420},[233,4602,4603],{}," \u003CTextView\n",[233,4605,4606],{"class":235,"line":426},[233,4607,4608],{}," android:id=\"@+id/text\"\n",[233,4610,4611],{"class":235,"line":657},[233,4612,4613],{}," android:layout_width=\"match_parent\"\n",[233,4615,4616],{"class":235,"line":662},[233,4617,4618],{}," android:layout_height=\"30dp\"\n",[233,4620,4621],{"class":235,"line":667},[233,4622,4623],{}," android:gravity=\"center_vertical\"\n",[233,4625,4626],{"class":235,"line":673},[233,4627,4628],{}," android:padding=\"5dp\"/>\n",[233,4630,4631],{"class":235,"line":679},[233,4632,4633],{},"\u003C/LinearLayout>\n",[19,4635,4636],{},"Then we add the created adapter to a ListView (or a ListActivity like here):",[225,4638,4640],{"className":227,"code":4639,"language":229,"meta":110,"style":110},"public class ExpandableListActivity extends ListActivity {\n@Override\npublic void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n List\u003CString> entries = new ArrayList\u003CString>();\n for (int i = 1; i \u003C= 101; i++) {\n entries.add(\"Entry \" + i);\n }\n setListAdapter(new ExpandableListAdapter(entries, this));\n}\n private class ExpandableListAdapter extends BaseAdapter{....}\n}\n",[176,4641,4642,4647,4651,4656,4661,4666,4671,4676,4680,4685,4689,4694],{"__ignoreMap":110},[233,4643,4644],{"class":235,"line":236},[233,4645,4646],{},"public class ExpandableListActivity extends ListActivity {\n",[233,4648,4649],{"class":235,"line":111},[233,4650,3104],{},[233,4652,4653],{"class":235,"line":402},[233,4654,4655],{},"public void onCreate(Bundle savedInstanceState) {\n",[233,4657,4658],{"class":235,"line":408},[233,4659,4660],{}," super.onCreate(savedInstanceState);\n",[233,4662,4663],{"class":235,"line":414},[233,4664,4665],{}," List\u003CString> entries = new ArrayList\u003CString>();\n",[233,4667,4668],{"class":235,"line":420},[233,4669,4670],{}," for (int i = 1; i \u003C= 101; i++) {\n",[233,4672,4673],{"class":235,"line":426},[233,4674,4675],{}," entries.add(\"Entry \" + i);\n",[233,4677,4678],{"class":235,"line":657},[233,4679,3596],{},[233,4681,4682],{"class":235,"line":662},[233,4683,4684],{}," setListAdapter(new ExpandableListAdapter(entries, this));\n",[233,4686,4687],{"class":235,"line":667},[233,4688,429],{},[233,4690,4691],{"class":235,"line":673},[233,4692,4693],{}," private class ExpandableListAdapter extends BaseAdapter{....}\n",[233,4695,4696],{"class":235,"line":679},[233,4697,429],{},[19,4699,4700],{},"The list as it is now shows all entries, so the next thing to do, is implementing the limiting function to the adapter.",[19,4702,4703],{},"Let’s start by giving our adapter a member variable for the limit:",[225,4705,4707],{"className":227,"code":4706,"language":229,"meta":110,"style":110},"private int limit = 20;\n",[176,4708,4709],{"__ignoreMap":110},[233,4710,4711],{"class":235,"line":236},[233,4712,4706],{},[19,4714,4715],{},"Next we need to modify some of the methods to get this working:",[19,4717,4718],{},"In getCount we need to either return the limit, or the size of the list, if the limit is greater than the list, because\nour list contains all entries and we only want to display a part of it.",[225,4720,4722],{"className":227,"code":4721,"language":229,"meta":110,"style":110},"@Override\npublic int getCount() {\n if (entries.size() \u003C= limit) {\n return entries.size();\n }\n return limit;\n}\n",[176,4723,4724,4728,4733,4738,4743,4747,4752],{"__ignoreMap":110},[233,4725,4726],{"class":235,"line":236},[233,4727,3104],{},[233,4729,4730],{"class":235,"line":111},[233,4731,4732],{},"public int getCount() {\n",[233,4734,4735],{"class":235,"line":402},[233,4736,4737],{}," if (entries.size() \u003C= limit) {\n",[233,4739,4740],{"class":235,"line":408},[233,4741,4742],{}," return entries.size();\n",[233,4744,4745],{"class":235,"line":414},[233,4746,3596],{},[233,4748,4749],{"class":235,"line":420},[233,4750,4751],{}," return limit;\n",[233,4753,4754],{"class":235,"line":426},[233,4755,429],{},[19,4757,4758],{},"Next, we need to adjust the getItem method with an further clause in the if",[225,4760,4762],{"className":227,"code":4761,"language":229,"meta":110,"style":110},"if (position >= 0 && position \u003C limit && position \u003C entries.size())\n return entries.get(position);\n",[176,4763,4764,4769],{"__ignoreMap":110},[233,4765,4766],{"class":235,"line":236},[233,4767,4768],{},"if (position >= 0 && position \u003C limit && position \u003C entries.size())\n",[233,4770,4771],{"class":235,"line":111},[233,4772,4773],{}," return entries.get(position);\n",[19,4775,4776],{},"Now for the biggest change for our new functionality: the implementation of the ‘more button’.",[225,4778,4780],{"className":227,"code":4779,"language":229,"meta":110,"style":110},"private LinearLayout getMoreView() {\n LinearLayout moreView = (LinearLayout) getLayoutInflater().inflate(R.layout.more_row, null);\n moreView.setOnClickListener(new View.OnClickListener() {\n public void onClick(View v) {\n listAdapter.increaseLimit();\n }\n });\n return moreView;\n}\n",[176,4781,4782,4787,4792,4797,4802,4807,4811,4816,4821],{"__ignoreMap":110},[233,4783,4784],{"class":235,"line":236},[233,4785,4786],{},"private LinearLayout getMoreView() {\n",[233,4788,4789],{"class":235,"line":111},[233,4790,4791],{}," LinearLayout moreView = (LinearLayout) getLayoutInflater().inflate(R.layout.more_row, null);\n",[233,4793,4794],{"class":235,"line":402},[233,4795,4796],{}," moreView.setOnClickListener(new View.OnClickListener() {\n",[233,4798,4799],{"class":235,"line":408},[233,4800,4801],{}," public void onClick(View v) {\n",[233,4803,4804],{"class":235,"line":414},[233,4805,4806],{}," listAdapter.increaseLimit();\n",[233,4808,4809],{"class":235,"line":420},[233,4810,417],{},[233,4812,4813],{"class":235,"line":426},[233,4814,4815],{}," });\n",[233,4817,4818],{"class":235,"line":657},[233,4819,4820],{}," return moreView;\n",[233,4822,4823],{"class":235,"line":662},[233,4824,429],{},[19,4826,4827],{},"Create another simple xml like this (disregarded i18n for this simple test case. Of course, Strings should normally be\ndeclared in the strings.xml):",[225,4829,4831],{"className":1649,"code":4830,"language":1651,"meta":110,"style":110},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n\u003CLinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:layout_width=\"fill_parent\"\n android:layout_height=\"wrap_content\"\n android:orientation=\"horizontal\"\n android:paddingLeft=\"20dp\"\n android:paddingRight=\"20dp\">\n \u003CTextView\n android:id=\"@+id/MoreRowText\"\n android:layout_width=\"fill_parent\"\n android:layout_height=\"40dp\"\n android:text=\"Load more...\"\n android:gravity=\"center\"/>\n\u003C/LinearLayout>\n",[176,4832,4833,4837,4841,4846,4851,4856,4861,4866,4870,4875,4880,4885,4890,4895],{"__ignoreMap":110},[233,4834,4835],{"class":235,"line":236},[233,4836,4578],{},[233,4838,4839],{"class":235,"line":111},[233,4840,4583],{},[233,4842,4843],{"class":235,"line":402},[233,4844,4845],{}," android:layout_width=\"fill_parent\"\n",[233,4847,4848],{"class":235,"line":408},[233,4849,4850],{}," android:layout_height=\"wrap_content\"\n",[233,4852,4853],{"class":235,"line":414},[233,4854,4855],{}," android:orientation=\"horizontal\"\n",[233,4857,4858],{"class":235,"line":420},[233,4859,4860],{}," android:paddingLeft=\"20dp\"\n",[233,4862,4863],{"class":235,"line":426},[233,4864,4865],{}," android:paddingRight=\"20dp\">\n",[233,4867,4868],{"class":235,"line":657},[233,4869,4603],{},[233,4871,4872],{"class":235,"line":662},[233,4873,4874],{}," android:id=\"@+id/MoreRowText\"\n",[233,4876,4877],{"class":235,"line":667},[233,4878,4879],{}," android:layout_width=\"fill_parent\"\n",[233,4881,4882],{"class":235,"line":673},[233,4883,4884],{}," android:layout_height=\"40dp\"\n",[233,4886,4887],{"class":235,"line":679},[233,4888,4889],{}," android:text=\"Load more...\"\n",[233,4891,4892],{"class":235,"line":684},[233,4893,4894],{}," android:gravity=\"center\"/>\n",[233,4896,4897],{"class":235,"line":689},[233,4898,4633],{},[19,4900,4901],{},"And add a method to the adapter to increase the limit:",[225,4903,4905],{"className":227,"code":4904,"language":229,"meta":110,"style":110}," public void increaseLimit() {\n limit+=20;\n //remove the button if we can no longer expand the list\n if (limit >= entries.size()) {\n getListView().removeFooterView(moreView);\n }\n //notify to redraw the list\n notifyDataSetChanged();\n }\n",[176,4906,4907,4912,4917,4922,4927,4932,4936,4941,4946],{"__ignoreMap":110},[233,4908,4909],{"class":235,"line":236},[233,4910,4911],{}," public void increaseLimit() {\n",[233,4913,4914],{"class":235,"line":111},[233,4915,4916],{}," limit+=20;\n",[233,4918,4919],{"class":235,"line":402},[233,4920,4921],{}," //remove the button if we can no longer expand the list\n",[233,4923,4924],{"class":235,"line":408},[233,4925,4926],{}," if (limit >= entries.size()) {\n",[233,4928,4929],{"class":235,"line":414},[233,4930,4931],{}," getListView().removeFooterView(moreView);\n",[233,4933,4934],{"class":235,"line":420},[233,4935,3596],{},[233,4937,4938],{"class":235,"line":426},[233,4939,4940],{}," //notify to redraw the list\n",[233,4942,4943],{"class":235,"line":657},[233,4944,4945],{}," notifyDataSetChanged();\n",[233,4947,4948],{"class":235,"line":662},[233,4949,3596],{},[19,4951,4952],{},"At last, we simply add the button as a footer view to our list (After creation of the ListView, but before setting the\nListAdapter to the ListView):",[225,4954,4956],{"className":227,"code":4955,"language":229,"meta":110,"style":110},"moreView = getMoreView();\nlistAdapter = new ExpandableListAdapter(entries);\ngetListView().addFooterView(moreView);\nsetListAdapter(listAdapter);\n",[176,4957,4958,4963,4968,4973],{"__ignoreMap":110},[233,4959,4960],{"class":235,"line":236},[233,4961,4962],{},"moreView = getMoreView();\n",[233,4964,4965],{"class":235,"line":111},[233,4966,4967],{},"listAdapter = new ExpandableListAdapter(entries);\n",[233,4969,4970],{"class":235,"line":402},[233,4971,4972],{},"getListView().addFooterView(moreView);\n",[233,4974,4975],{"class":235,"line":408},[233,4976,4977],{},"setListAdapter(listAdapter);\n",[19,4979,4980],{},"And that’s it for the simple case of a static list 🙂",[19,4982,4983],{},"If you want to do this a bit more dynamic or you don’t want to load whole database table (or the whole internet) into\nyour list, you have to do some extra work.",[516,4985,4986,4989,4992],{},[88,4987,4988],{},"Load the next X entries with an AsyncTask (display a ProgressDialog while it is getting the data!) and add a method\nto your adapter, that adds this entries to the list. Call this method from within the onPostExecute method of the\nAsyncTask",[88,4990,4991],{},"You may want to keep track of the total number of entries you have to know when the user can’t load any more entries\nand to remove the button. (If the dataset is not bound to grow every few seconds. Otherwise, maybe let the user try\nto hit the button and see for himself, if theres new data)",[88,4993,4994],{},"Keep in mind to notify your users if anything fails, like the next entries could not have been loaded, because they\nhave no internet connection.",[19,4996,4997,4998,5004],{},"A Little homework for you: Combine it with\nthe ",[159,4999,5003],{"href":5000,"rel":5001,"title":5002},"http://blog.synyx.de/2011/11/android-listview-with-rounded-corners/",[163],"android listview with rounded corners","rounded corners example","\nto make it look better 😛",[19,5006,5007,5008],{},"Here’s the source, btw:",[159,5009,5012],{"href":5010,"rel":5011},"https://media.synyx.de/uploads//2012/12/ExpandableListview.zip",[163],"ExpandableListview",[560,5014,562],{},{"title":110,"searchDepth":111,"depth":111,"links":5016},[],[120,2314],"2012-12-07T09:39:26","In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries.\\nWith a button at the end of the list, the user can load more entries.","https://synyx.de/blog/android-expandable-listview/",{},"/blog/android-expandable-listview",{"title":4366,"description":4375},"blog/android-expandable-listview",[133,5026,5027,5028,5029,5030,5031,5032],"entries","entry","expandable","limit","limited","list","listview","In today’s tutorial I’d like to show you how to implement a ListView, that only displays a limited number of entries. With a button at the end of the list,…","2cPxf8ntm24639cXpvDd1uvgjk37n-ulC-vUEA0BPZw",{"id":5036,"title":5037,"author":5038,"body":5040,"category":5147,"date":5148,"description":5149,"extension":124,"link":5150,"meta":5151,"navigation":127,"path":5152,"seo":5153,"slug":5044,"stem":5154,"tags":5155,"teaser":5164,"__hash__":5165},"blog/blog/synyx-besucht-die-mdc-2012-in-stuttgart.md","synyx besucht die MDC 2012 in Stuttgart",[5039],"arrasz",{"type":12,"value":5041,"toc":5138},[5042,5045,5048,5057,5060,5064,5067,5070,5073,5076,5079,5082,5086,5090,5093,5096,5099,5103,5106,5109,5113,5116,5119,5122,5126,5129,5132,5135],[15,5043,5037],{"id":5044},"synyx-besucht-die-mdc-2012-in-stuttgart",[19,5046,5047],{},"Dieses Jahr hatte unsere Mobile Developer Reisegruppe das Glück die MDC 2012 quasi vor der Haustüre, nämlich in\nStuttgart, vorzufinden. Das machte den Besuch natürlich noch viel reizvoller. Darüber hinaus kündigte sich mit Robert\nVirkus, dem Gründer eines unserer Partnerunternehmens, Enough Software aus Bremen, ein spannender Talk über das , meiner\nMeinung nach derzeit heißeste Thema im Mobile Bereich, an. Doch dazu später mehr.",[19,5049,5050,5051,5056],{},"Weitere spannende Talks versprachen Markus Jungingers GreenDAO Talk und natürlich auch ",[159,5052,5055],{"href":5053,"rel":5054},"http://vogella.de",[163],"Lars Vogel","\nseine angekündigte Übersicht über die Neuerungen von Android 4(.2)",[19,5058,5059],{},"Da unsere Gruppe aus iOS als auch Android Developern bestand war schnell klar, dass man sich unterschiedliche Talks\nanschauen möchte.",[23,5061,5063],{"id":5062},"_1tag","1.Tag",[19,5065,5066],{},"Los gings am ersten Tag nach der Keynote direkt mit der Einführung von Lars Vogel in die Neuerungen von Android 4.2.\nWie üblich war der Vortrag gespickt mit technischen Neuerungen und auch unsere absoluten Experten in der Android\nEntwicklung konnten doch das eine oder andere noch mitnehmen: beispielsweise die Tatsache, dass man sich mit den\naktuellsten ADT Tools versorgen lassen kann, wenn man weiss wo die Checkbox versteckt ist, die einem Zugriff darauf\nermöglicht.",[19,5068,5069],{},"Es war, wie meist bei Lars, ein rundum gelungener Vortrag. Man spürt bei ihm einfach diesen gewissen Enthusiasmus,\nwelchen man wohl braucht um solche, nicht unbedingt einfachen, Themen dennoch spannend zu vermitteln.",[19,5071,5072],{},"Nach einer kurzen Pause, auf das Catering gehe ich separat ein :-), war schnell das nächste Thema gefunden und mit\nData-Handling bei Offline-Apps ging es weiter",[19,5074,5075],{},"Über diesen Ausflug möchte ich nicht allzu viele Worte verlieren, ich würde mich wiederholen. Wieder kam ich mir fehl am\nPlatz vor. Um dies zu untermauern möchte ich nur ein paar kurze Zitate wiedergeben:",[19,5077,5078],{},"“Leute beachtet bitte, MD5 ist kein gutes Verschlüsselungsverfahren” worauf aus dem Publikum mehrfach die Frage kam\n“Warum denn nicht?”.",[19,5080,5081],{},"“Hat hier wer schon mal mit SQL gearbeitet?” Ich würde mal sagen, ca 25% der Teilnehmer haben sich hierauf gemeldet…",[23,5083,5085],{"id":5084},"_2-tag","2. Tag",[1147,5087,5089],{"id":5088},"_1-robert-virkus","1. Robert Virkus",[19,5091,5092],{},"Roberts Vortrag war für uns insbesondere deshalb spannend, da wir mit ihm schon einige Projekte realisiert haben und wir\nauf die Statements bzgl. Hybrid Apps und dem HTML5-Hype gespannt waren. Der Vortrag war sehr spannend gehalten, es\nwaren wohl die besten Folien der gesamten Konferenz, da einfach unterhaltsam. Robert zeigte gekonnt auf, warum HTML5\nwohl nicht die Lösung aller Appentwicklungsprobleme ist, zeigte die Grenzen auf und hatte durch den riesigen\nErfahrungsschatz von Enough auch einige spannende Anekdoten auf Lager.",[19,5094,5095],{},"Generell teilt er unsere Einschätzung zu HTML5 Apps, bei den Hybrid-Apps sah dies etwas anders aus. Letztlich wurde\naber deutlich klar dass man aufwendige, performance oder speicherkritische Dinge auf jeden Fall nativ implementieren\nwill, während man Handbücher, Tutorials oder dergleichen mittels HTML abbilden kann.",[19,5097,5098],{},"Vielen Dank an Robert für den kontroversen Vortrag, da er hiermit bestimmt den einen oder anderen Berater gegen sich\naufgebracht hat 🙂 Schliesslich wird mit HTML5 Beratung im Mobilen Bereich derzeit ordentlich Geld verdient.",[1147,5100,5102],{"id":5101},"_2-testdriven-ios-development","2. Testdriven iOS Development",[19,5104,5105],{},"Für mich als Android Developer war spannend zu sehen, wie komplett anders die Entwicklung unter iOS wohl wahrgenommen\nwird. Die Toolchain wirkt irgendwie kopiert, einige Dinge wurden aus der Java Welt nachgebaut. Alles wirkte ein wenig\n“under engineered” und deutlich mehr visuell getrieben. Die Softwaretechnik wird deutlich weniger wahrgenommen. Ich kam\nmir teilweise fehl am Platz vor, was aber nicht am Vortrag selbst lag sondern viel mehr an der Tatsache, das die meisten\num mich herum wirklich vieles noch nicht gehört hatten bzw. auch nicht wirklich verstanden, warum man denn Testdriven\narbeiten will.",[19,5107,5108],{},"Wüsste ich es nicht besser da wir selbst bei synyx auch iOS Developer haben würde ich hier mehr… nein ich lasse es 🙂",[1147,5110,5112],{"id":5111},"_3-greendao-von-markus-junginger","3. GreenDAO von Markus Junginger",[19,5114,5115],{},"Dieser Vortrag wurde mit Spannung erwartet aufgrund der Tatsache das wir sehen wollten, wie eine DAO Implementierung\nsich innerhalb der Android Entwicklung auswirkt. Bislang hatten wir noch nicht das Bedürfnis solche Techniken innerhalb\nunserer Projekte zu nutzen. Zum einen da es eine Menge Flexibilität und Möglichkeiten zunichte macht, zum anderen da wir\nnoch keine solch großen Datenmengen Clientseitig speichern mussten.",[19,5117,5118],{},"Markus verstand es hervorragend die seiner Meinung nach spannenden Einsatzgebiete aufzuzeigen und Vergleiche mit anderen\nDAO-Implementierungen zu ziehen. Was uns leider fehlte, war ein Vergleich der Performance, wenn man KEINE derartige\nImplementierung nutzt. Ansonsten wusste er auf die meisten aufkommenden Fragen eine Antwort und war auch nach dem\neigentlichen Talk noch zu einer kurzen Diskussionsrunde zu haben. Danke dafür!",[19,5120,5121],{},"Danach ging es mit einer sehr spannend gehaltenen Keynote zu Ende und es wurde Zeit sich auf den Weg nach Hause zu\nmachen.",[23,5123,5125],{"id":5124},"blicke-über-den-tellerrand","Blicke über den Tellerrand:",[19,5127,5128],{},"Allgemein war die Orga bis auf wenige Kleinigkeiten hervorragend, die Technik klappte fast ausnahmslos sehr gut (am\n2.ten Tag hatte ich kleine WLAN Aussetzer im hintersten Konferenzraum und ein Beamer meinte ab und an er müsse abheben,\nzumindest hörte er sich so an) und auch das Catering war geschmacklich erste Wahl!",[19,5130,5131],{},"Als einzigen Kritikpunkt am Catering möchte ich dennoch anbringen dass es mir als Vegetarier nicht möglich war alleine\nherauszufinden, was denn wohl für mich geeignet ist und was nicht. Auch das Personal, welches reichlich vorhanden war,\nkonnte mir diese Fragen nicht beantworten, so dass ein Koch!! gerufen wurde, welcher es dann exakt bestimmt hat. Hier\nkönnte man doch einfach Schildchen machen, oder aber es wie bei der Devoxx einfach die Essensausgaben separieren.",[19,5133,5134],{},"Ich denke wir sind 2013 wieder am Start, diesmal dann eventuell mit eigenen Vorträgen um bestimmte Themen gezielt\nanspruchsvoller zu gestalten…",[19,5136,5137],{},"Das war die MDC 2012, vielen Dank an Florian Bender und sein Team!!!",{"title":110,"searchDepth":111,"depth":111,"links":5139},[5140,5141,5146],{"id":5062,"depth":111,"text":5063},{"id":5084,"depth":111,"text":5085,"children":5142},[5143,5144,5145],{"id":5088,"depth":402,"text":5089},{"id":5101,"depth":402,"text":5102},{"id":5111,"depth":402,"text":5112},{"id":5124,"depth":111,"text":5125},[121],"2012-11-08T18:47:07","Dieses Jahr hatte unsere Mobile Developer Reisegruppe das Glück die MDC 2012 quasi vor der Haustüre, nämlich in\\nStuttgart, vorzufinden. Das machte den Besuch natürlich noch viel reizvoller. Darüber hinaus kündigte sich mit Robert\\nVirkus, dem Gründer eines unserer Partnerunternehmens, Enough Software aus Bremen, ein spannender Talk über das , meiner\\nMeinung nach derzeit heißeste Thema im Mobile Bereich, an. Doch dazu später mehr.","https://synyx.de/blog/synyx-besucht-die-mdc-2012-in-stuttgart/",{},"/blog/synyx-besucht-die-mdc-2012-in-stuttgart",{"title":5037,"description":5047},"blog/synyx-besucht-die-mdc-2012-in-stuttgart",[133,5156,5157,5158,5159,5160,5161,578,5162,5163],"apple","ausbildung","conference","html5","ios","mdc","objective-c","synyx","Dieses Jahr hatte unsere Mobile Developer Reisegruppe das Glück die MDC 2012 quasi vor der Haustüre, nämlich in Stuttgart, vorzufinden. Das machte den Besuch natürlich noch viel reizvoller. Darüber hinaus…","zC6fwExJgqo4oUeUk3333-6HnFJZSw5LAHTK2ushCto",{"id":5167,"title":5168,"author":5169,"body":5170,"category":5385,"date":5386,"description":5387,"extension":124,"link":5388,"meta":5389,"navigation":127,"path":5390,"seo":5391,"slug":5392,"stem":5393,"tags":5394,"teaser":5398,"__hash__":5399},"blog/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct.md","Android 2.1 SQLite: problem with QueryBuilder and Distinct",[586],{"type":12,"value":5171,"toc":5383},[5172,5175,5178,5181,5359,5366,5369,5378,5381],[15,5173,5168],{"id":5174},"android-21-sqlite-problem-with-querybuilder-and-distinct",[19,5176,5177],{},"In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly,\nbut on 2.1 it crashed every time when trying to get a column from a cursor.",[19,5179,5180],{},"Here’s the simplified code:",[225,5182,5184],{"className":227,"code":5183,"language":229,"meta":110,"style":110},"//member, a SQLiteOpenHelper\nBackendOpenHelper helper;\n//...\npublic List \u003CExample> getExamples(String arg){\nSQLiteQueryBuilder builder = new SQLiteQueryBuilder();\n builder.setTables(\"example e JOIN\n secondtable s ON e.id = s.example_id\");\n Map\u003CString, String> projectionMap =\n new HashMap\u003CString, String>();\n projectionMap.put(\"id\", \"e.id\");\n //... put in some more values ...\n builder.setProjectionMap(projectionMap);\n builder.setDistinct(true);\n builder.appendWhere(\" e.someRow = ? \");\n //... some more wheres ...\n SQLiteDatabase db = helper.getReadableDatabase();\n String[] selectionArgs = new String[] {\n arg\n };\n Cursor cursor = builder.query(db, null,\n null, selectionArgs, null, null, null);\n if (cursor.moveToFirst()) {\n while (cursor.isAfterLast() == false) {\n int index = cursor.getColumnIndex(\"id\");\n //on android 2.1, index is returned as -1\n //on newer versions as 1\n int id = cursor.getInt(index);\n //crashes if index is -1\n //...\n cursor.moveToNext();\n }\n }\n cursor.close();\n //...\n}\n",[176,5185,5186,5191,5196,5201,5206,5211,5216,5221,5226,5231,5236,5241,5246,5251,5256,5261,5266,5271,5276,5281,5286,5291,5296,5301,5306,5311,5316,5321,5326,5331,5336,5341,5345,5350,5355],{"__ignoreMap":110},[233,5187,5188],{"class":235,"line":236},[233,5189,5190],{},"//member, a SQLiteOpenHelper\n",[233,5192,5193],{"class":235,"line":111},[233,5194,5195],{},"BackendOpenHelper helper;\n",[233,5197,5198],{"class":235,"line":402},[233,5199,5200],{},"//...\n",[233,5202,5203],{"class":235,"line":408},[233,5204,5205],{},"public List \u003CExample> getExamples(String arg){\n",[233,5207,5208],{"class":235,"line":414},[233,5209,5210],{},"SQLiteQueryBuilder builder = new SQLiteQueryBuilder();\n",[233,5212,5213],{"class":235,"line":420},[233,5214,5215],{}," builder.setTables(\"example e JOIN\n",[233,5217,5218],{"class":235,"line":426},[233,5219,5220],{}," secondtable s ON e.id = s.example_id\");\n",[233,5222,5223],{"class":235,"line":657},[233,5224,5225],{}," Map\u003CString, String> projectionMap =\n",[233,5227,5228],{"class":235,"line":662},[233,5229,5230],{}," new HashMap\u003CString, String>();\n",[233,5232,5233],{"class":235,"line":667},[233,5234,5235],{}," projectionMap.put(\"id\", \"e.id\");\n",[233,5237,5238],{"class":235,"line":673},[233,5239,5240],{}," //... put in some more values ...\n",[233,5242,5243],{"class":235,"line":679},[233,5244,5245],{}," builder.setProjectionMap(projectionMap);\n",[233,5247,5248],{"class":235,"line":684},[233,5249,5250],{}," builder.setDistinct(true);\n",[233,5252,5253],{"class":235,"line":689},[233,5254,5255],{}," builder.appendWhere(\" e.someRow = ? \");\n",[233,5257,5258],{"class":235,"line":695},[233,5259,5260],{}," //... some more wheres ...\n",[233,5262,5263],{"class":235,"line":701},[233,5264,5265],{}," SQLiteDatabase db = helper.getReadableDatabase();\n",[233,5267,5268],{"class":235,"line":706},[233,5269,5270],{}," String[] selectionArgs = new String[] {\n",[233,5272,5273],{"class":235,"line":711},[233,5274,5275],{}," arg\n",[233,5277,5278],{"class":235,"line":717},[233,5279,5280],{}," };\n",[233,5282,5283],{"class":235,"line":723},[233,5284,5285],{}," Cursor cursor = builder.query(db, null,\n",[233,5287,5288],{"class":235,"line":728},[233,5289,5290],{}," null, selectionArgs, null, null, null);\n",[233,5292,5293],{"class":235,"line":733},[233,5294,5295],{}," if (cursor.moveToFirst()) {\n",[233,5297,5298],{"class":235,"line":1084},[233,5299,5300],{}," while (cursor.isAfterLast() == false) {\n",[233,5302,5303],{"class":235,"line":1089},[233,5304,5305],{}," int index = cursor.getColumnIndex(\"id\");\n",[233,5307,5308],{"class":235,"line":1094},[233,5309,5310],{}," //on android 2.1, index is returned as -1\n",[233,5312,5313],{"class":235,"line":1099},[233,5314,5315],{}," //on newer versions as 1\n",[233,5317,5318],{"class":235,"line":1105},[233,5319,5320],{}," int id = cursor.getInt(index);\n",[233,5322,5323],{"class":235,"line":1111},[233,5324,5325],{}," //crashes if index is -1\n",[233,5327,5328],{"class":235,"line":1116},[233,5329,5330],{}," //...\n",[233,5332,5333],{"class":235,"line":2144},[233,5334,5335],{}," cursor.moveToNext();\n",[233,5337,5338],{"class":235,"line":2149},[233,5339,5340],{}," }\n",[233,5342,5343],{"class":235,"line":2154},[233,5344,417],{},[233,5346,5347],{"class":235,"line":2160},[233,5348,5349],{}," cursor.close();\n",[233,5351,5352],{"class":235,"line":2165},[233,5353,5354],{}," //...\n",[233,5356,5357],{"class":235,"line":2171},[233,5358,429],{},[19,5360,5361,5362,5365],{},"After some research I found out that this apparently happens, when using ",[181,5363,5364],{},"distinct"," with the QueryBuilder on android\n2.1.",[19,5367,5368],{},"So a quick fix for this problem is to simply don’t use the getColumnIndex() method from the cursor, but instead just\naccess it by its id (Though you have to remember to change this part of the code if you make changes to your table\nrows).",[225,5370,5372],{"className":227,"code":5371,"language":229,"meta":110,"style":110}," int id = cursor.getInt(1);\n",[176,5373,5374],{"__ignoreMap":110},[233,5375,5376],{"class":235,"line":236},[233,5377,5371],{},[19,5379,5380],{},"I hope this will help someone who encounters the same problem, so he doesn’t have to search for a solution as long as I\nhad to.",[560,5382,562],{},{"title":110,"searchDepth":111,"depth":111,"links":5384},[],[120,2314],"2012-05-22T09:39:08","In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly,\\nbut on 2.1 it crashed every time when trying to get a column from a cursor.","https://synyx.de/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct/",{},"/blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct",{"title":5168,"description":5177},"android-2-1-sqlite-problem-with-querybuilder-and-distinct","blog/android-2-1-sqlite-problem-with-querybuilder-and-distinct",[5395,133,5396,578,5397],"2-1","database","sqlite","In a recent project I encountered a problem with SQLite on android 2.1. On later versions, my code worked perfectly, but on 2.1 it crashed every time when trying to…","x1Vxa8-B7BB8vb0rMrFKnHNmHw0JMSkiTZ3HMChf8mU",{"id":5401,"title":5402,"author":5403,"body":5404,"category":5720,"date":5721,"description":5722,"extension":124,"link":5723,"meta":5724,"navigation":127,"path":5725,"seo":5726,"slug":5408,"stem":5727,"tags":5728,"teaser":5730,"__hash__":5731},"blog/blog/android-listview-with-rounded-corners.md","Android: ListView with rounded corners",[586],{"type":12,"value":5405,"toc":5718},[5406,5409,5412,5415,5418,5421,5488,5491,5577,5580,5583,5626,5629,5632,5713,5716],[15,5407,5402],{"id":5408},"android-listview-with-rounded-corners",[19,5410,5411],{},"In my last project I needed to implement a ListView with rounded corners, because the app had to be supplied for Android\nand iPhone and they needed to look somewhat alike.",[19,5413,5414],{},"In this blogpost, I want to show you how I’ve implemented it and hopefully help some people who also want to use\nListViews with rounded corners:",[19,5416,5417],{},"First off, we need the drawables for the backgrounds of the Lists entries:",[19,5419,5420],{},"For the entries in the middle of the list, we don’t need rounded corners, so create a xml in your drawable folder\n“list_entry_middle.xml” with following content:",[225,5422,5424],{"className":1649,"code":5423,"language":1651,"meta":110,"style":110},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\u003Clayer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n \u003Citem>\n \u003Cshape>\n \u003Cstroke android:width=\"1px\" android:color=\"#ffbbbbbb\"/>\n \u003C/shape>\n \u003C/item>\n \u003Citem android:bottom=\"1dp\" android:left=\"1dp\" android:right=\"1dp\">\n \u003Cshape>\n \u003Csolid android:color=\"#ffffffff\"/>\n \u003C/shape>\n \u003C/item>\n\u003C/layer-list>\n",[176,5425,5426,5431,5436,5441,5446,5451,5456,5461,5466,5470,5475,5479,5483],{"__ignoreMap":110},[233,5427,5428],{"class":235,"line":236},[233,5429,5430],{},"\u003C?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",[233,5432,5433],{"class":235,"line":111},[233,5434,5435],{},"\u003Clayer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n",[233,5437,5438],{"class":235,"line":402},[233,5439,5440],{}," \u003Citem>\n",[233,5442,5443],{"class":235,"line":408},[233,5444,5445],{}," \u003Cshape>\n",[233,5447,5448],{"class":235,"line":414},[233,5449,5450],{}," \u003Cstroke android:width=\"1px\" android:color=\"#ffbbbbbb\"/>\n",[233,5452,5453],{"class":235,"line":420},[233,5454,5455],{}," \u003C/shape>\n",[233,5457,5458],{"class":235,"line":426},[233,5459,5460],{}," \u003C/item>\n",[233,5462,5463],{"class":235,"line":657},[233,5464,5465],{}," \u003Citem android:bottom=\"1dp\" android:left=\"1dp\" android:right=\"1dp\">\n",[233,5467,5468],{"class":235,"line":662},[233,5469,5445],{},[233,5471,5472],{"class":235,"line":667},[233,5473,5474],{}," \u003Csolid android:color=\"#ffffffff\"/>\n",[233,5476,5477],{"class":235,"line":673},[233,5478,5455],{},[233,5480,5481],{"class":235,"line":679},[233,5482,5460],{},[233,5484,5485],{"class":235,"line":684},[233,5486,5487],{},"\u003C/layer-list>\n",[19,5489,5490],{},"For the rounded corners, create another xml, “rounded_corner_top.xml”:",[225,5492,5494],{"className":1649,"code":5493,"language":1651,"meta":110,"style":110},"\u003C?xml version=\"1.0\" encoding=\"utf-8\"?>\n\u003Clayer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n \u003Citem>\n \u003Cshape>\n \u003Cstroke android:width=\"1dp\" android:color=\"#ffbbbbbb\"/>\n \u003Ccorners android:topLeftRadius=\"20dp\"\n android:topRightRadius=\"20dp\"\n />\n \u003C/shape>\n \u003C/item>\n \u003Citem android:top=\"1dp\" android:left=\"1dp\" android:right=\"1dp\" android:bottom=\"1dp\">\n \u003Cshape>\n \u003Csolid android:color=\"#ffffffff\"/>\n \u003Ccorners android:topLeftRadius=\"20dp\"\n android:topRightRadius=\"20dp\"\n />\n \u003C/shape>\n \u003C/item>\n\u003C/layer-list>\n",[176,5495,5496,5500,5504,5508,5512,5517,5522,5527,5532,5536,5540,5545,5549,5553,5557,5561,5565,5569,5573],{"__ignoreMap":110},[233,5497,5498],{"class":235,"line":236},[233,5499,4578],{},[233,5501,5502],{"class":235,"line":111},[233,5503,5435],{},[233,5505,5506],{"class":235,"line":402},[233,5507,5440],{},[233,5509,5510],{"class":235,"line":408},[233,5511,5445],{},[233,5513,5514],{"class":235,"line":414},[233,5515,5516],{}," \u003Cstroke android:width=\"1dp\" android:color=\"#ffbbbbbb\"/>\n",[233,5518,5519],{"class":235,"line":420},[233,5520,5521],{}," \u003Ccorners android:topLeftRadius=\"20dp\"\n",[233,5523,5524],{"class":235,"line":426},[233,5525,5526],{}," android:topRightRadius=\"20dp\"\n",[233,5528,5529],{"class":235,"line":657},[233,5530,5531],{}," />\n",[233,5533,5534],{"class":235,"line":662},[233,5535,5455],{},[233,5537,5538],{"class":235,"line":667},[233,5539,5460],{},[233,5541,5542],{"class":235,"line":673},[233,5543,5544],{}," \u003Citem android:top=\"1dp\" android:left=\"1dp\" android:right=\"1dp\" android:bottom=\"1dp\">\n",[233,5546,5547],{"class":235,"line":679},[233,5548,5445],{},[233,5550,5551],{"class":235,"line":684},[233,5552,5474],{},[233,5554,5555],{"class":235,"line":689},[233,5556,5521],{},[233,5558,5559],{"class":235,"line":695},[233,5560,5526],{},[233,5562,5563],{"class":235,"line":701},[233,5564,5531],{},[233,5566,5567],{"class":235,"line":706},[233,5568,5455],{},[233,5570,5571],{"class":235,"line":711},[233,5572,5460],{},[233,5574,5575],{"class":235,"line":717},[233,5576,5487],{},[19,5578,5579],{},"Implementing the bottom part is quite the same, just with bottomLeftRadius and bottomRightRadius. (maybe also create one\nwith all corners rounded, if the list only has one entry)",[19,5581,5582],{},"For better usability, also provide drawables with other colors for the different states, that the list item can have and\nreference them in another xml in the drawable folder (“selector_rounded_corner_top.xml”) as followed:",[225,5584,5586],{"className":1649,"code":5585,"language":1651,"meta":110,"style":110},"\n\u003Cselector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n \u003Citem android:drawable=\"@drawable/rounded_corner_top_click\"\n android:state_pressed=\"true\"/>\n \u003Citem android:drawable=\"@drawable/rounded_corner_top_click\"\n android:state_focused=\"true\"/>\n \u003Citem android:drawable=\"@drawable/rounded_corner_top\"/>\n\u003C/selector>\n",[176,5587,5588,5592,5597,5602,5607,5611,5616,5621],{"__ignoreMap":110},[233,5589,5590],{"class":235,"line":236},[233,5591,894],{"emptyLinePlaceholder":127},[233,5593,5594],{"class":235,"line":111},[233,5595,5596],{},"\u003Cselector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n",[233,5598,5599],{"class":235,"line":402},[233,5600,5601],{}," \u003Citem android:drawable=\"@drawable/rounded_corner_top_click\"\n",[233,5603,5604],{"class":235,"line":408},[233,5605,5606],{}," android:state_pressed=\"true\"/>\n",[233,5608,5609],{"class":235,"line":414},[233,5610,5601],{},[233,5612,5613],{"class":235,"line":420},[233,5614,5615],{}," android:state_focused=\"true\"/>\n",[233,5617,5618],{"class":235,"line":426},[233,5619,5620],{}," \u003Citem android:drawable=\"@drawable/rounded_corner_top\"/>\n",[233,5622,5623],{"class":235,"line":657},[233,5624,5625],{},"\u003C/selector>\n",[19,5627,5628],{},"Now do the same for the other backgrounds of the list.",[19,5630,5631],{},"All that is left now, is to assign the right backgrounds in our ListAdapter like following:",[225,5633,5635],{"className":227,"code":5634,"language":229,"meta":110,"style":110},"@Override\npublic View getView(int position, View convertView, ViewGroup parent) {\n //...\n //skipping the view reuse stuff\n if (position == 0 && entry_list.size() == 1) {\n view.setBackgroundResource(R.drawable.selector_rounded_corner);\n } else if (position == 0) {\n view.setBackgroundResource(R.drawable.selector_rounded_corner_top);\n } else if (position == entry_list.size() - 1) {\n view.setBackgroundResource(R.drawable.selector_rounded_corner_bottom);\n } else {\n view.setBackgroundResource(R.drawable.selector_middle);\n }\n //...\n //skipping the filling of the view\n}\n",[176,5636,5637,5641,5646,5651,5656,5661,5666,5671,5676,5681,5686,5691,5696,5700,5704,5709],{"__ignoreMap":110},[233,5638,5639],{"class":235,"line":236},[233,5640,3104],{},[233,5642,5643],{"class":235,"line":111},[233,5644,5645],{},"public View getView(int position, View convertView, ViewGroup parent) {\n",[233,5647,5648],{"class":235,"line":402},[233,5649,5650],{}," //...\n",[233,5652,5653],{"class":235,"line":408},[233,5654,5655],{}," //skipping the view reuse stuff\n",[233,5657,5658],{"class":235,"line":414},[233,5659,5660],{}," if (position == 0 && entry_list.size() == 1) {\n",[233,5662,5663],{"class":235,"line":420},[233,5664,5665],{}," view.setBackgroundResource(R.drawable.selector_rounded_corner);\n",[233,5667,5668],{"class":235,"line":426},[233,5669,5670],{}," } else if (position == 0) {\n",[233,5672,5673],{"class":235,"line":657},[233,5674,5675],{}," view.setBackgroundResource(R.drawable.selector_rounded_corner_top);\n",[233,5677,5678],{"class":235,"line":662},[233,5679,5680],{}," } else if (position == entry_list.size() - 1) {\n",[233,5682,5683],{"class":235,"line":667},[233,5684,5685],{}," view.setBackgroundResource(R.drawable.selector_rounded_corner_bottom);\n",[233,5687,5688],{"class":235,"line":673},[233,5689,5690],{}," } else {\n",[233,5692,5693],{"class":235,"line":679},[233,5694,5695],{}," view.setBackgroundResource(R.drawable.selector_middle);\n",[233,5697,5698],{"class":235,"line":684},[233,5699,423],{},[233,5701,5702],{"class":235,"line":689},[233,5703,5650],{},[233,5705,5706],{"class":235,"line":695},[233,5707,5708],{}," //skipping the filling of the view\n",[233,5710,5711],{"class":235,"line":701},[233,5712,429],{},[19,5714,5715],{},"Aaaand we’re done.",[560,5717,562],{},{"title":110,"searchDepth":111,"depth":111,"links":5719},[],[120,2314],"2011-11-21T10:38:22","In my last project I needed to implement a ListView with rounded corners, because the app had to be supplied for Android\\nand iPhone and they needed to look somewhat alike.","https://synyx.de/blog/android-listview-with-rounded-corners/",{},"/blog/android-listview-with-rounded-corners",{"title":5402,"description":5411},"blog/android-listview-with-rounded-corners",[133,5031,5729,5032],"lists","In my last project I needed to implement a ListView with rounded corners, because the app had to be supplied for Android and iPhone and they needed to look somewhat…","YJnytc_0AozIrr4hpGG2OyB-5_wi2WxdvyjS_ltNk3E",{"id":5733,"title":5734,"author":5735,"body":5736,"category":6029,"date":6030,"description":6031,"extension":124,"link":6032,"meta":6033,"navigation":127,"path":6034,"seo":6035,"slug":5740,"stem":6036,"tags":6037,"teaser":6039,"__hash__":6040},"blog/blog/evaluating-mobile-multiplatform-frameworks.md","Evaluating Mobile Multiplatform Frameworks",[586],{"type":12,"value":5737,"toc":6027},[5738,5741,5744,5768,5771,5783,5786,5789,5792,5849,5858,6009,6012,6015,6018,6021,6024],[15,5739,5734],{"id":5740},"evaluating-mobile-multiplatform-frameworks",[19,5742,5743],{},"For an upcoming, probably large mobile project, I was asked to look at the current situation on mobile multiplatform\nframeworks that cover at least Android and iOS and provide access to some native API’s like the camera. So I looked at\nseveral of the available frameworks, but only two of them fulfilled all requirements while also providing advantages\ntowards other ones.",[19,5745,5746,5750,5751,5756,5757,5762,5763],{},[33,5747],{"alt":5748,"src":5749},"\"phonegap_logo\"","https://media.synyx.de/uploads//2011/05/phonegap_symbian.jpg","\nFirst there is PhoneGap (",[159,5752,5755],{"href":5753,"rel":5754},"https://web.archive.org/web/20210302121558/https://phonegap.com/",[163],"http://www.phonegap.com/",")\nwhich additionally provides you a buildservice in their\ncloud (",[159,5758,5761],{"href":5759,"rel":5760},"https://web.archive.org/web/20210502104931/https://build.phonegap.com/",[163],"https://build.phonegap.com/"," – currently\nin beta), to which you can upload your project and it builds your Apps for Android, iOS, BlackBerry OS, Palm OS and\nSymbian. PhoneGap furthermore supports Windows Mobile and will be supporting Bada and MeeGo in the future. For an\noverview of the supported features on the different platforms, check this\nsite: ",[159,5764,5767],{"href":5765,"rel":5766},"https://web.archive.org/web/20120512021720/http://phonegap.com/about/features/",[163],"http://www.phonegap.com/features",[19,5769,5770],{},"With PhoneGap you write your App in HTML, CSS and Javascript and can also access platform dependent API’s through the\nframework (You can write your own native classes and access them as well, but you have to provide them for every\nplatform). The App runs inside the browser and so it doesn’t feel like an app, but more like a website. Moreover, you\nstyle it like a website, which makes it easier to design it for all the different platforms and display resolutions, and\nPhoneGap lets you even use your own JS-Framworks to do so.",[19,5772,5773,5774,5778,5779],{},"Then there’s Titanium (",[159,5775,5776],{"href":5776,"rel":5777},"http://www.appcelerator.com/",[163],"), with which you also write your app in HTML, CSS and JS, but in\nterms of JS you can only use the Titanium provided JS-Framework and no others. It only supports Android and iOS, but\nprovides you access to the native UI elements of both platforms and so doesn’t need to run in the browser, which is a\nbig benefit to the Apps look&feel as well as to the performance. Unfortunately there’s also a downside to that: based\non a few feedbacks from other projects, you have to differ between Android and iOS code on many occasions, because the\nUI elements are different or some of them only work on one of the\nplatforms.",[33,5780],{"alt":5781,"src":5782},"\"titanium_logo\"","https://media.synyx.de/uploads//2011/05/titanium_logo.png",[19,5784,5785],{},"And what’s more, I didn’t even get the Titanium showcase App (KitchenSink) running in the first place. At the beginning\nthere were several errors with the provided IDE that is needed to build the Apps. As the errors were cleared (took some\ntime to find the solutions in the forum and elsewhere), I could build the App, but it only got to the splashscreen – and\nthen it did nothing. I then decided to download it from someone who built it some time ago and it ran very smoothly and\nprovided access to all kind of features. But nevertheless, I couldn’t try it out myself, because it didn’t work for me.",[19,5787,5788],{},"So only PhoneGap was left and I decided to compare it effort- and performance wise with a Java coded Android App. I\nimplemented some basic elements in both apps, like Textfields, Lists (with much data, because the upcoming project will\nmost likely need that), dynamic SelectFields, Images, and of course multiple Activities/Pages.",[19,5790,5791],{},"What I noticed immediately is the difference in responsiveness of the elements. The ones in the browser need remarkably\nlonger to react on the users touch. Other than that, the App by itselfs needs much longer to load with PhoneGap. This\ncan be traced to the way transition animations are done in PhoneGap, according to the PhoneGap tutorials:",[225,5793,5795],{"className":1772,"code":5794,"language":1774,"meta":110,"style":110},"\u003Cdiv class=\"page\" id=\"page1\">...\u003C/div>\n\u003Cdiv class=\"page\" id=\"page2\">...\u003C/div>\n",[176,5796,5797,5824],{"__ignoreMap":110},[233,5798,5799,5801,5803,5805,5807,5810,5812,5814,5817,5820,5822],{"class":235,"line":236},[233,5800,1782],{"class":1781},[233,5802,3964],{"class":3790},[233,5804,3967],{"class":3794},[233,5806,3828],{"class":1781},[233,5808,5809],{"class":3831},"\"page\"",[233,5811,4110],{"class":3794},[233,5813,3828],{"class":1781},[233,5815,5816],{"class":3831},"\"page1\"",[233,5818,5819],{"class":1781},">...\u003C/",[233,5821,3964],{"class":3790},[233,5823,1789],{"class":1781},[233,5825,5826,5828,5830,5832,5834,5836,5838,5840,5843,5845,5847],{"class":235,"line":111},[233,5827,1782],{"class":1781},[233,5829,3964],{"class":3790},[233,5831,3967],{"class":3794},[233,5833,3828],{"class":1781},[233,5835,5809],{"class":3831},[233,5837,4110],{"class":3794},[233,5839,3828],{"class":1781},[233,5841,5842],{"class":3831},"\"page2\"",[233,5844,5819],{"class":1781},[233,5846,3964],{"class":3790},[233,5848,1789],{"class":1781},[19,5850,5851,5852,5857],{},"You have to declare the different pages in the same HTML file, with all but one styled as “display: none”. Then you\nswitch between them via javascript (",[159,5853,5856],{"href":5854,"rel":5855},"https://jqueryui.com/",[163],"jQueryUI","‘s hide- and show-methods for example).",[225,5859,5863],{"className":5860,"code":5861,"language":5862,"meta":110,"style":110},"language-javascript shiki shiki-themes github-light github-dark","function switchView(hideView, showView, hideDirection, showDirection) {\n $(\"#\" + hideView).hide(\n \"slide\",\n {\n direction: hideDirection,\n },\n 250,\n );\n $(\"#\" + showView).show(\n \"slide\",\n {\n direction: showDirection,\n },\n 250,\n );\n}\n","javascript",[176,5864,5865,5900,5922,5930,5935,5940,5945,5953,5958,5976,5982,5986,5991,5995,6001,6005],{"__ignoreMap":110},[233,5866,5867,5871,5874,5877,5881,5884,5887,5889,5892,5894,5897],{"class":235,"line":236},[233,5868,5870],{"class":5869},"szBVR","function",[233,5872,5873],{"class":3794}," switchView",[233,5875,5876],{"class":1781},"(",[233,5878,5880],{"class":5879},"s4XuR","hideView",[233,5882,5883],{"class":1781},", ",[233,5885,5886],{"class":5879},"showView",[233,5888,5883],{"class":1781},[233,5890,5891],{"class":5879},"hideDirection",[233,5893,5883],{"class":1781},[233,5895,5896],{"class":5879},"showDirection",[233,5898,5899],{"class":1781},") {\n",[233,5901,5902,5905,5907,5910,5913,5916,5919],{"class":235,"line":111},[233,5903,5904],{"class":3794}," $",[233,5906,5876],{"class":1781},[233,5908,5909],{"class":3831},"\"#\"",[233,5911,5912],{"class":5869}," +",[233,5914,5915],{"class":1781}," hideView).",[233,5917,5918],{"class":3794},"hide",[233,5920,5921],{"class":1781},"(\n",[233,5923,5924,5927],{"class":235,"line":402},[233,5925,5926],{"class":3831}," \"slide\"",[233,5928,5929],{"class":1781},",\n",[233,5931,5932],{"class":235,"line":408},[233,5933,5934],{"class":1781}," {\n",[233,5936,5937],{"class":235,"line":414},[233,5938,5939],{"class":1781}," direction: hideDirection,\n",[233,5941,5942],{"class":235,"line":420},[233,5943,5944],{"class":1781}," },\n",[233,5946,5947,5951],{"class":235,"line":426},[233,5948,5950],{"class":5949},"sj4cs"," 250",[233,5952,5929],{"class":1781},[233,5954,5955],{"class":235,"line":657},[233,5956,5957],{"class":1781}," );\n",[233,5959,5960,5962,5964,5966,5968,5971,5974],{"class":235,"line":662},[233,5961,5904],{"class":3794},[233,5963,5876],{"class":1781},[233,5965,5909],{"class":3831},[233,5967,5912],{"class":5869},[233,5969,5970],{"class":1781}," showView).",[233,5972,5973],{"class":3794},"show",[233,5975,5921],{"class":1781},[233,5977,5978,5980],{"class":235,"line":667},[233,5979,5926],{"class":3831},[233,5981,5929],{"class":1781},[233,5983,5984],{"class":235,"line":673},[233,5985,5934],{"class":1781},[233,5987,5988],{"class":235,"line":679},[233,5989,5990],{"class":1781}," direction: showDirection,\n",[233,5992,5993],{"class":235,"line":684},[233,5994,5944],{"class":1781},[233,5996,5997,5999],{"class":235,"line":689},[233,5998,5950],{"class":5949},[233,6000,5929],{"class":1781},[233,6002,6003],{"class":235,"line":695},[233,6004,5957],{"class":1781},[233,6006,6007],{"class":235,"line":701},[233,6008,429],{"class":1781},[19,6010,6011],{},"The problem in this case is, that multiple “pages” are being loaded right at the beginning, increasing the load time.\nAlso the transition animation with JavaScript isn’t that smooth – even with high-end smartphones.",[19,6013,6014],{},"Comparing the speed of development of my sample App, I was a little faster with PhoneGap than with the Java code. In\nterms of speed, PhoneGap is especially useful, if you’re developing your App for mulitple platforms – you will most\nlikely be a few times faster than developing them natively on each platform and you also don’t have to set up the\ndifferent IDE’s if you are using PhoneGap Build on top of that.",[19,6016,6017],{},"Like I already mentioned, the performance isn’t that great, though. I assume that you probably won’t use PhoneGap – or\nhtml and js in general – for larger Apps which you only need for one or two platforms. This is because the\nmaintainability and testability are (from my point of view as a java developer) much better with java and also in other\nlanguages than they are with javascript and html+css (Well, you DO have only one codebase here, but therefor have a set\nof different mobile browsers that you need to check it with).",[19,6019,6020],{},"In my opinion, PhoneGap only comes in handy if you want to distribute a small App for as many platforms as possible\nwhile keeping the needed effort as low as possible.",[19,6022,6023],{},"In our case, we decided not to use PhoneGap or another framework for the project, because it needs to perform well and\nwill be a bigger project where the maintainability is very important. Besides, it will only have to run on Android and (\nmaybe) iOS, which doesn’t make the benefit you gain from PhoneGap that great.",[560,6025,6026],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":110,"searchDepth":111,"depth":111,"links":6028},[],[120],"2011-06-27T08:52:15","For an upcoming, probably large mobile project, I was asked to look at the current situation on mobile multiplatform\\nframeworks that cover at least Android and iOS and provide access to some native API’s like the camera. So I looked at\\nseveral of the available frameworks, but only two of them fulfilled all requirements while also providing advantages\\ntowards other ones.","https://synyx.de/blog/evaluating-mobile-multiplatform-frameworks/",{},"/blog/evaluating-mobile-multiplatform-frameworks",{"title":5734,"description":5743},"blog/evaluating-mobile-multiplatform-frameworks",[133,5160,578,6038],"phonegap","For an upcoming, probably large mobile project, I was asked to look at the current situation on mobile multiplatform frameworks that cover at least Android and iOS and provide access…","UlZ2F2s_fxc2RGKJWGbsoS3h6uE_MAyirMi-b2IUjWE",{"id":6042,"title":6043,"author":6044,"body":6046,"category":6513,"date":6514,"description":6515,"extension":124,"link":6516,"meta":6517,"navigation":127,"path":6518,"seo":6519,"slug":6050,"stem":6521,"tags":6522,"teaser":6525,"__hash__":6526},"blog/blog/android-roboguice-against-oncreate-boilerplate.md","Android: RoboGuice against onCreate boilerplate",[6045],"krupicka",{"type":12,"value":6047,"toc":6507},[6048,6051,6062,6066,6094,6098,6101,6286,6292,6438,6444,6448,6486,6490,6502,6505],[15,6049,6043],{"id":6050},"android-roboguice-against-oncreate-boilerplate",[19,6052,6053,6054,6057,6058,6061],{},"When prototyping Android activities with a lot of view elements, the ",[176,6055,6056],{},"onCreate"," method can quickly become cluttered.\nSetup code that simply retrieves the views from the declarative layout (by using ",[176,6059,6060],{},"findViewById(int id)",") quickly fills\nup your code. Similar, small helper classes need to be instantiated and configured, though this code is not — strictly\nspeaking — part of the actual functionality of your activity class. Time to refactor and make your activity concentrate\non it’s actual task again!",[23,6063,6065],{"id":6064},"enter-dependency-injection","Enter dependency injection",[19,6067,6068,6069,6074,6075,6078,6079,6084,6085,6090,6091,6093],{},"A popular design pattern — usually accompanied by a framework — to get rid of object setup clutter\nis ",[159,6070,6073],{"href":6071,"rel":6072},"http://en.wikipedia.org/wiki/Dependency_injection",[163],"Dependency Injection"," (in short DI). The basic idea is, that\ninstead of pulling in all your dependencies (for example the views in our case) , a central point (the injection\ncontainer) manages and coordinates the creation of your dependencies and ",[181,6076,6077],{},"injects"," them into the objects that require\nthem. In the Java world several of these containers and frameworks exist, from the heavy weights like Spring to the very\nlightweight like ",[159,6080,6083],{"href":6081,"rel":6082},"http://code.google.com/p/google-guice/",[163],"Google Guice",". For the latter a young but promising add-on\nlibrary has come to fruition: ",[159,6086,6089],{"href":6087,"rel":6088},"http://code.google.com/p/roboguice/",[163],"RoboGuice"," makes DI available for your Android\nclasses. We will walk you through a small example, showing how it simplifies your ",[176,6092,6056],{}," method by comparing it\nbefore and after introduction of RoboGuice into your code. Afterwards we will give a short scoop on how to drop it into\nyour Android Eclipse project and start working with it right away.",[23,6095,6097],{"id":6096},"before-and-after","Before and after",[19,6099,6100],{},"We start right away with a small code snippet from an Android activity:",[225,6102,6104],{"className":227,"code":6103,"language":229,"meta":110,"style":110},"public class SpiderActivity extends Activity {\n // [..snip..] lots of other members not needed for the example\n private RelativeLayout rating;\n private ImageView ratingStars;\n private Button infoButton;\n private TextView type;\n private TextView language;\n private TextView spider;\n private TextView translation;\n private TextView author;\n private TextView page;\n private RatingBar ratingBar;\n private Handler handler;\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n setContentView(R.layout.main);\n infoButton = (Button) findViewById(R.id.InfoButton);\n infoButton.setOnClickListener(new InfosOnClickListener());\n rating = (RelativeLayout) findViewById(R.id.ratingDialog);\n ratingStars = (ImageView) findViewById(R.id.ratingStars);\n ratingStars.setOnClickListener(new MenuOnClickListener());\n type = (TextView) findViewById(R.id.TypeText);\n language = (TextView) findViewById(R.id.LanguageText);\n spider = (TextView) findViewById(R.id.SpiderText);\n translation = (TextView) findViewById(R.id.TranslationText);\n author = (TextView) findViewById(R.id.AuthorText);\n page = (TextView) findViewById(R.id.PageText);\n ratingBar = (RatingBar) findViewById(R.id.ratingBar);\n if (hasUnusualDeviceDimensions()) {\n adjustLayout();\n }\n if (!isLayoutSupported()) {\n Toast.makeText(this.getApplicationContext(),\n \"Sorry, this phone resolution is not supported.\",\n Toast.LENGTH_LONG).show();\n }\n }\n",[176,6105,6106,6111,6116,6121,6126,6131,6136,6141,6146,6151,6156,6161,6166,6171,6175,6179,6184,6189,6194,6199,6204,6209,6214,6219,6224,6229,6234,6239,6244,6249,6254,6258,6263,6268,6273,6278,6282],{"__ignoreMap":110},[233,6107,6108],{"class":235,"line":236},[233,6109,6110],{},"public class SpiderActivity extends Activity {\n",[233,6112,6113],{"class":235,"line":111},[233,6114,6115],{}," // [..snip..] lots of other members not needed for the example\n",[233,6117,6118],{"class":235,"line":402},[233,6119,6120],{}," private RelativeLayout rating;\n",[233,6122,6123],{"class":235,"line":408},[233,6124,6125],{}," private ImageView ratingStars;\n",[233,6127,6128],{"class":235,"line":414},[233,6129,6130],{}," private Button infoButton;\n",[233,6132,6133],{"class":235,"line":420},[233,6134,6135],{}," private TextView type;\n",[233,6137,6138],{"class":235,"line":426},[233,6139,6140],{}," private TextView language;\n",[233,6142,6143],{"class":235,"line":657},[233,6144,6145],{}," private TextView spider;\n",[233,6147,6148],{"class":235,"line":662},[233,6149,6150],{}," private TextView translation;\n",[233,6152,6153],{"class":235,"line":667},[233,6154,6155],{}," private TextView author;\n",[233,6157,6158],{"class":235,"line":673},[233,6159,6160],{}," private TextView page;\n",[233,6162,6163],{"class":235,"line":679},[233,6164,6165],{}," private RatingBar ratingBar;\n",[233,6167,6168],{"class":235,"line":684},[233,6169,6170],{}," private Handler handler;\n",[233,6172,6173],{"class":235,"line":689},[233,6174,778],{},[233,6176,6177],{"class":235,"line":695},[233,6178,783],{},[233,6180,6181],{"class":235,"line":701},[233,6182,6183],{}," setContentView(R.layout.main);\n",[233,6185,6186],{"class":235,"line":706},[233,6187,6188],{}," infoButton = (Button) findViewById(R.id.InfoButton);\n",[233,6190,6191],{"class":235,"line":711},[233,6192,6193],{}," infoButton.setOnClickListener(new InfosOnClickListener());\n",[233,6195,6196],{"class":235,"line":717},[233,6197,6198],{}," rating = (RelativeLayout) findViewById(R.id.ratingDialog);\n",[233,6200,6201],{"class":235,"line":723},[233,6202,6203],{}," ratingStars = (ImageView) findViewById(R.id.ratingStars);\n",[233,6205,6206],{"class":235,"line":728},[233,6207,6208],{}," ratingStars.setOnClickListener(new MenuOnClickListener());\n",[233,6210,6211],{"class":235,"line":733},[233,6212,6213],{}," type = (TextView) findViewById(R.id.TypeText);\n",[233,6215,6216],{"class":235,"line":1084},[233,6217,6218],{}," language = (TextView) findViewById(R.id.LanguageText);\n",[233,6220,6221],{"class":235,"line":1089},[233,6222,6223],{}," spider = (TextView) findViewById(R.id.SpiderText);\n",[233,6225,6226],{"class":235,"line":1094},[233,6227,6228],{}," translation = (TextView) findViewById(R.id.TranslationText);\n",[233,6230,6231],{"class":235,"line":1099},[233,6232,6233],{}," author = (TextView) findViewById(R.id.AuthorText);\n",[233,6235,6236],{"class":235,"line":1105},[233,6237,6238],{}," page = (TextView) findViewById(R.id.PageText);\n",[233,6240,6241],{"class":235,"line":1111},[233,6242,6243],{}," ratingBar = (RatingBar) findViewById(R.id.ratingBar);\n",[233,6245,6246],{"class":235,"line":1116},[233,6247,6248],{}," if (hasUnusualDeviceDimensions()) {\n",[233,6250,6251],{"class":235,"line":2144},[233,6252,6253],{}," adjustLayout();\n",[233,6255,6256],{"class":235,"line":2149},[233,6257,417],{},[233,6259,6260],{"class":235,"line":2154},[233,6261,6262],{}," if (!isLayoutSupported()) {\n",[233,6264,6265],{"class":235,"line":2160},[233,6266,6267],{}," Toast.makeText(this.getApplicationContext(),\n",[233,6269,6270],{"class":235,"line":2165},[233,6271,6272],{}," \"Sorry, this phone resolution is not supported.\",\n",[233,6274,6275],{"class":235,"line":2171},[233,6276,6277],{}," Toast.LENGTH_LONG).show();\n",[233,6279,6280],{"class":235,"line":2176},[233,6281,417],{},[233,6283,6284],{"class":235,"line":2181},[233,6285,423],{},[19,6287,6288,6289,6291],{},"It’s easy to see, that the code that is actually doing something useful — check for unusual device dimensions and adjust\nlayout parameters — is only a small part of the ",[176,6290,6056],{}," method. Let’s see how this would look like with RoboGuice\napplied to your project:",[225,6293,6295],{"className":227,"code":6294,"language":229,"meta":110,"style":110},"// these are the needed classes from the RoboGuice framework\nimport roboguice.activity.RoboActivity;\nimport roboguice.inject.InjectView;\n// [..snip..]\npublic class SpiderActivity extends RoboActivity {\n @InjectView(R.id.ratingDialog ) private RelativeLayout rating;\n @InjectView(R.id.ratingStars) private ImageView ratingStars;\n @InjectView(R.id.InfoButton) private Button infoButton;\n @InjectView(R.id.TypeText) private TextView type;\n @InjectView(R.id.LanguageText) private TextView language;\n @InjectView(R.id.SpiderText) private TextView spider;\n @InjectView(R.id.TranslationText) private TextView translation;\n @InjectView(R.id.AuthorText) private TextView author;\n @InjectView(R.id.PageText) private TextView page;\n @InjectView(R.id.ratingBar) private RatingBar ratingBar;\n protected void onCreate(Bundle savedInstanceState) {\n super.onCreate(savedInstanceState);\n // the following call actually injects your views ...\n setContentView(R.layout.main);\n // they are available afterwards\n infoButton.setOnClickListener(new InfosOnClickListener());\n ratingStars.setOnClickListener(new MenuOnClickListener());\n if (hasUnusualDeviceDimensions()) {\n adjustLayout();\n }\n if (!isLayoutSupported()) {\n Toast.makeText(this.getApplicationContext(),\n \"Sorry, this phone resolution is not supported.\",\n Toast.LENGTH_LONG).show();\n }\n }\n",[176,6296,6297,6302,6307,6312,6317,6322,6327,6332,6337,6342,6347,6352,6357,6362,6367,6372,6376,6380,6385,6389,6394,6398,6402,6406,6410,6414,6418,6422,6426,6430,6434],{"__ignoreMap":110},[233,6298,6299],{"class":235,"line":236},[233,6300,6301],{},"// these are the needed classes from the RoboGuice framework\n",[233,6303,6304],{"class":235,"line":111},[233,6305,6306],{},"import roboguice.activity.RoboActivity;\n",[233,6308,6309],{"class":235,"line":402},[233,6310,6311],{},"import roboguice.inject.InjectView;\n",[233,6313,6314],{"class":235,"line":408},[233,6315,6316],{},"// [..snip..]\n",[233,6318,6319],{"class":235,"line":414},[233,6320,6321],{},"public class SpiderActivity extends RoboActivity {\n",[233,6323,6324],{"class":235,"line":420},[233,6325,6326],{}," @InjectView(R.id.ratingDialog ) private RelativeLayout rating;\n",[233,6328,6329],{"class":235,"line":426},[233,6330,6331],{}," @InjectView(R.id.ratingStars) private ImageView ratingStars;\n",[233,6333,6334],{"class":235,"line":657},[233,6335,6336],{}," @InjectView(R.id.InfoButton) private Button infoButton;\n",[233,6338,6339],{"class":235,"line":662},[233,6340,6341],{}," @InjectView(R.id.TypeText) private TextView type;\n",[233,6343,6344],{"class":235,"line":667},[233,6345,6346],{}," @InjectView(R.id.LanguageText) private TextView language;\n",[233,6348,6349],{"class":235,"line":673},[233,6350,6351],{}," @InjectView(R.id.SpiderText) private TextView spider;\n",[233,6353,6354],{"class":235,"line":679},[233,6355,6356],{}," @InjectView(R.id.TranslationText) private TextView translation;\n",[233,6358,6359],{"class":235,"line":684},[233,6360,6361],{}," @InjectView(R.id.AuthorText) private TextView author;\n",[233,6363,6364],{"class":235,"line":689},[233,6365,6366],{}," @InjectView(R.id.PageText) private TextView page;\n",[233,6368,6369],{"class":235,"line":695},[233,6370,6371],{}," @InjectView(R.id.ratingBar) private RatingBar ratingBar;\n",[233,6373,6374],{"class":235,"line":701},[233,6375,778],{},[233,6377,6378],{"class":235,"line":706},[233,6379,783],{},[233,6381,6382],{"class":235,"line":711},[233,6383,6384],{}," // the following call actually injects your views ...\n",[233,6386,6387],{"class":235,"line":717},[233,6388,6183],{},[233,6390,6391],{"class":235,"line":723},[233,6392,6393],{}," // they are available afterwards\n",[233,6395,6396],{"class":235,"line":728},[233,6397,6193],{},[233,6399,6400],{"class":235,"line":733},[233,6401,6208],{},[233,6403,6404],{"class":235,"line":1084},[233,6405,6248],{},[233,6407,6408],{"class":235,"line":1089},[233,6409,6253],{},[233,6411,6412],{"class":235,"line":1094},[233,6413,417],{},[233,6415,6416],{"class":235,"line":1099},[233,6417,6262],{},[233,6419,6420],{"class":235,"line":1105},[233,6421,6267],{},[233,6423,6424],{"class":235,"line":1111},[233,6425,6272],{},[233,6427,6428],{"class":235,"line":1116},[233,6429,6277],{},[233,6431,6432],{"class":235,"line":2144},[233,6433,417],{},[233,6435,6436],{"class":235,"line":2149},[233,6437,423],{},[19,6439,6440,6441,6443],{},"See how the ",[176,6442,6056],{}," method now communicates more clearly what it actually does? At the same time we made the\nassociation between the member variables for the views and their counterpart in the declarative layout more visible, by\nputting this information right where the member is declared. This makes the code instantly more accessible.",[23,6445,6447],{"id":6446},"setting-up-roboguice","Setting up RoboGuice",[19,6449,6450,6451,6456,6457,6461,6462,6465,6466,6469,6470,6473,6474,6477,6478,6481,6482,6485],{},"Now to get you up and running with RoboGuice, we will give quick instructions on how to obtain the necessary\ndependencies and how to configure your Android Eclipse project. First we need the JAR files:\ndownload ",[159,6452,6455],{"href":6453,"rel":6454},"https://web.archive.org/web/20160713065637/http://google-guice.googlecode.com:80/files/guice-2.0-no_aop.jar",[163],"Guice 2.0"," (\nwhich is the base for RoboGuice)\nand ",[159,6458,6089],{"href":6459,"rel":6460},"http://download.java.net/maven/2/roboguice/roboguice/1.1-SNAPSHOT/roboguice-1.1-20100408.222944-3.jar",[163]," (\nin our example we used the development version 1.1) into a ",[176,6463,6464],{},"lib"," subdirectory of your project. Add both the libraries\nto your build path by selecting ",[299,6467,6468],{},"Build Path > Add to Build Path"," from the context menu of each of the libraries.\nFinally a small change to your ",[176,6471,6472],{},"AndroidManifest.xml"," will be needed, to make RoboGuice work. In the ",[299,6475,6476],{},"Application"," tab\nset the ",[299,6479,6480],{},"Name"," attribute to ",[176,6483,6484],{},"roboguice.application.RoboApplication"," (or make your own Application class inherit from\nit).",[23,6487,6489],{"id":6488},"finishing-words","Finishing words",[19,6491,6492,6493,6497,6498,6501],{},"While this is only an appetizer for using Dependency Injection in your Android application and making your code more\nexpressive, there are still more resources to be found on the corresponding project pages:\nthe ",[159,6494,6496],{"href":6081,"rel":6495},[163],"Guice"," pages introduce the general idioms of the framework, while\nthe ",[159,6499,6089],{"href":6087,"rel":6500},[163]," pages contain some more examples and the documentation.",[19,6503,6504],{},"I hope this short introduction proves useful and helps you making your applications code more readable and testable in\nthe end.",[560,6506,562],{},{"title":110,"searchDepth":111,"depth":111,"links":6508},[6509,6510,6511,6512],{"id":6064,"depth":111,"text":6065},{"id":6096,"depth":111,"text":6097},{"id":6446,"depth":111,"text":6447},{"id":6488,"depth":111,"text":6489},[120,2314],"2010-09-17T18:06:36","When prototyping Android activities with a lot of view elements, the onCreate method can quickly become cluttered.\\nSetup code that simply retrieves the views from the declarative layout (by using findViewById(int id)) quickly fills\\nup your code. Similar, small helper classes need to be instantiated and configured, though this code is not — strictly\\nspeaking — part of the actual functionality of your activity class. Time to refactor and make your activity concentrate\\non it’s actual task again!","https://synyx.de/blog/android-roboguice-against-oncreate-boilerplate/",{},"/blog/android-roboguice-against-oncreate-boilerplate",{"title":6043,"description":6520},"When prototyping Android activities with a lot of view elements, the onCreate method can quickly become cluttered.\nSetup code that simply retrieves the views from the declarative layout (by using findViewById(int id)) quickly fills\nup your code. Similar, small helper classes need to be instantiated and configured, though this code is not — strictly\nspeaking — part of the actual functionality of your activity class. Time to refactor and make your activity concentrate\non it’s actual task again!","blog/android-roboguice-against-oncreate-boilerplate",[133,6523,6524,2314],"guice","roboguice","When prototyping Android activities with a lot of view elements, the onCreate method can quickly become cluttered. Setup code that simply retrieves the views from the declarative layout (by using…","WWGtuzKM-rsAWuMzV5nJuXOh5e4_kiJ7iFMPe0sPkMY",{"id":6528,"title":6529,"author":6530,"body":6532,"category":6545,"date":6547,"description":110,"extension":124,"link":6548,"meta":6549,"navigation":127,"path":6550,"seo":6551,"slug":6552,"stem":6553,"tags":6554,"teaser":6557,"__hash__":6558},"blog/blog/i-think-i-spider-1-0-released.md","I think I spider 1.0 released",[6531],"linsin",{"type":12,"value":6533,"toc":6543},[6534,6537],[15,6535,6529],{"id":6536},"i-think-i-spider-10-released",[19,6538,6539],{},[33,6540],{"alt":6541,"src":6542},"I Think I Spider","https://media.synyx.de/uploads//2010/09/app-icon-512px.png",{"title":110,"searchDepth":111,"depth":111,"links":6544},[],[120,6546],"our-apps","2010-09-11T17:54:14","https://synyx.de/blog/i-think-i-spider-1-0-released/",{},"/blog/i-think-i-spider-1-0-released",{"title":6529,"description":110},"i-think-i-spider-1-0-released","blog/i-think-i-spider-1-0-released",[133,5156,6555,6556,2326],"iphone","ithinkispider","Today we are proud to present our own very first App (it’s actually our second Android App) that made it to the App Store and Android Market – I think…","ihp4cPfv4tMEA3ebuCKmOKtft0Yrr9slF_Lsd0LB1fw",{"id":6560,"title":6561,"author":6562,"body":6563,"category":6634,"date":6635,"description":6636,"extension":124,"link":6637,"meta":6638,"navigation":127,"path":6639,"seo":6640,"slug":6642,"stem":6643,"tags":6644,"teaser":6645,"__hash__":6646},"blog/blog/mobile-solutions-summary-5.md","Mobile Solutions – Summary",[6531],{"type":12,"value":6564,"toc":6632},[6565,6568,6582,6590,6596,6605],[15,6566,6561],{"id":6567},"mobile-solutions-summary",[19,6569,6570,6571,6576,6577,1581],{},"It has been a while since my ",[159,6572,6575],{"href":6573,"rel":6574},"http://blog.synyx.de/2010/07/mobile-solutions-summary-2/",[163],"last update"," on our efforts over\nat the ",[159,6578,6581],{"href":6579,"rel":6580},"http://mobile.synyx.de/",[163],"mobile solutions blog",[19,6583,6584,6585,324],{},"The most important announcement was without any doubt the imminent release of our new side\nproject: ",[159,6586,6589],{"href":6587,"rel":6588},"http://mobile.synyx.de/2010/07/i-think-i-spider/",[163],"“I think I spider”",[6591,6592,6593],"blockquote",{},[19,6594,6595],{},"The “I think I spider”App idea was born, when we discovered the corresponding web site. We had so much fun with it,\nthat we decided to bring all the joy to the major mobile platforms.",[19,6597,6598,6599,6604],{},"Although, we haven’t revealed any specific features yet, reading\nour ",[159,6600,6603],{"href":6601,"rel":6602},"http://mobile.synyx.de/category/our-apps/",[163],"vast number of blog posts"," on “I think I spider”, you’ve probably got an\nidea of what the App is gonna bring to you.",[19,6606,6607,6608,6613,6614,6619,6620,6625,6626,6631],{},"A very interesting topic\nis ",[159,6609,6612],{"href":6610,"rel":6611},"http://dlinsin.blogspot.com/2010/03/is-future-of-mobile-apps-web.html",[163],"cross-device mobile development",", which\nmight be where the future lies, especially with Apple’s\nrevised ",[159,6615,6618],{"href":6616,"rel":6617},"http://daringfireball.net/2010/09/app_store_guidelines",[163],"iOS Developer Program License Agreements",". Our very\nown ",[159,6621,6624],{"href":6622,"rel":6623},"http://mobile.synyx.de/authors/?uid=9",[163],"Florian"," wrote\nan ",[159,6627,6630],{"href":6628,"rel":6629},"http://mobile.synyx.de/2010/09/on-cross-device-mobile-development-part-2/",[163],"extensive two-part piece"," on the state\nof cross-device mobile development, which is definitely worth a read.",{"title":110,"searchDepth":111,"depth":111,"links":6633},[],[118],"2010-09-10T08:44:39","It has been a while since my last update on our efforts over\\nat the mobile solutions blog.","https://synyx.de/blog/mobile-solutions-summary-5/",{},"/blog/mobile-solutions-summary-5",{"title":6561,"description":6641},"It has been a while since my last update on our efforts over\nat the mobile solutions blog.","mobile-solutions-summary-5","blog/mobile-solutions-summary-5",[133,5156,6555,578],"It has been a while since my last update on our efforts over at the mobile solutions blog. The most important announcement was without any doubt the imminent release of…","JLhxo_R5oDX4p05l4611cjUW8lEQUM0SDnOGCfiC8yw",{"id":6648,"title":6649,"author":6650,"body":6651,"category":6748,"date":6749,"description":110,"extension":124,"link":6750,"meta":6751,"navigation":127,"path":6752,"seo":6753,"slug":6754,"stem":6755,"tags":6756,"teaser":6759,"__hash__":6760},"blog/blog/resolutions-on-android.md","Android resolution and layout problems",[586],{"type":12,"value":6652,"toc":6742},[6653,6656,6661,6665,6668,6671,6674,6677,6680,6684,6687,6690,6698,6701,6705,6708,6711,6714,6717,6720,6723,6726,6729,6732,6736,6739],[15,6654,6649],{"id":6655},"android-resolution-and-layout-problems",[19,6657,6658],{},[33,6659],{"alt":6541,"src":6660},"https://media.synyx.de/uploads//2010/07/512px.png",[23,6662,6664],{"id":6663},"dp-or-not-dp","dp or not dp?",[19,6666,6667],{},"It was some work, but after a little time, the layout fitted for each density – well, at least so it seemed…",[19,6669,6670],{},"It fitted only for the three default dpi values (120, 160 and 240).",[19,6672,6673],{},"If you used a larger / smaller screen with the same density, the positions didn’t match exactly any more.",[19,6675,6676],{},"The problem was, that we used (as we are also said to always use) dp to set the views. It’s true that its nice to do\nthis, if you use the standard widgets and if you don’t have such a highly customized layout, but in our case it seemed\nlike the wrong decision to use dp.",[19,6678,6679],{},"The solution to this was to use pixel values instead and to create the layouts for the different resolutions and not the\ndensities. The new values were easy to calculate, because you only needed to take the values of the 480x320px\nresolution (exactly the same values as in dp) and multiply them by 0.75 for the 320x240px resolution and by 1.5 for\nthe 854×480 one (and adjust the height a little here…).",[23,6681,6683],{"id":6682},"changing-the-layout-in-your-code","Changing the layout in your code",[19,6685,6686],{},"The next problem was (even before we converted the values in px) to display the layout on the 800×480 and 854×480\nresolutions, because you can’t declare layouts for both of them – they always take the same one.",[19,6688,6689],{},"We also didn’t want to have a black border for the bigger resolution, scaling the background to a bigger length was also\nno problem, so we decided to adjust the layout for this particular case in our code:",[225,6691,6696],{"className":6692,"code":6694,"language":6695},[6693],"language-text","\n//called in onCreate()\nif (getWindowManager().getDefaultDisplay().getHeight() == 800) {\n AbsoluteLayout.LayoutParams params;\n params = (LayoutParams) findViewById(R.id.someView).getLayoutParams();\n params.y = params.y - 7;\n// more adjusting here...\n}\n\n","text",[176,6697,6694],{"__ignoreMap":110},[19,6699,6700],{},"Well, maybe it isn’t a good solution, but we didn’t find any other possibility here.",[23,6702,6704],{"id":6703},"using-resource-qualifiers-for-the-different-resolutions","Using resource qualifiers for the different resolutions",[19,6706,6707],{},"A downside of this approach is that you have to add adjustments to every resolution that is available now and that comes\nout in the future, like the bigger ones for tablets.",[19,6709,6710],{},"Right now, you can support most of the resolutions by simply declaring different layouts by using different folders (\nexcept for 800×480 and 854×480), but I’m not sure how it is going to be with new resolutions.",[19,6712,6713],{},"The layouts we declare are seperated in the following folders:",[19,6715,6716],{},"layout-normal-mdpi -> 320×480",[19,6718,6719],{},"layout-normal-hdpi -> 800×480 and 854×480 (adjusted in the code)",[19,6721,6722],{},"layout-normal-ldpi -> 400×240",[19,6724,6725],{},"layout-large-mdpi -> 800×480 tablet (mostly the same as the layout-normal-hdpi layout, but needs to be in this\nfolder)",[19,6727,6728],{},"layout-small-ldpi -> 320×240",[19,6730,6731],{},"Also one -landscape folder for each of them for the landscape layout for the widget (the rest of the app is portrait\nonly)",[23,6733,6735],{"id":6734},"conclusion","Conclusion",[19,6737,6738],{},"If someone downloads the App with an unsupported resolution, it will not be displayed correctly. For such cases it could\nbe helpful if you could declare the resolutions that your app supports, or at least the aspect ratios and to have\nresource directory qualifiers for this, so that you don’t have to adjust your layout in the code.",[19,6740,6741],{},"My conclusion of this is that the wide variety of different resolutions, densities and especially aspect ratios makes it\nreally hard to create a good looking app that supports all of them. Google should really have put more restrictions to\nthese terms to make it easier for the developers so that they can provide better quality apps for the users.",{"title":110,"searchDepth":111,"depth":111,"links":6743},[6744,6745,6746,6747],{"id":6663,"depth":111,"text":6664},{"id":6682,"depth":111,"text":6683},{"id":6703,"depth":111,"text":6704},{"id":6734,"depth":111,"text":6735},[120,6546,2314],"2010-09-08T13:28:24","https://synyx.de/blog/resolutions-on-android/",{},"/blog/resolutions-on-android",{"title":6649,"description":110},"resolutions-on-android","blog/resolutions-on-android",[133,6757,6556,3172,6758],"i-think-i-spider","resolution","During the process of developing I think I spider we discovered various problems regarding Android’s different resolutions. Here you can see which problems we encountered and how we solved them:…","zE1O2G5yrUPOJAy03zZlQOFstFCOfkCJc5_scwvunN8",{"id":6762,"title":6763,"author":6764,"body":6765,"category":7030,"date":7031,"description":7032,"extension":124,"link":7033,"meta":7034,"navigation":127,"path":7035,"seo":7036,"slug":6769,"stem":7038,"tags":7039,"teaser":7041,"__hash__":7042},"blog/blog/on-cross-device-mobile-development-part-2.md","On cross-device mobile development – Part 2",[6045],{"type":12,"value":6766,"toc":7023},[6767,6770,6783,6787,6810,6814,6845,6850,6874,6878,6885,6889,6892,6896,6900,6925,6929,6948,6952,6967,6971,7003,7007,7020],[15,6768,6763],{"id":6769},"on-cross-device-mobile-development-part-2",[19,6771,6772,6773,6778,6779,6782],{},"In the ",[159,6774,6777],{"href":6775,"rel":6776},"http://mobile.synyx.de/2010/08/on-cross-device-mobile-development-part-1/",[163],"previous part"," of this series we took\na look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as ",[181,6780,6781],{},"the web\nstack","). A few pieces of absolutely laid out CSS for the views, a dash of custom jQuery events for controller code\ninvocation and standard in-browser SQLite access for the model — every aspect of a MVC application should be accounted\nfor, shouldn’t it? Not quite …",[23,6784,6786],{"id":6785},"what-does-native-have-what-web-stack-hasnt","What does native have, what web stack hasn’t?",[19,6788,6789,6790,6793,6794,6797,6798,6801,6802,6805,6806,6809],{},"If you want the answer to this question boiled down to one simple word: ",[299,6791,6792],{},"abstraction",". In an application you don’t\nwant to think in terms of ",[176,6795,6796],{},"\u003Cul>"," elements, to which you append ",[176,6799,6800],{},"\u003Cli>"," elements, styled adequately with CSS to harbor\nitems populated from SQLite statements and react to code which is painstakingly attached to every list item to react to\ndifferent user inputs. You would usually think about the problem in terms of — for example — a ",[176,6803,6804],{},"ListController"," and an\nattached ",[176,6807,6808],{},"DataSource",", freeing you from the task of layouting your list. And you don’t want to have to code all this\nboilerplate from scratch. It is not the problem you want to solve. It is something left for a framework.",[23,6811,6813],{"id":6812},"competing-with-native-sdks","Competing with native SDKs",[19,6815,6816,6817,6821,6822,6827,6828,6830,6831,6836,6837,6840,6841,6844],{},"Even before the days of mobile applications, web application developers pushed hard to create frameworks to match up\nagainst their competitors from the native world. An increasing number of those are now also reconsidering deployment on\nsmall mobile devices and are joined by new JavaScript frameworks especially tailored to mobile\ndevices. ",[159,6818,6820],{"href":5753,"rel":6819},[163],"PhoneGap"," — which we refered to already in\nthe first part of the series — is\nnow ",[159,6823,6826],{"href":6824,"rel":6825},"https://web.archive.org/web/20140122061413/http://phonegap.com/2010/07/19/it%e2%80%99s-easier-than-ever-for-symbian-developers-to-build-mobile-apps-with-phonegap/",[163],"fully embraced","\nby Nokia for their Symbian smartphone operating system. Palm has his whole mobile user experience built around ",[181,6829,6781],{},", accordingly naming their operating system ",[159,6832,6835],{"href":6833,"rel":6834},"http://developer.palm.com/",[163],"WebOS",". The need for mobile ",[181,6838,6839],{},"web stack","\nframeworks is growing. The rest of this article will introduce some of those frameworks, but first a short recap of what\n",[299,6842,6843],{},"abstractions"," we would expect from such a framework, compared to their native siblings:",[6846,6847,6849],"h4",{"id":6848},"models","Models",[19,6851,6852,6853,6858,6859,6866,6867,1581],{},"The way the data is retrieved and stored on the device comprises our data model. Because of constraints in memory\nfootprint and also because of security considerations, mobile SDKs should try to provide abstraction layers for your\ndata storage. As an example, the iPhone SDK incorporates a subsystem\ncalled ",[159,6854,6857],{"href":6855,"rel":6856},"http://developer.apple.com/iphone/library/documentation/DataManagement/Conceptual/iPhoneCoreData01/",[163],"CoreData",".\nIt provides modelling tools and stub generation to easily integrate with ",[159,6860,6863],{"href":6861,"rel":6862},"http://developer.apple.com/iphone/library/documentation/CoreData/Reference/NSFetchedResultsController_Class/Reference/Reference.html",[163],[176,6864,6865],{},"UITableView","\ncontrols. Android, while staying more on the lower SQLite level, also provides similar integration to their native ",[159,6868,6871],{"href":6869,"rel":6870},"http://developer.android.com/reference/android/widget/SimpleCursorAdapter.html",[163],[176,6872,6873],{},"ListView",[6846,6875,6877],{"id":6876},"views-user-interface-ui-elements","Views & user interface (UI) elements",[19,6879,6880,6884],{},[33,6881],{"alt":6882,"src":6883},"\"iPhone UI Controls\"","https://media.synyx.de/uploads//2010/07/iPhone-UI-Controls-e1280510112988.png","\nEvery mobile SDK contains a selection of useful UI elements: buttons, sliders, text entry controls, switches and on a\nlarger scale prebuilt view elements for tabular data, images, display of rich text or maps for geolocative services.\nUsually every of those elements or views exhibits a callback interface tailored towards the particular item. These can\nbecome quite sophisticated: views for tabular data call back for touches on a particular row and/or column or map views\nrequesting to redraw the viewport according to new coordinates after scrolling occured.",[6846,6886,6888],{"id":6887},"controllers","Controllers",[19,6890,6891],{},"The backbone of every applications, they are the heavy lifters which glue your models and views together. Often the\ntasks to be done — for example managing the display of overviews and increasingly detailled item views or also an\neditable table view of data from an underlying data source — are abstract and common enough, that a selection of generic\ncontrollers can eliminate boilerplate code and provide a feature rich set of actions out of the box.",[1147,6893,6895],{"id":6894},"javascript-frameworks-the-small-the-huge-and-the-familiar","JavaScript frameworks … the small, the huge and the familiar",[6846,6897,6899],{"id":6898},"jqtouch","jQTouch",[19,6901,6902,6903,6907,6908,6913,6914,6921,6922,6924],{},"Our first framework is perhaps the one which contradicts our demands the most. ",[159,6904,6899],{"href":6905,"rel":6906},"http://jqtouch.com",[163]," is a\nlightweight layer on the ever-present ",[159,6909,6912],{"href":6910,"rel":6911},"http://jquery.com",[163],"jQuery"," library. And with lightweight we are talking about a\nmeager 577 source lines of code. Obviously one cannot expect a lot of the desired abstractions, yet still jQTouch has\nits own eligibility. It provides mainly an implementation of one of the most common view abstractions — a stack of menus\nand toolbars to navigate forward and backward — bundled with finely tuned themes for an iPhone-like or more generic\nmobile UI. While you still have to provide the code for more complex controllers and also data model abstraction, it can\nbe used for those cases, where an already deployed web application employing jQuery, should be quickly ported to a\nmobile device. Furthermore it features resource-preloading as also access to geolocation APIs in mobile WebKit\nimplementations. The missing model and controller code can be adapted from existing desktop browser jQuery extensions,\nwhich are numerous. It is now maintained by Jonathan Stark, whose book ",[159,6915,6918],{"href":6916,"rel":6917},"http://building-iphone-apps.labs.oreilly.com/",[163],[181,6919,6920],{},"Building iPhone Apps with HTML, CSS &\nJavaScript"," we already mentioned in part one of this article series and\nwhich is still a great resource for ",[181,6923,6839],{}," development on mobile devices.",[6846,6926,6928],{"id":6927},"sencha-touch","Sencha Touch",[19,6930,6931,6936,6937,6941,6942,6947],{},[159,6932,6935],{"href":6933,"rel":6934},"http://www.sencha.com/",[163],"Sencha Labs"," (formerly ExtJS) are already well-known for their desktop browser frameworks.\nWith ",[159,6938,6928],{"href":6939,"rel":6940},"http://www.sencha.com/products/touch/",[163]," they provide abstractions for many of the use cases we talked\nabout above. This includes controllers for tool- or tabbar navigation, data sources which can be attached to analogous\nviews — which then are updated automatically, common entry controls like textfields or date pickers, map views and media\nlike audio and video. Every control provided has an extensive list of event callbacks, that you can easily attach your\nfunctions to. From the ",[159,6943,6946],{"href":6944,"rel":6945},"http://www.sencha.com/products/touch/demos.php",[163],"demos"," it becomes clear that they also push hard\ntowards the emerging tablet devices by providing a full-stack framework for even sophisticated applications. It should\nbe noted though, that it is dual-licensed, with commercial use entailing a (small) fee.",[6846,6949,6951],{"id":6950},"jquery-mobile","jQuery mobile",[19,6953,6954,6955,6960,6961,6966],{},"For people, who in the past have worked with ",[159,6956,6959],{"href":6957,"rel":6958},"http://jqueryui.com",[163],"jQuery UI"," for web applications, John Resig recently\nannounced ",[159,6962,6965],{"href":6963,"rel":6964},"http://jquerymobile.com/",[163],"jQuery Mobile",". While it is still in planning & internal beta, it could prove as\nthe missing link from the formerly mentioned jQTouch. Projecting from the existing jQuery UI framework, this would\nprovide a good amount of UI controls and common view abstractions, while maintaining the slick feeling of traditional\njQuery programming. It will also come with themes, which match typical native mobile controls and a strong mission\nstatement to make it a real cross-platform alternative, beyond the iOS platform usually targeted by other frameworks.",[6846,6968,6970],{"id":6969},"google-web-toolkit","Google Web Toolkit",[19,6972,6973,6974,6978,6979,6984,6985,6990,6991,6996,6997,7002],{},"Last but definitely not least, we have to include an old familiar\nfriend: ",[159,6975,6970],{"href":6976,"rel":6977},"http://code.google.com/webtoolkit/",[163]," (GWT) has already matured for desktop browsers. It also\nhas something to provide, which other JavaScript frameworks lack: extensive IDE support with a strictly typed language\nunderneath. Especially Android developers will feel more at home, when developing with Java. While originally targeted\nat desktop browsers, its extensibility makes it easy to retarget mobile\nplatforms. ",[159,6980,6983],{"href":6981,"rel":6982},"http://code.google.com/p/gwt-mobile-webkit/",[163],"GWT Mobile WebKit"," extends GWT with support for touch\ninterfaces and also bundles libraries to\nabstract ",[159,6986,6989],{"href":6987,"rel":6988},"http://code.google.com/p/gwt-mobile-webkit/downloads/list?q=label:API-Geolocation",[163],"geolocation services"," and\nthe ",[159,6992,6995],{"href":6993,"rel":6994},"http://code.google.com/p/gwt-mobile-webkit/downloads/list?q=label:API-Database",[163],"SQLite"," database — something even\nmissing from the original Android SDK. Views can be created programmatically\nor ",[159,6998,7001],{"href":6999,"rel":7000},"http://code.google.com/webtoolkit/doc/latest/DevGuideUiBinder.html",[163],"declaratively"," and provide a rich API for\ncallbacks on user input.",[23,7004,7006],{"id":7005},"what-is-left-to-be-said","What is left to be said",[19,7008,7009,7010,7012,7013,7015,7016,7019],{},"The ",[181,7011,6839],{}," mobile development community is certainly on the move to sophisticated frameworks. From small\nmicroframeworks to full-stack frameworks like ",[181,7014,6928],{}," or ",[181,7017,7018],{},"GWT",", a range of tastes for different development\nstyles is served. While we tried to give a bigger perspective on what is available, we still haven’t even touched topics\nlike the upcoming HTML5 — which together with CSS3 will bring fast animations and sophisticated graphics — or actual\ncross-device behaviour. What can be definitely said is, that these developments — wether or not one decides to jump on\nthis bandwagon — increase the diversity of mobile development and provide new insights in how we think about mobile\napplication development.",[19,7021,7022],{},"To be continued some time …",{"title":110,"searchDepth":111,"depth":111,"links":7024},[7025,7026,7029],{"id":6785,"depth":111,"text":6786},{"id":6812,"depth":111,"text":6813,"children":7027},[7028],{"id":6894,"depth":402,"text":6895},{"id":7005,"depth":111,"text":7006},[120],"2010-09-07T12:00:39","In the previous part of this series we took\\na look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as the web\\nstack). A few pieces of absolutely laid out CSS for the views, a dash of custom jQuery events for controller code\\ninvocation and standard in-browser SQLite access for the model — every aspect of a MVC application should be accounted\\nfor, shouldn’t it? Not quite …","https://synyx.de/blog/on-cross-device-mobile-development-part-2/",{},"/blog/on-cross-device-mobile-development-part-2",{"title":6763,"description":7037},"In the previous part of this series we took\na look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as the web\nstack). A few pieces of absolutely laid out CSS for the views, a dash of custom jQuery events for controller code\ninvocation and standard in-browser SQLite access for the model — every aspect of a MVC application should be accounted\nfor, shouldn’t it? Not quite …","blog/on-cross-device-mobile-development-part-2",[133,7040,1774,6555,5862],"css","In the previous part of this series we took a look on how to develop mobile applications with plain HTML, CSS & JavaScript (furthermore refered to as the web stack). A…","YieTuihm_5oZzpoNHWVn3JDuz9uBqr4EfoBq26hjIl0",{"id":7044,"title":7045,"author":7046,"body":7047,"category":8601,"date":8602,"description":8603,"extension":124,"link":8604,"meta":8605,"navigation":127,"path":8606,"seo":8607,"slug":7051,"stem":8609,"tags":8610,"teaser":8611,"__hash__":8612},"blog/blog/on-cross-device-mobile-development-part-1.md","On cross-device mobile development – Part 1",[6045],{"type":12,"value":7048,"toc":8595},[7049,7052,7059,7063,7066,7070,7078,7382,7385,8104,8107,8312,8323,8373,8376,8544,8548,8558,8561,8568,8572,8575,8589,8592],[15,7050,7045],{"id":7051},"on-cross-device-mobile-development-part-1",[19,7053,7054,7055,7058],{},"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\ndeployment target for mobile applications. ",[181,7056,7057],{},"That’s the theory at least."," In this series of articles we will provide an\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\nseveral mobile platforms at once.",[23,7060,7062],{"id":7061},"its-not-in-the-browser","It’s (not) in the browser",[19,7064,7065],{},"Developing applications with HTML, CSS & Javascript is a fundamentally different experience. It already is on the\nnormal desktop with its widescreen browsers. On a mobile device it becomes a game changer. We are accustomed to\ngraphical user interface (GUI) toolkits with their more direct manipulation of data through on-screen elements like\nbuttons. While browsers and HTML provide their own set of UI elements and the ability to wire events like clicks to\nJavascript callbacks, they lack a lot of the bells and whistles. Especially in the handling of client-side data — the\nmodel in model-view-controller (MVC) so to say — and the controller abstraction even modern browsers lack the feature\nrichness of common native SDKs. In the rest of this article we will give a short overview on which web technologies map\nto which concept in the MVC approach and how you would basically structure an application based on such technologies.",[23,7067,7069],{"id":7068},"of-models-views-controllers","Of models, views & controllers",[19,7071,1624,7072,7077],{},[159,7073,7076],{"href":7074,"rel":7075},"http://building-iphone-apps.labs.oreilly.com/ch04.html",[163],"“Building iPhone Apps with HTML, CSS and JavaScript”","\nJonathan Stark describes how to turn websites into single page iPhone applications. Views are implemented straight\nforward with plain HTML and CSS, throwing in some bits and pieces to make browser standard behavior of HTML elements\nmore native like. To give an example, we will show how to create a graphical navigation bar and switch between different\nscreens. First we need some HTML and CSS snippets to create the layout:",[225,7079,7081],{"className":1772,"code":7080,"language":1774,"meta":110,"style":110},"\u003Cul id=\"navigation\">\n \u003Cli>\u003Ca class=\"current\" href=\"#home\">Home\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#new\">New things\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#favorites\">I like those\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#more\">More content\u003C/a>\u003C/li>\n \u003Cli>\u003Ca href=\"#info\">About\u003C/a>\u003C/li>\n\u003C/ul>\n\u003Cdiv class=\"view\" id=\"home\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"new\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"favorites\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"more\">...\u003C/div>\n\u003Cdiv class=\"view\" id=\"info\">...\u003C/div>\n",[176,7082,7083,7098,7136,7164,7192,7220,7248,7256,7282,7307,7332,7357],{"__ignoreMap":110},[233,7084,7085,7087,7089,7091,7093,7096],{"class":235,"line":236},[233,7086,1782],{"class":1781},[233,7088,85],{"class":3790},[233,7090,4110],{"class":3794},[233,7092,3828],{"class":1781},[233,7094,7095],{"class":3831},"\"navigation\"",[233,7097,1789],{"class":1781},[233,7099,7100,7102,7104,7107,7109,7111,7113,7116,7119,7121,7124,7127,7129,7132,7134],{"class":235,"line":111},[233,7101,1794],{"class":1781},[233,7103,88],{"class":3790},[233,7105,7106],{"class":1781},">\u003C",[233,7108,159],{"class":3790},[233,7110,3967],{"class":3794},[233,7112,3828],{"class":1781},[233,7114,7115],{"class":3831},"\"current\"",[233,7117,7118],{"class":3794}," href",[233,7120,3828],{"class":1781},[233,7122,7123],{"class":3831},"\"#home\"",[233,7125,7126],{"class":1781},">Home\u003C/",[233,7128,159],{"class":3790},[233,7130,7131],{"class":1781},">\u003C/",[233,7133,88],{"class":3790},[233,7135,1789],{"class":1781},[233,7137,7138,7140,7142,7144,7146,7148,7150,7153,7156,7158,7160,7162],{"class":235,"line":402},[233,7139,1794],{"class":1781},[233,7141,88],{"class":3790},[233,7143,7106],{"class":1781},[233,7145,159],{"class":3790},[233,7147,7118],{"class":3794},[233,7149,3828],{"class":1781},[233,7151,7152],{"class":3831},"\"#new\"",[233,7154,7155],{"class":1781},">New things\u003C/",[233,7157,159],{"class":3790},[233,7159,7131],{"class":1781},[233,7161,88],{"class":3790},[233,7163,1789],{"class":1781},[233,7165,7166,7168,7170,7172,7174,7176,7178,7181,7184,7186,7188,7190],{"class":235,"line":408},[233,7167,1794],{"class":1781},[233,7169,88],{"class":3790},[233,7171,7106],{"class":1781},[233,7173,159],{"class":3790},[233,7175,7118],{"class":3794},[233,7177,3828],{"class":1781},[233,7179,7180],{"class":3831},"\"#favorites\"",[233,7182,7183],{"class":1781},">I like those\u003C/",[233,7185,159],{"class":3790},[233,7187,7131],{"class":1781},[233,7189,88],{"class":3790},[233,7191,1789],{"class":1781},[233,7193,7194,7196,7198,7200,7202,7204,7206,7209,7212,7214,7216,7218],{"class":235,"line":414},[233,7195,1794],{"class":1781},[233,7197,88],{"class":3790},[233,7199,7106],{"class":1781},[233,7201,159],{"class":3790},[233,7203,7118],{"class":3794},[233,7205,3828],{"class":1781},[233,7207,7208],{"class":3831},"\"#more\"",[233,7210,7211],{"class":1781},">More content\u003C/",[233,7213,159],{"class":3790},[233,7215,7131],{"class":1781},[233,7217,88],{"class":3790},[233,7219,1789],{"class":1781},[233,7221,7222,7224,7226,7228,7230,7232,7234,7237,7240,7242,7244,7246],{"class":235,"line":420},[233,7223,1794],{"class":1781},[233,7225,88],{"class":3790},[233,7227,7106],{"class":1781},[233,7229,159],{"class":3790},[233,7231,7118],{"class":3794},[233,7233,3828],{"class":1781},[233,7235,7236],{"class":3831},"\"#info\"",[233,7238,7239],{"class":1781},">About\u003C/",[233,7241,159],{"class":3790},[233,7243,7131],{"class":1781},[233,7245,88],{"class":3790},[233,7247,1789],{"class":1781},[233,7249,7250,7252,7254],{"class":235,"line":426},[233,7251,1828],{"class":1781},[233,7253,85],{"class":3790},[233,7255,1789],{"class":1781},[233,7257,7258,7260,7262,7264,7266,7269,7271,7273,7276,7278,7280],{"class":235,"line":657},[233,7259,1782],{"class":1781},[233,7261,3964],{"class":3790},[233,7263,3967],{"class":3794},[233,7265,3828],{"class":1781},[233,7267,7268],{"class":3831},"\"view\"",[233,7270,4110],{"class":3794},[233,7272,3828],{"class":1781},[233,7274,7275],{"class":3831},"\"home\"",[233,7277,5819],{"class":1781},[233,7279,3964],{"class":3790},[233,7281,1789],{"class":1781},[233,7283,7284,7286,7288,7290,7292,7294,7296,7298,7301,7303,7305],{"class":235,"line":662},[233,7285,1782],{"class":1781},[233,7287,3964],{"class":3790},[233,7289,3967],{"class":3794},[233,7291,3828],{"class":1781},[233,7293,7268],{"class":3831},[233,7295,4110],{"class":3794},[233,7297,3828],{"class":1781},[233,7299,7300],{"class":3831},"\"new\"",[233,7302,5819],{"class":1781},[233,7304,3964],{"class":3790},[233,7306,1789],{"class":1781},[233,7308,7309,7311,7313,7315,7317,7319,7321,7323,7326,7328,7330],{"class":235,"line":667},[233,7310,1782],{"class":1781},[233,7312,3964],{"class":3790},[233,7314,3967],{"class":3794},[233,7316,3828],{"class":1781},[233,7318,7268],{"class":3831},[233,7320,4110],{"class":3794},[233,7322,3828],{"class":1781},[233,7324,7325],{"class":3831},"\"favorites\"",[233,7327,5819],{"class":1781},[233,7329,3964],{"class":3790},[233,7331,1789],{"class":1781},[233,7333,7334,7336,7338,7340,7342,7344,7346,7348,7351,7353,7355],{"class":235,"line":673},[233,7335,1782],{"class":1781},[233,7337,3964],{"class":3790},[233,7339,3967],{"class":3794},[233,7341,3828],{"class":1781},[233,7343,7268],{"class":3831},[233,7345,4110],{"class":3794},[233,7347,3828],{"class":1781},[233,7349,7350],{"class":3831},"\"more\"",[233,7352,5819],{"class":1781},[233,7354,3964],{"class":3790},[233,7356,1789],{"class":1781},[233,7358,7359,7361,7363,7365,7367,7369,7371,7373,7376,7378,7380],{"class":235,"line":679},[233,7360,1782],{"class":1781},[233,7362,3964],{"class":3790},[233,7364,3967],{"class":3794},[233,7366,3828],{"class":1781},[233,7368,7268],{"class":3831},[233,7370,4110],{"class":3794},[233,7372,3828],{"class":1781},[233,7374,7375],{"class":3831},"\"info\"",[233,7377,5819],{"class":1781},[233,7379,3964],{"class":3790},[233,7381,1789],{"class":1781},[19,7383,7384],{},"The navigational items are then replaced with nice button graphics via CSS and positioned on the page.",[225,7386,7389],{"className":7387,"code":7388,"language":7040,"meta":110,"style":110},"language-css shiki shiki-themes github-light github-dark","/* navigation is a fixed block */\n#navigation {\n position: fixed;\n top: 0px;\n left: 0px;\n width: 320px;\n margin: 0;\n padding: 0;\n}\n#navigation li {\n display: block;\n position: absolute;\n}\n#navigation li a {\n display: block;\n position: absolute;\n height: 48px;\n width: 80px;\n top: 0px;\n text-indent: -9999px;\n}\na[href=\"#home\"] {\n background: url(home.png);\n left: 0px;\n}\na[href=\"#home\"].current {\n background: url(home-current.png);\n}\na[href=\"#new\"] {\n background: url(new.png);\n left: 80px;\n}\na[href=\"#new\"].current {\n background: url(new-current.png);\n}\na[href=\"#favorites\"] {\n background: url(favorites.png);\n left: 160px;\n}\na[href=\"#favorites\"].current {\n background: url(favorites-current.png);\n}\na[href=\"#more\"] {\n background: url(more.png);\n left: 240px;\n}\na[href=\"#info\"] {\n background: url(info.png);\n position: fixed;\n width: 48px;\n height: 48px;\n bottom: 16px;\n right: 16px;\n}\n/* finally position the views themselves */\ndiv.view {\n position: absolute;\n top: 48px;\n left: 0px;\n width: 320px;\n height: 396px;\n}\n",[176,7390,7391,7397,7405,7419,7434,7447,7461,7472,7483,7487,7496,7508,7519,7523,7534,7544,7554,7568,7581,7593,7607,7611,7628,7646,7658,7662,7682,7697,7701,7715,7730,7742,7746,7764,7779,7783,7797,7812,7825,7829,7847,7862,7867,7882,7898,7912,7917,7932,7948,7959,7972,7985,8000,8014,8019,8025,8035,8046,8059,8072,8085,8099],{"__ignoreMap":110},[233,7392,7393],{"class":235,"line":236},[233,7394,7396],{"class":7395},"sJ8bj","/* navigation is a fixed block */\n",[233,7398,7399,7402],{"class":235,"line":111},[233,7400,7401],{"class":3794},"#navigation",[233,7403,7404],{"class":1781}," {\n",[233,7406,7407,7410,7413,7416],{"class":235,"line":402},[233,7408,7409],{"class":5949}," position",[233,7411,7412],{"class":1781},": ",[233,7414,7415],{"class":5949},"fixed",[233,7417,7418],{"class":1781},";\n",[233,7420,7421,7424,7426,7429,7432],{"class":235,"line":408},[233,7422,7423],{"class":5949}," top",[233,7425,7412],{"class":1781},[233,7427,7428],{"class":5949},"0",[233,7430,7431],{"class":5869},"px",[233,7433,7418],{"class":1781},[233,7435,7436,7439,7441,7443,7445],{"class":235,"line":414},[233,7437,7438],{"class":5949}," left",[233,7440,7412],{"class":1781},[233,7442,7428],{"class":5949},[233,7444,7431],{"class":5869},[233,7446,7418],{"class":1781},[233,7448,7449,7452,7454,7457,7459],{"class":235,"line":420},[233,7450,7451],{"class":5949}," width",[233,7453,7412],{"class":1781},[233,7455,7456],{"class":5949},"320",[233,7458,7431],{"class":5869},[233,7460,7418],{"class":1781},[233,7462,7463,7466,7468,7470],{"class":235,"line":426},[233,7464,7465],{"class":5949}," margin",[233,7467,7412],{"class":1781},[233,7469,7428],{"class":5949},[233,7471,7418],{"class":1781},[233,7473,7474,7477,7479,7481],{"class":235,"line":657},[233,7475,7476],{"class":5949}," padding",[233,7478,7412],{"class":1781},[233,7480,7428],{"class":5949},[233,7482,7418],{"class":1781},[233,7484,7485],{"class":235,"line":662},[233,7486,429],{"class":1781},[233,7488,7489,7491,7494],{"class":235,"line":667},[233,7490,7401],{"class":3794},[233,7492,7493],{"class":3790}," li",[233,7495,7404],{"class":1781},[233,7497,7498,7501,7503,7506],{"class":235,"line":673},[233,7499,7500],{"class":5949}," display",[233,7502,7412],{"class":1781},[233,7504,7505],{"class":5949},"block",[233,7507,7418],{"class":1781},[233,7509,7510,7512,7514,7517],{"class":235,"line":679},[233,7511,7409],{"class":5949},[233,7513,7412],{"class":1781},[233,7515,7516],{"class":5949},"absolute",[233,7518,7418],{"class":1781},[233,7520,7521],{"class":235,"line":684},[233,7522,429],{"class":1781},[233,7524,7525,7527,7529,7532],{"class":235,"line":689},[233,7526,7401],{"class":3794},[233,7528,7493],{"class":3790},[233,7530,7531],{"class":3790}," a",[233,7533,7404],{"class":1781},[233,7535,7536,7538,7540,7542],{"class":235,"line":695},[233,7537,7500],{"class":5949},[233,7539,7412],{"class":1781},[233,7541,7505],{"class":5949},[233,7543,7418],{"class":1781},[233,7545,7546,7548,7550,7552],{"class":235,"line":701},[233,7547,7409],{"class":5949},[233,7549,7412],{"class":1781},[233,7551,7516],{"class":5949},[233,7553,7418],{"class":1781},[233,7555,7556,7559,7561,7564,7566],{"class":235,"line":706},[233,7557,7558],{"class":5949}," height",[233,7560,7412],{"class":1781},[233,7562,7563],{"class":5949},"48",[233,7565,7431],{"class":5869},[233,7567,7418],{"class":1781},[233,7569,7570,7572,7574,7577,7579],{"class":235,"line":711},[233,7571,7451],{"class":5949},[233,7573,7412],{"class":1781},[233,7575,7576],{"class":5949},"80",[233,7578,7431],{"class":5869},[233,7580,7418],{"class":1781},[233,7582,7583,7585,7587,7589,7591],{"class":235,"line":717},[233,7584,7423],{"class":5949},[233,7586,7412],{"class":1781},[233,7588,7428],{"class":5949},[233,7590,7431],{"class":5869},[233,7592,7418],{"class":1781},[233,7594,7595,7598,7600,7603,7605],{"class":235,"line":723},[233,7596,7597],{"class":5949}," text-indent",[233,7599,7412],{"class":1781},[233,7601,7602],{"class":5949},"-9999",[233,7604,7431],{"class":5869},[233,7606,7418],{"class":1781},[233,7608,7609],{"class":235,"line":728},[233,7610,429],{"class":1781},[233,7612,7613,7615,7618,7621,7623,7625],{"class":235,"line":733},[233,7614,159],{"class":3790},[233,7616,7617],{"class":1781},"[",[233,7619,7620],{"class":3794},"href",[233,7622,3828],{"class":5869},[233,7624,7123],{"class":3831},[233,7626,7627],{"class":1781},"] {\n",[233,7629,7630,7633,7635,7638,7640,7643],{"class":235,"line":1084},[233,7631,7632],{"class":5949}," background",[233,7634,7412],{"class":1781},[233,7636,7637],{"class":5949},"url",[233,7639,5876],{"class":1781},[233,7641,7642],{"class":5879},"home.png",[233,7644,7645],{"class":1781},");\n",[233,7647,7648,7650,7652,7654,7656],{"class":235,"line":1089},[233,7649,7438],{"class":5949},[233,7651,7412],{"class":1781},[233,7653,7428],{"class":5949},[233,7655,7431],{"class":5869},[233,7657,7418],{"class":1781},[233,7659,7660],{"class":235,"line":1094},[233,7661,429],{"class":1781},[233,7663,7664,7666,7668,7670,7672,7674,7677,7680],{"class":235,"line":1099},[233,7665,159],{"class":3790},[233,7667,7617],{"class":1781},[233,7669,7620],{"class":3794},[233,7671,3828],{"class":5869},[233,7673,7123],{"class":3831},[233,7675,7676],{"class":1781},"]",[233,7678,7679],{"class":3794},".current",[233,7681,7404],{"class":1781},[233,7683,7684,7686,7688,7690,7692,7695],{"class":235,"line":1105},[233,7685,7632],{"class":5949},[233,7687,7412],{"class":1781},[233,7689,7637],{"class":5949},[233,7691,5876],{"class":1781},[233,7693,7694],{"class":5879},"home-current.png",[233,7696,7645],{"class":1781},[233,7698,7699],{"class":235,"line":1111},[233,7700,429],{"class":1781},[233,7702,7703,7705,7707,7709,7711,7713],{"class":235,"line":1116},[233,7704,159],{"class":3790},[233,7706,7617],{"class":1781},[233,7708,7620],{"class":3794},[233,7710,3828],{"class":5869},[233,7712,7152],{"class":3831},[233,7714,7627],{"class":1781},[233,7716,7717,7719,7721,7723,7725,7728],{"class":235,"line":2144},[233,7718,7632],{"class":5949},[233,7720,7412],{"class":1781},[233,7722,7637],{"class":5949},[233,7724,5876],{"class":1781},[233,7726,7727],{"class":5879},"new.png",[233,7729,7645],{"class":1781},[233,7731,7732,7734,7736,7738,7740],{"class":235,"line":2149},[233,7733,7438],{"class":5949},[233,7735,7412],{"class":1781},[233,7737,7576],{"class":5949},[233,7739,7431],{"class":5869},[233,7741,7418],{"class":1781},[233,7743,7744],{"class":235,"line":2154},[233,7745,429],{"class":1781},[233,7747,7748,7750,7752,7754,7756,7758,7760,7762],{"class":235,"line":2160},[233,7749,159],{"class":3790},[233,7751,7617],{"class":1781},[233,7753,7620],{"class":3794},[233,7755,3828],{"class":5869},[233,7757,7152],{"class":3831},[233,7759,7676],{"class":1781},[233,7761,7679],{"class":3794},[233,7763,7404],{"class":1781},[233,7765,7766,7768,7770,7772,7774,7777],{"class":235,"line":2165},[233,7767,7632],{"class":5949},[233,7769,7412],{"class":1781},[233,7771,7637],{"class":5949},[233,7773,5876],{"class":1781},[233,7775,7776],{"class":5879},"new-current.png",[233,7778,7645],{"class":1781},[233,7780,7781],{"class":235,"line":2171},[233,7782,429],{"class":1781},[233,7784,7785,7787,7789,7791,7793,7795],{"class":235,"line":2176},[233,7786,159],{"class":3790},[233,7788,7617],{"class":1781},[233,7790,7620],{"class":3794},[233,7792,3828],{"class":5869},[233,7794,7180],{"class":3831},[233,7796,7627],{"class":1781},[233,7798,7799,7801,7803,7805,7807,7810],{"class":235,"line":2181},[233,7800,7632],{"class":5949},[233,7802,7412],{"class":1781},[233,7804,7637],{"class":5949},[233,7806,5876],{"class":1781},[233,7808,7809],{"class":5879},"favorites.png",[233,7811,7645],{"class":1781},[233,7813,7814,7816,7818,7821,7823],{"class":235,"line":2186},[233,7815,7438],{"class":5949},[233,7817,7412],{"class":1781},[233,7819,7820],{"class":5949},"160",[233,7822,7431],{"class":5869},[233,7824,7418],{"class":1781},[233,7826,7827],{"class":235,"line":2191},[233,7828,429],{"class":1781},[233,7830,7831,7833,7835,7837,7839,7841,7843,7845],{"class":235,"line":2196},[233,7832,159],{"class":3790},[233,7834,7617],{"class":1781},[233,7836,7620],{"class":3794},[233,7838,3828],{"class":5869},[233,7840,7180],{"class":3831},[233,7842,7676],{"class":1781},[233,7844,7679],{"class":3794},[233,7846,7404],{"class":1781},[233,7848,7849,7851,7853,7855,7857,7860],{"class":235,"line":2202},[233,7850,7632],{"class":5949},[233,7852,7412],{"class":1781},[233,7854,7637],{"class":5949},[233,7856,5876],{"class":1781},[233,7858,7859],{"class":5879},"favorites-current.png",[233,7861,7645],{"class":1781},[233,7863,7865],{"class":235,"line":7864},42,[233,7866,429],{"class":1781},[233,7868,7870,7872,7874,7876,7878,7880],{"class":235,"line":7869},43,[233,7871,159],{"class":3790},[233,7873,7617],{"class":1781},[233,7875,7620],{"class":3794},[233,7877,3828],{"class":5869},[233,7879,7208],{"class":3831},[233,7881,7627],{"class":1781},[233,7883,7885,7887,7889,7891,7893,7896],{"class":235,"line":7884},44,[233,7886,7632],{"class":5949},[233,7888,7412],{"class":1781},[233,7890,7637],{"class":5949},[233,7892,5876],{"class":1781},[233,7894,7895],{"class":5879},"more.png",[233,7897,7645],{"class":1781},[233,7899,7901,7903,7905,7908,7910],{"class":235,"line":7900},45,[233,7902,7438],{"class":5949},[233,7904,7412],{"class":1781},[233,7906,7907],{"class":5949},"240",[233,7909,7431],{"class":5869},[233,7911,7418],{"class":1781},[233,7913,7915],{"class":235,"line":7914},46,[233,7916,429],{"class":1781},[233,7918,7920,7922,7924,7926,7928,7930],{"class":235,"line":7919},47,[233,7921,159],{"class":3790},[233,7923,7617],{"class":1781},[233,7925,7620],{"class":3794},[233,7927,3828],{"class":5869},[233,7929,7236],{"class":3831},[233,7931,7627],{"class":1781},[233,7933,7935,7937,7939,7941,7943,7946],{"class":235,"line":7934},48,[233,7936,7632],{"class":5949},[233,7938,7412],{"class":1781},[233,7940,7637],{"class":5949},[233,7942,5876],{"class":1781},[233,7944,7945],{"class":5879},"info.png",[233,7947,7645],{"class":1781},[233,7949,7951,7953,7955,7957],{"class":235,"line":7950},49,[233,7952,7409],{"class":5949},[233,7954,7412],{"class":1781},[233,7956,7415],{"class":5949},[233,7958,7418],{"class":1781},[233,7960,7962,7964,7966,7968,7970],{"class":235,"line":7961},50,[233,7963,7451],{"class":5949},[233,7965,7412],{"class":1781},[233,7967,7563],{"class":5949},[233,7969,7431],{"class":5869},[233,7971,7418],{"class":1781},[233,7973,7975,7977,7979,7981,7983],{"class":235,"line":7974},51,[233,7976,7558],{"class":5949},[233,7978,7412],{"class":1781},[233,7980,7563],{"class":5949},[233,7982,7431],{"class":5869},[233,7984,7418],{"class":1781},[233,7986,7988,7991,7993,7996,7998],{"class":235,"line":7987},52,[233,7989,7990],{"class":5949}," bottom",[233,7992,7412],{"class":1781},[233,7994,7995],{"class":5949},"16",[233,7997,7431],{"class":5869},[233,7999,7418],{"class":1781},[233,8001,8003,8006,8008,8010,8012],{"class":235,"line":8002},53,[233,8004,8005],{"class":5949}," right",[233,8007,7412],{"class":1781},[233,8009,7995],{"class":5949},[233,8011,7431],{"class":5869},[233,8013,7418],{"class":1781},[233,8015,8017],{"class":235,"line":8016},54,[233,8018,429],{"class":1781},[233,8020,8022],{"class":235,"line":8021},55,[233,8023,8024],{"class":7395},"/* finally position the views themselves */\n",[233,8026,8028,8030,8033],{"class":235,"line":8027},56,[233,8029,3964],{"class":3790},[233,8031,8032],{"class":3794},".view",[233,8034,7404],{"class":1781},[233,8036,8038,8040,8042,8044],{"class":235,"line":8037},57,[233,8039,7409],{"class":5949},[233,8041,7412],{"class":1781},[233,8043,7516],{"class":5949},[233,8045,7418],{"class":1781},[233,8047,8049,8051,8053,8055,8057],{"class":235,"line":8048},58,[233,8050,7423],{"class":5949},[233,8052,7412],{"class":1781},[233,8054,7563],{"class":5949},[233,8056,7431],{"class":5869},[233,8058,7418],{"class":1781},[233,8060,8062,8064,8066,8068,8070],{"class":235,"line":8061},59,[233,8063,7438],{"class":5949},[233,8065,7412],{"class":1781},[233,8067,7428],{"class":5949},[233,8069,7431],{"class":5869},[233,8071,7418],{"class":1781},[233,8073,8075,8077,8079,8081,8083],{"class":235,"line":8074},60,[233,8076,7451],{"class":5949},[233,8078,7412],{"class":1781},[233,8080,7456],{"class":5949},[233,8082,7431],{"class":5869},[233,8084,7418],{"class":1781},[233,8086,8088,8090,8092,8095,8097],{"class":235,"line":8087},61,[233,8089,7558],{"class":5949},[233,8091,7412],{"class":1781},[233,8093,8094],{"class":5949},"396",[233,8096,7431],{"class":5869},[233,8098,7418],{"class":1781},[233,8100,8102],{"class":235,"line":8101},62,[233,8103,429],{"class":1781},[19,8105,8106],{},"This can act as a simple framework for an application with multiple views. Now we still need to make our navigation\ncontroller to switch between the different views. jQuery to the rescue — a small snippet initializes our view hierarchy\nand provides switching capabilities:",[225,8108,8110],{"className":5860,"code":8109,"language":5862,"meta":110,"style":110},"$(document).ready(function () {\n var navitems = $(\"#navigation li a\");\n navitems.click(function () {\n navitems.removeClass(\"current\");\n var ref = $(this).addClass(\"current\").attr(\"href\");\n /* hide the other views, show the one navigated to\n and trigger a custom event */\n $(\"div.view\").hide();\n $(ref).show().trigger(\"becameActive\");\n });\n $(\"div.view\").hide();\n $(\"div.view.current\").show().trigger(\"becameActive\");\n});\n",[176,8111,8112,8130,8150,8164,8178,8216,8221,8226,8243,8265,8270,8284,8307],{"__ignoreMap":110},[233,8113,8114,8117,8120,8123,8125,8127],{"class":235,"line":236},[233,8115,8116],{"class":3794},"$",[233,8118,8119],{"class":1781},"(document).",[233,8121,8122],{"class":3794},"ready",[233,8124,5876],{"class":1781},[233,8126,5870],{"class":5869},[233,8128,8129],{"class":1781}," () {\n",[233,8131,8132,8135,8138,8140,8143,8145,8148],{"class":235,"line":111},[233,8133,8134],{"class":5869}," var",[233,8136,8137],{"class":1781}," navitems ",[233,8139,3828],{"class":5869},[233,8141,8142],{"class":3794}," $",[233,8144,5876],{"class":1781},[233,8146,8147],{"class":3831},"\"#navigation li a\"",[233,8149,7645],{"class":1781},[233,8151,8152,8155,8158,8160,8162],{"class":235,"line":402},[233,8153,8154],{"class":1781}," navitems.",[233,8156,8157],{"class":3794},"click",[233,8159,5876],{"class":1781},[233,8161,5870],{"class":5869},[233,8163,8129],{"class":1781},[233,8165,8166,8169,8172,8174,8176],{"class":235,"line":408},[233,8167,8168],{"class":1781}," navitems.",[233,8170,8171],{"class":3794},"removeClass",[233,8173,5876],{"class":1781},[233,8175,7115],{"class":3831},[233,8177,7645],{"class":1781},[233,8179,8180,8183,8186,8188,8190,8192,8195,8197,8200,8202,8204,8206,8209,8211,8214],{"class":235,"line":414},[233,8181,8182],{"class":5869}," var",[233,8184,8185],{"class":1781}," ref ",[233,8187,3828],{"class":5869},[233,8189,8142],{"class":3794},[233,8191,5876],{"class":1781},[233,8193,8194],{"class":5949},"this",[233,8196,2355],{"class":1781},[233,8198,8199],{"class":3794},"addClass",[233,8201,5876],{"class":1781},[233,8203,7115],{"class":3831},[233,8205,2355],{"class":1781},[233,8207,8208],{"class":3794},"attr",[233,8210,5876],{"class":1781},[233,8212,8213],{"class":3831},"\"href\"",[233,8215,7645],{"class":1781},[233,8217,8218],{"class":235,"line":420},[233,8219,8220],{"class":7395}," /* hide the other views, show the one navigated to\n",[233,8222,8223],{"class":235,"line":426},[233,8224,8225],{"class":7395}," and trigger a custom event */\n",[233,8227,8228,8231,8233,8236,8238,8240],{"class":235,"line":657},[233,8229,8230],{"class":3794}," $",[233,8232,5876],{"class":1781},[233,8234,8235],{"class":3831},"\"div.view\"",[233,8237,2355],{"class":1781},[233,8239,5918],{"class":3794},[233,8241,8242],{"class":1781},"();\n",[233,8244,8245,8247,8250,8252,8255,8258,8260,8263],{"class":235,"line":662},[233,8246,8230],{"class":3794},[233,8248,8249],{"class":1781},"(ref).",[233,8251,5973],{"class":3794},[233,8253,8254],{"class":1781},"().",[233,8256,8257],{"class":3794},"trigger",[233,8259,5876],{"class":1781},[233,8261,8262],{"class":3831},"\"becameActive\"",[233,8264,7645],{"class":1781},[233,8266,8267],{"class":235,"line":667},[233,8268,8269],{"class":1781}," });\n",[233,8271,8272,8274,8276,8278,8280,8282],{"class":235,"line":673},[233,8273,5904],{"class":3794},[233,8275,5876],{"class":1781},[233,8277,8235],{"class":3831},[233,8279,2355],{"class":1781},[233,8281,5918],{"class":3794},[233,8283,8242],{"class":1781},[233,8285,8286,8288,8290,8293,8295,8297,8299,8301,8303,8305],{"class":235,"line":679},[233,8287,5904],{"class":3794},[233,8289,5876],{"class":1781},[233,8291,8292],{"class":3831},"\"div.view.current\"",[233,8294,2355],{"class":1781},[233,8296,5973],{"class":3794},[233,8298,8254],{"class":1781},[233,8300,8257],{"class":3794},[233,8302,5876],{"class":1781},[233,8304,8262],{"class":3831},[233,8306,7645],{"class":1781},[233,8308,8309],{"class":235,"line":684},[233,8310,8311],{"class":1781},"});\n",[19,8313,8314,8315,8318,8319,8322],{},"With custom events like ",[176,8316,8317],{},"becameActive"," it is easy to create new callbacks that are invoked when state changes occur — in\nthis example when switching the active view through our navigation controller. For brevity we will omit a elaborate\nexample of controller code, but if for example you want code to run when switching to the ",[176,8320,8321],{},"#new"," view, you could simply\nwrite:",[225,8324,8326],{"className":5860,"code":8325,"language":5862,"meta":110,"style":110},"$(\"#new\").bind(\"becameActive\", function (event) {\n $(event.target).doSomething();\n});\n",[176,8327,8328,8357,8369],{"__ignoreMap":110},[233,8329,8330,8332,8334,8336,8338,8341,8343,8345,8347,8349,8352,8355],{"class":235,"line":236},[233,8331,8116],{"class":3794},[233,8333,5876],{"class":1781},[233,8335,7152],{"class":3831},[233,8337,2355],{"class":1781},[233,8339,8340],{"class":3794},"bind",[233,8342,5876],{"class":1781},[233,8344,8262],{"class":3831},[233,8346,5883],{"class":1781},[233,8348,5870],{"class":5869},[233,8350,8351],{"class":1781}," (",[233,8353,8354],{"class":5879},"event",[233,8356,5899],{"class":1781},[233,8358,8359,8361,8364,8367],{"class":235,"line":111},[233,8360,5904],{"class":3794},[233,8362,8363],{"class":1781},"(event.target).",[233,8365,8366],{"class":3794},"doSomething",[233,8368,8242],{"class":1781},[233,8370,8371],{"class":235,"line":402},[233,8372,8311],{"class":1781},[19,8374,8375],{},"So what is still missing now? We still haven’t provided a data model to operate on. While data provided via web services\nis not a big deal by the use of JSON-APIs, we would certainly want to have a local data storage too. For quite some\ntime already, web engines provide a local SQLite-based storage system. On application initialization we can simply\nsetup some tables to hold our data and send statements from anywhere in the application later on:",[225,8377,8379],{"className":5860,"code":8378,"language":5862,"meta":110,"style":110},"// open a database, providing it's name, version,\n// maximum size and display name\nvar database = openDatabase(\"Example\", \"1.0\", 1048576, \"Example Database\");\ndatabase.transaction(function (transaction) {\n transaction.executeSQL(\n \"CREATE TABLE IF NOT EXISTS data\" +\n \"(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\" +\n \"... more declarations ...);\",\n );\n});\n// other statements are issued the same way\ndatabase.transaction(function (transaction) {\n transaction.executeSQL(\"SELECT * FROM data;\", function (transaction, result) {\n // do something with 'result'\n });\n});\n",[176,8380,8381,8386,8391,8426,8444,8454,8462,8469,8476,8480,8484,8489,8505,8531,8536,8540],{"__ignoreMap":110},[233,8382,8383],{"class":235,"line":236},[233,8384,8385],{"class":7395},"// open a database, providing it's name, version,\n",[233,8387,8388],{"class":235,"line":111},[233,8389,8390],{"class":7395},"// maximum size and display name\n",[233,8392,8393,8396,8399,8401,8404,8406,8409,8411,8414,8416,8419,8421,8424],{"class":235,"line":402},[233,8394,8395],{"class":5869},"var",[233,8397,8398],{"class":1781}," database ",[233,8400,3828],{"class":5869},[233,8402,8403],{"class":3794}," openDatabase",[233,8405,5876],{"class":1781},[233,8407,8408],{"class":3831},"\"Example\"",[233,8410,5883],{"class":1781},[233,8412,8413],{"class":3831},"\"1.0\"",[233,8415,5883],{"class":1781},[233,8417,8418],{"class":5949},"1048576",[233,8420,5883],{"class":1781},[233,8422,8423],{"class":3831},"\"Example Database\"",[233,8425,7645],{"class":1781},[233,8427,8428,8431,8434,8436,8438,8440,8442],{"class":235,"line":408},[233,8429,8430],{"class":1781},"database.",[233,8432,8433],{"class":3794},"transaction",[233,8435,5876],{"class":1781},[233,8437,5870],{"class":5869},[233,8439,8351],{"class":1781},[233,8441,8433],{"class":5879},[233,8443,5899],{"class":1781},[233,8445,8446,8449,8452],{"class":235,"line":414},[233,8447,8448],{"class":1781}," transaction.",[233,8450,8451],{"class":3794},"executeSQL",[233,8453,5921],{"class":1781},[233,8455,8456,8459],{"class":235,"line":420},[233,8457,8458],{"class":3831}," \"CREATE TABLE IF NOT EXISTS data\"",[233,8460,8461],{"class":5869}," +\n",[233,8463,8464,8467],{"class":235,"line":426},[233,8465,8466],{"class":3831}," \"(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\"",[233,8468,8461],{"class":5869},[233,8470,8471,8474],{"class":235,"line":657},[233,8472,8473],{"class":3831}," \"... more declarations ...);\"",[233,8475,5929],{"class":1781},[233,8477,8478],{"class":235,"line":662},[233,8479,5957],{"class":1781},[233,8481,8482],{"class":235,"line":667},[233,8483,8311],{"class":1781},[233,8485,8486],{"class":235,"line":673},[233,8487,8488],{"class":7395},"// other statements are issued the same way\n",[233,8490,8491,8493,8495,8497,8499,8501,8503],{"class":235,"line":679},[233,8492,8430],{"class":1781},[233,8494,8433],{"class":3794},[233,8496,5876],{"class":1781},[233,8498,5870],{"class":5869},[233,8500,8351],{"class":1781},[233,8502,8433],{"class":5879},[233,8504,5899],{"class":1781},[233,8506,8507,8509,8511,8513,8516,8518,8520,8522,8524,8526,8529],{"class":235,"line":684},[233,8508,8448],{"class":1781},[233,8510,8451],{"class":3794},[233,8512,5876],{"class":1781},[233,8514,8515],{"class":3831},"\"SELECT * FROM data;\"",[233,8517,5883],{"class":1781},[233,8519,5870],{"class":5869},[233,8521,8351],{"class":1781},[233,8523,8433],{"class":5879},[233,8525,5883],{"class":1781},[233,8527,8528],{"class":5879},"result",[233,8530,5899],{"class":1781},[233,8532,8533],{"class":235,"line":689},[233,8534,8535],{"class":7395}," // do something with 'result'\n",[233,8537,8538],{"class":235,"line":695},[233,8539,8269],{"class":1781},[233,8541,8542],{"class":235,"line":701},[233,8543,8311],{"class":1781},[23,8545,8547],{"id":8546},"putting-it-all-together","Putting it all together",[19,8549,8550,8551,8554,8555],{},"A question remains: ",[181,8552,8553],{},"when this is the basic skeleton of an application, how do i put this on an actual\ndevice?"," ",[33,8556],{"alt":110,"src":8557},"https://media.synyx.de/uploads//2010/08/jquery-html-css-example-e1281692547570.png",[19,8559,8560],{},"Example in Simulator",[19,8562,8563,8564,8567],{},"While this could be simply served by a web server to a mobile device, the main reason for developing applications\ninstead of websites is the ability to expose them through an application store (e.g. Apples AppStore or the Android\nMarket). We won’t go into much detail here now — this is something to be discussed in the following blog posts in this\nseries — a simple answer is provided by a small framework\nnamed ",[159,8565,6820],{"href":5753,"rel":8566},[163],". PhoneGap provides the developer with\na cross-platform deployment environment, which makes use of the web engines on the different mobile devices. It\nbasically is a fullscreen web browser view, without any UI elements and other decorations but with added support for\naccessing hardware features of the actual device. Next to the already mentioned features, which are part of the modern\nweb engines, it gives access to features like cameras, accelerometer or access to the native phonebook of your mobile\nphone. You simply drop all your HTML, CSS & JavaScript into a PhoneGap application package which is built for your\nparticular device and are ready to deploy through any application store or similar channel you like.",[23,8569,8571],{"id":8570},"whats-next","What’s next?",[19,8573,8574],{},"So developing mobile applications with HTML, CSS & JavaScript is actually easy, isn’t it? Well, with the basic ideas\noutlined above it is — until it isn’t anymore. In the parts of this series which are still to come, we will answer some\nquestions, that might be crucial to your development cycle:",[85,8576,8577,8580,8583,8586],{},[88,8578,8579],{},"What problems might arise with JavaScript as a development language? Is there enough tool support for ease of\ndevelopment?",[88,8581,8582],{},"What do i have to program from scratch and which frameworks are available, that erase my need of boilerplate code?",[88,8584,8585],{},"What about the performance, for example when sophisticated animations are desired?",[88,8587,8588],{},"Is my application really cross-platform, when using these technologies, and does it behave exactly the same on every\ndevice?",[19,8590,8591],{},"Stay tuned for more.",[560,8593,8594],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":110,"searchDepth":111,"depth":111,"links":8596},[8597,8598,8599,8600],{"id":7061,"depth":111,"text":7062},{"id":7068,"depth":111,"text":7069},{"id":8546,"depth":111,"text":8547},{"id":8570,"depth":111,"text":8571},[120,2314],"2010-08-13T12:17:29","Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\\ndeployment target for mobile applications. That’s the theory at least. In this series of articles we will provide an\\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\\nseveral mobile platforms at once.","https://synyx.de/blog/on-cross-device-mobile-development-part-1/",{},"/blog/on-cross-device-mobile-development-part-1",{"title":7045,"description":8608},"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team\nof developers might be small or knowledge about the different Software Development Kits (SDKs) involved is scarce. But\nhey, perhaps you know how to write HTML & CSS and are also experienced with JavaScript. With the advent of the Apple\niPhone and briefly afterwards the Android line of phones, mobile devices started to support a lot of modern HTML & CSS\nfeatures. The progressing “applification” of the WWW further boosted the development of fast JavaScript engines. And\nwith the recent addition of multitouch, geolocation and fast CSS3 animation support, the mobile browser has become a new\ndeployment target for mobile applications. That’s the theory at least. In this series of articles we will provide an\noverview on the technologies involved, available frameworks and the approaches taken to bring your application to\nseveral mobile platforms at once.","blog/on-cross-device-mobile-development-part-1",[133,7040,1774,6555,5862],"Once in a while a demand for fast development of a mobile application for several platforms at once comes up. Your team of developers might be small or knowledge about…","yJGRhWop8a6UwlupSTSgkogMBabnFF7yVveZDcNWM8Y",{"id":8614,"title":8615,"author":8616,"body":8617,"category":8628,"date":8629,"description":110,"extension":124,"link":8630,"meta":8631,"navigation":127,"path":8632,"seo":8633,"slug":8621,"stem":8634,"tags":8635,"teaser":8636,"__hash__":8637},"blog/blog/human-maier-we-are-in-beta.md","Human Maier! We are in Beta!",[6531],{"type":12,"value":8618,"toc":8626},[8619,8622],[15,8620,8615],{"id":8621},"human-maier-we-are-in-beta",[19,8623,8624],{},[33,8625],{"alt":6541,"src":6660},{"title":110,"searchDepth":111,"depth":111,"links":8627},[],[120,6546],"2010-07-30T14:30:44","https://synyx.de/blog/human-maier-we-are-in-beta/",{},"/blog/human-maier-we-are-in-beta",{"title":8615,"description":110},"blog/human-maier-we-are-in-beta",[133,5156,6555],"Our little pet project “I think I spider” is almost ready to be thrown out into the wild! That means we are running a closed beta for iOS devices for…","Tv3D01DCynO0dlp6K4oLCcEF6qiHcJkQCehI25ZSfc0",{"id":8639,"title":8640,"author":8641,"body":8642,"category":8652,"date":8653,"description":110,"extension":124,"link":8654,"meta":8655,"navigation":127,"path":8656,"seo":8657,"slug":6757,"stem":8658,"tags":8659,"teaser":8660,"__hash__":8661},"blog/blog/i-think-i-spider.md","I think I spider",[6531],{"type":12,"value":8643,"toc":8650},[8644,8646],[15,8645,8640],{"id":6757},[19,8647,8648],{},[33,8649],{"alt":6541,"src":6660},{"title":110,"searchDepth":111,"depth":111,"links":8651},[],[120,6546],"2010-07-26T06:56:04","https://synyx.de/blog/i-think-i-spider/",{},"/blog/i-think-i-spider",{"title":8640,"description":110},"blog/i-think-i-spider",[133,6555],"We are working on a fair number of Apps here at our mobile team and today we are proud to announce one of them: I think I spider! The “I…","Q4uy3XPVMpWwUGobhts0mEDqawiYKLMPBPP9qYTF_us",{"id":8663,"title":6561,"author":8664,"body":8665,"category":8747,"date":8748,"description":8749,"extension":124,"link":8750,"meta":8751,"navigation":127,"path":8752,"seo":8753,"slug":8755,"stem":8756,"tags":8757,"teaser":8758,"__hash__":8759},"blog/blog/mobile-solutions-summary-4.md",[6531],{"type":12,"value":8666,"toc":8745},[8667,8669,8683,8703,8711,8726,8731],[15,8668,6561],{"id":6567},[19,8670,8671,8672,8676,8677,8682],{},"There’s a lot going on over at the ",[159,8673,6581],{"href":8674,"rel":8675},"http://mobile.synyx.de",[163],", so in case you are not subscribed to\nour feed, which I hope you are, you can grab it ",[159,8678,8681],{"href":8679,"rel":8680},"http://mobile.synyx.de/feed/",[163],"here",". In order to convince you to hook\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.",[19,8684,8685,8690,8691,8696,8697,8702],{},[159,8686,8689],{"href":8687,"rel":8688},"http://mobile.synyx.de/authors/?uid=3",[163],"Tobias’","\npost ",[159,8692,8695],{"href":8693,"rel":8694},"http://mobile.synyx.de/2010/06/android-and-self-signed-ssl-certificates/",[163],"“Android and self-signed ssl certificates”","\ngained a lot of attraction over the past couple of weeks. He basically brings you up to speed on how to tweak Android’s\nversion of ",[159,8698,8701],{"href":8699,"rel":8700},"http://hc.apache.org/httpcomponents-client/httpclient/",[163],"Apache Commons Http"," to work with your own\ncertificate:",[6591,8704,8705,8708],{},[19,8706,8707],{},"Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and\nlet android accept them.",[19,8709,8710],{},"But fortunately, there’s a workaround that uses an own SSLSocketFactory and an own TrustManager. With this, only your\nadded site is beeing able to be called, so theres no security issue.",[19,8712,8713,8714,8719,8720,8725],{},"Another blog post I’d like to point out is by yours truly,\non ",[159,8715,8718],{"href":8716,"rel":8717},"http://mobile.synyx.de/2010/06/ui-prototyping-iphone-apps/",[163],"“UI Prototyping iPhone Apps”",". It covers a very simple\nconcept and provides you with a ",[159,8721,8724],{"href":8722,"rel":8723},"http://github.com/dlinsin/district9/tree/master/UIPrototyping/",[163],"framework"," to employ it\nin your App development:",[6591,8727,8728],{},[19,8729,8730],{},"I watched a whole bunch of sessions from 2009. Among others a session on “Prototyping iPhone User Interfaces” by Bret\nVictor… In his session, Bret shows how to prototype an interface only by using with screenshots! … It inspired me to use\nhis framework and the whole process for our own development… Unfortunately, the code for the session isn’t available …\nAfter some digging, I found Michael Fey’s blog, who was able to successfully reverse engineer the missing parts, which\nwere not shown in the presentation.",[19,8732,8733,8734,8738,8739,8744],{},"I hope by this time you have already subscribed to our ",[159,8735,8737],{"href":8674,"rel":8736},[163],"mobile blog"," and discovered a couple\nof ",[159,8740,8743],{"href":8741,"rel":8742},"http://mobile.synyx.de/2010/07/split-nsstring-by-characters/",[163],"interesting posts",", that our team put together over\npast couple of months.",{"title":110,"searchDepth":111,"depth":111,"links":8746},[],[118],"2010-07-16T07:28:03","There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to\\nour feed, which I hope you are, you can grab it here. In order to convince you to hook\\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.","https://synyx.de/blog/mobile-solutions-summary-4/",{},"/blog/mobile-solutions-summary-4",{"title":6561,"description":8754},"There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to\nour feed, which I hope you are, you can grab it here. In order to convince you to hook\nyour favorite reader up to our mobile blog, I’ll highlight a couple of blog posts for you.","mobile-solutions-summary-4","blog/mobile-solutions-summary-4",[133,5156,6555,578],"There’s a lot going on over at the mobile solutions blog, so in case you are not subscribed to our feed, which I hope you are, you can grab it…","6e3hlP9lM0lmBm1BXb-OBwSJM2DFtSNp5TIpHdJ1OXE",{"id":8761,"title":8762,"author":8763,"body":8764,"category":9905,"date":9906,"description":9907,"extension":124,"link":9908,"meta":9909,"navigation":127,"path":9910,"seo":9911,"slug":8768,"stem":9912,"tags":9913,"teaser":9919,"__hash__":9920},"blog/blog/android-and-self-signed-ssl-certificates.md","Android and self-signed ssl certificates",[586],{"type":12,"value":8765,"toc":9903},[8766,8769,8772,8774,8777,9354,9357,9672,9681,9684,9713,9855,9898,9901],[15,8767,8762],{"id":8768},"android-and-self-signed-ssl-certificates",[19,8770,8771],{},"Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and let\nandroid accept them.",[19,8773,8710],{},[19,8775,8776],{},"First you have to create the SSLFactory:",[225,8778,8780],{"className":227,"code":8779,"language":229,"meta":110,"style":110},"\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.InetSocketAddress;\nimport java.net.Socket;\nimport java.net.UnknownHostException;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLSocket;\nimport javax.net.ssl.TrustManager;\nimport org.apache.http.conn.ConnectTimeoutException;\nimport org.apache.http.conn.scheme.LayeredSocketFactory;\nimport org.apache.http.conn.scheme.SocketFactory;\nimport org.apache.http.params.HttpConnectionParams;\nimport org.apache.http.params.HttpParams;\n/**\n * This socket factory will create ssl socket that accepts self signed certificate\n *\n * @author olamy\n * @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse $\n * @since 1.2.3\n */\npublic class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory {\n private SSLContext sslcontext = null;\n private static SSLContext createEasySSLContext() throws IOException {\n try {\n SSLContext context = SSLContext.getInstance(\"TLS\");\n context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null);\n return context;\n } catch (Exception e) {\n throw new IOException(e.getMessage());\n }\n }\n private SSLContext getSSLContext() throws IOException {\n if (this.sslcontext == null) {\n this.sslcontext = createEasySSLContext();\n }\n return this.sslcontext;\n }\n /**\n * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int,\n * java.net.InetAddress, int, org.apache.http.params.HttpParams)\n */\n public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort,\n HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {\n int connTimeout = HttpConnectionParams.getConnectionTimeout(params);\n int soTimeout = HttpConnectionParams.getSoTimeout(params);\n InetSocketAddress remoteAddress = new InetSocketAddress(host, port);\n SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());\n if ((localAddress != null) || (localPort > 0)) {\n // we need to bind explicitly\n if (localPort \u003C 0) {\n localPort = 0; // indicates \"any\"\n }\n InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);\n sslsock.bind(isa);\n }\n sslsock.connect(remoteAddress, connTimeout);\n sslsock.setSoTimeout(soTimeout);\n return sslsock;\n }\n /**\n * @see org.apache.http.conn.scheme.SocketFactory#createSocket()\n */\n public Socket createSocket() throws IOException {\n return getSSLContext().getSocketFactory().createSocket();\n }\n /**\n * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket)\n */\n public boolean isSecure(Socket socket) throws IllegalArgumentException {\n return true;\n }\n /**\n * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int,\n * boolean)\n */\n public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,\n UnknownHostException {\n return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);\n }\n // -------------------------------------------------------------------\n // javadoc in org.apache.http.conn.scheme.SocketFactory says :\n // Both Object.equals() and Object.hashCode() must be overridden\n // for the correct operation of some connection managers\n // -------------------------------------------------------------------\n public boolean equals(Object obj) {\n return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class));\n }\n public int hashCode() {\n return EasySSLSocketFactory.class.hashCode();\n }\n}\n\n",[176,8781,8782,8786,8791,8796,8801,8806,8811,8816,8821,8826,8831,8836,8840,8845,8850,8855,8860,8865,8870,8875,8880,8885,8890,8895,8900,8905,8910,8915,8920,8925,8930,8935,8940,8945,8950,8954,8959,8964,8969,8973,8978,8983,8988,8992,8997,9002,9007,9012,9017,9021,9025,9030,9035,9040,9044,9049,9053,9057,9062,9067,9072,9077,9082,9088,9094,9100,9106,9112,9118,9124,9130,9135,9141,9147,9152,9158,9164,9170,9175,9180,9186,9191,9197,9203,9208,9213,9219,9224,9230,9236,9241,9246,9252,9258,9263,9269,9275,9281,9286,9292,9298,9304,9310,9315,9321,9327,9332,9338,9344,9349],{"__ignoreMap":110},[233,8783,8784],{"class":235,"line":236},[233,8785,894],{"emptyLinePlaceholder":127},[233,8787,8788],{"class":235,"line":111},[233,8789,8790],{},"/*\n",[233,8792,8793],{"class":235,"line":402},[233,8794,8795],{}," * Licensed to the Apache Software Foundation (ASF) under one\n",[233,8797,8798],{"class":235,"line":408},[233,8799,8800],{}," * or more contributor license agreements. See the NOTICE file\n",[233,8802,8803],{"class":235,"line":414},[233,8804,8805],{}," * distributed with this work for additional information\n",[233,8807,8808],{"class":235,"line":420},[233,8809,8810],{}," * regarding copyright ownership. The ASF licenses this file\n",[233,8812,8813],{"class":235,"line":426},[233,8814,8815],{}," * to you under the Apache License, Version 2.0 (the\n",[233,8817,8818],{"class":235,"line":657},[233,8819,8820],{}," * \"License\"); you may not use this file except in compliance\n",[233,8822,8823],{"class":235,"line":662},[233,8824,8825],{}," * with the License. You may obtain a copy of the License at\n",[233,8827,8828],{"class":235,"line":667},[233,8829,8830],{}," *\n",[233,8832,8833],{"class":235,"line":673},[233,8834,8835],{}," * http://www.apache.org/licenses/LICENSE-2.0\n",[233,8837,8838],{"class":235,"line":679},[233,8839,8830],{},[233,8841,8842],{"class":235,"line":684},[233,8843,8844],{}," * Unless required by applicable law or agreed to in writing,\n",[233,8846,8847],{"class":235,"line":689},[233,8848,8849],{}," * software distributed under the License is distributed on an\n",[233,8851,8852],{"class":235,"line":695},[233,8853,8854],{}," * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n",[233,8856,8857],{"class":235,"line":701},[233,8858,8859],{}," * KIND, either express or implied. See the License for the\n",[233,8861,8862],{"class":235,"line":706},[233,8863,8864],{}," * specific language governing permissions and limitations\n",[233,8866,8867],{"class":235,"line":711},[233,8868,8869],{}," * under the License.\n",[233,8871,8872],{"class":235,"line":717},[233,8873,8874],{}," */\n",[233,8876,8877],{"class":235,"line":723},[233,8878,8879],{},"import java.io.IOException;\n",[233,8881,8882],{"class":235,"line":728},[233,8883,8884],{},"import java.net.InetAddress;\n",[233,8886,8887],{"class":235,"line":733},[233,8888,8889],{},"import java.net.InetSocketAddress;\n",[233,8891,8892],{"class":235,"line":1084},[233,8893,8894],{},"import java.net.Socket;\n",[233,8896,8897],{"class":235,"line":1089},[233,8898,8899],{},"import java.net.UnknownHostException;\n",[233,8901,8902],{"class":235,"line":1094},[233,8903,8904],{},"import javax.net.ssl.SSLContext;\n",[233,8906,8907],{"class":235,"line":1099},[233,8908,8909],{},"import javax.net.ssl.SSLSocket;\n",[233,8911,8912],{"class":235,"line":1105},[233,8913,8914],{},"import javax.net.ssl.TrustManager;\n",[233,8916,8917],{"class":235,"line":1111},[233,8918,8919],{},"import org.apache.http.conn.ConnectTimeoutException;\n",[233,8921,8922],{"class":235,"line":1116},[233,8923,8924],{},"import org.apache.http.conn.scheme.LayeredSocketFactory;\n",[233,8926,8927],{"class":235,"line":2144},[233,8928,8929],{},"import org.apache.http.conn.scheme.SocketFactory;\n",[233,8931,8932],{"class":235,"line":2149},[233,8933,8934],{},"import org.apache.http.params.HttpConnectionParams;\n",[233,8936,8937],{"class":235,"line":2154},[233,8938,8939],{},"import org.apache.http.params.HttpParams;\n",[233,8941,8942],{"class":235,"line":2160},[233,8943,8944],{},"/**\n",[233,8946,8947],{"class":235,"line":2165},[233,8948,8949],{}," * This socket factory will create ssl socket that accepts self signed certificate\n",[233,8951,8952],{"class":235,"line":2171},[233,8953,8830],{},[233,8955,8956],{"class":235,"line":2176},[233,8957,8958],{}," * @author olamy\n",[233,8960,8961],{"class":235,"line":2181},[233,8962,8963],{}," * @version $Id: EasySSLSocketFactory.java 765355 2009-04-15 20:59:07Z evenisse $\n",[233,8965,8966],{"class":235,"line":2186},[233,8967,8968],{}," * @since 1.2.3\n",[233,8970,8971],{"class":235,"line":2191},[233,8972,8874],{},[233,8974,8975],{"class":235,"line":2196},[233,8976,8977],{},"public class EasySSLSocketFactory implements SocketFactory, LayeredSocketFactory {\n",[233,8979,8980],{"class":235,"line":2202},[233,8981,8982],{}," private SSLContext sslcontext = null;\n",[233,8984,8985],{"class":235,"line":7864},[233,8986,8987],{}," private static SSLContext createEasySSLContext() throws IOException {\n",[233,8989,8990],{"class":235,"line":7869},[233,8991,4254],{},[233,8993,8994],{"class":235,"line":7884},[233,8995,8996],{}," SSLContext context = SSLContext.getInstance(\"TLS\");\n",[233,8998,8999],{"class":235,"line":7900},[233,9000,9001],{}," context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null);\n",[233,9003,9004],{"class":235,"line":7914},[233,9005,9006],{}," return context;\n",[233,9008,9009],{"class":235,"line":7919},[233,9010,9011],{}," } catch (Exception e) {\n",[233,9013,9014],{"class":235,"line":7934},[233,9015,9016],{}," throw new IOException(e.getMessage());\n",[233,9018,9019],{"class":235,"line":7950},[233,9020,417],{},[233,9022,9023],{"class":235,"line":7961},[233,9024,423],{},[233,9026,9027],{"class":235,"line":7974},[233,9028,9029],{}," private SSLContext getSSLContext() throws IOException {\n",[233,9031,9032],{"class":235,"line":7987},[233,9033,9034],{}," if (this.sslcontext == null) {\n",[233,9036,9037],{"class":235,"line":8002},[233,9038,9039],{}," this.sslcontext = createEasySSLContext();\n",[233,9041,9042],{"class":235,"line":8016},[233,9043,417],{},[233,9045,9046],{"class":235,"line":8021},[233,9047,9048],{}," return this.sslcontext;\n",[233,9050,9051],{"class":235,"line":8027},[233,9052,423],{},[233,9054,9055],{"class":235,"line":8037},[233,9056,1406],{},[233,9058,9059],{"class":235,"line":8048},[233,9060,9061],{}," * @see org.apache.http.conn.scheme.SocketFactory#connectSocket(java.net.Socket, java.lang.String, int,\n",[233,9063,9064],{"class":235,"line":8061},[233,9065,9066],{}," * java.net.InetAddress, int, org.apache.http.params.HttpParams)\n",[233,9068,9069],{"class":235,"line":8074},[233,9070,9071],{}," */\n",[233,9073,9074],{"class":235,"line":8087},[233,9075,9076],{}," public Socket connectSocket(Socket sock, String host, int port, InetAddress localAddress, int localPort,\n",[233,9078,9079],{"class":235,"line":8101},[233,9080,9081],{}," HttpParams params) throws IOException, UnknownHostException, ConnectTimeoutException {\n",[233,9083,9085],{"class":235,"line":9084},63,[233,9086,9087],{}," int connTimeout = HttpConnectionParams.getConnectionTimeout(params);\n",[233,9089,9091],{"class":235,"line":9090},64,[233,9092,9093],{}," int soTimeout = HttpConnectionParams.getSoTimeout(params);\n",[233,9095,9097],{"class":235,"line":9096},65,[233,9098,9099],{}," InetSocketAddress remoteAddress = new InetSocketAddress(host, port);\n",[233,9101,9103],{"class":235,"line":9102},66,[233,9104,9105],{}," SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());\n",[233,9107,9109],{"class":235,"line":9108},67,[233,9110,9111],{}," if ((localAddress != null) || (localPort > 0)) {\n",[233,9113,9115],{"class":235,"line":9114},68,[233,9116,9117],{}," // we need to bind explicitly\n",[233,9119,9121],{"class":235,"line":9120},69,[233,9122,9123],{}," if (localPort \u003C 0) {\n",[233,9125,9127],{"class":235,"line":9126},70,[233,9128,9129],{}," localPort = 0; // indicates \"any\"\n",[233,9131,9133],{"class":235,"line":9132},71,[233,9134,5340],{},[233,9136,9138],{"class":235,"line":9137},72,[233,9139,9140],{}," InetSocketAddress isa = new InetSocketAddress(localAddress, localPort);\n",[233,9142,9144],{"class":235,"line":9143},73,[233,9145,9146],{}," sslsock.bind(isa);\n",[233,9148,9150],{"class":235,"line":9149},74,[233,9151,417],{},[233,9153,9155],{"class":235,"line":9154},75,[233,9156,9157],{}," sslsock.connect(remoteAddress, connTimeout);\n",[233,9159,9161],{"class":235,"line":9160},76,[233,9162,9163],{}," sslsock.setSoTimeout(soTimeout);\n",[233,9165,9167],{"class":235,"line":9166},77,[233,9168,9169],{}," return sslsock;\n",[233,9171,9173],{"class":235,"line":9172},78,[233,9174,423],{},[233,9176,9178],{"class":235,"line":9177},79,[233,9179,1406],{},[233,9181,9183],{"class":235,"line":9182},80,[233,9184,9185],{}," * @see org.apache.http.conn.scheme.SocketFactory#createSocket()\n",[233,9187,9189],{"class":235,"line":9188},81,[233,9190,9071],{},[233,9192,9194],{"class":235,"line":9193},82,[233,9195,9196],{}," public Socket createSocket() throws IOException {\n",[233,9198,9200],{"class":235,"line":9199},83,[233,9201,9202],{}," return getSSLContext().getSocketFactory().createSocket();\n",[233,9204,9206],{"class":235,"line":9205},84,[233,9207,423],{},[233,9209,9211],{"class":235,"line":9210},85,[233,9212,1406],{},[233,9214,9216],{"class":235,"line":9215},86,[233,9217,9218],{}," * @see org.apache.http.conn.scheme.SocketFactory#isSecure(java.net.Socket)\n",[233,9220,9222],{"class":235,"line":9221},87,[233,9223,9071],{},[233,9225,9227],{"class":235,"line":9226},88,[233,9228,9229],{}," public boolean isSecure(Socket socket) throws IllegalArgumentException {\n",[233,9231,9233],{"class":235,"line":9232},89,[233,9234,9235],{}," return true;\n",[233,9237,9239],{"class":235,"line":9238},90,[233,9240,423],{},[233,9242,9244],{"class":235,"line":9243},91,[233,9245,1406],{},[233,9247,9249],{"class":235,"line":9248},92,[233,9250,9251],{}," * @see org.apache.http.conn.scheme.LayeredSocketFactory#createSocket(java.net.Socket, java.lang.String, int,\n",[233,9253,9255],{"class":235,"line":9254},93,[233,9256,9257],{}," * boolean)\n",[233,9259,9261],{"class":235,"line":9260},94,[233,9262,9071],{},[233,9264,9266],{"class":235,"line":9265},95,[233,9267,9268],{}," public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException,\n",[233,9270,9272],{"class":235,"line":9271},96,[233,9273,9274],{}," UnknownHostException {\n",[233,9276,9278],{"class":235,"line":9277},97,[233,9279,9280],{}," return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);\n",[233,9282,9284],{"class":235,"line":9283},98,[233,9285,423],{},[233,9287,9289],{"class":235,"line":9288},99,[233,9290,9291],{}," // -------------------------------------------------------------------\n",[233,9293,9295],{"class":235,"line":9294},100,[233,9296,9297],{}," // javadoc in org.apache.http.conn.scheme.SocketFactory says :\n",[233,9299,9301],{"class":235,"line":9300},101,[233,9302,9303],{}," // Both Object.equals() and Object.hashCode() must be overridden\n",[233,9305,9307],{"class":235,"line":9306},102,[233,9308,9309],{}," // for the correct operation of some connection managers\n",[233,9311,9313],{"class":235,"line":9312},103,[233,9314,9291],{},[233,9316,9318],{"class":235,"line":9317},104,[233,9319,9320],{}," public boolean equals(Object obj) {\n",[233,9322,9324],{"class":235,"line":9323},105,[233,9325,9326],{}," return ((obj != null) && obj.getClass().equals(EasySSLSocketFactory.class));\n",[233,9328,9330],{"class":235,"line":9329},106,[233,9331,423],{},[233,9333,9335],{"class":235,"line":9334},107,[233,9336,9337],{}," public int hashCode() {\n",[233,9339,9341],{"class":235,"line":9340},108,[233,9342,9343],{}," return EasySSLSocketFactory.class.hashCode();\n",[233,9345,9347],{"class":235,"line":9346},109,[233,9348,423],{},[233,9350,9352],{"class":235,"line":9351},110,[233,9353,429],{},[19,9355,9356],{},"And the TrustManager:",[225,9358,9360],{"className":227,"code":9359,"language":229,"meta":110,"style":110},"\n/*\n * Licensed to the Apache Software Foundation (ASF) under one\n * or more contributor license agreements. See the NOTICE file\n * distributed with this work for additional information\n * regarding copyright ownership. The ASF licenses this file\n * to you under the Apache License, Version 2.0 (the\n * \"License\"); you may not use this file except in compliance\n * with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing,\n * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied. See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\nimport java.security.KeyStore;\nimport java.security.KeyStoreException;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.cert.CertificateException;\nimport java.security.cert.X509Certificate;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.TrustManagerFactory;\nimport javax.net.ssl.X509TrustManager;\n/**\n * @author olamy\n * @version $Id: EasyX509TrustManager.java 765355 2009-04-15 20:59:07Z evenisse $\n * @since 1.2.3\n */\npublic class EasyX509TrustManager implements X509TrustManager {\n private X509TrustManager standardTrustManager = null;\n /**\n * Constructor for EasyX509TrustManager.\n */\n public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {\n super();\n TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n factory.init(keystore);\n TrustManager[] trustmanagers = factory.getTrustManagers();\n if (trustmanagers.length == 0) {\n throw new NoSuchAlgorithmException(\"no trust manager found\");\n }\n this.standardTrustManager = (X509TrustManager) trustmanagers[0];\n }\n /**\n * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)\n */\n public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n standardTrustManager.checkClientTrusted(certificates, authType);\n }\n /**\n * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)\n */\n public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n if ((certificates != null) && (certificates.length == 1)) {\n certificates[0].checkValidity();\n } else {\n standardTrustManager.checkServerTrusted(certificates, authType);\n }\n }\n /**\n * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()\n */\n public X509Certificate[] getAcceptedIssuers() {\n return this.standardTrustManager.getAcceptedIssuers();\n }\n}\n\n",[176,9361,9362,9366,9370,9374,9378,9382,9386,9390,9394,9398,9402,9406,9410,9414,9418,9422,9426,9430,9434,9438,9443,9448,9453,9458,9463,9467,9472,9477,9481,9485,9490,9494,9498,9503,9508,9512,9517,9521,9526,9531,9536,9541,9546,9551,9556,9560,9565,9569,9573,9578,9582,9587,9592,9596,9600,9605,9609,9614,9619,9624,9628,9633,9637,9641,9645,9650,9654,9659,9664,9668],{"__ignoreMap":110},[233,9363,9364],{"class":235,"line":236},[233,9365,894],{"emptyLinePlaceholder":127},[233,9367,9368],{"class":235,"line":111},[233,9369,8790],{},[233,9371,9372],{"class":235,"line":402},[233,9373,8795],{},[233,9375,9376],{"class":235,"line":408},[233,9377,8800],{},[233,9379,9380],{"class":235,"line":414},[233,9381,8805],{},[233,9383,9384],{"class":235,"line":420},[233,9385,8810],{},[233,9387,9388],{"class":235,"line":426},[233,9389,8815],{},[233,9391,9392],{"class":235,"line":657},[233,9393,8820],{},[233,9395,9396],{"class":235,"line":662},[233,9397,8825],{},[233,9399,9400],{"class":235,"line":667},[233,9401,8830],{},[233,9403,9404],{"class":235,"line":673},[233,9405,8835],{},[233,9407,9408],{"class":235,"line":679},[233,9409,8830],{},[233,9411,9412],{"class":235,"line":684},[233,9413,8844],{},[233,9415,9416],{"class":235,"line":689},[233,9417,8849],{},[233,9419,9420],{"class":235,"line":695},[233,9421,8854],{},[233,9423,9424],{"class":235,"line":701},[233,9425,8859],{},[233,9427,9428],{"class":235,"line":706},[233,9429,8864],{},[233,9431,9432],{"class":235,"line":711},[233,9433,8869],{},[233,9435,9436],{"class":235,"line":717},[233,9437,8874],{},[233,9439,9440],{"class":235,"line":723},[233,9441,9442],{},"import java.security.KeyStore;\n",[233,9444,9445],{"class":235,"line":728},[233,9446,9447],{},"import java.security.KeyStoreException;\n",[233,9449,9450],{"class":235,"line":733},[233,9451,9452],{},"import java.security.NoSuchAlgorithmException;\n",[233,9454,9455],{"class":235,"line":1084},[233,9456,9457],{},"import java.security.cert.CertificateException;\n",[233,9459,9460],{"class":235,"line":1089},[233,9461,9462],{},"import java.security.cert.X509Certificate;\n",[233,9464,9465],{"class":235,"line":1094},[233,9466,8914],{},[233,9468,9469],{"class":235,"line":1099},[233,9470,9471],{},"import javax.net.ssl.TrustManagerFactory;\n",[233,9473,9474],{"class":235,"line":1105},[233,9475,9476],{},"import javax.net.ssl.X509TrustManager;\n",[233,9478,9479],{"class":235,"line":1111},[233,9480,8944],{},[233,9482,9483],{"class":235,"line":1116},[233,9484,8958],{},[233,9486,9487],{"class":235,"line":2144},[233,9488,9489],{}," * @version $Id: EasyX509TrustManager.java 765355 2009-04-15 20:59:07Z evenisse $\n",[233,9491,9492],{"class":235,"line":2149},[233,9493,8968],{},[233,9495,9496],{"class":235,"line":2154},[233,9497,8874],{},[233,9499,9500],{"class":235,"line":2160},[233,9501,9502],{},"public class EasyX509TrustManager implements X509TrustManager {\n",[233,9504,9505],{"class":235,"line":2165},[233,9506,9507],{}," private X509TrustManager standardTrustManager = null;\n",[233,9509,9510],{"class":235,"line":2171},[233,9511,1406],{},[233,9513,9514],{"class":235,"line":2176},[233,9515,9516],{}," * Constructor for EasyX509TrustManager.\n",[233,9518,9519],{"class":235,"line":2181},[233,9520,9071],{},[233,9522,9523],{"class":235,"line":2186},[233,9524,9525],{}," public EasyX509TrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException {\n",[233,9527,9528],{"class":235,"line":2191},[233,9529,9530],{}," super();\n",[233,9532,9533],{"class":235,"line":2196},[233,9534,9535],{}," TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());\n",[233,9537,9538],{"class":235,"line":2202},[233,9539,9540],{}," factory.init(keystore);\n",[233,9542,9543],{"class":235,"line":7864},[233,9544,9545],{}," TrustManager[] trustmanagers = factory.getTrustManagers();\n",[233,9547,9548],{"class":235,"line":7869},[233,9549,9550],{}," if (trustmanagers.length == 0) {\n",[233,9552,9553],{"class":235,"line":7884},[233,9554,9555],{}," throw new NoSuchAlgorithmException(\"no trust manager found\");\n",[233,9557,9558],{"class":235,"line":7900},[233,9559,417],{},[233,9561,9562],{"class":235,"line":7914},[233,9563,9564],{}," this.standardTrustManager = (X509TrustManager) trustmanagers[0];\n",[233,9566,9567],{"class":235,"line":7919},[233,9568,423],{},[233,9570,9571],{"class":235,"line":7934},[233,9572,1406],{},[233,9574,9575],{"class":235,"line":7950},[233,9576,9577],{}," * @see javax.net.ssl.X509TrustManager#checkClientTrusted(X509Certificate[],String authType)\n",[233,9579,9580],{"class":235,"line":7961},[233,9581,9071],{},[233,9583,9584],{"class":235,"line":7974},[233,9585,9586],{}," public void checkClientTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n",[233,9588,9589],{"class":235,"line":7987},[233,9590,9591],{}," standardTrustManager.checkClientTrusted(certificates, authType);\n",[233,9593,9594],{"class":235,"line":8002},[233,9595,423],{},[233,9597,9598],{"class":235,"line":8016},[233,9599,1406],{},[233,9601,9602],{"class":235,"line":8021},[233,9603,9604],{}," * @see javax.net.ssl.X509TrustManager#checkServerTrusted(X509Certificate[],String authType)\n",[233,9606,9607],{"class":235,"line":8027},[233,9608,9071],{},[233,9610,9611],{"class":235,"line":8037},[233,9612,9613],{}," public void checkServerTrusted(X509Certificate[] certificates, String authType) throws CertificateException {\n",[233,9615,9616],{"class":235,"line":8048},[233,9617,9618],{}," if ((certificates != null) && (certificates.length == 1)) {\n",[233,9620,9621],{"class":235,"line":8061},[233,9622,9623],{}," certificates[0].checkValidity();\n",[233,9625,9626],{"class":235,"line":8074},[233,9627,1261],{},[233,9629,9630],{"class":235,"line":8087},[233,9631,9632],{}," standardTrustManager.checkServerTrusted(certificates, authType);\n",[233,9634,9635],{"class":235,"line":8101},[233,9636,417],{},[233,9638,9639],{"class":235,"line":9084},[233,9640,423],{},[233,9642,9643],{"class":235,"line":9090},[233,9644,1406],{},[233,9646,9647],{"class":235,"line":9096},[233,9648,9649],{}," * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers()\n",[233,9651,9652],{"class":235,"line":9102},[233,9653,9071],{},[233,9655,9656],{"class":235,"line":9108},[233,9657,9658],{}," public X509Certificate[] getAcceptedIssuers() {\n",[233,9660,9661],{"class":235,"line":9114},[233,9662,9663],{}," return this.standardTrustManager.getAcceptedIssuers();\n",[233,9665,9666],{"class":235,"line":9120},[233,9667,423],{},[233,9669,9670],{"class":235,"line":9126},[233,9671,429],{},[19,9673,9674,9675,9680],{},"(Both classes are\nfrom ",[159,9676,9679],{"href":9677,"rel":9678},"https://web.archive.org/web/20111230175916/http://exchangeit.googlecode.com:80/svn-history/r23/trunk/src/com/byarger/exchangeit/",[163],"exchangeit","\nwith a small change on the EasySSLSocketFactory to work on android 2.2)",[19,9682,9683],{},"Now we have to do some other preparations and create a HttpClient that we can use to establish the connection:",[225,9685,9687],{"className":227,"code":9686,"language":229,"meta":110,"style":110},"\n//members\nprivate ClientConnectionManager clientConnectionManager;\nprivate HttpContext context;\nprivate HttpParams params;\n\n",[176,9688,9689,9693,9698,9703,9708],{"__ignoreMap":110},[233,9690,9691],{"class":235,"line":236},[233,9692,894],{"emptyLinePlaceholder":127},[233,9694,9695],{"class":235,"line":111},[233,9696,9697],{},"//members\n",[233,9699,9700],{"class":235,"line":402},[233,9701,9702],{},"private ClientConnectionManager clientConnectionManager;\n",[233,9704,9705],{"class":235,"line":408},[233,9706,9707],{},"private HttpContext context;\n",[233,9709,9710],{"class":235,"line":414},[233,9711,9712],{},"private HttpParams params;\n",[225,9714,9716],{"className":227,"code":9715,"language":229,"meta":110,"style":110},"\n//constructor\npublic WebService(){\n setup();\n}\n//prepare for the https connection\n//call this in the constructor of the class that does the connection if\n//it's used multiple times\nprivate void setup(){\nSchemeRegistry schemeRegistry = new SchemeRegistry();\n // http scheme\n schemeRegistry.register(new Scheme(\"http\", PlainSocketFactory.getSocketFactory(), 80));\n // https scheme\n schemeRegistry.register(new Scheme(\"https\", new EasySSLSocketFactory(), 443));\n params = new BasicHttpParams();\n params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);\n params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));\n params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);\n HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);\n HttpProtocolParams.setContentCharset(params, \"utf8\");\n CredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n //set the user credentials for our site \"example.com\"\n credentialsProvider.setCredentials(new AuthScope(\"example.com\", AuthScope.ANY_PORT),\n new UsernamePasswordCredentials(\"UserNameHere\", \"UserPasswordHere\"));\n clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry);\n context = new BasicHttpContext();\n context.setAttribute(\"http.auth.credentials-provider\", credentialsProvider);\n}\n\n",[176,9717,9718,9722,9727,9732,9737,9741,9746,9751,9756,9761,9766,9771,9776,9781,9786,9791,9796,9801,9806,9811,9816,9821,9826,9831,9836,9841,9846,9851],{"__ignoreMap":110},[233,9719,9720],{"class":235,"line":236},[233,9721,894],{"emptyLinePlaceholder":127},[233,9723,9724],{"class":235,"line":111},[233,9725,9726],{},"//constructor\n",[233,9728,9729],{"class":235,"line":402},[233,9730,9731],{},"public WebService(){\n",[233,9733,9734],{"class":235,"line":408},[233,9735,9736],{}," setup();\n",[233,9738,9739],{"class":235,"line":414},[233,9740,429],{},[233,9742,9743],{"class":235,"line":420},[233,9744,9745],{},"//prepare for the https connection\n",[233,9747,9748],{"class":235,"line":426},[233,9749,9750],{},"//call this in the constructor of the class that does the connection if\n",[233,9752,9753],{"class":235,"line":657},[233,9754,9755],{},"//it's used multiple times\n",[233,9757,9758],{"class":235,"line":662},[233,9759,9760],{},"private void setup(){\n",[233,9762,9763],{"class":235,"line":667},[233,9764,9765],{},"SchemeRegistry schemeRegistry = new SchemeRegistry();\n",[233,9767,9768],{"class":235,"line":673},[233,9769,9770],{}," // http scheme\n",[233,9772,9773],{"class":235,"line":679},[233,9774,9775],{}," schemeRegistry.register(new Scheme(\"http\", PlainSocketFactory.getSocketFactory(), 80));\n",[233,9777,9778],{"class":235,"line":684},[233,9779,9780],{}," // https scheme\n",[233,9782,9783],{"class":235,"line":689},[233,9784,9785],{}," schemeRegistry.register(new Scheme(\"https\", new EasySSLSocketFactory(), 443));\n",[233,9787,9788],{"class":235,"line":695},[233,9789,9790],{}," params = new BasicHttpParams();\n",[233,9792,9793],{"class":235,"line":701},[233,9794,9795],{}," params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 1);\n",[233,9797,9798],{"class":235,"line":706},[233,9799,9800],{}," params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(1));\n",[233,9802,9803],{"class":235,"line":711},[233,9804,9805],{}," params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);\n",[233,9807,9808],{"class":235,"line":717},[233,9809,9810],{}," HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);\n",[233,9812,9813],{"class":235,"line":723},[233,9814,9815],{}," HttpProtocolParams.setContentCharset(params, \"utf8\");\n",[233,9817,9818],{"class":235,"line":728},[233,9819,9820],{}," CredentialsProvider credentialsProvider = new BasicCredentialsProvider();\n",[233,9822,9823],{"class":235,"line":733},[233,9824,9825],{}," //set the user credentials for our site \"example.com\"\n",[233,9827,9828],{"class":235,"line":1084},[233,9829,9830],{}," credentialsProvider.setCredentials(new AuthScope(\"example.com\", AuthScope.ANY_PORT),\n",[233,9832,9833],{"class":235,"line":1089},[233,9834,9835],{}," new UsernamePasswordCredentials(\"UserNameHere\", \"UserPasswordHere\"));\n",[233,9837,9838],{"class":235,"line":1094},[233,9839,9840],{}," clientConnectionManager = new ThreadSafeClientConnManager(params, schemeRegistry);\n",[233,9842,9843],{"class":235,"line":1099},[233,9844,9845],{}," context = new BasicHttpContext();\n",[233,9847,9848],{"class":235,"line":1105},[233,9849,9850],{}," context.setAttribute(\"http.auth.credentials-provider\", credentialsProvider);\n",[233,9852,9853],{"class":235,"line":1111},[233,9854,429],{},[225,9856,9858],{"className":227,"code":9857,"language":229,"meta":110,"style":110},"\npublic HttpResponse getResponseFromUrl(String url){\n//connection (client has to be created for every new connection)\nclient = new DefaultHttpClient(clientConnectionManager, params);\nHttpGet get = new HttpGet(url);\nHttpResponse response = client.execute(get, context);\nreturn response;\n}\n\n",[176,9859,9860,9864,9869,9874,9879,9884,9889,9894],{"__ignoreMap":110},[233,9861,9862],{"class":235,"line":236},[233,9863,894],{"emptyLinePlaceholder":127},[233,9865,9866],{"class":235,"line":111},[233,9867,9868],{},"public HttpResponse getResponseFromUrl(String url){\n",[233,9870,9871],{"class":235,"line":402},[233,9872,9873],{},"//connection (client has to be created for every new connection)\n",[233,9875,9876],{"class":235,"line":408},[233,9877,9878],{},"client = new DefaultHttpClient(clientConnectionManager, params);\n",[233,9880,9881],{"class":235,"line":414},[233,9882,9883],{},"HttpGet get = new HttpGet(url);\n",[233,9885,9886],{"class":235,"line":420},[233,9887,9888],{},"HttpResponse response = client.execute(get, context);\n",[233,9890,9891],{"class":235,"line":426},[233,9892,9893],{},"return response;\n",[233,9895,9896],{"class":235,"line":657},[233,9897,429],{},[19,9899,9900],{},"And that's it. I hope this will help some of you to solve their problems with self-signed certs!",[560,9902,562],{},{"title":110,"searchDepth":111,"depth":111,"links":9904},[],[120,2314],"2010-06-24T16:08:24","Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and let\\nandroid accept them.","https://synyx.de/blog/android-and-self-signed-ssl-certificates/",{},"/blog/android-and-self-signed-ssl-certificates",{"title":8762,"description":8771},"blog/android-and-self-signed-ssl-certificates",[133,9914,9915,9916,9917,9918],"cert","certificate","https","self-signed","ssl","Dealing with self-signed ssl certificates is a real pain, because it’s not that simple to add them in your app and let android accept them. But fortunately, there’s a workaround…","gDXsqYNJRaGcit1nh6xgZqXcXw3DL1tsKjV5BU5dPqA",{"id":9922,"title":9923,"author":9924,"body":9925,"category":10545,"date":10546,"description":10547,"extension":124,"link":10548,"meta":10549,"navigation":127,"path":10550,"seo":10551,"slug":9929,"stem":10553,"tags":10554,"teaser":10559,"__hash__":10560},"blog/blog/routing-driving-directions-on-android-part-2-draw-the-route.md","Routing / Driving directions on Android – Part 2: Draw the route",[586],{"type":12,"value":9926,"toc":10540},[9927,9930,9939,9943,9946,9995,9998,10071,10074,10107,10111,10114,10117,10131,10134,10139,10146,10211,10214,10348,10352,10355,10358,10439,10442,10466,10469,10472,10475,10518,10521,10535,10538],[15,9928,9923],{"id":9929},"routing-driving-directions-on-android-part-2-draw-the-route",[19,9931,9932,9933,9938],{},"After you ",[159,9934,9937],{"href":9935,"rel":9936},"http://mobile.synyx.de/2010/06/routing-driving-directions-on-android-part-1-get-the-route/",[163],"got the route","\nfrom wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now.",[23,9940,9942],{"id":9941},"create-a-suiting-overlay","Create a suiting Overlay",[19,9944,9945],{},"We basically need a Overlay that takes two Geopoints and maybe a color in which the lines should be drawn. So here We\nhave:",[225,9947,9949],{"className":227,"code":9948,"language":229,"meta":110,"style":110},"public class RouteOverlay extends Overlay {\n private GeoPoint gp1;\n private GeoPoint gp2;\n private int color;\npublic RouteOverlay(GeoPoint gp1, GeoPoint gp2, int color) {\n this.gp1 = gp1;\n this.gp2 = gp2;\n this.color = color;\n }\n",[176,9950,9951,9956,9961,9966,9971,9976,9981,9986,9991],{"__ignoreMap":110},[233,9952,9953],{"class":235,"line":236},[233,9954,9955],{},"public class RouteOverlay extends Overlay {\n",[233,9957,9958],{"class":235,"line":111},[233,9959,9960],{}," private GeoPoint gp1;\n",[233,9962,9963],{"class":235,"line":402},[233,9964,9965],{}," private GeoPoint gp2;\n",[233,9967,9968],{"class":235,"line":408},[233,9969,9970],{}," private int color;\n",[233,9972,9973],{"class":235,"line":414},[233,9974,9975],{},"public RouteOverlay(GeoPoint gp1, GeoPoint gp2, int color) {\n",[233,9977,9978],{"class":235,"line":420},[233,9979,9980],{}," this.gp1 = gp1;\n",[233,9982,9983],{"class":235,"line":426},[233,9984,9985],{}," this.gp2 = gp2;\n",[233,9987,9988],{"class":235,"line":657},[233,9989,9990],{}," this.color = color;\n",[233,9992,9993],{"class":235,"line":662},[233,9994,423],{},[19,9996,9997],{},"Now all that’s left now for our Overlay is to override the draw() method and draw the line as we need it:",[225,9999,10001],{"className":227,"code":10000,"language":229,"meta":110,"style":110},"@Override\npublic void draw(Canvas canvas, MapView mapView, boolean shadow) {\n Projection projection = mapView.getProjection();\n Paint paint = new Paint();\n Point point = new Point();\n projection.toPixels(gp1, point);\n paint.setColor(color);\n Point point2 = new Point();\n projection.toPixels(gp2, point2);\n paint.setStrokeWidth(5);\n paint.setAlpha(120);\n canvas.drawLine(point.x, point.y, point2.x, point2.y, paint);\n super.draw(canvas, mapView, shadow);\n}\n",[176,10002,10003,10007,10012,10017,10022,10027,10032,10037,10042,10047,10052,10057,10062,10067],{"__ignoreMap":110},[233,10004,10005],{"class":235,"line":236},[233,10006,3104],{},[233,10008,10009],{"class":235,"line":111},[233,10010,10011],{},"public void draw(Canvas canvas, MapView mapView, boolean shadow) {\n",[233,10013,10014],{"class":235,"line":402},[233,10015,10016],{}," Projection projection = mapView.getProjection();\n",[233,10018,10019],{"class":235,"line":408},[233,10020,10021],{}," Paint paint = new Paint();\n",[233,10023,10024],{"class":235,"line":414},[233,10025,10026],{}," Point point = new Point();\n",[233,10028,10029],{"class":235,"line":420},[233,10030,10031],{}," projection.toPixels(gp1, point);\n",[233,10033,10034],{"class":235,"line":426},[233,10035,10036],{}," paint.setColor(color);\n",[233,10038,10039],{"class":235,"line":657},[233,10040,10041],{}," Point point2 = new Point();\n",[233,10043,10044],{"class":235,"line":662},[233,10045,10046],{}," projection.toPixels(gp2, point2);\n",[233,10048,10049],{"class":235,"line":667},[233,10050,10051],{}," paint.setStrokeWidth(5);\n",[233,10053,10054],{"class":235,"line":673},[233,10055,10056],{}," paint.setAlpha(120);\n",[233,10058,10059],{"class":235,"line":679},[233,10060,10061],{}," canvas.drawLine(point.x, point.y, point2.x, point2.y, paint);\n",[233,10063,10064],{"class":235,"line":684},[233,10065,10066],{}," super.draw(canvas, mapView, shadow);\n",[233,10068,10069],{"class":235,"line":689},[233,10070,429],{},[19,10072,10073],{},"Back in the Activity, just iterate over the GeoPoints that you got from google maps and add each of them to the MapView:",[225,10075,10077],{"className":227,"code":10076,"language":229,"meta":110,"style":110},"private void drawPath(List geoPoints, int color) {\n List overlays = mapView.getOverlays();\n for (int i = 1; i \u003C geoPoints.size(); i++) {\n overlays.add(new RouteOverlay(geoPoints.get(i - 1), geoPoints.get(i), color));\n }\n}\n",[176,10078,10079,10084,10089,10094,10099,10103],{"__ignoreMap":110},[233,10080,10081],{"class":235,"line":236},[233,10082,10083],{},"private void drawPath(List geoPoints, int color) {\n",[233,10085,10086],{"class":235,"line":111},[233,10087,10088],{}," List overlays = mapView.getOverlays();\n",[233,10090,10091],{"class":235,"line":402},[233,10092,10093],{}," for (int i = 1; i \u003C geoPoints.size(); i++) {\n",[233,10095,10096],{"class":235,"line":408},[233,10097,10098],{}," overlays.add(new RouteOverlay(geoPoints.get(i - 1), geoPoints.get(i), color));\n",[233,10100,10101],{"class":235,"line":414},[233,10102,3596],{},[233,10104,10105],{"class":235,"line":420},[233,10106,429],{},[23,10108,10110],{"id":10109},"get-location-updates-from-the-location-manager","Get location updates from the location manager",[19,10112,10113],{},"So now we have the geopoints and also the overlays, but we’ve only got the last known location of the user! The app\ndoesn’t even updates his location!",[19,10115,10116],{},"What we need to achieve this is a listener from the location manager. That’s quite simple and we also got the\nLocationManager ready in onCreate, so we just have to add this little line to it:",[225,10118,10120],{"className":227,"code":10119,"language":229,"meta":110,"style":110},"\nlocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n\n",[176,10121,10122,10126],{"__ignoreMap":110},[233,10123,10124],{"class":235,"line":236},[233,10125,894],{"emptyLinePlaceholder":127},[233,10127,10128],{"class":235,"line":111},[233,10129,10130],{},"locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n",[19,10132,10133],{},"The first number here is the timespan in milliseconds in which we want want to receive the updates, the second one is\nthe distance in meters that the user has to move before we get them. In our app we don’t have to update the route all\nthe time, so we go with 5 minutes and 5 kilometers.",[19,10135,10136],{},[299,10137,10138],{},"Be very careful with the values here, because the whole gps thing consumes a lot of energy! (And if the values are way\nto small it also blocks the whole app)",[19,10140,10141,10142,10145],{},"Also don’t forget to ",[299,10143,10144],{},"remove the listener"," if the MapView isn’t visible:",[225,10147,10149],{"className":227,"code":10148,"language":229,"meta":110,"style":110},"\n@Override\n protected void onPause() {\n //remove the listener\n locationManager.removeUpdates(this);\n super.onPause();\n }\n@Override\n protected void onResume() {\n //add the listener again\n locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n super.onResume();\n }\n\n",[176,10150,10151,10155,10159,10164,10169,10174,10179,10183,10187,10192,10197,10202,10207],{"__ignoreMap":110},[233,10152,10153],{"class":235,"line":236},[233,10154,894],{"emptyLinePlaceholder":127},[233,10156,10157],{"class":235,"line":111},[233,10158,3104],{},[233,10160,10161],{"class":235,"line":402},[233,10162,10163],{}," protected void onPause() {\n",[233,10165,10166],{"class":235,"line":408},[233,10167,10168],{}," //remove the listener\n",[233,10170,10171],{"class":235,"line":414},[233,10172,10173],{}," locationManager.removeUpdates(this);\n",[233,10175,10176],{"class":235,"line":420},[233,10177,10178],{}," super.onPause();\n",[233,10180,10181],{"class":235,"line":426},[233,10182,423],{},[233,10184,10185],{"class":235,"line":657},[233,10186,3104],{},[233,10188,10189],{"class":235,"line":662},[233,10190,10191],{}," protected void onResume() {\n",[233,10193,10194],{"class":235,"line":667},[233,10195,10196],{}," //add the listener again\n",[233,10198,10199],{"class":235,"line":673},[233,10200,10201],{}," locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 300000, 5000, this);\n",[233,10203,10204],{"class":235,"line":679},[233,10205,10206],{}," super.onResume();\n",[233,10208,10209],{"class":235,"line":684},[233,10210,423],{},[19,10212,10213],{},"Now we have to let our MapActivity implement LocationListener and react to the updates:",[225,10215,10217],{"className":227,"code":10216,"language":229,"meta":110,"style":110},"\npublic class RouteActivity extends MapActivity implements LocationListener {\n@Override\npublic void onLocationChanged(Location location) {\ndrawUserPosition(location);\n}\nprivate void drawUserPosition(Location location) {\n GeoPoint currentLocation;\n currentLocation = new GeoPoint((int) ( location.getLatitude() * 1E6), (int) ( location\n getLongitude() * 1E6));\n OverlayItem currentLocationOverlay = new OverlayItem(currentLocation, getString(R.string.your_location),\n getString(R.string.current_location));\n mapOverlays.clear();\n if (locationOverlays.size() > 1) {\n // remove the old user position if there is one\n locationOverlays.removeOverlay(1);\n }\n //add new user position\n locationOverlays.addOverlay(currentLocationOverlay, this.getResources().getDrawable(R.drawable.someImage));\n mapOverlays.add(locationOverlays);\n //.\n //. calculate / set the mapcenter, zoom to span\n //. see in previous posts\n //.\n RouteThread rt = new RouteThread(currentLocation, synyxGeoPoint, routeHandler);\n rt.start();\n}\n",[176,10218,10219,10223,10228,10232,10237,10242,10246,10251,10256,10261,10266,10271,10276,10281,10286,10291,10296,10300,10305,10310,10315,10320,10325,10330,10334,10339,10344],{"__ignoreMap":110},[233,10220,10221],{"class":235,"line":236},[233,10222,894],{"emptyLinePlaceholder":127},[233,10224,10225],{"class":235,"line":111},[233,10226,10227],{},"public class RouteActivity extends MapActivity implements LocationListener {\n",[233,10229,10230],{"class":235,"line":402},[233,10231,3104],{},[233,10233,10234],{"class":235,"line":408},[233,10235,10236],{},"public void onLocationChanged(Location location) {\n",[233,10238,10239],{"class":235,"line":414},[233,10240,10241],{},"drawUserPosition(location);\n",[233,10243,10244],{"class":235,"line":420},[233,10245,429],{},[233,10247,10248],{"class":235,"line":426},[233,10249,10250],{},"private void drawUserPosition(Location location) {\n",[233,10252,10253],{"class":235,"line":657},[233,10254,10255],{}," GeoPoint currentLocation;\n",[233,10257,10258],{"class":235,"line":662},[233,10259,10260],{}," currentLocation = new GeoPoint((int) ( location.getLatitude() * 1E6), (int) ( location\n",[233,10262,10263],{"class":235,"line":667},[233,10264,10265],{}," getLongitude() * 1E6));\n",[233,10267,10268],{"class":235,"line":673},[233,10269,10270],{}," OverlayItem currentLocationOverlay = new OverlayItem(currentLocation, getString(R.string.your_location),\n",[233,10272,10273],{"class":235,"line":679},[233,10274,10275],{}," getString(R.string.current_location));\n",[233,10277,10278],{"class":235,"line":684},[233,10279,10280],{}," mapOverlays.clear();\n",[233,10282,10283],{"class":235,"line":689},[233,10284,10285],{}," if (locationOverlays.size() > 1) {\n",[233,10287,10288],{"class":235,"line":695},[233,10289,10290],{}," // remove the old user position if there is one\n",[233,10292,10293],{"class":235,"line":701},[233,10294,10295],{}," locationOverlays.removeOverlay(1);\n",[233,10297,10298],{"class":235,"line":706},[233,10299,3596],{},[233,10301,10302],{"class":235,"line":711},[233,10303,10304],{}," //add new user position\n",[233,10306,10307],{"class":235,"line":717},[233,10308,10309],{}," locationOverlays.addOverlay(currentLocationOverlay, this.getResources().getDrawable(R.drawable.someImage));\n",[233,10311,10312],{"class":235,"line":723},[233,10313,10314],{}," mapOverlays.add(locationOverlays);\n",[233,10316,10317],{"class":235,"line":728},[233,10318,10319],{}," //.\n",[233,10321,10322],{"class":235,"line":733},[233,10323,10324],{}," //. calculate / set the mapcenter, zoom to span\n",[233,10326,10327],{"class":235,"line":1084},[233,10328,10329],{}," //. see in previous posts\n",[233,10331,10332],{"class":235,"line":1089},[233,10333,10319],{},[233,10335,10336],{"class":235,"line":1094},[233,10337,10338],{}," RouteThread rt = new RouteThread(currentLocation, synyxGeoPoint, routeHandler);\n",[233,10340,10341],{"class":235,"line":1099},[233,10342,10343],{}," rt.start();\n",[233,10345,10346],{"class":235,"line":1105},[233,10347,429],{},[23,10349,10351],{"id":10350},"make-it-threaded","Make it threaded",[19,10353,10354],{},"But we’re not quite finished yet, because you can’t just put the internet connection and parsing into the main thread!\nThis would block the ui for quite a long time if the users internet connection isn’t so fast.",[19,10356,10357],{},"So what we have to do is to create a handler as an inner class of our Activity that takes messages from the thread which\ngets us the geopoints:",[225,10359,10361],{"className":227,"code":10360,"language":229,"meta":110,"style":110},"\nprivate class RouteHandler extends Handler {\n public void handleMessage(Message msg) {\n boolean error = msg.getData().getBoolean(\"error\", false);\n if (!error) {\n // set the geopoints (we can't just add the overlays\n // to the map here, because it's on a different thread\n geoPoints = (List\u003CGeoPoint>) msg.obj;\n post(updateRoute);\n } else {\n // maybe you want to show an error message here to\n // notice the user that the route can not be displayed\n // because there's no connection to the internet\n }\n }\n }\n",[176,10362,10363,10367,10372,10377,10382,10387,10392,10397,10402,10407,10412,10417,10422,10427,10431,10435],{"__ignoreMap":110},[233,10364,10365],{"class":235,"line":236},[233,10366,894],{"emptyLinePlaceholder":127},[233,10368,10369],{"class":235,"line":111},[233,10370,10371],{},"private class RouteHandler extends Handler {\n",[233,10373,10374],{"class":235,"line":402},[233,10375,10376],{}," public void handleMessage(Message msg) {\n",[233,10378,10379],{"class":235,"line":408},[233,10380,10381],{}," boolean error = msg.getData().getBoolean(\"error\", false);\n",[233,10383,10384],{"class":235,"line":414},[233,10385,10386],{}," if (!error) {\n",[233,10388,10389],{"class":235,"line":420},[233,10390,10391],{}," // set the geopoints (we can't just add the overlays\n",[233,10393,10394],{"class":235,"line":426},[233,10395,10396],{}," // to the map here, because it's on a different thread\n",[233,10398,10399],{"class":235,"line":657},[233,10400,10401],{}," geoPoints = (List\u003CGeoPoint>) msg.obj;\n",[233,10403,10404],{"class":235,"line":662},[233,10405,10406],{}," post(updateRoute);\n",[233,10408,10409],{"class":235,"line":667},[233,10410,10411],{}," } else {\n",[233,10413,10414],{"class":235,"line":673},[233,10415,10416],{}," // maybe you want to show an error message here to\n",[233,10418,10419],{"class":235,"line":679},[233,10420,10421],{}," // notice the user that the route can not be displayed\n",[233,10423,10424],{"class":235,"line":684},[233,10425,10426],{}," // because there's no connection to the internet\n",[233,10428,10429],{"class":235,"line":689},[233,10430,5340],{},[233,10432,10433],{"class":235,"line":695},[233,10434,417],{},[233,10436,10437],{"class":235,"line":701},[233,10438,423],{},[19,10440,10441],{},"Send the geopoints from the RouteThread:",[225,10443,10445],{"className":227,"code":10444,"language":229,"meta":110,"style":110},"\nMessage msg = new Message();\nmsg.obj = decodePoly(encoded);\nhandler.dispatchMessage(msg);\n\n",[176,10446,10447,10451,10456,10461],{"__ignoreMap":110},[233,10448,10449],{"class":235,"line":236},[233,10450,894],{"emptyLinePlaceholder":127},[233,10452,10453],{"class":235,"line":111},[233,10454,10455],{},"Message msg = new Message();\n",[233,10457,10458],{"class":235,"line":402},[233,10459,10460],{},"msg.obj = decodePoly(encoded);\n",[233,10462,10463],{"class":235,"line":408},[233,10464,10465],{},"handler.dispatchMessage(msg);\n",[19,10467,10468],{},"(If you have further data to send, use a Bundle object and add it to the Message.)",[19,10470,10471],{},"And now we need the main thread to update the overlays if there are new geopoints available. Again, we need the handler\nto accomplish that, because it can start runnables into the same thread the handler was created in.",[19,10473,10474],{},"First we create a runnable in the Activity:",[225,10476,10478],{"className":227,"code":10477,"language":229,"meta":110,"style":110},"\nfinal Runnable updateRoute = new Runnable() {\n public void run() {\n // draw the path and then invalidate the mapview so that it redraws itself\n drawPath(geoPoints, Color.GREEN);\n mapView.invalidate();\n }\n };\n\n",[176,10479,10480,10484,10489,10494,10499,10504,10509,10513],{"__ignoreMap":110},[233,10481,10482],{"class":235,"line":236},[233,10483,894],{"emptyLinePlaceholder":127},[233,10485,10486],{"class":235,"line":111},[233,10487,10488],{},"final Runnable updateRoute = new Runnable() {\n",[233,10490,10491],{"class":235,"line":402},[233,10492,10493],{}," public void run() {\n",[233,10495,10496],{"class":235,"line":408},[233,10497,10498],{}," // draw the path and then invalidate the mapview so that it redraws itself\n",[233,10500,10501],{"class":235,"line":414},[233,10502,10503],{}," drawPath(geoPoints, Color.GREEN);\n",[233,10505,10506],{"class":235,"line":420},[233,10507,10508],{}," mapView.invalidate();\n",[233,10510,10511],{"class":235,"line":426},[233,10512,417],{},[233,10514,10515],{"class":235,"line":657},[233,10516,10517],{}," };\n",[19,10519,10520],{},"And all that is left to do is to call it from inside the handler:",[225,10522,10524],{"className":227,"code":10523,"language":229,"meta":110,"style":110},"\npost(updateRoute);\n\n",[176,10525,10526,10530],{"__ignoreMap":110},[233,10527,10528],{"class":235,"line":236},[233,10529,894],{"emptyLinePlaceholder":127},[233,10531,10532],{"class":235,"line":111},[233,10533,10534],{},"post(updateRoute);\n",[19,10536,10537],{},"Anyway, this is how we solved the routing in our app. If you have a question, or have suggestions how we could make it\nbetter, please leave us a comment!",[560,10539,562],{},{"title":110,"searchDepth":111,"depth":111,"links":10541},[10542,10543,10544],{"id":9941,"depth":111,"text":9942},{"id":10109,"depth":111,"text":10110},{"id":10350,"depth":111,"text":10351},[120,2314],"2010-06-16T14:14:35","After you got the route\\nfrom wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now.","https://synyx.de/blog/routing-driving-directions-on-android-part-2-draw-the-route/",{},"/blog/routing-driving-directions-on-android-part-2-draw-the-route",{"title":9923,"description":10552},"After you got the route\nfrom wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now.","blog/routing-driving-directions-on-android-part-2-draw-the-route",[133,10555,10556,10557,10558],"driving-directions","map","overlays","routing","After you got the route from wherever, you probably want to draw it on a MapView. But how to do it? That’s what I will show you now. Create a…","ef_SMhSPJEztFJzu7jCXik6lZ16Do7MuCSWGxxWmEuc",{"id":10562,"title":10563,"author":10564,"body":10565,"category":11044,"date":11045,"description":11046,"extension":124,"link":11047,"meta":11048,"navigation":127,"path":11049,"seo":11050,"slug":10569,"stem":11052,"tags":11053,"teaser":11055,"__hash__":11056},"blog/blog/routing-driving-directions-on-android-part-1-get-the-route.md","Routing / Driving directions on Android – Part 1: Get the route",[586],{"type":12,"value":10566,"toc":11035},[10567,10570,10585,10588,10592,10595,10598,10607,10610,10645,10648,10652,10656,10659,10665,10673,10676,10753,10756,10793,10796,10799,10808,10812,10815,10818,10857,10866,11010,11014,11017,11025,11029,11032],[15,10568,10563],{"id":10569},"routing-driving-directions-on-android-part-1-get-the-route",[19,10571,10572,10573,10578,10579,10584],{},"Complementary to Sebastian’s posts\nabout ",[159,10574,10577],{"href":10575,"rel":10576},"http://mobile.synyx.de/2010/04/google-maps-on-android/",[163],"how to navigate with the MapView","\nand ",[159,10580,10583],{"href":10581,"rel":10582},"http://mobile.synyx.de/2010/05/google-maps-on-android-part-2-overlays/",[163],"how to add customized overlays to it",", I\nwant to show you, how you display the route between two points.",[19,10586,10587],{},"Well, the whole thing wouldn’t be a pain now, if google hadn’t removed the DrivingDirection class since API 1.0, with\nwhich you could solve this problem in no time and with just a few lines of code. But because they did remove it, we have\nto go through a little bit more trouble.",[23,10589,10591],{"id":10590},"getting-started","Getting started",[19,10593,10594],{},"First you have to realize, if you only need the coordinates of the route, or also driving directions like “go left on\nthis street, drive right on that street…”.",[19,10596,10597],{},"The first step in both cases is to get the two locations between which the routing should happen. In our case we wanted\nto show the route from the current location of the user to our office, so one of the points is fixed:",[225,10599,10601],{"className":227,"code":10600,"language":229,"meta":110,"style":110},"synyxGeoPoint = new GeoPoint(49002175, 8394160);\n",[176,10602,10603],{"__ignoreMap":110},[233,10604,10605],{"class":235,"line":236},[233,10606,10600],{},[19,10608,10609],{},"And the user location is also quite easy to get:",[225,10611,10613],{"className":227,"code":10612,"language":229,"meta":110,"style":110},"locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\nCriteria criteria = new Criteria();\ncriteria.setAccuracy(Criteria.ACCURACY_FINE);\ncriteria.setAltitudeRequired(false);\nLocation lastKnownLocation =\nlocationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, true));\n",[176,10614,10615,10620,10625,10630,10635,10640],{"__ignoreMap":110},[233,10616,10617],{"class":235,"line":236},[233,10618,10619],{},"locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);\n",[233,10621,10622],{"class":235,"line":111},[233,10623,10624],{},"Criteria criteria = new Criteria();\n",[233,10626,10627],{"class":235,"line":402},[233,10628,10629],{},"criteria.setAccuracy(Criteria.ACCURACY_FINE);\n",[233,10631,10632],{"class":235,"line":408},[233,10633,10634],{},"criteria.setAltitudeRequired(false);\n",[233,10636,10637],{"class":235,"line":414},[233,10638,10639],{},"Location lastKnownLocation =\n",[233,10641,10642],{"class":235,"line":420},[233,10643,10644],{},"locationManager.getLastKnownLocation(locationManager.getBestProvider(criteria, true));\n",[19,10646,10647],{},"With this we get the last known, accurate location of the user. So all there is left now, is to get the route.",[23,10649,10651],{"id":10650},"getting-the-geopoints-from-google-maps","Getting the geopoints from google maps",[1147,10653,10655],{"id":10654},"kml-with-driving-directions","Kml (with driving directions)",[19,10657,10658],{},"If you need the driving directions, you can build yourself a url like this one to get a kml file with all the\ninformation:",[19,10660,10661],{},[159,10662,10663],{"href":10663,"rel":10664},"http://maps.google.com/maps?f=d&hl=en&saddr=9.18333,48.7667&daddr=8.394160,49.002175&ie=UTF8&0&om=0&z=20&output=kml",[163],[19,10666,10667,10668,1690],{},"(For the list of parameters of google maps, see\nhere: ",[159,10669,10672],{"href":10670,"rel":10671},"https://web.archive.org/web/20080901081831/http://mapki.com/wiki/Google_Map_Parameters",[163],"mapki.com",[19,10674,10675],{},"Here’s how we did it:",[225,10677,10679],{"className":227,"code":10678,"language":229,"meta":110,"style":110},"\npublic String getUrl(GeoPoint src, GeoPoint dest){\nStringBuilder urlString = new StringBuilder();\nurlString.append(\"http://maps.google.com/maps?f=d&hl=en\");\nurlString.append(\"&saddr=\");\nurlString.append(Double.toString((double) src.getLatitudeE6() / 1.0E6));\nurlString.append(\",\");\nurlString.append(Double.toString((double) src.getLongitudeE6() / 1.0E6));\nurlString.append(\"&daddr=\");// to\nurlString.append(Double.toString((double) dest.getLatitudeE6() / 1.0E6));\nurlString.append(\",\");\nurlString.append(Double.toString((double) dest.getLongitudeE6() / 1.0E6));\nurlString.append(\"&ie=UTF8&0&om=0&output=kml\");\nreturn urlString;\n}\n",[176,10680,10681,10685,10690,10695,10700,10705,10710,10715,10720,10725,10730,10734,10739,10744,10749],{"__ignoreMap":110},[233,10682,10683],{"class":235,"line":236},[233,10684,894],{"emptyLinePlaceholder":127},[233,10686,10687],{"class":235,"line":111},[233,10688,10689],{},"public String getUrl(GeoPoint src, GeoPoint dest){\n",[233,10691,10692],{"class":235,"line":402},[233,10693,10694],{},"StringBuilder urlString = new StringBuilder();\n",[233,10696,10697],{"class":235,"line":408},[233,10698,10699],{},"urlString.append(\"http://maps.google.com/maps?f=d&hl=en\");\n",[233,10701,10702],{"class":235,"line":414},[233,10703,10704],{},"urlString.append(\"&saddr=\");\n",[233,10706,10707],{"class":235,"line":420},[233,10708,10709],{},"urlString.append(Double.toString((double) src.getLatitudeE6() / 1.0E6));\n",[233,10711,10712],{"class":235,"line":426},[233,10713,10714],{},"urlString.append(\",\");\n",[233,10716,10717],{"class":235,"line":657},[233,10718,10719],{},"urlString.append(Double.toString((double) src.getLongitudeE6() / 1.0E6));\n",[233,10721,10722],{"class":235,"line":662},[233,10723,10724],{},"urlString.append(\"&daddr=\");// to\n",[233,10726,10727],{"class":235,"line":667},[233,10728,10729],{},"urlString.append(Double.toString((double) dest.getLatitudeE6() / 1.0E6));\n",[233,10731,10732],{"class":235,"line":673},[233,10733,10714],{},[233,10735,10736],{"class":235,"line":679},[233,10737,10738],{},"urlString.append(Double.toString((double) dest.getLongitudeE6() / 1.0E6));\n",[233,10740,10741],{"class":235,"line":684},[233,10742,10743],{},"urlString.append(\"&ie=UTF8&0&om=0&output=kml\");\n",[233,10745,10746],{"class":235,"line":689},[233,10747,10748],{},"return urlString;\n",[233,10750,10751],{"class":235,"line":695},[233,10752,429],{},[19,10754,10755],{},"The file you get from this url looks like this:",[225,10757,10759],{"className":1649,"code":10758,"language":1651,"meta":110,"style":110},"\nHohenheimer Str./B27 to Karlstraße/L561\n....\nHead southeast on Hohenheimer Str./B27 toward Bopserwaldstraße Continue to follow B27\n....\n9.183560,48.766820,0.000000 9.183690,48.766670,0.000000 9.183640,48.766480,0.000000 9.183470,48.766380,0.000000\n....\n",[176,10760,10761,10765,10770,10775,10780,10784,10789],{"__ignoreMap":110},[233,10762,10763],{"class":235,"line":236},[233,10764,894],{"emptyLinePlaceholder":127},[233,10766,10767],{"class":235,"line":111},[233,10768,10769],{},"Hohenheimer Str./B27 to Karlstraße/L561\n",[233,10771,10772],{"class":235,"line":402},[233,10773,10774],{},"....\n",[233,10776,10777],{"class":235,"line":408},[233,10778,10779],{},"Head southeast on Hohenheimer Str./B27 toward Bopserwaldstraße Continue to follow B27\n",[233,10781,10782],{"class":235,"line":414},[233,10783,10774],{},[233,10785,10786],{"class":235,"line":420},[233,10787,10788],{},"9.183560,48.766820,0.000000 9.183690,48.766670,0.000000 9.183640,48.766480,0.000000 9.183470,48.766380,0.000000\n",[233,10790,10791],{"class":235,"line":426},[233,10792,10774],{},[19,10794,10795],{},"(the first number of each pair is the longitude, the second the latitude)",[19,10797,10798],{},"To convert them to GeoPoints use:",[225,10800,10802],{"className":227,"code":10801,"language":229,"meta":110,"style":110},"GeoPoint geoPoint = new GeoPoint((int)(Double.parseDouble(latitude[0])*1E6),(int)(Double.parseDouble(longitude[0])*1E6));\n",[176,10803,10804],{"__ignoreMap":110},[233,10805,10806],{"class":235,"line":236},[233,10807,10801],{},[1147,10809,10811],{"id":10810},"json-without-driving-directions","JSON (without driving directions)",[19,10813,10814],{},"If you don’t need the driving directions, you can save a few kilobytes by changing the output parameter to\noutput=dragdir (else it’s exactly the same url as above) , which delivers you a json string with encrypted geopoints.",[19,10816,10817],{},"again an example of what the server returns:",[225,10819,10821],{"className":5860,"code":10820,"language":5862,"meta":110,"style":110},"{tooltipHtml:\" (572x26#160;km / 5 hours 14 mins)\",polylines:[{id:\"route0\",points:\"se}bIgcwt@BSzA_D??Xh@dC|G??hDlIpBzFrAvC`@`BZjCV|@nApBtDvEx@rA| .....\n",[176,10822,10823],{"__ignoreMap":110},[233,10824,10825,10828,10831,10833,10836,10839,10842,10845,10848,10851,10854],{"class":235,"line":236},[233,10826,10827],{"class":1781},"{",[233,10829,10830],{"class":3794},"tooltipHtml",[233,10832,324],{"class":1781},[233,10834,10835],{"class":3831},"\" (572x26#160;km / 5 hours 14 mins)\"",[233,10837,10838],{"class":1781},",",[233,10840,10841],{"class":3794},"polylines",[233,10843,10844],{"class":1781},":[{id:",[233,10846,10847],{"class":3831},"\"route0\"",[233,10849,10850],{"class":1781},",points:",[233,10852,10853],{"class":3831},"\"se}bIgcwt@BSzA_D??Xh@dC|G??hDlIpBzFrAvC`@`BZjCV|@nApBtDvEx@rA| ....",[233,10855,10856],{"class":1785},".\n",[19,10858,10859,10860,10865],{},"So as you can see, you can’t just parse the string and get the geopoints out of it. You first have to decode them.\nHere’s a method that solves this for you (algorithm\nfrom ",[159,10861,10864],{"href":10862,"rel":10863},"http://facstaff.unca.edu/mcmcclur/googlemaps/encodepolyline/",[163],"http://facstaff.unca.edu/",") :",[225,10867,10869],{"className":227,"code":10868,"language":229,"meta":110,"style":110},"// get only the encoded geopoints\nencoded = encoded.split(\"points:\"\")[1].split(\"\",\")[0];\n// replace two backslashes by one (some error from the transmission)\nencoded = encoded.replace(\"\\\\\", \"\\\");\n//decoding\nList poly = new ArrayList();\n int index = 0, len = encoded.length();\n int lat = 0, lng = 0;\n while (index \u003C len) {\n int b, shift = 0, result = 0;\n do {\n b = encoded.charAt(index++) - 63;\n result |= (b & 0x1f) \u003C\u003C shift;\n shift += 5;\n } while (b >= 0x20);\n int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n lat += dlat;\n shift = 0;\n result = 0;\n do {\n b = encoded.charAt(index++) - 63;\n result |= (b & 0x1f) \u003C\u003C shift;\n shift += 5;\n } while (b >= 0x20);\n int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n lng += dlng;\n GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));\n poly.add(p);\n }\n",[176,10870,10871,10876,10881,10886,10891,10896,10901,10906,10911,10916,10921,10926,10931,10936,10941,10946,10951,10956,10961,10966,10970,10974,10978,10982,10986,10991,10996,11001,11006],{"__ignoreMap":110},[233,10872,10873],{"class":235,"line":236},[233,10874,10875],{},"// get only the encoded geopoints\n",[233,10877,10878],{"class":235,"line":111},[233,10879,10880],{},"encoded = encoded.split(\"points:\"\")[1].split(\"\",\")[0];\n",[233,10882,10883],{"class":235,"line":402},[233,10884,10885],{},"// replace two backslashes by one (some error from the transmission)\n",[233,10887,10888],{"class":235,"line":408},[233,10889,10890],{},"encoded = encoded.replace(\"\\\\\", \"\\\");\n",[233,10892,10893],{"class":235,"line":414},[233,10894,10895],{},"//decoding\n",[233,10897,10898],{"class":235,"line":420},[233,10899,10900],{},"List poly = new ArrayList();\n",[233,10902,10903],{"class":235,"line":426},[233,10904,10905],{}," int index = 0, len = encoded.length();\n",[233,10907,10908],{"class":235,"line":657},[233,10909,10910],{}," int lat = 0, lng = 0;\n",[233,10912,10913],{"class":235,"line":662},[233,10914,10915],{}," while (index \u003C len) {\n",[233,10917,10918],{"class":235,"line":667},[233,10919,10920],{}," int b, shift = 0, result = 0;\n",[233,10922,10923],{"class":235,"line":673},[233,10924,10925],{}," do {\n",[233,10927,10928],{"class":235,"line":679},[233,10929,10930],{}," b = encoded.charAt(index++) - 63;\n",[233,10932,10933],{"class":235,"line":684},[233,10934,10935],{}," result |= (b & 0x1f) \u003C\u003C shift;\n",[233,10937,10938],{"class":235,"line":689},[233,10939,10940],{}," shift += 5;\n",[233,10942,10943],{"class":235,"line":695},[233,10944,10945],{}," } while (b >= 0x20);\n",[233,10947,10948],{"class":235,"line":701},[233,10949,10950],{}," int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n",[233,10952,10953],{"class":235,"line":706},[233,10954,10955],{}," lat += dlat;\n",[233,10957,10958],{"class":235,"line":711},[233,10959,10960],{}," shift = 0;\n",[233,10962,10963],{"class":235,"line":717},[233,10964,10965],{}," result = 0;\n",[233,10967,10968],{"class":235,"line":723},[233,10969,10925],{},[233,10971,10972],{"class":235,"line":728},[233,10973,10930],{},[233,10975,10976],{"class":235,"line":733},[233,10977,10935],{},[233,10979,10980],{"class":235,"line":1084},[233,10981,10940],{},[233,10983,10984],{"class":235,"line":1089},[233,10985,10945],{},[233,10987,10988],{"class":235,"line":1094},[233,10989,10990],{}," int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));\n",[233,10992,10993],{"class":235,"line":1099},[233,10994,10995],{}," lng += dlng;\n",[233,10997,10998],{"class":235,"line":1105},[233,10999,11000],{}," GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));\n",[233,11002,11003],{"class":235,"line":1111},[233,11004,11005],{}," poly.add(p);\n",[233,11007,11008],{"class":235,"line":1116},[233,11009,417],{},[23,11011,11013],{"id":11012},"getting-the-geopoints-from-open-streetmap","Getting the geopoints from open streetmap",[19,11015,11016],{},"You can also get the geopoints from open streetmap. It’s quite the same procedure, so i don’t write it all again.",[19,11018,11019,11020],{},"Here you can see for yourself: ",[159,11021,11024],{"href":11022,"rel":11023},"http://wiki.openstreetmap.org/wiki/YOURS#Routing_API",[163],"YOURS Routing_API",[23,11026,11028],{"id":11027},"whats-in-the-next-post","What’s in the next post",[19,11030,11031],{},"That’s it for today, in the next post i will show you how to draw the route on your MapView properly.",[560,11033,11034],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":110,"searchDepth":111,"depth":111,"links":11036},[11037,11038,11042,11043],{"id":10590,"depth":111,"text":10591},{"id":10650,"depth":111,"text":10651,"children":11039},[11040,11041],{"id":10654,"depth":402,"text":10655},{"id":10810,"depth":402,"text":10811},{"id":11012,"depth":111,"text":11013},{"id":11027,"depth":111,"text":11028},[120,2314],"2010-06-14T14:00:19","Complementary to Sebastian’s posts\\nabout how to navigate with the MapView\\nand how to add customized overlays to it, I\\nwant to show you, how you display the route between two points.","https://synyx.de/blog/routing-driving-directions-on-android-part-1-get-the-route/",{},"/blog/routing-driving-directions-on-android-part-1-get-the-route",{"title":10563,"description":11051},"Complementary to Sebastian’s posts\nabout how to navigate with the MapView\nand how to add customized overlays to it, I\nwant to show you, how you display the route between two points.","blog/routing-driving-directions-on-android-part-1-get-the-route",[133,11054,10556,10558],"driving-direction","Complementary to Sebastian’s posts about how to navigate with the MapView and how to add customized overlays to it, I want to show you, how you display the route between…","23Pc9hKpFDatuwCoBwCqHTTtAhn5eTixkOfHWMuYPBM",{"id":11058,"title":11059,"author":11060,"body":11061,"category":11298,"date":11299,"description":11300,"extension":124,"link":11301,"meta":11302,"navigation":127,"path":11303,"seo":11304,"slug":11065,"stem":11306,"tags":11307,"teaser":11309,"__hash__":11310},"blog/blog/performance-optimization-in-synyx-sudoku.md","Performance optimization in Synyx Sudoku",[586],{"type":12,"value":11062,"toc":11296},[11063,11066,11075,11078,11081,11084,11087,11096,11099,11102,11105,11149,11152,11263,11266,11281,11288,11291,11294],[15,11064,11059],{"id":11065},"performance-optimization-in-synyx-sudoku",[19,11067,11068,11069,11074],{},"Now that ",[159,11070,11073],{"href":11071,"rel":11072},"http://mobile.synyx.de/2010/04/22/release-of-synyxsudoku",[163],"SynyxSudoku"," has been on the market a little while,\nI want to tell a little about what kind of measurements we took to optimize the performance of it.",[19,11076,11077],{},"First of all I have to say, that this was our first android project and I’m also just a first year trainee at the\nmoment, so there were quite a few issues, not only about the performance that had to be solved. I learned a lot in this\nproject and want to share a bit of what I learned with you, so I hope this will help you to improve the performance of\nyour apps, as well.",[19,11079,11080],{},"The biggest problem was probably the large number of views that the activity had at start (somewhat about 300…), so the\napp needed really long to start and didn’t run that fast because of it.",[19,11082,11083],{},"To track this kind of problem, google integrated the hierarchy viewer to the toolkit of android, which gives you an\noverview of all the currently loaded views in the selected app.",[19,11085,11086],{},"So it was soon clear, that the highscore was the big problem, because it was built as a table with as much rows as there\nwas data – what actually causes two different performance issues, first the big number of views and second the\ninefficient way of creating this views.",[19,11088,11089,11090,11095],{},"After a little bit of research we found the solution to this in the google I/O\nvideo ",[159,11091,11094],{"href":11092,"rel":11093},"http://www.youtube.com/watch?v=N6YdwzAvwOA",[163],"“Make your Android UI Fast and Efficient”",", which you really should\nwatch, if you haven’t by now!",[19,11097,11098],{},"So we found out that there’s a ListView element that takes care of such big lists as the highscore quite easily. The\ntrick to it is, that it only has that much views loaded, as there are visible to the user and it recycles them if the\nscroll out of the screen -> the views that scroll out of the screen are taken out of the list, have their data\nreplaced, and are put in on the opposite side again.",[19,11100,11101],{},"And that’s how it’s looking in the code:",[19,11103,11104],{},"First you have to overwrite the BaseAdapter from android so that you can give it your specific views to display:",[225,11106,11108],{"className":227,"code":11107,"language":229,"meta":110,"style":110},"public class HighscoreListAdapter extends BaseAdapter {\n private List valueList;\n private LayoutInflater inflater;\n public HighscoreListAdapter(List highscoreValueList, LayoutInflater inflater) {\n this.inflater = inflater;\n this.valueList = highscoreValueList;\n }\n}\n",[176,11109,11110,11115,11120,11125,11130,11135,11140,11145],{"__ignoreMap":110},[233,11111,11112],{"class":235,"line":236},[233,11113,11114],{},"public class HighscoreListAdapter extends BaseAdapter {\n",[233,11116,11117],{"class":235,"line":111},[233,11118,11119],{}," private List valueList;\n",[233,11121,11122],{"class":235,"line":402},[233,11123,11124],{}," private LayoutInflater inflater;\n",[233,11126,11127],{"class":235,"line":408},[233,11128,11129],{}," public HighscoreListAdapter(List highscoreValueList, LayoutInflater inflater) {\n",[233,11131,11132],{"class":235,"line":414},[233,11133,11134],{}," this.inflater = inflater;\n",[233,11136,11137],{"class":235,"line":420},[233,11138,11139],{}," this.valueList = highscoreValueList;\n",[233,11141,11142],{"class":235,"line":426},[233,11143,11144],{}," }\n",[233,11146,11147],{"class":235,"line":657},[233,11148,429],{},[19,11150,11151],{},"and also overwrite the getView method from it, so that you can make your recycling in there:",[225,11153,11155],{"className":227,"code":11154,"language":229,"meta":110,"style":110},"public View getView(int position, View convertView, ViewGroup parent) {\n HighscoreViewHolder highscoreViewHolder;\n if (convertView == null) {\n // the first few elements of the list are created out of the xml\n convertView = inflater.inflate(R.layout.highscore_list_entry, null);\n highscoreViewHolder = new HighscoreViewHolder();\n highscoreViewHolder.name = (TextView) convertView.findViewById(R.id.highscore_list_entry_name);\n highscoreViewHolder.score = (TextView) convertView.findViewById(R.id.highscore_list_entry_score);\n highscoreViewHolder.rank = (TextView) convertView.findViewById(R.id.highscore_list_entry_rank);\n convertView.setTag(highscoreViewHolder);\n convertView.setFocusable(false);\n } else {\n // recycle of the view that went out of the view\n highscoreViewHolder = (HighscoreViewHolder) convertView.getTag();\n }\n HighscoreValue value = this.valueList.get(position);\n // set the new values\n highscoreViewHolder.name.setText(value.getName());\n highscoreViewHolder.score.setText(Integer.toString(value.getScore()));\n highscoreViewHolder.rank.setText(Integer.toString(value.getRank()));\n return convertView;\n}\n",[176,11156,11157,11161,11166,11171,11176,11181,11186,11191,11196,11201,11206,11211,11215,11220,11225,11229,11234,11239,11244,11249,11254,11259],{"__ignoreMap":110},[233,11158,11159],{"class":235,"line":236},[233,11160,5645],{},[233,11162,11163],{"class":235,"line":111},[233,11164,11165],{}," HighscoreViewHolder highscoreViewHolder;\n",[233,11167,11168],{"class":235,"line":402},[233,11169,11170],{}," if (convertView == null) {\n",[233,11172,11173],{"class":235,"line":408},[233,11174,11175],{}," // the first few elements of the list are created out of the xml\n",[233,11177,11178],{"class":235,"line":414},[233,11179,11180],{}," convertView = inflater.inflate(R.layout.highscore_list_entry, null);\n",[233,11182,11183],{"class":235,"line":420},[233,11184,11185],{}," highscoreViewHolder = new HighscoreViewHolder();\n",[233,11187,11188],{"class":235,"line":426},[233,11189,11190],{}," highscoreViewHolder.name = (TextView) convertView.findViewById(R.id.highscore_list_entry_name);\n",[233,11192,11193],{"class":235,"line":657},[233,11194,11195],{}," highscoreViewHolder.score = (TextView) convertView.findViewById(R.id.highscore_list_entry_score);\n",[233,11197,11198],{"class":235,"line":662},[233,11199,11200],{}," highscoreViewHolder.rank = (TextView) convertView.findViewById(R.id.highscore_list_entry_rank);\n",[233,11202,11203],{"class":235,"line":667},[233,11204,11205],{}," convertView.setTag(highscoreViewHolder);\n",[233,11207,11208],{"class":235,"line":673},[233,11209,11210],{}," convertView.setFocusable(false);\n",[233,11212,11213],{"class":235,"line":679},[233,11214,3577],{},[233,11216,11217],{"class":235,"line":684},[233,11218,11219],{}," // recycle of the view that went out of the view\n",[233,11221,11222],{"class":235,"line":689},[233,11223,11224],{}," highscoreViewHolder = (HighscoreViewHolder) convertView.getTag();\n",[233,11226,11227],{"class":235,"line":695},[233,11228,11144],{},[233,11230,11231],{"class":235,"line":701},[233,11232,11233],{}," HighscoreValue value = this.valueList.get(position);\n",[233,11235,11236],{"class":235,"line":706},[233,11237,11238],{}," // set the new values\n",[233,11240,11241],{"class":235,"line":711},[233,11242,11243],{}," highscoreViewHolder.name.setText(value.getName());\n",[233,11245,11246],{"class":235,"line":717},[233,11247,11248],{}," highscoreViewHolder.score.setText(Integer.toString(value.getScore()));\n",[233,11250,11251],{"class":235,"line":723},[233,11252,11253],{}," highscoreViewHolder.rank.setText(Integer.toString(value.getRank()));\n",[233,11255,11256],{"class":235,"line":728},[233,11257,11258],{}," return convertView;\n",[233,11260,11261],{"class":235,"line":733},[233,11262,429],{},[19,11264,11265],{},"now whats only left is to create the adapter, and assign it to the ListView:",[225,11267,11269],{"className":227,"code":11268,"language":229,"meta":110,"style":110},"highscoreListAdapter = new HighscoreListAdapter(highscoreList, getLayoutInflater());\nlistView.setAdapter(highscoreListAdapter);\n",[176,11270,11271,11276],{"__ignoreMap":110},[233,11272,11273],{"class":235,"line":236},[233,11274,11275],{},"highscoreListAdapter = new HighscoreListAdapter(highscoreList, getLayoutInflater());\n",[233,11277,11278],{"class":235,"line":111},[233,11279,11280],{},"listView.setAdapter(highscoreListAdapter);\n",[19,11282,11283,11284,11287],{},"If you make some changes in the data set you gave to the adapter, just call ",[181,11285,11286],{},"notifyDataSetChanged()"," on it to let it\nrefresh itself.",[19,11289,11290],{},"So after this change the app started remarkably faster and also ran faster then before, the views were cut to a merely\n120 in numbers.",[19,11292,11293],{},"Well, that’s it for the moment. If you want further advices regarding the performance of android apps, please watch the\nvideo I mentioned above. It helped me a lot and I’m sure it will also show you a few tricks how to make your app faster.",[560,11295,562],{},{"title":110,"searchDepth":111,"depth":111,"links":11297},[],[120,2314],"2010-05-28T08:06:05","Now that SynyxSudoku has been on the market a little while,\\nI want to tell a little about what kind of measurements we took to optimize the performance of it.","https://synyx.de/blog/performance-optimization-in-synyx-sudoku/",{},"/blog/performance-optimization-in-synyx-sudoku",{"title":11059,"description":11305},"Now that SynyxSudoku has been on the market a little while,\nI want to tell a little about what kind of measurements we took to optimize the performance of it.","blog/performance-optimization-in-synyx-sudoku",[133,11308],"performance","Now that SynyxSudoku has been on the market a little while, I want to tell a little about what kind of measurements we took to optimize the performance of it.…","5S0ZY4kFZSda0-ABG8x4vCBcnZfT2QfTdDlf0aTxKYk",{"id":11312,"title":11313,"author":11314,"body":11315,"category":11435,"date":11436,"description":11437,"extension":124,"link":11438,"meta":11439,"navigation":127,"path":11440,"seo":11441,"slug":11319,"stem":11442,"tags":11443,"teaser":11445,"__hash__":11446},"blog/blog/in-my-humble-opinion-froyo-rocks.md","In my humble opinion — FroYo rocks!",[5039],{"type":12,"value":11316,"toc":11433},[11317,11320,11323,11326,11331,11334,11342,11345,11353,11358,11361,11369,11371,11397,11402,11413,11416,11421,11424,11427],[15,11318,11313],{"id":11319},"in-my-humble-opinion-froyo-rocks",[19,11321,11322],{},"FroYo (Frozen Yoghurt) is the name Google gave its new Android 2.2 Release. FroYo is like each previous version a\nmixture between API Changes, new Userfeatures and some new cool Apps.",[19,11324,11325],{},"So, let’s divide this review into theese several specific parts and some addons at the end:",[19,11327,11328],{},[299,11329,11330],{},"First, the imho, absolutely most important part, the Enterprise Features:",[19,11332,11333],{},"New API-Features:",[85,11335,11336,11339],{},[88,11337,11338],{},"Data Backup API",[88,11340,11341],{},"Possibility to save passwords secure",[19,11343,11344],{},"New User Features:",[85,11346,11347,11350],{},[88,11348,11349],{},"updated Exchange Features",[88,11351,11352],{},"Remote Wipe",[19,11354,11355],{},[299,11356,11357],{},"Furthermore there are, of course, some more, not so enterprisy features 😉 :",[19,11359,11360],{},"New Apps:",[85,11362,11363,11366],{},[88,11364,11365],{},"Camera and Camcorder updated (possible to enable manually the leds for usage within camcorder)",[88,11367,11368],{},"Android Tethering and Usage as Hotspot",[19,11370,11344],{},[85,11372,11373,11376,11379,11382,11385,11388,11391,11394],{},[88,11374,11375],{},"Flashsupport , Airsupport (useful?)",[88,11377,11378],{},"Possibility to save Apps on SD-Card",[88,11380,11381],{},"JIT Compiler (hey this improves performance 3-5 times!!!)",[88,11383,11384],{},"HTML5 compatible browser?",[88,11386,11387],{},"With FroYo, users will be able to sync their local music collection with their Android device and stream wirelessly.",[88,11389,11390],{},"Users will also be available to backup their Apps in the Cloud",[88,11392,11393],{},"Android 2.2 finally supports multi-language keyboards too!",[88,11395,11396],{},"a lot of cool Marketplace updates (Webmarket like Androidpit.de and Search and Categories! and One click to update\nall, allow automatic update!!!! )",[19,11398,11399],{},[299,11400,11401],{},"And now some more rumors on what helps FroYo being the Apple poison 😉 :",[85,11403,11404,11407,11410],{},[88,11405,11406],{},"Kernel 2.6.32 will also improve speed for snapdragon processor smartphones like the Desire or the Milestone",[88,11408,11409],{},"Developers will be able to implement Services which interact with kind of Push Notifications. The killer here is that\nthis works bidirectional so that apps can also notifiy of feed Webapps in the Cloud with data! Interesting could be if\nGoogle is here open minded enough not to chain this, imho killerfeature, to Chrome.",[88,11411,11412],{},"Browserapps are able to use camera or sensors directly via JS! Is this the end of Tools like PhoneGap?",[19,11414,11415],{},"For those who like it … the new Google Buzz app should be much better than the crappy web version.",[85,11417,11418],{},[88,11419,11420],{},"Rumors told also that the new market is able to keep parts of froyo OS up to date automatically… could help to reduce\ndevice fragmentation!!!",[19,11422,11423],{},"As you can see, the list of new improvements is very long and in my opinion this is only the start of a little\nrevolution. All theese new features will enable Android Smartphone for a wide variety of really cool apps which we\nactually never thaught about because it was just not possible to implement ’em… we will dig more into Froyo if the first\nupdated Smartphone is available for us at Synyx… so stay tuned…",[19,11425,11426],{},"As addon, today the first Nexus One users told about their experience after the update… read more",[19,11428,11429],{},[159,11430,11431],{"href":11431,"rel":11432},"http://forum.xda-developers.com/showthread.php?t=686591",[163],{"title":110,"searchDepth":111,"depth":111,"links":11434},[],[120],"2010-05-26T11:39:24","FroYo (Frozen Yoghurt) is the name Google gave its new Android 2.2 Release. FroYo is like each previous version a\\nmixture between API Changes, new Userfeatures and some new cool Apps.","https://synyx.de/blog/in-my-humble-opinion-froyo-rocks/",{},"/blog/in-my-humble-opinion-froyo-rocks",{"title":11313,"description":11322},"blog/in-my-humble-opinion-froyo-rocks",[133,11444,229],"froyo","FroYo (Frozen Yoghurt) is the name Google gave its new Android 2.2 Release. FroYo is like each previous version a mixture between API Changes, new Userfeatures and some new cool…","pNHe7FxDSHWPlTA-DybrIz7fTVlnHhoV7GHkOZCAr2I",{"id":11448,"title":11449,"author":11450,"body":11451,"category":11571,"date":11572,"description":11573,"extension":124,"link":11574,"meta":11575,"navigation":127,"path":11576,"seo":11577,"slug":11455,"stem":11579,"tags":11580,"teaser":11582,"__hash__":11583},"blog/blog/user-statistics-from-synyxsudoku.md","User statistics from SynyxSudoku",[586],{"type":12,"value":11452,"toc":11569},[11453,11456,11464,11472,11475,11519,11522,11566],[15,11454,11449],{"id":11455},"user-statistics-from-synyxsudoku",[19,11457,11458,11459,11463],{},"First of all, I was quite surprised as i saw that 70% of the ",[159,11460,11073],{"href":11461,"rel":11462},"http://tinyurl.com/SynyxSudoku",[163]," users that\nuploaded their highscores have also agreed to send us their device specific data, because I really didn’t expect more\nthan 10-20%.",[19,11465,11466,11467,11471],{},"So as you can imagine, we’ve got quite a few samples of data since\nthe ",[159,11468,2326],{"href":11469,"rel":11470},"http://mobile.synyx.de/2010/04/22/release-of-synyxsudoku/",[163],", on that we now want to give you a little\noverview.",[19,11473,11474],{},"The devices came mostly with the latest Android versions available:",[11476,11477,11478,11491],"table",{},[11479,11480,11481],"thead",{},[11482,11483,11484,11488],"tr",{},[11485,11486,11487],"th",{},"Version",[11485,11489,11490],{},"Count",[11492,11493,11494,11503,11511],"tbody",{},[11482,11495,11496,11500],{},[11497,11498,11499],"td",{},"1.6",[11497,11501,11502],{},"39%",[11482,11504,11505,11508],{},[11497,11506,11507],{},"2.1",[11497,11509,11510],{},"11%",[11482,11512,11513,11516],{},[11497,11514,11515],{},"2.1-update1",[11497,11517,11518],{},"50%",[19,11520,11521],{},"What I can say about the reported resolutions is, that the smaller devices (240×320) aren’t that popular as it seems (\nmaybe because there’s only the HTC Tattoo that uses this resolution), but the others are quite evenly matched.",[11476,11523,11524,11533],{},[11479,11525,11526],{},[11482,11527,11528,11531],{},[11485,11529,11530],{},"Resolution",[11485,11532,11490],{},[11492,11534,11535,11543,11551,11559],{},[11482,11536,11537,11540],{},[11497,11538,11539],{},"240×320",[11497,11541,11542],{},"9%",[11482,11544,11545,11548],{},[11497,11546,11547],{},"320×480",[11497,11549,11550],{},"33%",[11482,11552,11553,11556],{},[11497,11554,11555],{},"480×800",[11497,11557,11558],{},"25%",[11482,11560,11561,11564],{},[11497,11562,11563],{},"480×854",[11497,11565,11550],{},[19,11567,11568],{},"In terms of the reported devices, the Motorola Droid and the HTC Desire are at the top of the list, followed by the HTC\nMagic, HTC Tattoo, HTC Legend, G1 and the Sony Ericsson Xperia X10i.",{"title":110,"searchDepth":111,"depth":111,"links":11570},[],[120,6546],"2010-05-10T17:30:45","First of all, I was quite surprised as i saw that 70% of the SynyxSudoku users that\\nuploaded their highscores have also agreed to send us their device specific data, because I really didn’t expect more\\nthan 10-20%.","https://synyx.de/blog/user-statistics-from-synyxsudoku/",{},"/blog/user-statistics-from-synyxsudoku",{"title":11449,"description":11578},"First of all, I was quite surprised as i saw that 70% of the SynyxSudoku users that\nuploaded their highscores have also agreed to send us their device specific data, because I really didn’t expect more\nthan 10-20%.","blog/user-statistics-from-synyxsudoku",[133,11581],"statistics","First of all, I was quite surprised as i saw that 70% of the SynyxSudoku users that uploaded their highscores have also agreed to send us their device specific data,…","Lu0rWnhyc5_KUTrPjR1VMv11nvaY7-qq8Fh410R9lDA",{"id":11585,"title":11586,"author":11587,"body":11588,"category":11626,"date":11627,"description":11628,"extension":124,"link":11629,"meta":11630,"navigation":127,"path":11631,"seo":11632,"slug":11634,"stem":11635,"tags":11636,"teaser":11638,"__hash__":11639},"blog/blog/synyxsudoku-update-to-version-1-02.md","SynyxSudoku update to version 1.02",[586],{"type":12,"value":11589,"toc":11624},[11590,11593,11600,11603,11614,11617,11620],[15,11591,11586],{"id":11592},"synyxsudoku-update-to-version-102",[19,11594,11595,11596,11599],{},"There were a few little things that had to be changed on SynyxSudoku after\nthe ",[159,11597,2326],{"href":11469,"rel":11598},[163],", so today we uploaded an update to the version\n1.02.",[19,11601,11602],{},"Here’s a quick changelog:",[85,11604,11605,11608,11611],{},[88,11606,11607],{},"You can now only resume a game after a restart of the app, if you started a game in the previous session (or before)\nand didn’t solve it. (If the app was moved into the background while a solved game was active, you can still resume it\nif you like. It’s only gone if you close the app.)",[88,11609,11610],{},"If you created a game and restarted the app twice you without resuming the sudoku on the first time, you couldn’t\nresume it the second time, it only displayed a empty field (Thanks to Markus who found this bug).",[88,11612,11613],{},"The sleep mode is now deactivated if you are on the sudoku screen. (It’s quite annoying if you are in midst of your\nthinking process and then the screen suddenly turns black…)",[19,11615,11616],{},"If you notice any bugs or if something bothers you about the game, please leave a comment, or write us an email.",[19,11618,11619],{},"Download:",[19,11621,11622],{},[33,11623],{"alt":110,"src":11461},{"title":110,"searchDepth":111,"depth":111,"links":11625},[],[120,6546],"2010-05-10T14:09:19","There were a few little things that had to be changed on SynyxSudoku after\\nthe release, so today we uploaded an update to the version\\n1.02.","https://synyx.de/blog/synyxsudoku-update-to-version-1-02/",{},"/blog/synyxsudoku-update-to-version-1-02",{"title":11586,"description":11633},"There were a few little things that had to be changed on SynyxSudoku after\nthe release, so today we uploaded an update to the version\n1.02.","synyxsudoku-update-to-version-1-02","blog/synyxsudoku-update-to-version-1-02",[133,11637],"sudoku","There were a few little things that had to be changed on SynyxSudoku after the release, so today we uploaded an update to the version 1.02. Here’s a quick changelog:…","mUUFtoMEioYpW-319oulDYaHulQXuJwqKQ9BWwLeFqc",{"id":11641,"title":11642,"author":11643,"body":11645,"category":11805,"date":11806,"description":11807,"extension":124,"link":11808,"meta":11809,"navigation":127,"path":11810,"seo":11811,"slug":11649,"stem":11813,"tags":11814,"teaser":11817,"__hash__":11818},"blog/blog/google-maps-on-android-part-2-overlays.md","Google Maps on Android – Part 2: Overlays",[11644],"heib",{"type":12,"value":11646,"toc":11803},[11647,11650,11659,11677,11680,11683,11686,11689,11692,11695,11698,11709,11723,11726,11729,11732,11735,11742,11745,11748,11764,11767,11770,11773,11775,11781],[15,11648,11642],{"id":11649},"google-maps-on-android-part-2-overlays",[19,11651,11652,11653,11658],{},"In my ",[159,11654,11657],{"href":11655,"rel":11656},"http://mobile.synyx.de/2010/04/30/google-maps-on-android/",[163],"last post about Google Maps on Android"," I showed you\nhow to use the basic navigation features of google maps, like moving the map to a defined area and zooming to a given\nlevel.",[19,11660,11661,11662,11667,11668,11672,11673,11676],{},"Now as you have centred your map and zoomed in, it would be a good idea to show some kind of marker. Otherwise the user\nwon’t actually realize what you want to show him. How a basic overlay is done, is described in\nthe ",[159,11663,11666],{"href":11664,"rel":11665},"http://developer.android.com/resources/tutorials/views/hello-mapview.html",[163],"tutorial on the android developer site",",\nalready mentioned in the ",[159,11669,11671],{"href":11655,"rel":11670},[163],"last post",". The\n",[181,11674,11675],{},"HelloItemizedOverlay"," that is created there, enables you to add as many markers to your map as you want:",[19,11678,11679],{},"`List mapOverlays = mapView.getOverlays();",[19,11681,11682],{},"Drawable drawable = this.getResources().getDrawable(R.drawable.synyxLogo);",[19,11684,11685],{},"HelloItemizedOverlay itemizedOverlay = new HelloItemizedOverlay(drawable);",[19,11687,11688],{},"OverlayItem synyxOfficeOverlay = new OverlayItem(synyxOfficeLocation, \"Synyx\", \"This is the Synyx office!\");",[19,11690,11691],{},"itemizedOverlay.addOverlay(synyxOfficeOverlay);",[19,11693,11694],{},"// add more overlayItems...",[19,11696,11697],{},"mapOverlays.add(itemizedOverlay);`",[19,11699,11700,11701,11704,11705,11708],{},"This will draw a nice marker just at the location of the synyx office. The drawback of this method is, that all markers\non the map just look the same. You might think: No problem – just use the ",[181,11702,11703],{},"setMarker()"," method of the ",[181,11706,11707],{},"OverlayItem"," to\nset a new marker for just this item. As this looks like the way to do it, it won’t work. All you get is no marker (not\nthe one you just set, nor the default one). The map is just shown as if your marker was not defined at all.",[19,11710,11711,11712,11715,11716,11719,11720,11722],{},"So how to do it? The trick is the static method ",[181,11713,11714],{},"boundCenterBottom()"," of the ",[181,11717,11718],{},"ItemizedOverlay"," class. This method is\nalso called in the constructor of the ",[181,11721,11675],{},", when the default marker is set:",[19,11724,11725],{},"`public RouteMapOverlay(Drawable defaultMarker, Context context) {",[19,11727,11728],{},"super(boundCenterBottom(defaultMarker));",[19,11730,11731],{},"this.context = context;",[19,11733,11734],{},"}`",[19,11736,11737,11738,11741],{},"The problem is, that the visibility of this method is set to ",[181,11739,11740],{},"protected"," (why?!). So calling it when setting the marker\nwon’t work:",[19,11743,11744],{},"`// won't work as boundCenterBottom is protected",[19,11746,11747],{},"synyxOfficeOverlay.setOverlay(ItemizedOverlay.boundCenterBottom(myNewMarker));`",[19,11749,11750,11751,11753,11754,11756,11757,11760,11761,11763],{},"Because of this visibility feature, the “easier” way is to add a new setter to your ",[181,11752,11675],{}," class,\naccepting an ",[181,11755,11707],{}," and a ",[181,11758,11759],{},"Drawable"," to use as the marker. Within that setter, you are able to call the\n",[181,11762,11714],{}," method to set up the marker correctly:",[19,11765,11766],{},"`public void addOverlay(OverlayItem overlayItem, Drawable marker) {",[19,11768,11769],{},"overlayItem.setMarker(boundCenterBottom(marker));",[19,11771,11772],{},"addOverlay(overlayItem);",[19,11774,11734],{},[19,11776,11777,11778,11780],{},"Now if you use that setter, the map will correctly show your ",[181,11779,11707],{}," with your marker.",[19,11782,11783,11784,11786,11787,11789,11790,11793,11794,11797,11798,11800,11801,1581],{},"Btw: instead of calling ",[181,11785,11714],{}," (which places the bottom centre of your ",[181,11788,11759],{}," over the defined\n",[181,11791,11792],{},"GeoPoint","), you can also use ",[181,11795,11796],{},"boundCenter()"," to place the centre of your ",[181,11799,11759],{}," over the ",[181,11802,11792],{},{"title":110,"searchDepth":111,"depth":111,"links":11804},[],[120,2314],"2010-05-07T16:52:13","In my last post about Google Maps on Android I showed you\\nhow to use the basic navigation features of google maps, like moving the map to a defined area and zooming to a given\\nlevel.","https://synyx.de/blog/google-maps-on-android-part-2-overlays/",{},"/blog/google-maps-on-android-part-2-overlays",{"title":11642,"description":11812},"In my last post about Google Maps on Android I showed you\nhow to use the basic navigation features of google maps, like moving the map to a defined area and zooming to a given\nlevel.","blog/google-maps-on-android-part-2-overlays",[133,11815,11816,10557],"google-maps","marker","In my last post about Google Maps on Android I showed you how to use the basic navigation features of google maps, like moving the map to a defined area…","GBZ-bHS5o5wBdJ9oZTRvsybQjazx-suQybxkfoerVPM",{"id":11820,"title":11821,"author":11822,"body":11823,"category":11895,"date":11896,"description":11897,"extension":124,"link":11898,"meta":11899,"navigation":127,"path":11900,"seo":11901,"slug":11903,"stem":11904,"tags":11905,"teaser":11907,"__hash__":11908},"blog/blog/google-maps-on-android.md","Google Maps on Android – Part 1: Navigation",[11644],{"type":12,"value":11824,"toc":11893},[11825,11828,11834,11846,11851,11864,11867,11870,11873,11876],[15,11826,11821],{"id":11827},"google-maps-on-android-part-1-navigation",[19,11829,11830,11831,1581],{},"Integrating a google map on android is quiet simple – how to do this basically is shown in\nthe ",[159,11832,11666],{"href":11664,"rel":11833},[163],[19,11835,11836,11837,11842,11843,11845],{},"Showing the map itself is one part, but most likely you want to interact with it in some way. The default map is\ncompletely zoomed out and centred somewhere over America. As this gives you a good idea how America looks like, it is\nnot very useful for showing the location of the Synyx office. So what we need to do, is to move the map to some point\nand zoom in to a certain level. First, lets get the coordinates for the Synyx office. This can easily be done by using\ngoogle maps: Navigate to ",[159,11838,11841],{"href":11839,"rel":11840},"http://maps.google.com",[163],"maps.google.com",", click on the “New” link on the top right and\nactivate the “LatLng Marker” from the Google Maps Labs. This adds an option to the context menu to drop a marker that\nshows the latitude/ longitude values. Use those values to create a new ",[181,11844,11792],{}," in your Android app:",[19,11847,11848],{},[176,11849,11850],{},"GeoPoint synyxOfficeLocation = new GeoPoint(49002175, 8394160);",[19,11852,11853,11854,11856,11857,11860,11861,324],{},"As the ",[181,11855,11792],{}," handles its values in microdegrees, the values obtained from google maps must be multiplied with 100 000. Now as we have the GeoPoint, we need to centre the map to it and zoom in to a certain level. To perform this\ntasks, each ",[181,11858,11859],{},"MapView"," has a ",[181,11862,11863],{},"MapController",[19,11865,11866],{},"`MapController mapController = mapView.getController();",[19,11868,11869],{},"mapController.setCenter(synyxOfficeLocation);",[19,11871,11872],{},"mapController.setZoom(20);`",[19,11874,11875],{},"This centres the map to the Synyx office. The zoom level must be a value between 1 (fully zoomed out) and 21 (fully\nzoomed in), so the value of 20 is already quiet close. Please note, that the highest zoom levels might not be available\nfor all areas.",[19,11877,11878,11879,5929,11882,254,11885,11888,11889,11892],{},"The above mentioned methods change the map in a very static way. For some more visual effects try ",[181,11880,11881],{},"animateTo()",[181,11883,11884],{},"zoomIn()",[181,11886,11887],{},"zoomOut()"," to change the view of the map by showing a short animation. An also very helpful method is\n",[181,11890,11891],{},"zoomToSpan()"," which lets you define a latitude and longitude span that should be visible on the map – very handy if you\nwant to ensure that two or more points are visible to the user on the map.",{"title":110,"searchDepth":111,"depth":111,"links":11894},[],[120,2314],"2010-04-30T12:42:54","Integrating a google map on android is quiet simple – how to do this basically is shown in\\nthe tutorial on the android developer site.","https://synyx.de/blog/google-maps-on-android/",{},"/blog/google-maps-on-android",{"title":11821,"description":11902},"Integrating a google map on android is quiet simple – how to do this basically is shown in\nthe tutorial on the android developer site.","google-maps-on-android","blog/google-maps-on-android",[133,11815,11906],"interaction","Integrating a google map on android is quiet simple – how to do this basically is shown in the tutorial on the android developer site. Showing the map itself is…","M12KhufnwFTj7q3RUZch3BzzQs8S7YaCZwbJ9q7ROfs",{"id":11910,"title":11911,"author":11912,"body":11913,"category":11956,"date":11957,"description":11958,"extension":124,"link":11959,"meta":11960,"navigation":127,"path":11961,"seo":11962,"slug":11917,"stem":11963,"tags":11964,"teaser":11966,"__hash__":11967},"blog/blog/release-of-synyxsudoku.md","Release of SynyxSudoku",[586],{"type":12,"value":11914,"toc":11954},[11915,11918,11921,11924,11927,11930,11933,11936,11940,11943],[15,11916,11911],{"id":11917},"release-of-synyxsudoku",[19,11919,11920],{},"We are proud to announce, that our sudoku app, “SynyxSudoku” is now available on the android store. And best of all:\nIt’s completely free of charge !(except for your traffic costs, of course 🙂 )",[19,11922,11923],{},"SynyxSudoku offers 3 difficulty levels, containing nearly unlimited puzzling fun due to new created sudokus each time\nyou launch!",[19,11925,11926],{},"And even if none of these difficulty levels fits your needs – SynyxSudoku has the option to let you create a difficulty\nlevel of your own.",[19,11928,11929],{},"You can also save your best performances in solving the sudokus in the highscore and even upload it to our server to\ncompete with other sudoku players around the world to finally answer the question: “Who is the fastest sudoku-solver?”.",[19,11931,11932],{},"Need a little break during a game? No problem! Just pause, or even close it and you can continue the next time you\nstart. And if somebody calls you in midst of a game, it is automatically paused!",[19,11934,11935],{},"Download our game by searching for Synyx Sudoku on the android market or using this qr-code:",[19,11937,11938],{},[33,11939],{"alt":110,"src":11461},[19,11941,11942],{},"If you are curious how SynyxSudoku looks like, here we have a few screenshots:",[19,11944,11945,8554,11948,8554,11951],{},[33,11946],{"alt":110,"src":11947},"https://media.synyx.de/uploads//2010/04/sudoku_1.png",[33,11949],{"alt":110,"src":11950},"https://media.synyx.de/uploads//2010/04/sudoku_menu_en.png",[33,11952],{"alt":110,"src":11953},"https://media.synyx.de/uploads//2010/04/sudoku_4_en.png",{"title":110,"searchDepth":111,"depth":111,"links":11955},[],[120,6546],"2010-04-22T17:12:34","We are proud to announce, that our sudoku app, “SynyxSudoku” is now available on the android store. And best of all:\\nIt’s completely free of charge !(except for your traffic costs, of course 🙂 )","https://synyx.de/blog/release-of-synyxsudoku/",{},"/blog/release-of-synyxsudoku",{"title":11911,"description":11920},"blog/release-of-synyxsudoku",[133,11965,11637],"gaming","We are proud to announce, that our sudoku app, “SynyxSudoku” is now available on the android store. And best of all: It’s completely free of charge ! (except for your traffic costs,…","MEK2L19-Qxl007OJY5Iv85w14HBR-yyjszz0hZsjCbI",{"id":11969,"title":11970,"author":11971,"body":11972,"category":11990,"date":11991,"description":11979,"extension":124,"link":11992,"meta":11993,"navigation":127,"path":11994,"seo":11995,"slug":11996,"stem":11997,"tags":11998,"teaser":11999,"__hash__":12000},"blog/blog/sudoku-on-android-2.md","Sudoku on Android",[586],{"type":12,"value":11973,"toc":11988},[11974,11977,11980,11985],[15,11975,11970],{"id":11976},"sudoku-on-android",[19,11978,11979],{},"Here’s a quick preview of our upcoming sudoku app on android:",[19,11981,11982],{},[233,11983,11984],{},"youtube S_wjWf7V660",[19,11986,11987],{},"The layout has yet to be polished a little, but altogether the app is nearly finished.",{"title":110,"searchDepth":111,"depth":111,"links":11989},[],[120,6546],"2010-04-15T18:49:51","https://synyx.de/blog/sudoku-on-android-2/",{},"/blog/sudoku-on-android-2",{"title":11970,"description":11979},"sudoku-on-android-2","blog/sudoku-on-android-2",[133,11965],"Here’s a quick preview of our upcoming sudoku app on android: [youtube S_wjWf7V660] The layout has yet to be polished a little, but altogether the app is nearly finished.","tzc504LoreZVNA5CrnE9g9ba4AGugW0r1Z80n8PaOmU",{"id":12002,"title":12003,"author":12004,"body":12005,"category":12040,"date":12041,"description":12042,"extension":124,"link":12043,"meta":12044,"navigation":127,"path":12045,"seo":12046,"slug":12009,"stem":12048,"tags":12049,"teaser":12051,"__hash__":12052},"blog/blog/welcome.md","Welcome",[6531],{"type":12,"value":12006,"toc":12038},[12007,12010,12030],[15,12008,12003],{"id":12009},"welcome",[19,12011,12012,12013,5883,12018,12023,12024,12029],{},"This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\nmobile space. No matter if it’s ",[159,12014,12017],{"href":12015,"rel":12016},"http://android.org",[163],"Google’s Android",[159,12019,12022],{"href":12020,"rel":12021},"http://apple.com/iphone",[163],"Apple’s iPhone","\nor ",[159,12025,12028],{"href":12026,"rel":12027},"http://www.meego.com/",[163],"MeeGo"," we have something to say about it!",[19,12031,12032,12033,1581],{},"Stay tuned and subscribe to our ",[159,12034,12037],{"href":12035,"rel":12036},"http://mobile.synyx.de/?feed=rss2",[163],"RSS feed",{"title":110,"searchDepth":111,"depth":111,"links":12039},[],[120],"2010-03-26T17:24:38","This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\\nmobile space. No matter if it’s Google’s Android, Apple’s iPhone\\nor MeeGo we have something to say about it!","https://synyx.de/blog/welcome/",{},"/blog/welcome",{"title":12003,"description":12047},"This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the\nmobile space. No matter if it’s Google’s Android, Apple’s iPhone\nor MeeGo we have something to say about it!","blog/welcome",[133,6555,12050,5163],"meego","This is the inception of Synyx Mobile Solutions. In the near future we’ll blog here about a wide range of topics in the mobile space. No matter if it’s Google’s…","fxYe2l28A3Tr4AN4j9XfZ6v3dxy5234kvArmmK3JoB4",[12054,12057,12060,12063,12065,12068,12071,12074,12077,12080,12083,12086,12089,12092,12095,12098,12101,12104,12107,12110,12113,12116,12118,12121,12124,12127,12130,12132,12135,12138,12141,12144,12147,12150,12153,12156,12159,12162,12165,12168,12171,12174,12176,12179,12181,12184,12187,12190,12193,12196,12198,12201,12204,12207,12210,12213,12216,12219,12222,12225,12228,12231,12234,12236,12239,12242,12245,12248,12250,12253,12256,12259,12262,12265,12267,12270,12273,12276,12279,12282,12285,12288,12291,12294,12297,12300,12303,12306,12309,12312,12315,12318,12321,12324,12327,12330,12333,12336,12339,12341,12344,12347,12350,12353,12355,12358,12361,12364,12367,12370,12373,12376,12379,12382,12385,12388,12391,12394,12397,12400,12403,12406,12409,12412,12415,12418,12421,12424,12427,12430,12431,12434,12437,12440,12443,12446,12449,12451,12454,12457,12460,12463],{"slug":12055,"name":12056},"abel","Jennifer Abel",{"slug":12058,"name":12059},"allmendinger","Otto Allmendinger",{"slug":12061,"name":12062},"antony","Ben Antony",{"slug":5039,"name":12064},"Joachim Arrasz",{"slug":12066,"name":12067},"bauer","David Bauer",{"slug":12069,"name":12070},"bechtold","Janine Bechtold",{"slug":12072,"name":12073},"boersig","Jasmin Börsig",{"slug":12075,"name":12076},"buch","Fabian Buch",{"slug":12078,"name":12079},"buchloh","Aljona Buchloh",{"slug":12081,"name":12082},"burgard","Julia Burgard",{"slug":12084,"name":12085},"caspar-schwedes","Caspar Schwedes",{"slug":12087,"name":12088},"christina-schmitt","Christina Schmitt",{"slug":12090,"name":12091},"clausen","Michael Clausen",{"slug":12093,"name":12094},"contargo_poetzsch","Thomas Pötzsch",{"slug":12096,"name":12097},"damrath","Sebastian Damrath",{"slug":12099,"name":12100},"daniel","Markus Daniel",{"slug":12102,"name":12103},"dasch","Julia Dasch",{"slug":12105,"name":12106},"denman","Joffrey Denman",{"slug":12108,"name":12109},"dfuchs","Daniel Fuchs",{"slug":12111,"name":12112},"dobler","Max Dobler",{"slug":12114,"name":12115},"dobriakov","Vladimir Dobriakov",{"slug":12117,"name":12117},"dreiqbik",{"slug":12119,"name":12120},"dschaefer","Denise Schäfer",{"slug":12122,"name":12123},"dschneider","Dominik Schneider",{"slug":12125,"name":12126},"duerlich","Isabell Duerlich",{"slug":12128,"name":12129},"dutkowski","Bernd Dutkowski",{"slug":12131,"name":12131},"eifler",{"slug":12133,"name":12134},"essig","Tim Essig",{"slug":12136,"name":12137},"ferstl","Maximilian Ferstl",{"slug":12139,"name":12140},"fey","Prisca Fey",{"slug":12142,"name":12143},"frank","Leonard Frank",{"slug":12145,"name":12146},"franke","Arnold Franke",{"slug":12148,"name":12149},"frischer","Nicolette Rudmann",{"slug":12151,"name":12152},"fuchs","Petra Fuchs",{"slug":12154,"name":12155},"gari","Sarah Gari",{"slug":12157,"name":12158},"gast","Gast",{"slug":12160,"name":12161},"graf","Johannes Graf",{"slug":12163,"name":12164},"grammlich","Daniela Grammlich",{"slug":12166,"name":12167},"guthardt","Sabrina Guthardt",{"slug":12169,"name":12170},"haeussler","Johannes Häussler",{"slug":12172,"name":12173},"hammann","Daniel Hammann",{"slug":10,"name":12175},"Julian Heetel",{"slug":12177,"name":12178},"heft","Florian Heft",{"slug":11644,"name":12180},"Sebastian Heib",{"slug":12182,"name":12183},"heisler","Ida Heisler",{"slug":12185,"name":12186},"helm","Patrick Helm",{"slug":12188,"name":12189},"herbold","Michael Herbold",{"slug":12191,"name":12192},"hofmann","Peter Hofmann",{"slug":12194,"name":12195},"hopf","Florian Hopf",{"slug":9,"name":12197},"Alina Jaud",{"slug":12199,"name":12200},"jayasinghe","Robin De Silva Jayasinghe",{"slug":12202,"name":12203},"jbuch","Jonathan Buch",{"slug":12205,"name":12206},"junghanss","Gitta Junghanß",{"slug":12208,"name":12209},"kadyietska","Khrystyna Kadyietska",{"slug":12211,"name":12212},"kannegiesser","Marc Kannegiesser",{"slug":12214,"name":12215},"karoly","Robert Károly",{"slug":12217,"name":12218},"karrasz","Katja Arrasz-Schepanski",{"slug":12220,"name":12221},"kaufmann","Florian Kaufmann",{"slug":12223,"name":12224},"kesler","Mike Kesler",{"slug":12226,"name":12227},"kirchgaessner","Bettina Kirchgäßner",{"slug":12229,"name":12230},"klem","Yannic Klem",{"slug":12232,"name":12233},"klenk","Timo Klenk",{"slug":586,"name":12235},"Tobias Knell",{"slug":12237,"name":12238},"knoll","Anna-Lena Knoll",{"slug":12240,"name":12241},"knorre","Matthias Knorre",{"slug":12243,"name":12244},"koenig","Melanie König",{"slug":12246,"name":12247},"kraft","Thomas Kraft",{"slug":6045,"name":12249},"Florian Krupicka",{"slug":12251,"name":12252},"kuehn","Christian Kühn",{"slug":12254,"name":12255},"lange","Christian Lange",{"slug":12257,"name":12258},"larrasz","Luca Arrasz",{"slug":12260,"name":12261},"leist","Sascha Leist",{"slug":12263,"name":12264},"lihs","Michael Lihs",{"slug":6531,"name":12266},"David Linsin",{"slug":12268,"name":12269},"maniyar","Christian Maniyar",{"slug":12271,"name":12272},"martin","Björnie",{"slug":12274,"name":12275},"martin-koch","Martin Koch",{"slug":12277,"name":12278},"matt","Tobias Matt",{"slug":12280,"name":12281},"mennerich","Christian Mennerich",{"slug":12283,"name":12284},"menz","Alexander Menz",{"slug":12286,"name":12287},"meseck","Frederick Meseck",{"slug":12289,"name":12290},"messner","Oliver Messner",{"slug":12292,"name":12293},"michael-ploed","Michael Plöd",{"slug":12295,"name":12296},"mies","Marius Mies",{"slug":12298,"name":12299},"mihai","Alina Mihai",{"slug":12301,"name":12302},"moeller","Jörg Möller",{"slug":12304,"name":12305},"mohr","Rebecca Mohr",{"slug":12307,"name":12308},"moretti","David Moretti",{"slug":12310,"name":12311},"mueller","Sven Müller",{"slug":12313,"name":12314},"muessig","Alexander Müssig",{"slug":12316,"name":12317},"neupokoev","Grigory Neupokoev",{"slug":12319,"name":12320},"nussbaecher","Carmen Nussbächer",{"slug":12322,"name":12323},"ochs","Pascal Ochs",{"slug":12325,"name":12326},"oelhoff","Jan Oelhoff",{"slug":12328,"name":12329},"oengel","Yasin Öngel",{"slug":12331,"name":12332},"oezsoy","Enis Özsoy",{"slug":12334,"name":12335},"posch","Maya Posch",{"slug":12337,"name":12338},"ralfmueller","Ralf Müller",{"slug":12340,"name":12340},"redakteur",{"slug":12342,"name":12343},"reich","Michael Reich",{"slug":12345,"name":12346},"reinhard","Karl-Ludwig Reinhard",{"slug":12348,"name":12349},"rmueller","Rebecca Müller",{"slug":12351,"name":12352},"rosum","Jan Rosum",{"slug":12354,"name":12354},"rueckert",{"slug":12356,"name":12357},"ruessel","Sascha Rüssel",{"slug":12359,"name":12360},"sauter","Moritz Sauter",{"slug":12362,"name":12363},"schaefer","Julian Schäfer",{"slug":12365,"name":12366},"scherer","Petra Scherer",{"slug":12368,"name":12369},"schlicht","Anne Schlicht",{"slug":12371,"name":12372},"schmidt","Jürgen Schmidt",{"slug":12374,"name":12375},"schneider","Tobias Schneider",{"slug":12377,"name":12378},"seber","Benjamin Seber",{"slug":12380,"name":12381},"sommer","Marc Sommer",{"slug":12383,"name":12384},"speaker-fels","Jakob Fels",{"slug":12386,"name":12387},"speaker-gierke","Oliver Gierke",{"slug":12389,"name":12390},"speaker-krupa","Malte Krupa",{"slug":12392,"name":12393},"speaker-mader","Jochen Mader",{"slug":12395,"name":12396},"speaker-meusel","Tim Meusel",{"slug":12398,"name":12399},"speaker-milke","Oliver Milke",{"slug":12401,"name":12402},"speaker-paluch","Mark Paluch",{"slug":12404,"name":12405},"speaker-schad","Jörg Schad",{"slug":12407,"name":12408},"speaker-schalanda","Jochen Schalanda",{"slug":12410,"name":12411},"speaker-schauder","Jens Schauder",{"slug":12413,"name":12414},"speaker-unterstein","Johannes Unterstein",{"slug":12416,"name":12417},"speaker-wolff","Eberhard Wolff",{"slug":12419,"name":12420},"speaker-zoerner","Stefan Zörner",{"slug":12422,"name":12423},"stefan-belger","Stefan Belger",{"slug":12425,"name":12426},"steinegger","Roland Steinegger",{"slug":12428,"name":12429},"stern","sternchen synyx",{"slug":5163,"name":5163},{"slug":12432,"name":12433},"szulc","Mateusz Szulc",{"slug":12435,"name":12436},"tamara","Tamara Tunczinger",{"slug":12438,"name":12439},"theuer","Tobias Theuer",{"slug":12441,"name":12442},"thieme","Sandra Thieme",{"slug":12444,"name":12445},"thies-clasen","Marudor",{"slug":12447,"name":12448},"toernstroem","Olle Törnström",{"slug":143,"name":12450},"Max Ullinger",{"slug":12452,"name":12453},"ulrich","Stephan Ulrich",{"slug":12455,"name":12456},"wagner","Stefan Wagner",{"slug":12458,"name":12459},"weigel","Andreas Weigel",{"slug":12461,"name":12462},"werner","Fabian Werner",{"slug":12464,"name":12465},"wolke","Sören Wolke",["Reactive",12467],{"$scookieConsent":12468,"$ssite-config":12470},{"functional":12469,"analytics":12469},false,{"_priority":12471,"env":12475,"name":12476,"url":12477},{"name":12472,"env":12473,"url":12474},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",12480],{"category-android":-1,"authors":-1},"/blog/tags/android"]