\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","xml",[153,154,155,163,168,174,180,186,192,198,204,210,216,222,228],"code",{"__ignoreMap":83},[156,157,160],"span",{"class":158,"line":159},"line",1,[156,161,162],{"emptyLinePlaceholder":94},"\n",[156,164,165],{"class":158,"line":84},[156,166,167],{},"\u003Cpermission\n",[156,169,171],{"class":158,"line":170},3,[156,172,173],{}," android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"\n",[156,175,177],{"class":158,"line":176},4,[156,178,179],{}," android:protectionLevel=\"signature\"/>\n",[156,181,183],{"class":158,"line":182},5,[156,184,185],{},"\u003Cuses-permission android:name=\"com.synyx.cloudmessagetest.permission.C2D_MESSAGE\"/>\n",[156,187,189],{"class":158,"line":188},6,[156,190,191],{}," \u003C!-- App receives GCM messages. -->\n",[156,193,195],{"class":158,"line":194},7,[156,196,197],{},"\u003Cuses-permission android:name=\"com.google.android.c2dm.permission.RECEIVE\"/>\n",[156,199,201],{"class":158,"line":200},8,[156,202,203],{}," \u003C!-- GCM connects to Google Services. -->\n",[156,205,207],{"class":158,"line":206},9,[156,208,209],{},"\u003Cuses-permission android:name=\"android.permission.INTERNET\"/>\n",[156,211,213],{"class":158,"line":212},10,[156,214,215],{}," \u003C!-- GCM requires a Google account. -->\n",[156,217,219],{"class":158,"line":218},11,[156,220,221],{},"\u003Cuses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>\n",[156,223,225],{"class":158,"line":224},12,[156,226,227],{}," \u003C!-- Keeps the processor from sleeping when a message is received. -->\n",[156,229,231],{"class":158,"line":230},13,[156,232,233],{},"\u003Cuses-permission android:name=\"android.permission.WAKE_LOCK\"/>\n",[18,235,236],{},"And declare a GCM broadcast receiver within the application tag:",[147,238,240],{"className":149,"code":239,"language":151,"meta":83,"style":83},"\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",[153,241,242,246,251,256,261,266,271,276,281,286,291],{"__ignoreMap":83},[156,243,244],{"class":158,"line":159},[156,245,162],{"emptyLinePlaceholder":94},[156,247,248],{"class":158,"line":84},[156,249,250],{},"\u003Creceiver\n",[156,252,253],{"class":158,"line":170},[156,254,255],{}," android:name=\"com.google.android.gcm.GCMBroadcastReceiver\"\n",[156,257,258],{"class":158,"line":176},[156,259,260],{}," android:permission=\"com.google.android.c2dm.permission.SEND\">\n",[156,262,263],{"class":158,"line":182},[156,264,265],{}," \u003Cintent-filter>\n",[156,267,268],{"class":158,"line":188},[156,269,270],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.RECEIVE\"/>\n",[156,272,273],{"class":158,"line":194},[156,274,275],{}," \u003Caction android:name=\"com.google.android.c2dm.intent.REGISTRATION\"/>\n",[156,277,278],{"class":158,"line":200},[156,279,280],{}," \u003Ccategory android:name=\"com.synyx.cloudmessagetest\"/>\n",[156,282,283],{"class":158,"line":206},[156,284,285],{}," \u003C/intent-filter>\n",[156,287,288],{"class":158,"line":212},[156,289,290],{},"\u003C/receiver>\n",[156,292,293],{"class":158,"line":218},[156,294,295],{},"\u003Cservice android:name=\".GCMIntentService\"/>\n",[18,297,298],{},"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:",[147,300,304],{"className":301,"code":302,"language":303,"meta":83,"style":83},"language-java shiki shiki-themes github-light github-dark","public class GCMIntentService extends GCMBaseIntentService {\n","java",[153,305,306],{"__ignoreMap":83},[156,307,308],{"class":158,"line":159},[156,309,302],{},[18,311,312],{},"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.",[18,314,315],{},"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.",[18,317,318],{},"First in the onMessage() method, we need to get access to the Main Thread of our App.",[147,320,322],{"className":301,"code":321,"language":303,"meta":83,"style":83},"Handler h = new Handler(Looper.getMainLooper());\nh.post(new Runnable() {\n public void run() {\n }\n}\n",[153,323,324,329,334,339,344],{"__ignoreMap":83},[156,325,326],{"class":158,"line":159},[156,327,328],{},"Handler h = new Handler(Looper.getMainLooper());\n",[156,330,331],{"class":158,"line":84},[156,332,333],{},"h.post(new Runnable() {\n",[156,335,336],{"class":158,"line":170},[156,337,338],{}," public void run() {\n",[156,340,341],{"class":158,"line":176},[156,342,343],{}," }\n",[156,345,346],{"class":158,"line":182},[156,347,348],{},"}\n",[18,350,351],{},"In the run() method, we get us an Intent from our MainActivity",[147,353,355],{"className":301,"code":354,"language":303,"meta":83,"style":83},"Intent notificationIntent = new Intent(context, CloudMessageTestActivity.class);\n",[153,356,357],{"__ignoreMap":83},[156,358,359],{"class":158,"line":159},[156,360,354],{},[18,362,363],{},"And then wrap it with a PendingIntent for the NotificationBuilder",[147,365,367],{"className":301,"code":366,"language":303,"meta":83,"style":83},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n notificationIntent, 0);\n",[153,368,369,374],{"__ignoreMap":83},[156,370,371],{"class":158,"line":159},[156,372,373],{},"PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,\n",[156,375,376],{"class":158,"line":84},[156,377,378],{}," notificationIntent, 0);\n",[18,380,381],{},"Finally, use the NotificationBuilder to create and send the notification",[147,383,385],{"className":301,"code":384,"language":303,"meta":83,"style":83},"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",[153,386,387,392,397,402,407,412,417,422,427,432,437,442,447],{"__ignoreMap":83},[156,388,389],{"class":158,"line":159},[156,390,391],{},"NotificationCompat.Builder builder = new NotificationCompat.Builder(\n",[156,393,394],{"class":158,"line":84},[156,395,396],{}," context);\n",[156,398,399],{"class":158,"line":170},[156,400,401],{},"builder.setContentIntent(pendingIntent);\n",[156,403,404],{"class":158,"line":176},[156,405,406],{},"builder.setAutoCancel(true);\n",[156,408,409],{"class":158,"line":182},[156,410,411],{},"builder.setSmallIcon(R.drawable.ic_launcher);\n",[156,413,414],{"class":158,"line":188},[156,415,416],{},"//this is added on the server side\n",[156,418,419],{"class":158,"line":194},[156,420,421],{},"String text = intent.getStringExtra(\"text\");\n",[156,423,424],{"class":158,"line":200},[156,425,426],{},"builder.setContentText(text);\n",[156,428,429],{"class":158,"line":206},[156,430,431],{},"builder.setContentTitle(\"New message from the cloud!\");\n",[156,433,434],{"class":158,"line":212},[156,435,436],{},"Notification noti = builder.build();\n",[156,438,439],{"class":158,"line":218},[156,440,441],{},"NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);\n",[156,443,444],{"class":158,"line":224},[156,445,446],{},"//just set the mId to 1, because we don't care about it in this case\n",[156,448,449],{"class":158,"line":230},[156,450,451],{},"mNotificationManager.notify(1, noti);\n",[18,453,454],{},"That’ it with the GCMIntentService.",[18,456,457],{},"Now we let the app register itself with GCM in our MainActivity, which is fairly easy:",[147,459,461],{"className":301,"code":460,"language":303,"meta":83,"style":83},"//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",[153,462,463,468,473,478,483,488,493,498,503,508,513,518,523,528,534,540,546,551,557],{"__ignoreMap":83},[156,464,465],{"class":158,"line":159},[156,466,467],{},"//set your senderId from the API here!\n",[156,469,470],{"class":158,"line":84},[156,471,472],{}," private static final String SENDER_ID = \"1234567890\";\n",[156,474,475],{"class":158,"line":170},[156,476,477],{},"@Override\n",[156,479,480],{"class":158,"line":176},[156,481,482],{},"protected void onCreate(Bundle savedInstanceState) {\n",[156,484,485],{"class":158,"line":182},[156,486,487],{}," GCMRegistrar.checkDevice(this);\n",[156,489,490],{"class":158,"line":188},[156,491,492],{}," GCMRegistrar.checkManifest(this);\n",[156,494,495],{"class":158,"line":194},[156,496,497],{}," final String regId = GCMRegistrar.getRegistrationId(this);\n",[156,499,500],{"class":158,"line":200},[156,501,502],{}," // if we don't have a regId yet, register at gcm\n",[156,504,505],{"class":158,"line":206},[156,506,507],{}," if (regId.equals(\"\")) {\n",[156,509,510],{"class":158,"line":212},[156,511,512],{}," GCMRegistrar.register(this, SENDER_ID);\n",[156,514,515],{"class":158,"line":218},[156,516,517],{}," Toast.makeText(getApplicationContext(), \"Registered GCM!\", Toast.LENGTH_LONG).show();\n",[156,519,520],{"class":158,"line":224},[156,521,522],{}," // just log the registrationId for this test case.\n",[156,524,525],{"class":158,"line":230},[156,526,527],{}," Log.i(this.getClass().getName(), \"id: \" + GCMRegistrar.getRegistrationId(this));\n",[156,529,531],{"class":158,"line":530},14,[156,532,533],{}," } else {\n",[156,535,537],{"class":158,"line":536},15,[156,538,539],{}," Log.i(this.getClass().getName(), \"Already registered\");\n",[156,541,543],{"class":158,"line":542},16,[156,544,545],{}," Toast.makeText(getApplicationContext(), \"Already registered at GCM!\", Toast.LENGTH_LONG).show();\n",[156,547,549],{"class":158,"line":548},17,[156,550,527],{},[156,552,554],{"class":158,"line":553},18,[156,555,556],{}," }\n",[156,558,560],{"class":158,"line":559},19,[156,561,348],{},[18,563,564],{},"Don’t forget to replace the SENDER_ID with yours!",[18,566,567],{},"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.",[18,569,570],{},"With this, we finished our small app and can begin implementing the server part.",[136,572,574],{"id":573},"the-web-application","The Web Application",[18,576,577],{},"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.",[18,579,580,581,585],{},"Instead of just copying the GCM server library into the project, I searched a bit and found someone, who created a\nrepository for\nit. (",[123,582,583],{"href":583,"rel":584},"https://github.com/slorber/gcm-server-repository",[127],")",[18,587,588],{},"We start with creating the Sender class, which isn’t that hard either.",[147,590,592],{"className":301,"code":591,"language":303,"meta":83,"style":83},"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",[153,593,594,599,604,609,614,619,624,629,634,639,644,649,654,659,664,669,674,679,684,689,695,701,706,711,717,722],{"__ignoreMap":83},[156,595,596],{"class":158,"line":159},[156,597,598],{},"public class GCMSender {\n",[156,600,601],{"class":158,"line":84},[156,602,603],{}," public String apiKey = null;\n",[156,605,606],{"class":158,"line":170},[156,607,608],{}," public GCMSender(String apiKey) {\n",[156,610,611],{"class":158,"line":176},[156,612,613],{}," this.apiKey = apiKey;\n",[156,615,616],{"class":158,"line":182},[156,617,618],{}," }\n",[156,620,621],{"class":158,"line":188},[156,622,623],{}," public String send(String text, String id) throws IOException {\n",[156,625,626],{"class":158,"line":194},[156,627,628],{}," Sender sender = new Sender(apiKey);\n",[156,630,631],{"class":158,"line":200},[156,632,633],{}," Builder builder = new Message.Builder();\n",[156,635,636],{"class":158,"line":206},[156,637,638],{}," builder.addData(\"text\", text);\n",[156,640,641],{"class":158,"line":212},[156,642,643],{}," Result result = sender.send(builder.build(), id, 5);\n",[156,645,646],{"class":158,"line":218},[156,647,648],{}," if (result.getMessageId() != null) {\n",[156,650,651],{"class":158,"line":224},[156,652,653],{}," String canonicalRegId = result.getCanonicalRegistrationId();\n",[156,655,656],{"class":158,"line":230},[156,657,658],{}," if (canonicalRegId != null) {\n",[156,660,661],{"class":158,"line":530},[156,662,663],{}," // same device has more than on registration ID: update database\n",[156,665,666],{"class":158,"line":536},[156,667,668],{}," return \"same device has more than on registration ID: update database\";\n",[156,670,671],{"class":158,"line":542},[156,672,673],{}," }\n",[156,675,676],{"class":158,"line":548},[156,677,678],{}," } else {\n",[156,680,681],{"class":158,"line":553},[156,682,683],{}," String error = result.getErrorCodeName();\n",[156,685,686],{"class":158,"line":559},[156,687,688],{}," if (error.equals(Constants.ERROR_NOT_REGISTERED)) {\n",[156,690,692],{"class":158,"line":691},20,[156,693,694],{}," // application has been removed from device - unregister database\n",[156,696,698],{"class":158,"line":697},21,[156,699,700],{}," return \"application has been removed from device - unregister database\";\n",[156,702,704],{"class":158,"line":703},22,[156,705,673],{},[156,707,709],{"class":158,"line":708},23,[156,710,556],{},[156,712,714],{"class":158,"line":713},24,[156,715,716],{}," return null;\n",[156,718,720],{"class":158,"line":719},25,[156,721,618],{},[156,723,725],{"class":158,"line":724},26,[156,726,348],{},[18,728,729],{},"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:",[147,731,735],{"className":732,"code":733,"language":734,"meta":83,"style":83},"language-html shiki shiki-themes github-light github-dark","\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","html",[153,736,737,752,757,773,781,791,820,827,837,847,857,867,872,886,895,904,916,933,956,965,980,995,1000,1014,1023,1031,1048,1070,1108,1127,1158,1174,1183,1192],{"__ignoreMap":83},[156,738,739,743,747,749],{"class":158,"line":159},[156,740,742],{"class":741},"s7hpK","\u003C",[156,744,746],{"class":745},"sVt8B","%@ taglib prefix=\"c\" uri=\"http://java.sun.com/jsp/jstl/core\" %> ",[156,748,742],{"class":741},[156,750,751],{"class":745},"%@page\n",[156,753,754],{"class":158,"line":84},[156,755,756],{"class":745},"contentType=\"text/html\" pageEncoding=\"UTF-8\"%>\n",[156,758,759,762,766,770],{"class":158,"line":170},[156,760,761],{"class":745},"\u003C!",[156,763,765],{"class":764},"s9eBZ","DOCTYPE",[156,767,769],{"class":768},"sScJk"," html",[156,771,772],{"class":745},">\n",[156,774,775,777,779],{"class":158,"line":176},[156,776,742],{"class":745},[156,778,734],{"class":764},[156,780,772],{"class":745},[156,782,783,786,789],{"class":158,"line":182},[156,784,785],{"class":745}," \u003C",[156,787,788],{"class":764},"head",[156,790,772],{"class":745},[156,792,793,796,799,802,805,809,812,814,817],{"class":158,"line":188},[156,794,795],{"class":745}," \u003C",[156,797,798],{"class":764},"meta",[156,800,801],{"class":768}," http-equiv",[156,803,804],{"class":745},"=",[156,806,808],{"class":807},"sZZnC","\"Content-Type\"",[156,810,811],{"class":768}," content",[156,813,804],{"class":745},[156,815,816],{"class":807},"\"text/html; charset=UTF-8\"",[156,818,819],{"class":745}," />\n",[156,821,822,824],{"class":158,"line":194},[156,823,795],{"class":745},[156,825,826],{"class":764},"link\n",[156,828,829,832,834],{"class":158,"line":200},[156,830,831],{"class":768}," rel",[156,833,804],{"class":745},[156,835,836],{"class":807},"\"stylesheet\"\n",[156,838,839,842,844],{"class":158,"line":206},[156,840,841],{"class":768}," type",[156,843,804],{"class":745},[156,845,846],{"class":807},"\"text/css\"\n",[156,848,849,852,854],{"class":158,"line":212},[156,850,851],{"class":768}," href",[156,853,804],{"class":745},[156,855,856],{"class":807},"\"/GCMTestServer/frontend_resources/style.css\"\n",[156,858,859,862,864],{"class":158,"line":218},[156,860,861],{"class":768}," media",[156,863,804],{"class":745},[156,865,866],{"class":807},"\"screen\"\n",[156,868,869],{"class":158,"line":224},[156,870,871],{"class":745}," />\n",[156,873,874,876,879,882,884],{"class":158,"line":230},[156,875,795],{"class":745},[156,877,878],{"class":764},"title",[156,880,881],{"class":745},">GCM Sender\u003C/",[156,883,878],{"class":764},[156,885,772],{"class":745},[156,887,888,891,893],{"class":158,"line":530},[156,889,890],{"class":745}," \u003C/",[156,892,788],{"class":764},[156,894,772],{"class":745},[156,896,897,899,902],{"class":158,"line":536},[156,898,785],{"class":745},[156,900,901],{"class":764},"body",[156,903,772],{"class":745},[156,905,906,908,910,912,914],{"class":158,"line":542},[156,907,795],{"class":745},[156,909,14],{"class":764},[156,911,881],{"class":745},[156,913,14],{"class":764},[156,915,772],{"class":745},[156,917,918,920,923,926,928,931],{"class":158,"line":548},[156,919,795],{"class":745},[156,921,922],{"class":741},"c:if",[156,924,925],{"class":768}," test",[156,927,804],{"class":745},[156,929,930],{"class":807},"\"${success == true}\"",[156,932,772],{"class":745},[156,934,935,938,941,944,946,949,952,954],{"class":158,"line":553},[156,936,937],{"class":745}," \u003C",[156,939,940],{"class":764},"div",[156,942,943],{"class":768}," class",[156,945,804],{"class":745},[156,947,948],{"class":807},"\"success\"",[156,950,951],{"class":745},">sending successful!\u003C/",[156,953,940],{"class":764},[156,955,772],{"class":745},[156,957,958,961,963],{"class":158,"line":559},[156,959,960],{"class":745}," \u003C/",[156,962,922],{"class":741},[156,964,772],{"class":745},[156,966,967,969,971,973,975,978],{"class":158,"line":691},[156,968,795],{"class":745},[156,970,922],{"class":741},[156,972,925],{"class":768},[156,974,804],{"class":745},[156,976,977],{"class":807},"\"${error == true}\"",[156,979,772],{"class":745},[156,981,982,984,986,988,990,993],{"class":158,"line":697},[156,983,937],{"class":745},[156,985,940],{"class":764},[156,987,943],{"class":768},[156,989,804],{"class":745},[156,991,992],{"class":807},"\"error\"",[156,994,772],{"class":745},[156,996,997],{"class":158,"line":703},[156,998,999],{"class":745}," Error at sending!\n",[156,1001,1002,1005,1007,1010,1012],{"class":158,"line":708},[156,1003,1004],{"class":745}," \u003C",[156,1006,18],{"class":764},[156,1008,1009],{"class":745},">${errormessage}\u003C/",[156,1011,18],{"class":764},[156,1013,772],{"class":745},[156,1015,1016,1019,1021],{"class":158,"line":713},[156,1017,1018],{"class":745}," \u003C/",[156,1020,940],{"class":764},[156,1022,772],{"class":745},[156,1024,1025,1027,1029],{"class":158,"line":719},[156,1026,960],{"class":745},[156,1028,922],{"class":741},[156,1030,772],{"class":745},[156,1032,1033,1035,1038,1041,1043,1046],{"class":158,"line":724},[156,1034,795],{"class":745},[156,1036,1037],{"class":764},"form",[156,1039,1040],{"class":768}," method",[156,1042,804],{"class":745},[156,1044,1045],{"class":807},"\"POST\"",[156,1047,772],{"class":745},[156,1049,1051,1053,1056,1059,1061,1064,1067],{"class":158,"line":1050},27,[156,1052,937],{"class":745},[156,1054,1055],{"class":764},"label",[156,1057,1058],{"class":768}," for",[156,1060,804],{"class":745},[156,1062,1063],{"class":807},"\"text\"",[156,1065,1066],{"class":745},">Text\u003C/",[156,1068,1069],{"class":764},"label\n",[156,1071,1073,1076,1079,1082,1084,1086,1089,1091,1093,1096,1098,1100,1103,1106],{"class":158,"line":1072},28,[156,1074,1075],{"class":745}," >\u003C",[156,1077,1078],{"class":764},"input",[156,1080,1081],{"class":768}," name",[156,1083,804],{"class":745},[156,1085,1063],{"class":807},[156,1087,1088],{"class":768}," id",[156,1090,804],{"class":745},[156,1092,1063],{"class":807},[156,1094,1095],{"class":768}," type",[156,1097,804],{"class":745},[156,1099,1063],{"class":807},[156,1101,1102],{"class":745}," />\u003C",[156,1104,1105],{"class":764},"br",[156,1107,819],{"class":745},[156,1109,1111,1113,1115,1117,1119,1122,1125],{"class":158,"line":1110},29,[156,1112,937],{"class":745},[156,1114,1055],{"class":764},[156,1116,1058],{"class":768},[156,1118,804],{"class":745},[156,1120,1121],{"class":807},"\"id\"",[156,1123,1124],{"class":745},">Registration-Id\u003C/",[156,1126,1069],{"class":764},[156,1128,1130,1132,1134,1136,1138,1140,1142,1144,1146,1148,1150,1152,1154,1156],{"class":158,"line":1129},30,[156,1131,1075],{"class":745},[156,1133,1078],{"class":764},[156,1135,1081],{"class":768},[156,1137,804],{"class":745},[156,1139,1121],{"class":807},[156,1141,1088],{"class":768},[156,1143,804],{"class":745},[156,1145,1121],{"class":807},[156,1147,1095],{"class":768},[156,1149,804],{"class":745},[156,1151,1063],{"class":807},[156,1153,1102],{"class":745},[156,1155,1105],{"class":764},[156,1157,819],{"class":745},[156,1159,1161,1163,1165,1167,1169,1172],{"class":158,"line":1160},31,[156,1162,937],{"class":745},[156,1164,1078],{"class":764},[156,1166,1095],{"class":768},[156,1168,804],{"class":745},[156,1170,1171],{"class":807},"\"submit\"",[156,1173,819],{"class":745},[156,1175,1177,1179,1181],{"class":158,"line":1176},32,[156,1178,960],{"class":745},[156,1180,1037],{"class":764},[156,1182,772],{"class":745},[156,1184,1186,1188,1190],{"class":158,"line":1185},33,[156,1187,890],{"class":745},[156,1189,901],{"class":764},[156,1191,772],{"class":745},[156,1193,1195,1198,1200],{"class":158,"line":1194},34,[156,1196,1197],{"class":745},"\u003C/",[156,1199,734],{"class":764},[156,1201,772],{"class":745},[18,1203,1204],{},"In the corresponding Controller to process the request, send the message:",[147,1206,1208],{"className":301,"code":1207,"language":303,"meta":83,"style":83}," @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",[153,1209,1210,1215,1220,1225,1230,1235,1240,1245,1250,1255,1260,1265,1270,1275,1280,1284,1289,1293,1298],{"__ignoreMap":83},[156,1211,1212],{"class":158,"line":159},[156,1213,1214],{}," @RequestMapping(value = \"send\", method = RequestMethod.POST)\n",[156,1216,1217],{"class":158,"line":84},[156,1218,1219],{}," public String send(Model model,\n",[156,1221,1222],{"class":158,"line":170},[156,1223,1224],{}," @RequestParam(\"text\") String text,\n",[156,1226,1227],{"class":158,"line":176},[156,1228,1229],{}," @RequestParam(\"id\") String id) {\n",[156,1231,1232],{"class":158,"line":182},[156,1233,1234],{}," String error = null;\n",[156,1236,1237],{"class":158,"line":188},[156,1238,1239],{}," try {\n",[156,1241,1242],{"class":158,"line":194},[156,1243,1244],{}," error = gcmSender.send(text, id);\n",[156,1246,1247],{"class":158,"line":200},[156,1248,1249],{}," } catch (IOException ex) {\n",[156,1251,1252],{"class":158,"line":206},[156,1253,1254],{}," model.addAttribute(\"error\", true);\n",[156,1256,1257],{"class":158,"line":212},[156,1258,1259],{}," model.addAttribute(\"errormessage\", ex.getMessage());\n",[156,1261,1262],{"class":158,"line":218},[156,1263,1264],{}," }\n",[156,1266,1267],{"class":158,"line":224},[156,1268,1269],{}," if (error == null) {\n",[156,1271,1272],{"class":158,"line":230},[156,1273,1274],{}," model.addAttribute(\"success\", true);\n",[156,1276,1277],{"class":158,"line":530},[156,1278,1279],{}," } else {\n",[156,1281,1282],{"class":158,"line":536},[156,1283,1254],{},[156,1285,1286],{"class":158,"line":542},[156,1287,1288],{}," model.addAttribute(\"errormessage\", error);\n",[156,1290,1291],{"class":158,"line":548},[156,1292,1264],{},[156,1294,1295],{"class":158,"line":553},[156,1296,1297],{}," return \"sender\";\n",[156,1299,1300],{"class":158,"line":559},[156,1301,343],{},[18,1303,1304],{},"Now we are ready to test it!",[18,1306,1307],{},"Start the app, copy the RegistrationId from the Logs, start the Server, enter the RegistrationId and a small text, and\nthere you go:",[18,1309,1310],{},[24,1311],{"alt":1312,"src":1313},"\"notification\"","https://media.synyx.de/uploads//2012/12/notification-300x221.jpg",[18,1315,1316],{},"To get a better understanding, you can also read the rest of the starting guide and the other documentation.",[18,1318,1319],{},"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 😛",[18,1321,1322,1323],{},"As promised, here are the full sources:",[123,1324,1327],{"href":1325,"rel":1326},"https://media.synyx.de/uploads//2012/12/CloudMessageTest.zip",[127],"CloudMessageTest",[1329,1330,1331],"style",{},"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":83,"searchDepth":84,"depth":84,"links":1333},[1334,1335],{"id":138,"depth":84,"text":139},{"id":573,"depth":84,"text":574},[1337,1338],"mobile-blog","tutorial","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":108,"description":118},"blog/a-small-look-into-google-cloud-messages",[1346,1347,1348,1349,1350,1351,1352],"android","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":1356,"title":1357,"author":1358,"body":1360,"category":1499,"date":1500,"description":1367,"extension":91,"link":1501,"meta":1502,"navigation":94,"path":1503,"seo":1504,"slug":1364,"stem":1505,"tags":1506,"teaser":1510,"__hash__":1511},"blog/blog/google-maps-on-maemo-5-part-1.md","Google Maps on Maemo 5 Part 1",[1359],"gast",{"type":11,"value":1361,"toc":1496},[1362,1365,1368,1372,1380,1383,1386,1389,1392,1395,1398,1401,1404,1407,1410,1413,1416,1419,1422,1424,1427,1430,1433,1436,1438,1441,1445,1448,1451,1453,1455,1458,1461,1463,1466,1469,1471,1474,1479,1482,1487,1490,1493],[14,1363,1357],{"id":1364},"google-maps-on-maemo-5-part-1",[18,1366,1367],{},"In this post i will show you how to realize a Maemo 5 Qt 4.6 application with google maps integration.",[136,1369,1371],{"id":1370},"the-map","The map:",[18,1373,1374,1375,1379],{},"There is a good short tutorial with included source code: ",[123,1376,1377],{"href":1377,"rel":1378},"http://efforts.embedded.ufcg.edu.br/qt/?p=80",[127],". Because there\nis no native library for the N900 you have to go another way to get the map in your application. The tutorial describes\nhowto load and render the map by the webkit library. I used parts of the html file of that project for my little app.",[18,1381,1382],{},"The idea is quite simple. Webkit will render a webpage insider your app. That webpage consists of javascript methods\nwhich use the Google Maps-API. The javascript methods can be triggered from the app. The map class acts as proxy for\nthe comunication between your app and the website. Quite simple hm?",[18,1384,1385],{},"The benefit of that method is: you may use the complete power of Google Maps-API. But if your internet connection is\nslow, you will have to wait until the map is loaded and that may need some time and a white page just looks ugly. Native\nimplementations often have some startup animations that have an effect of compensation.",[18,1387,1388],{},"Sourcecode of index.html with javascript methods for google’s api:",[18,1390,1391],{},"`function initialize(){",[18,1393,1394],{},"map = new GMap2(document.getElementById(\"map\"));",[18,1396,1397],{},"map.setCenter( new GLatLng(49.002397,8.394251),10 );",[18,1399,1400],{},"var point = new GLatLng(49.002397,8.394251);",[18,1402,1403],{},"map.addOverlay(new GMarker(point));",[18,1405,1406],{},"openSynyx();",[18,1408,1409],{},"}`",[18,1411,1412],{},"`function openSynyx()",[18,1414,1415],{},"{",[18,1417,1418],{},"map.setCenter( new GLatLng(49.002397,8.394251),15 );",[18,1420,1421],{},"map.openInfoWindow(map.getCenter(),document.createTextNode(\"Synyx GmbH & Co. KG\"));",[18,1423,1409],{},[18,1425,1426],{},"`function route(from){",[18,1428,1429],{},"map.clearOverlays();",[18,1431,1432],{},"directions = new GDirections(map);",[18,1434,1435],{},"directions.load(\"from: \"+from+\" to: Karlsruhe, Karlstrasse 68\");",[18,1437,1409],{},[18,1439,1440],{},"`",[940,1442],{"id":1443,"style":1444},"map","width: 450px; height: 400px",[18,1446,1447],{},"The map.cpp acts is a Children of QWebView and acts as poxy to the javascript methods:",[18,1449,1450],{},"`Map::Map(QWidget *parent) : QWebView(parent)",[18,1452,1415],{},[18,1454,1409],{},[18,1456,1457],{},"`void Map::naviFrom(QString from){",[18,1459,1460],{},"this->page()->mainFrame()->evaluateJavaScript(QString(\"route(\"%1\")\").arg(from));",[18,1462,1409],{},[18,1464,1465],{},"`void Map::openSynyx(){",[18,1467,1468],{},"this->page()->mainFrame()->evaluateJavaScript(\"openSynyx()\");",[18,1470,1409],{},[18,1472,1473],{},"mainscreen.cpp initializes the map with the path to the html file",[18,1475,1476],{},[153,1477,1478],{},"map->load(QUrl(\"./index.html\") ) ;",[18,1480,1481],{},"Now if you put all things together in an app, by adding a few buttons, a textfield and connect them to the Map::naviFrom\nmethod it could look like this:",[18,1483,1484],{},[24,1485],{"alt":83,"src":1486},"https://media.synyx.de/uploads//2010/06/maps_navi.png",[18,1488,1489],{},"google maps in maemo 5",[18,1491,1492],{},"The evaluation of the position (coordinates, or town labels, street names etc.) and the rendering of the route will be\ndone by google’s api. So in contrast to other mobile plattforms with native libraries, you do not have to bother about\nthat.",[18,1494,1495],{},"Part 2 will describe a way how to use the internal gps device of your N900 and how to use callback mechanisms for\nabstraction of the position handling.",{"title":83,"searchDepth":84,"depth":84,"links":1497},[1498],{"id":1370,"depth":84,"text":1371},[1337,1338],"2010-06-07T22:11:18","https://synyx.de/blog/google-maps-on-maemo-5-part-1/",{},"/blog/google-maps-on-maemo-5-part-1",{"title":1357,"description":1367},"blog/google-maps-on-maemo-5-part-1",[1507,1508,1509],"google-maps","maemo-5","qt-4-6","In this post i will show you how to realize a Maemo 5 Qt 4.6 application with google maps integration. The map: There is a good short tutorial with included…","4g6rav3KqWuFVlf1u80dnCPLsXJOJ9HW3KqsIkfGE_A",{"id":1513,"title":1514,"author":1515,"body":1517,"category":1815,"date":1816,"description":1817,"extension":91,"link":1818,"meta":1819,"navigation":94,"path":1820,"seo":1821,"slug":1521,"stem":1823,"tags":1824,"teaser":1826,"__hash__":1827},"blog/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii.md","How to add a “Find Your Company” feature to your iPhone App – Part II",[1516],"linsin",{"type":11,"value":1518,"toc":1813},[1519,1522,1537,1540,1546,1549,1565,1735,1738,1745,1770,1777,1791,1798,1803,1810],[14,1520,1514],{"id":1521},"how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",[18,1523,1524,1525,1530,1531,1536],{},"In\nmy ",[123,1526,1529],{"href":1527,"rel":1528},"http://mobile.synyx.de/2010/05/06/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i/",[127],"first installment",",\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\ndownload the code for this tutorial on ",[123,1532,1535],{"href":1533,"rel":1534},"http://gist.github.com/388323/ea35c6d31270babb73ec052f1442c43afd6b5510",[127],"github",".",[18,1538,1539],{},"If you start the App where we left off, you get the two pins, but it won’t be properly zoomed. In fact, it would look\nsomething like this:",[18,1541,1542],{},[24,1543],{"alt":1544,"src":1545},"\"screen_synyx_map\"","https://media.synyx.de/uploads//2010/05/maps-3.png",[18,1547,1548],{},"As you can see, the pins are not really helpful at this zoom level and it’s definitely not user friendly, if you have to\nzoom in by yourself. What we need is a way to zoom in, so that the pin and the current location nicely fit on the\nscreen.",[18,1550,1551,1552,1555,1556,1560,1561,1564],{},"That’s exactly what the method ",[132,1553,1554],{},"centerMapAroundAnnotations"," at\nline ",[123,1557,1559],{"href":1533,"rel":1558},[127],"108"," of our ",[132,1562,1563],{},"UIViewController"," does:",[147,1566,1570],{"className":1567,"code":1568,"language":1569,"meta":83,"style":83},"language-objc shiki shiki-themes github-light github-dark","\nif ( [[self.mapView annotations] count] \u003C 2 )\n return;\nCLLocationCoordinate2D min;\nCLLocationCoordinate2D max;\nBOOL minMaxInitialized = NO;\nfor ( id\u003CMKAnnotation> a in [self.mapView annotations] ) {\n if ( !minMaxInitialized ) {\n min = a.coordinate;\n max = a.coordinate;\n minMaxInitialized = YES;\n } else {\n min.latitude = MIN( min.latitude, a.coordinate.latitude );\n min.longitude = MIN( min.longitude, a.coordinate.longitude );\n max.latitude = MAX( max.latitude, a.coordinate.latitude );\n max.longitude = MAX( max.longitude, a.coordinate.longitude );\n }\n}\u003Cbr/>\nCLLocation* locSouthWest = [[CLLocation alloc] initWithLatitude: min.latitude longitude: min.longitude];\nCLLocation* locSouthEast = [[CLLocation alloc] initWithLatitude: min.latitude longitude: max.longitude];\nCLLocation* locNorthEast = [[CLLocation alloc] initWithLatitude: max.latitude longitude: max.longitude];\nCLLocationCoordinate2D regionCenter;\nregionCenter.latitude = (min.latitude + max.latitude) / 2.0;\nregionCenter.longitude = (min.longitude + max.longitude) / 2.0;\u003Cbr/>\nCLLocationDistance latMeters = [locSouthEast getDistanceFrom: locNorthEast];\nCLLocationDistance lonMeters = [locSouthEast getDistanceFrom: locSouthWest];\nMKCoordinateRegion region;\nregion = MKCoordinateRegionMakeWithDistance( regionCenter, latMeters, lonMeters );\nMKCoordinateRegion fitRegion = [self.mapView regionThatFits: region];\n[self.mapView setRegion: fitRegion animated: YES];\n[locSouthWest release];\n[locSouthEast release];\n[locNorthEast release];\n\n","objc",[153,1571,1572,1576,1581,1586,1591,1596,1601,1606,1611,1616,1621,1626,1630,1635,1640,1645,1650,1655,1660,1665,1670,1675,1680,1685,1690,1695,1700,1705,1710,1715,1720,1725,1730],{"__ignoreMap":83},[156,1573,1574],{"class":158,"line":159},[156,1575,162],{"emptyLinePlaceholder":94},[156,1577,1578],{"class":158,"line":84},[156,1579,1580],{},"if ( [[self.mapView annotations] count] \u003C 2 )\n",[156,1582,1583],{"class":158,"line":170},[156,1584,1585],{}," return;\n",[156,1587,1588],{"class":158,"line":176},[156,1589,1590],{},"CLLocationCoordinate2D min;\n",[156,1592,1593],{"class":158,"line":182},[156,1594,1595],{},"CLLocationCoordinate2D max;\n",[156,1597,1598],{"class":158,"line":188},[156,1599,1600],{},"BOOL minMaxInitialized = NO;\n",[156,1602,1603],{"class":158,"line":194},[156,1604,1605],{},"for ( id\u003CMKAnnotation> a in [self.mapView annotations] ) {\n",[156,1607,1608],{"class":158,"line":200},[156,1609,1610],{}," if ( !minMaxInitialized ) {\n",[156,1612,1613],{"class":158,"line":206},[156,1614,1615],{}," min = a.coordinate;\n",[156,1617,1618],{"class":158,"line":212},[156,1619,1620],{}," max = a.coordinate;\n",[156,1622,1623],{"class":158,"line":218},[156,1624,1625],{}," minMaxInitialized = YES;\n",[156,1627,1628],{"class":158,"line":224},[156,1629,533],{},[156,1631,1632],{"class":158,"line":230},[156,1633,1634],{}," min.latitude = MIN( min.latitude, a.coordinate.latitude );\n",[156,1636,1637],{"class":158,"line":530},[156,1638,1639],{}," min.longitude = MIN( min.longitude, a.coordinate.longitude );\n",[156,1641,1642],{"class":158,"line":536},[156,1643,1644],{}," max.latitude = MAX( max.latitude, a.coordinate.latitude );\n",[156,1646,1647],{"class":158,"line":542},[156,1648,1649],{}," max.longitude = MAX( max.longitude, a.coordinate.longitude );\n",[156,1651,1652],{"class":158,"line":548},[156,1653,1654],{}," }\n",[156,1656,1657],{"class":158,"line":553},[156,1658,1659],{},"}\u003Cbr/>\n",[156,1661,1662],{"class":158,"line":559},[156,1663,1664],{},"CLLocation* locSouthWest = [[CLLocation alloc] initWithLatitude: min.latitude longitude: min.longitude];\n",[156,1666,1667],{"class":158,"line":691},[156,1668,1669],{},"CLLocation* locSouthEast = [[CLLocation alloc] initWithLatitude: min.latitude longitude: max.longitude];\n",[156,1671,1672],{"class":158,"line":697},[156,1673,1674],{},"CLLocation* locNorthEast = [[CLLocation alloc] initWithLatitude: max.latitude longitude: max.longitude];\n",[156,1676,1677],{"class":158,"line":703},[156,1678,1679],{},"CLLocationCoordinate2D regionCenter;\n",[156,1681,1682],{"class":158,"line":708},[156,1683,1684],{},"regionCenter.latitude = (min.latitude + max.latitude) / 2.0;\n",[156,1686,1687],{"class":158,"line":713},[156,1688,1689],{},"regionCenter.longitude = (min.longitude + max.longitude) / 2.0;\u003Cbr/>\n",[156,1691,1692],{"class":158,"line":719},[156,1693,1694],{},"CLLocationDistance latMeters = [locSouthEast getDistanceFrom: locNorthEast];\n",[156,1696,1697],{"class":158,"line":724},[156,1698,1699],{},"CLLocationDistance lonMeters = [locSouthEast getDistanceFrom: locSouthWest];\n",[156,1701,1702],{"class":158,"line":1050},[156,1703,1704],{},"MKCoordinateRegion region;\n",[156,1706,1707],{"class":158,"line":1072},[156,1708,1709],{},"region = MKCoordinateRegionMakeWithDistance( regionCenter, latMeters, lonMeters );\n",[156,1711,1712],{"class":158,"line":1110},[156,1713,1714],{},"MKCoordinateRegion fitRegion = [self.mapView regionThatFits: region];\n",[156,1716,1717],{"class":158,"line":1129},[156,1718,1719],{},"[self.mapView setRegion: fitRegion animated: YES];\n",[156,1721,1722],{"class":158,"line":1160},[156,1723,1724],{},"[locSouthWest release];\n",[156,1726,1727],{"class":158,"line":1176},[156,1728,1729],{},"[locSouthEast release];\n",[156,1731,1732],{"class":158,"line":1185},[156,1733,1734],{},"[locNorthEast release];\n",[18,1736,1737],{},"The code might look complicated, but we can break it down into 3 steps:",[1739,1740,1741],"ol",{},[1742,1743,1744],"li",{},"Iterate over all our custom annotations (we only have one) to determine the max/min latitude and longitude",[1746,1747,1748,1763],"ul",{},[1742,1749,1750,1751,1754,1755,1758,1759,1762],{},"This results in a triangle ",[132,1752,1753],{},"locSouthWest",", ",[132,1756,1757],{},"locSouthEast"," and ",[132,1760,1761],{},"locNorthEast",", that we can use to determine the center\nfor our zoom",[1742,1764,1765,1766,1769],{},"Using the distance between the triangle points and the center, we can fit the map into a ",[132,1767,1768],{},"MKCoordinateRegion",", that\nwill zoom it so that everything fits on the screen.",[18,1771,1772,1773,1776],{},"One thing this algorithm doesn’t include, is the ",[132,1774,1775],{},"AnnotationView"," that display the name and location of the red pin. It\nsometimes happens, that it’s displayed outside of the screen, once you tap on the pin. However, if you add the code\nabove and hook it into the following method:",[147,1778,1780],{"className":1567,"code":1779,"language":1569,"meta":83,"style":83},"\n- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views\n\n",[153,1781,1782,1786],{"__ignoreMap":83},[156,1783,1784],{"class":158,"line":159},[156,1785,162],{"emptyLinePlaceholder":94},[156,1787,1788],{"class":158,"line":84},[156,1789,1790],{},"- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views\n",[18,1792,1793,1794,1797],{},"You still need to add the code, which handles the annotation, but you can feel free to steal it\nfrom ",[123,1795,1535],{"href":1533,"rel":1796},[127],". The result should look something\nlike this:",[18,1799,1800],{},[24,1801],{"alt":1544,"src":1802},"https://media.synyx.de/uploads//2010/05/screen_synyx_map.png",[18,1804,1805,1806,1809],{},"This concludes our little tutorial on Google Maps and ",[132,1807,1808],{},"MapKit",". I hope it was helpful! If you need any assistance, just\nleave a comment or drop me an email, I’ll be happy to help you out.",[1329,1811,1812],{},"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);}",{"title":83,"searchDepth":84,"depth":84,"links":1814},[],[1337,1338],"2010-05-19T06:44:08","In\\nmy first installment,\\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\\ndownload the code for this tutorial on github.","https://synyx.de/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii/",{},"/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",{"title":1514,"description":1822},"In\nmy first installment,\nwe laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can\ndownload the code for this tutorial on github.","blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-ii",[1507,1825],"iphone","In my first installment, we laid the foundation for todays blog post. So don’t hesitate to head back for a recap, if you need to. You can download the code…","Hpc7jZiUS-9fX42oCKNm3kaFp-aPqLQkR6HNXUf1bUU",{"id":1829,"title":1830,"author":1831,"body":1833,"category":1992,"date":1993,"description":1994,"extension":91,"link":1995,"meta":1996,"navigation":94,"path":1997,"seo":1998,"slug":1837,"stem":2000,"tags":2001,"teaser":2004,"__hash__":2005},"blog/blog/google-maps-on-android-part-2-overlays.md","Google Maps on Android – Part 2: Overlays",[1832],"heib",{"type":11,"value":1834,"toc":1990},[1835,1838,1847,1865,1868,1871,1874,1877,1880,1883,1886,1897,1911,1914,1917,1920,1922,1929,1932,1935,1951,1954,1957,1960,1962,1968],[14,1836,1830],{"id":1837},"google-maps-on-android-part-2-overlays",[18,1839,1840,1841,1846],{},"In my ",[123,1842,1845],{"href":1843,"rel":1844},"http://mobile.synyx.de/2010/04/30/google-maps-on-android/",[127],"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.",[18,1848,1849,1850,1855,1856,1860,1861,1864],{},"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 ",[123,1851,1854],{"href":1852,"rel":1853},"http://developer.android.com/resources/tutorials/views/hello-mapview.html",[127],"tutorial on the android developer site",",\nalready mentioned in the ",[123,1857,1859],{"href":1843,"rel":1858},[127],"last post",". The\n",[132,1862,1863],{},"HelloItemizedOverlay"," that is created there, enables you to add as many markers to your map as you want:",[18,1866,1867],{},"`List mapOverlays = mapView.getOverlays();",[18,1869,1870],{},"Drawable drawable = this.getResources().getDrawable(R.drawable.synyxLogo);",[18,1872,1873],{},"HelloItemizedOverlay itemizedOverlay = new HelloItemizedOverlay(drawable);",[18,1875,1876],{},"OverlayItem synyxOfficeOverlay = new OverlayItem(synyxOfficeLocation, \"Synyx\", \"This is the Synyx office!\");",[18,1878,1879],{},"itemizedOverlay.addOverlay(synyxOfficeOverlay);",[18,1881,1882],{},"// add more overlayItems...",[18,1884,1885],{},"mapOverlays.add(itemizedOverlay);`",[18,1887,1888,1889,1892,1893,1896],{},"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 ",[132,1890,1891],{},"setMarker()"," method of the ",[132,1894,1895],{},"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.",[18,1898,1899,1900,1903,1904,1907,1908,1910],{},"So how to do it? The trick is the static method ",[132,1901,1902],{},"boundCenterBottom()"," of the ",[132,1905,1906],{},"ItemizedOverlay"," class. This method is\nalso called in the constructor of the ",[132,1909,1863],{},", when the default marker is set:",[18,1912,1913],{},"`public RouteMapOverlay(Drawable defaultMarker, Context context) {",[18,1915,1916],{},"super(boundCenterBottom(defaultMarker));",[18,1918,1919],{},"this.context = context;",[18,1921,1409],{},[18,1923,1924,1925,1928],{},"The problem is, that the visibility of this method is set to ",[132,1926,1927],{},"protected"," (why?!). So calling it when setting the marker\nwon’t work:",[18,1930,1931],{},"`// won't work as boundCenterBottom is protected",[18,1933,1934],{},"synyxOfficeOverlay.setOverlay(ItemizedOverlay.boundCenterBottom(myNewMarker));`",[18,1936,1937,1938,1940,1941,1943,1944,1947,1948,1950],{},"Because of this visibility feature, the “easier” way is to add a new setter to your ",[132,1939,1863],{}," class,\naccepting an ",[132,1942,1895],{}," and a ",[132,1945,1946],{},"Drawable"," to use as the marker. Within that setter, you are able to call the\n",[132,1949,1902],{}," method to set up the marker correctly:",[18,1952,1953],{},"`public void addOverlay(OverlayItem overlayItem, Drawable marker) {",[18,1955,1956],{},"overlayItem.setMarker(boundCenterBottom(marker));",[18,1958,1959],{},"addOverlay(overlayItem);",[18,1961,1409],{},[18,1963,1964,1965,1967],{},"Now if you use that setter, the map will correctly show your ",[132,1966,1895],{}," with your marker.",[18,1969,1970,1971,1973,1974,1976,1977,1980,1981,1984,1985,1987,1988,1536],{},"Btw: instead of calling ",[132,1972,1902],{}," (which places the bottom centre of your ",[132,1975,1946],{}," over the defined\n",[132,1978,1979],{},"GeoPoint","), you can also use ",[132,1982,1983],{},"boundCenter()"," to place the centre of your ",[132,1986,1946],{}," over the ",[132,1989,1979],{},{"title":83,"searchDepth":84,"depth":84,"links":1991},[],[1337,1338],"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":1830,"description":1999},"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",[1346,1507,2002,2003],"marker","overlays","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":2007,"title":2008,"author":2009,"body":2010,"category":2212,"date":2213,"description":2214,"extension":91,"link":2215,"meta":2216,"navigation":94,"path":2217,"seo":2218,"slug":2014,"stem":2220,"tags":2221,"teaser":2222,"__hash__":2223},"blog/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i.md","How to add a 'Find Your Company' feature to your iPhone App – Part I",[1516],{"type":11,"value":2011,"toc":2210},[2012,2016,2028,2035,2045,2048,2056,2072,2084,2137,2152,2186,2205,2208],[14,2013,2015],{"id":2014},"how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i","How to add a \"Find Your Company\" feature to your iPhone App – Part I",[18,2017,2018,2019,2021,2022,2027],{},"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\nGoogle Maps and the ",[132,2020,1808],{}," framework. I won’t go into the details of MapKit here, since Apple’s documentation is\nawesome and they provide a lot\nof ",[123,2023,2026],{"href":2024,"rel":2025},"http://developer.apple.com/iphone/library/samplecode/CurrentAddress/Introduction/Intro.html#//apple_ref/doc/uid/DTS40009469",[127],"sample code",",\nwhich gets you up and running in no time.",[18,2029,2030,2031,2034],{},"What I’d like to show you today, is some code, which nicely zooms the Map to your office and current location of the App\nuser, once the ",[132,2032,2033],{},"MapView"," is loaded. Here are a couple of screenshots to give you an idea of what I’m talking about:",[18,2036,2037,2039,2040,2044],{},[24,2038],{"alt":1544,"src":1802}," ",[24,2041],{"alt":2042,"src":2043},"\"Synyx Routing\"","https://media.synyx.de/uploads//2010/05/screen_synyx_map_1.png","\nAs you can see, Synyx moved their offices to San Francisco. The iPhone App user, with the blue dot, is currently in\ncupertino. If he taps on the white/blue arrow in the annotation of the red dot, he is asked whether he’d really want to\nleave the App and start Google Maps to route from his current location to the Synyx Offices in San Francisco.",[18,2046,2047],{},"Now, the main focus on this blog post is how to get you using this in your App. I’m gonna split this little tutorial in\n2 parts:",[1739,2049,2050,2053],{},[1742,2051,2052],{},"Showing the Synyx Offices on the Map",[1742,2054,2055],{},"Zooming in, so that everything fits on the screen",[18,2057,2058,2059,2061,2062,2065,2066,2071],{},"If you feel like going off on your own, the code for the ",[132,2060,1563],{}," used, is available for download\non ",[123,2063,1535],{"href":1533,"rel":2064},[127],". The zooming algorithm is borrowed\nfrom ",[123,2067,2070],{"href":2068,"rel":2069},"http://stackoverflow.com/questions/1303265/algorithm-for-determining-minimum-bounding-rectangle-for-collection-of-lat-lon-co/1413264#1413264",[127],"stackoverflow",".\nHowever, the code is one of my first iPhone projects and not polished, so don’t use it blindly. There are a couple of\nissue, e.g. I’m pretty sure it won’t survive a memory warning. It’s meant for demonstration solely!",[18,2073,2074,2075,2077,2078,2080,2081,2083],{},"So let’s get into it. We first need a ",[132,2076,1563],{}," with a ",[132,2079,2033],{}," associated. It is pretty straightforward and\nyou should have no problems doing that in Interface Builder. The next thing you need and that is missing from the code\non github is a custom ",[132,2082,1808],{}," annotation:",[147,2085,2087],{"className":1567,"code":2086,"language":1569,"meta":83,"style":83},"\n@interface AddressAnnotation : NSObject\u003CMKAnnotation> {\n CLLocationCoordinate2D coordinate;\n NSString *title;\n NSString *subtitle;\n}\n- (id)initWith:(CLLocationCoordinate2D)_coords;\n@property(retain,nonatomic) NSString *title;\n@property(retain,nonatomic) NSString *subtitle;\n@end\n\n",[153,2088,2089,2093,2098,2103,2108,2113,2117,2122,2127,2132],{"__ignoreMap":83},[156,2090,2091],{"class":158,"line":159},[156,2092,162],{"emptyLinePlaceholder":94},[156,2094,2095],{"class":158,"line":84},[156,2096,2097],{},"@interface AddressAnnotation : NSObject\u003CMKAnnotation> {\n",[156,2099,2100],{"class":158,"line":170},[156,2101,2102],{}," CLLocationCoordinate2D coordinate;\n",[156,2104,2105],{"class":158,"line":176},[156,2106,2107],{}," NSString *title;\n",[156,2109,2110],{"class":158,"line":182},[156,2111,2112],{}," NSString *subtitle;\n",[156,2114,2115],{"class":158,"line":188},[156,2116,348],{},[156,2118,2119],{"class":158,"line":194},[156,2120,2121],{},"- (id)initWith:(CLLocationCoordinate2D)_coords;\n",[156,2123,2124],{"class":158,"line":200},[156,2125,2126],{},"@property(retain,nonatomic) NSString *title;\n",[156,2128,2129],{"class":158,"line":206},[156,2130,2131],{},"@property(retain,nonatomic) NSString *subtitle;\n",[156,2133,2134],{"class":158,"line":212},[156,2135,2136],{},"@end\n",[18,2138,2139,2140,2144,2145,2148,2149,1536],{},"This will be responsible for displaying the name and city of the Synyx Offices above the red pin on the map. But it’s\nonly responsible for displaying the information, it doesn’t know where yet. In order to find the location of our offices\nusing the Google Maps API, I came up with a little helper method\ncalled ",[123,2141,2143],{"href":1533,"rel":2142},[127],"synyxLocation (line 175)",". It simply\nreturns the ",[132,2146,2147],{},"CLLocationCoordinate2D"," struct, which is needed in our ",[132,2150,2151],{},"AddressAnnotation",[147,2153,2155],{"className":1567,"code":2154,"language":1569,"meta":83,"style":83},"\nCLLocationCoordinate2D location = [self synyxLocation];\nself.synyx = [[AddressAnnotation alloc] initWith:location];\n[self.synyx setTitle:@\"Synyx GmbH & Co. KG\"];\n[self.synyx setSubtitle:synyxLoc];\n[mapView addAnnotation:synyx];\n\n",[153,2156,2157,2161,2166,2171,2176,2181],{"__ignoreMap":83},[156,2158,2159],{"class":158,"line":159},[156,2160,162],{"emptyLinePlaceholder":94},[156,2162,2163],{"class":158,"line":84},[156,2164,2165],{},"CLLocationCoordinate2D location = [self synyxLocation];\n",[156,2167,2168],{"class":158,"line":170},[156,2169,2170],{},"self.synyx = [[AddressAnnotation alloc] initWith:location];\n",[156,2172,2173],{"class":158,"line":176},[156,2174,2175],{},"[self.synyx setTitle:@\"Synyx GmbH & Co. KG\"];\n",[156,2177,2178],{"class":158,"line":182},[156,2179,2180],{},"[self.synyx setSubtitle:synyxLoc];\n",[156,2182,2183],{"class":158,"line":188},[156,2184,2185],{},"[mapView addAnnotation:synyx];\n",[18,2187,2188,2189,2191,2192,2194,2195,1903,2198,2200,2201,2204],{},"Instantiating the ",[132,2190,2151],{}," with the previously determined location and adding it to the ",[132,2193,2033],{}," will to the\nrest. I did this in the ",[132,2196,2197],{},"viewDidLoad",[132,2199,1563],{},", which is not be the best place. Maybe the\n",[132,2202,2203],{},"viewWillAppear"," method would have been better.",[18,2206,2207],{},"After adding those parts discussed above, you can fire up the Simulator and get the location of our offices with a red\npin and your current location (in the Simulator it’s always Cupertino). The map doesn’t zoom yet and is not properly\nlocated, we leave that to our next installment.",[1329,2209,1812],{},{"title":83,"searchDepth":84,"depth":84,"links":2211},[],[1337,1338],"2010-05-06T09:19:07","We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\\nGoogle Maps and the MapKit framework. I won’t go into the details of MapKit here, since Apple’s documentation is\\nawesome and they provide a lot\\nof sample code,\\nwhich gets you up and running in no time.","https://synyx.de/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i/",{},"/blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i",{"title":2008,"description":2219},"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use\nGoogle Maps and the MapKit framework. I won’t go into the details of MapKit here, since Apple’s documentation is\nawesome and they provide a lot\nof sample code,\nwhich gets you up and running in no time.","blog/how-to-add-a-find-your-company-feature-to-your-iphone-app-part-i",[1507,1825],"We wanted to give our users the possibility to find our office. On the iPhone, the simplest way to do it, is to use Google Maps and the MapKit framework.…","xf7CAeTBsMxG4WUKphnA4aL0RIh0fwQNjjYZpLpVrbw",{"id":2225,"title":2226,"author":2227,"body":2228,"category":2301,"date":2302,"description":2303,"extension":91,"link":2304,"meta":2305,"navigation":94,"path":2306,"seo":2307,"slug":2309,"stem":2310,"tags":2311,"teaser":2313,"__hash__":2314},"blog/blog/google-maps-on-android.md","Google Maps on Android – Part 1: Navigation",[1832],{"type":11,"value":2229,"toc":2299},[2230,2233,2239,2251,2256,2269,2272,2275,2278,2281],[14,2231,2226],{"id":2232},"google-maps-on-android-part-1-navigation",[18,2234,2235,2236,1536],{},"Integrating a google map on android is quiet simple – how to do this basically is shown in\nthe ",[123,2237,1854],{"href":1852,"rel":2238},[127],[18,2240,2241,2242,2247,2248,2250],{},"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 ",[123,2243,2246],{"href":2244,"rel":2245},"http://maps.google.com",[127],"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 ",[132,2249,1979],{}," in your Android app:",[18,2252,2253],{},[153,2254,2255],{},"GeoPoint synyxOfficeLocation = new GeoPoint(49002175, 8394160);",[18,2257,2258,2259,2261,2262,2264,2265,2268],{},"As the ",[132,2260,1979],{}," 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 ",[132,2263,2033],{}," has a ",[132,2266,2267],{},"MapController",":",[18,2270,2271],{},"`MapController mapController = mapView.getController();",[18,2273,2274],{},"mapController.setCenter(synyxOfficeLocation);",[18,2276,2277],{},"mapController.setZoom(20);`",[18,2279,2280],{},"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.",[18,2282,2283,2284,2287,2288,1758,2291,2294,2295,2298],{},"The above mentioned methods change the map in a very static way. For some more visual effects try ",[132,2285,2286],{},"animateTo()",",\n",[132,2289,2290],{},"zoomIn()",[132,2292,2293],{},"zoomOut()"," to change the view of the map by showing a short animation. An also very helpful method is\n",[132,2296,2297],{},"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":83,"searchDepth":84,"depth":84,"links":2300},[],[1337,1338],"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":2226,"description":2308},"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",[1346,1507,2312],"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",[2316,2319,2322,2325,2328,2331,2334,2337,2340,2343,2346,2349,2352,2355,2358,2361,2364,2367,2370,2373,2376,2379,2381,2384,2387,2390,2393,2395,2398,2401,2404,2407,2410,2413,2416,2419,2421,2424,2427,2430,2433,2436,2439,2442,2444,2447,2450,2453,2456,2459,2462,2465,2468,2471,2474,2477,2480,2483,2486,2489,2492,2495,2498,2500,2503,2506,2509,2511,2514,2517,2520,2523,2526,2529,2531,2534,2537,2540,2543,2546,2549,2552,2555,2558,2561,2564,2567,2570,2573,2576,2579,2582,2585,2588,2591,2594,2597,2600,2603,2605,2608,2611,2614,2617,2619,2622,2625,2628,2631,2634,2637,2640,2643,2646,2649,2652,2655,2658,2661,2664,2667,2670,2673,2676,2679,2682,2685,2688,2691,2694,2695,2698,2701,2704,2707,2710,2713,2716,2719,2722,2725,2728],{"slug":2317,"name":2318},"abel","Jennifer Abel",{"slug":2320,"name":2321},"allmendinger","Otto Allmendinger",{"slug":2323,"name":2324},"antony","Ben Antony",{"slug":2326,"name":2327},"arrasz","Joachim Arrasz",{"slug":2329,"name":2330},"bauer","David Bauer",{"slug":2332,"name":2333},"bechtold","Janine Bechtold",{"slug":2335,"name":2336},"boersig","Jasmin Börsig",{"slug":2338,"name":2339},"buch","Fabian Buch",{"slug":2341,"name":2342},"buchloh","Aljona Buchloh",{"slug":2344,"name":2345},"burgard","Julia Burgard",{"slug":2347,"name":2348},"caspar-schwedes","Caspar Schwedes",{"slug":2350,"name":2351},"christina-schmitt","Christina Schmitt",{"slug":2353,"name":2354},"clausen","Michael Clausen",{"slug":2356,"name":2357},"contargo_poetzsch","Thomas Pötzsch",{"slug":2359,"name":2360},"damrath","Sebastian Damrath",{"slug":2362,"name":2363},"daniel","Markus Daniel",{"slug":2365,"name":2366},"dasch","Julia Dasch",{"slug":2368,"name":2369},"denman","Joffrey Denman",{"slug":2371,"name":2372},"dfuchs","Daniel Fuchs",{"slug":2374,"name":2375},"dobler","Max Dobler",{"slug":2377,"name":2378},"dobriakov","Vladimir Dobriakov",{"slug":2380,"name":2380},"dreiqbik",{"slug":2382,"name":2383},"dschaefer","Denise Schäfer",{"slug":2385,"name":2386},"dschneider","Dominik Schneider",{"slug":2388,"name":2389},"duerlich","Isabell Duerlich",{"slug":2391,"name":2392},"dutkowski","Bernd Dutkowski",{"slug":2394,"name":2394},"eifler",{"slug":2396,"name":2397},"essig","Tim Essig",{"slug":2399,"name":2400},"ferstl","Maximilian Ferstl",{"slug":2402,"name":2403},"fey","Prisca Fey",{"slug":2405,"name":2406},"frank","Leonard Frank",{"slug":2408,"name":2409},"franke","Arnold Franke",{"slug":2411,"name":2412},"frischer","Nicolette Rudmann",{"slug":2414,"name":2415},"fuchs","Petra Fuchs",{"slug":2417,"name":2418},"gari","Sarah Gari",{"slug":1359,"name":2420},"Gast",{"slug":2422,"name":2423},"graf","Johannes Graf",{"slug":2425,"name":2426},"grammlich","Daniela Grammlich",{"slug":2428,"name":2429},"guthardt","Sabrina Guthardt",{"slug":2431,"name":2432},"haeussler","Johannes Häussler",{"slug":2434,"name":2435},"hammann","Daniel Hammann",{"slug":2437,"name":2438},"heetel","Julian Heetel",{"slug":2440,"name":2441},"heft","Florian Heft",{"slug":1832,"name":2443},"Sebastian Heib",{"slug":2445,"name":2446},"heisler","Ida Heisler",{"slug":2448,"name":2449},"helm","Patrick Helm",{"slug":2451,"name":2452},"herbold","Michael Herbold",{"slug":2454,"name":2455},"hofmann","Peter Hofmann",{"slug":2457,"name":2458},"hopf","Florian Hopf",{"slug":2460,"name":2461},"jaud","Alina Jaud",{"slug":2463,"name":2464},"jayasinghe","Robin De Silva Jayasinghe",{"slug":2466,"name":2467},"jbuch","Jonathan Buch",{"slug":2469,"name":2470},"junghanss","Gitta Junghanß",{"slug":2472,"name":2473},"kadyietska","Khrystyna Kadyietska",{"slug":2475,"name":2476},"kannegiesser","Marc Kannegiesser",{"slug":2478,"name":2479},"karoly","Robert Károly",{"slug":2481,"name":2482},"karrasz","Katja Arrasz-Schepanski",{"slug":2484,"name":2485},"kaufmann","Florian Kaufmann",{"slug":2487,"name":2488},"kesler","Mike Kesler",{"slug":2490,"name":2491},"kirchgaessner","Bettina Kirchgäßner",{"slug":2493,"name":2494},"klem","Yannic Klem",{"slug":2496,"name":2497},"klenk","Timo Klenk",{"slug":110,"name":2499},"Tobias Knell",{"slug":2501,"name":2502},"knoll","Anna-Lena Knoll",{"slug":2504,"name":2505},"knorre","Matthias Knorre",{"slug":2507,"name":2508},"koenig","Melanie König",{"slug":9,"name":2510},"Thomas Kraft",{"slug":2512,"name":2513},"krupicka","Florian Krupicka",{"slug":2515,"name":2516},"kuehn","Christian Kühn",{"slug":2518,"name":2519},"lange","Christian Lange",{"slug":2521,"name":2522},"larrasz","Luca Arrasz",{"slug":2524,"name":2525},"leist","Sascha Leist",{"slug":2527,"name":2528},"lihs","Michael Lihs",{"slug":1516,"name":2530},"David Linsin",{"slug":2532,"name":2533},"maniyar","Christian Maniyar",{"slug":2535,"name":2536},"martin","Björnie",{"slug":2538,"name":2539},"martin-koch","Martin Koch",{"slug":2541,"name":2542},"matt","Tobias Matt",{"slug":2544,"name":2545},"mennerich","Christian Mennerich",{"slug":2547,"name":2548},"menz","Alexander Menz",{"slug":2550,"name":2551},"meseck","Frederick Meseck",{"slug":2553,"name":2554},"messner","Oliver Messner",{"slug":2556,"name":2557},"michael-ploed","Michael Plöd",{"slug":2559,"name":2560},"mies","Marius Mies",{"slug":2562,"name":2563},"mihai","Alina Mihai",{"slug":2565,"name":2566},"moeller","Jörg Möller",{"slug":2568,"name":2569},"mohr","Rebecca Mohr",{"slug":2571,"name":2572},"moretti","David Moretti",{"slug":2574,"name":2575},"mueller","Sven Müller",{"slug":2577,"name":2578},"muessig","Alexander Müssig",{"slug":2580,"name":2581},"neupokoev","Grigory Neupokoev",{"slug":2583,"name":2584},"nussbaecher","Carmen Nussbächer",{"slug":2586,"name":2587},"ochs","Pascal Ochs",{"slug":2589,"name":2590},"oelhoff","Jan Oelhoff",{"slug":2592,"name":2593},"oengel","Yasin Öngel",{"slug":2595,"name":2596},"oezsoy","Enis Özsoy",{"slug":2598,"name":2599},"posch","Maya Posch",{"slug":2601,"name":2602},"ralfmueller","Ralf Müller",{"slug":2604,"name":2604},"redakteur",{"slug":2606,"name":2607},"reich","Michael Reich",{"slug":2609,"name":2610},"reinhard","Karl-Ludwig Reinhard",{"slug":2612,"name":2613},"rmueller","Rebecca Müller",{"slug":2615,"name":2616},"rosum","Jan Rosum",{"slug":2618,"name":2618},"rueckert",{"slug":2620,"name":2621},"ruessel","Sascha Rüssel",{"slug":2623,"name":2624},"sauter","Moritz Sauter",{"slug":2626,"name":2627},"schaefer","Julian Schäfer",{"slug":2629,"name":2630},"scherer","Petra Scherer",{"slug":2632,"name":2633},"schlicht","Anne Schlicht",{"slug":2635,"name":2636},"schmidt","Jürgen Schmidt",{"slug":2638,"name":2639},"schneider","Tobias Schneider",{"slug":2641,"name":2642},"seber","Benjamin Seber",{"slug":2644,"name":2645},"sommer","Marc Sommer",{"slug":2647,"name":2648},"speaker-fels","Jakob Fels",{"slug":2650,"name":2651},"speaker-gierke","Oliver Gierke",{"slug":2653,"name":2654},"speaker-krupa","Malte Krupa",{"slug":2656,"name":2657},"speaker-mader","Jochen Mader",{"slug":2659,"name":2660},"speaker-meusel","Tim Meusel",{"slug":2662,"name":2663},"speaker-milke","Oliver Milke",{"slug":2665,"name":2666},"speaker-paluch","Mark Paluch",{"slug":2668,"name":2669},"speaker-schad","Jörg Schad",{"slug":2671,"name":2672},"speaker-schalanda","Jochen Schalanda",{"slug":2674,"name":2675},"speaker-schauder","Jens Schauder",{"slug":2677,"name":2678},"speaker-unterstein","Johannes Unterstein",{"slug":2680,"name":2681},"speaker-wolff","Eberhard Wolff",{"slug":2683,"name":2684},"speaker-zoerner","Stefan Zörner",{"slug":2686,"name":2687},"stefan-belger","Stefan Belger",{"slug":2689,"name":2690},"steinegger","Roland Steinegger",{"slug":2692,"name":2693},"stern","sternchen synyx",{"slug":103,"name":103},{"slug":2696,"name":2697},"szulc","Mateusz Szulc",{"slug":2699,"name":2700},"tamara","Tamara Tunczinger",{"slug":2702,"name":2703},"theuer","Tobias Theuer",{"slug":2705,"name":2706},"thieme","Sandra Thieme",{"slug":2708,"name":2709},"thies-clasen","Marudor",{"slug":2711,"name":2712},"toernstroem","Olle Törnström",{"slug":2714,"name":2715},"ullinger","Max Ullinger",{"slug":2717,"name":2718},"ulrich","Stephan Ulrich",{"slug":2720,"name":2721},"wagner","Stefan Wagner",{"slug":2723,"name":2724},"weigel","Andreas Weigel",{"slug":2726,"name":2727},"werner","Fabian Werner",{"slug":2729,"name":2730},"wolke","Sören Wolke",["Reactive",2732],{"$scookieConsent":2733,"$ssite-config":2735},{"functional":2734,"analytics":2734},false,{"_priority":2736,"env":2740,"name":2741,"url":2742},{"name":2737,"env":2738,"url":2739},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",2745],{"category-google":-1,"authors":-1},"/blog/tags/google"]