How Fake Camera works

After recent Kik update I have full mailbox of messages like „Fix fake camera asap“ and a lot of one stars ratings at Google Play. So let’s have a look into how Fake Camera works and how Kik/Android camera works – to explain, why I am unable to do anything with this. Do you think … Pokračovat ve čtení „How Fake Camera works“

After recent Kik update I have full mailbox of messages like „Fix fake camera asap“ and a lot of one stars ratings at Google Play. So let’s have a look into how Fake Camera works and how Kik/Android camera works – to explain, why I am unable to do anything with this.

Do you think Fake Camera is that „Camera picker dialog“ which appears after you chose to take a picture? Wrong! That’s Android system dialog!! Take a look what happens, when application (like old version of Kik) asks for taking a picture. There are two possible scenarios: there is just one camera application on your phone (or some of you cameras is set as default) OR there are more camera applications on your phone. Let’s draw a simple diagram of communication between application and android phone – for both of those cases.

In case one application (for example old version of Kik) asks for taking a picture, so phone answers with just one camera application and runs it for Kik. Let’s say it opens your phone default camera application directly.

Phone runs camera application directly
Phone runs camera application directly

In the second case, when there are more camera applications to chose, phone displays dialog – camera picker and lets user choose which camera application use. And runs it for him again.

Phone lets user choose, which camera application should be used
Phone lets user choose, which camera application should be used

Can you see that? That dialog is not displayed by Fake Camera application, but by your Android phone itself! Fake camera is just another camera application within that list!!!

Now let’s see how all this work with new Kik update: this new update has its own (in-application) camera, so it does not call external camera application for taking pictures. It just takes a picture within that application and does not say anything to Phone nor other application.

How new Kik makes pictures
How new Kik makes pictures

Again – can you see that? There is no way to let your Android phone to display that Camera picker dialog, because all that stuff does application itself (Kik). And because there is no way your phone can display that dialog, there is no way to run Fake camera (that gallery picker) nor any other Camera application. Deal with that or write a message to Kik (or whatever application you want to use) authors and not me, I have nothing to do with this!! And of course – if you changed your rating of Fake Camera because of this, put it back please, because Fake camera still works correctly!!

PS if you are still unable to deal with that, take the MinusIQ pill and go chatting to g+ page, if there is some place between animated gifs and selfies.

Glass hackathon Brno 2014

Přestože jsem se spolupodílel na organizaci této akce, nebudu psát o slastech a strastech organizování, ale o tématu, které se přímo týká kódování a docela nepříjemně mě na této akci spálilo. Totiž o googlovském (dá se říci) házení klacků early adopterům a vývojářům pod nohy (hateposty dneska frčí, musím se trendu chytit!). Spolčil jsem se … Pokračovat ve čtení „Glass hackathon Brno 2014“

Přestože jsem se spolupodílel na organizaci této akce, nebudu psát o slastech a strastech organizování, ale o tématu, které se přímo týká kódování a docela nepříjemně mě na této akci spálilo. Totiž o googlovském (dá se říci) házení klacků early adopterům a vývojářům pod nohy (hateposty dneska frčí, musím se trendu chytit!).

Spolčil jsem se s Radkem Bartoňem z Trinerdisu, protože jsem měl náladu stvořit nějaký backend, více než drátovat pro brýle. Radek přišel s nápadem čtečky ebooků ne nepodobnou Spritzu, na který mě shodou okolností nedávno odkázal jeden známý. Idea byla taková, že uživatel na telefonu vybere knížku a nechá si ji poslat do brýlí. Tudíž nějaký „push to device“. Pojďme se podívat na možnosti, které pro tuto funkcionalitu připadají v úvahu.

Glglglass

Nejjednodušší možností je využít Mirror API a intent filter. Pomocí Mirror API se pushne na timeline speciální karta, která obsahuje volání URI definované ve výše zmíněném intent filteru. Pěkný příklad použití je uveden v jedné odpovědi na Stack overflow.

Možností druhou je Google Cloud Messaging. Ten nabízí metody pushování notifikací dvě: pomocí HTTP a XMPP. HTTP volání je čistě synchronní, což vyplývá z podstaty protokolu – z mé aplikace (serveru) pošlu zprávu serverům GCM a čekám na odpověď o přijetí, což mě blokuje ve snaze odeslat zprávu další. Naproti tomu XMPP využívá GCM server jako broker, do kterého pere zprávy a odpovědi může až dodatečně (na základě id) vyhodnotit. Z toho je jasné, že pomocí HTTP je možné komunikovat pouze ve směru server->cloud->zařízení, kdežto v případě XMPP je díky držení otevřeného spojení možná obousměrná komunikace.

Protože byl na hákování vyhrazen celý den, zvolil jsem možnost b) Google Cloud Messaging a sice s využitím XMPP, abych neudělal během pěti minut servlet pro Mirror API a pak celý den jen neseděl a nekroutil palcama. Mimo jiné by potom bylo možné tento stejný přístup použít i pro mobilní aplikaci, která pushnutí iniciuje. Dokumentace je přehledná, dokonce je její součástí celý zdroják pro navázání komunikace s GCM. Existují i nějaké další poměrně detailní tutoriály. Kámen úrazu byl, že jsem (už po několikáté) přehlédnul nenápadný řádek „Note: To try out this feature, sign up using this form,“ takže jsem se nemálo divil odpovědi „Project 858xyz396 not whitelisted.“ Až po chvíli jsem daný formulář našel a zaregistroval do něj náš projekt pro hackathon. Bohužel, není to jenom robotem schvalovaná registrace, takže „lefuq“, zabil jsem spoustu času, tohle je jen pro vyvolené, až mi to někdy někdo schválí.

Po obědě jsem se pustil do GCM pomocí HTTP. Ovšem opět jsem to nedotáhl do konce, protože záhy mi Radek hlásí, že brýle nepodporují Google Play Services. To je teda fail, srsly, na co ty brejle jsou, opravdu jen na to pushování karet pomocí Mirror API? Nebo opět pouze pro vyvolené (to by mě zajímalo, kdo to je, když vývojáři zaplatili nemalé peníze za samotné pitomé brýle)?

Takže nakonec nouzovka v podobě jednoduchého REST rozhraní, kdy si brýle samy tahají seznam a odpovídající knížky. Škoda, teď by se hodil další den hackathonu, kdy bychom přidali to pushování pomocí Mirror API a k tomu appku pro telefon s tydlidrojdem (nebo tlačítko do prohlížeče – na pushnutí článku k pozdějšímu přečtení).

Poučení na závěr: důvěřuj, ale prověřuj se ve spojitosti s Googlem mění na: nedůvěřuj, ale prověř, neuvěř a radši to udělej jinak. Každopádně aspoň nějakým výstupem je zdroják k „serveru“ na GitHubu a běžící endpoint na OpenShiftu. Tak alespoň něco, mimo akce samotné a následných škopků.

Howto clear defaults to get Fake Camera running again

Quick note: please use Google plus community for asking a questions. I like to read reviews of my applications on Google Play, especially those with suggestions and questions :). Last few days I’m getting more and more questions like this one for Fake Camera Won’t open on kik When I tried to send a pic … Pokračovat ve čtení „Howto clear defaults to get Fake Camera running again“

Quick note: please use Google plus community for asking a questions.

I like to read reviews of my applications on Google Play, especially those with suggestions and questions :). Last few days I’m getting more and more questions like this one for Fake Camera

Won’t open on kik When I tried to send a pic as a live pic from the gallery it took me to the regular camera and didn’t give me the option to choose the app or the regular camera.

or this one

Fake Camera does not work for me, you sucks!

So because of that I’m going to write a step by step how to clear defaults and let camera picker appear again, because that is the main reason of those problems. We’ll start quickly, I just want to say following at the beginning: If this work for you, don’t forget to change your rating and review on Google Play!!!!

If you accidentally selected „Use as default“ checkbox in Camera picker, it’s possible to revert this, Just follow these steps:

Open phone settings
Open phone settings
Choose "Applications"
Choose „Applications“
Then "Manage applications"
Then „Manage applications“

And now comes the tricky part: in application list select that app, which opens automatically instead of camera picker, in my case it’s standard Camera app:

In All apps select Camera application
In All apps select Camera application
Tap "Clear defaults" button and confirm
Tap „Clear defaults“ button and confirm

And we are done for now, camera picker dialog should appear instead of default selected application. Please note that this has nothing to do with inapp camera some applications may use – that’s impossible to fake.

One more time – if your Fake Camera works again, don’t forget to change your rating and review on Google Play, thank you! And, of course, if you’d like to say thanks for Fake Camera, just buy a Donate Version, or send me some bitcoins or so. Thank you!

Howto access recent query suggestions on Android and populate ListView with them

I’m working on search activity for Beermapa and because I did not find any topic covering reading access to SearchRecentSuggestions and I needed to load saved suggestions and fill a ListView with them, so I did some research and wrote this post. What I want to achieve is an input for writing search query, which … Pokračovat ve čtení „Howto access recent query suggestions on Android and populate ListView with them“

I’m working on search activity for Beermapa and because I did not find any topic covering reading access to SearchRecentSuggestions and I needed to load saved suggestions and fill a ListView with them, so I did some research and wrote this post.

What I want to achieve is an input for writing search query, which displays custom suggestions (based on searching query) and also ListView placed right under input box, which displays recent search queries.

SearchView with ListView
SearchView with ListView

My first try was with EditText and TextWatcher, which after each written character filtered my listAdapter backing ListView, something like this:

[code lang=“java“]
searchEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {}

public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

public void onTextChanged(CharSequence s, int start, int before, int count) {
adapter.getFilter().filter(s);
}
});
[/code]

But since Android has pretty good Search Interface, I was wondering how to use it with my search (because of possible future use of voice search etc.). Adding recent query suggestion is described in tutorial, also custom suggestion is. But there is nothing about how to access saved queries another way then in SearchView whispering box. Saving is really easy:

[code lang=“java“]
SearchRecentSuggestions suggestions = new SearchRecentSuggestions(getApplicationContext(), MySuggestionProvider.AUTHORITY, MySuggestionProvider.MODE);

suggestions.saveRecentQuery(query, null);
[/code]

First solution which came on my mind was to implement own saveRecentQuery routine, which saves last queries to local database, but that’s not the way – why to write already written code again?

Finally – there is possibility to access those saved queries through ContentResolver, which returns Cursor:

[code lang=“java“]
ContentResolver contentResolver = getApplicationContext().getContentResolver();

String contentUri = „content://“ + MySuggestionProvider.AUTHORITY + ‚/‘ + SearchManager.SUGGEST_URI_PATH_QUERY;
Uri uri = Uri.parse(uriStr);

Cursor cursor = contentResolver.query(uri, null, null, new String[] { query }, null);
[/code]

Where query is String representing searching query, or null for returning all records. Now with received cursor it’s simple to populate ListView:

[code lang=“java“]
cursor.moveToFirst();

String[] columns = new String[] { SearchManager.SUGGEST_COLUMN_TEXT_1 };
int[] views = new int[] { R.id.name };

ListAdapter listAdapter = new SimpleCursorAdapter(this, R.layout.component_pub_row, cursor, columns, views, 0);
listView.setAdapter(listAdapter);
[/code]

Note that last parameter in SimpleCursorAdapter – it’s there because of deprecation of constructor with FLAG_AUTO_REQUERY, for more details see this Stack Overflow topic.

Verzování a testování Android aplikací

Vrátil jsem se nedávno k jedné odložené aplikaci a začínám ji křísit s novými nápady a s trochu změněným konceptem (s detaily přijdu v brzké budoucnosti) a protože jsem se dostal k potřebě aplikaci testovat ve více lidech, kdy někteří mají ne zrovna aktuální verzi buildu a přesto reportují chyby, začal jsem používat systém číslování … Pokračovat ve čtení „Verzování a testování Android aplikací“

Vrátil jsem se nedávno k jedné odložené aplikaci a začínám ji křísit s novými nápady a s trochu změněným konceptem (s detaily přijdu v brzké budoucnosti) a protože jsem se dostal k potřebě aplikaci testovat ve více lidech, kdy někteří mají ne zrovna aktuální verzi buildu a přesto reportují chyby, začal jsem používat systém číslování verzí, který bych chtěl tímto postem popsat.

Problém je následující: vypustím testovací verzi mezi testery a mezitím vývoj nezamrznu, ale pokračuju v opravách, úpravách a (přiznám se k tomu) někdy i nových featurách. Často se stane, že tester pošle stacktrace starší verze, nebo přijde aktuální verze, jenomže po úpravách už nesedí čísla řádků, či díky refactoru je daný fragment někde úplně jinde a podobně. Jak teda zkontrolovat, jestli už chyba byla odstraněna, nebo tam je pořád? Poměrně jednoduše, stačí pár nástrojů.

Celý řetězec sestává z IDE Eclipse, Pivotal Trackeru, PT-mylyn connectoru a nějakého systému zprávy verzí (používám Git na Bitbucketu). Základem je si navyknout odklepávat aktivní tasky zadané pomocí Pivotal Trackeru v Mylynu, měnit odpovídající čísla patchů v Manifestu a commity provádět s daným identifikátorem (ideálně do vlastního branche, který se po release zmerguje, ale kdo by se s tím sr…l, žejo :)). Pojďme ale postupně, ukážu to na konkrétním příkladě – tasku. (Pozn. používaná skladba číslování verzí vychází z následujícího schématu, uvedeného např. na wikipedii).

Schéma číslování verzí
Schéma číslování verzí

Veškeré změny, požadavky a bugy zadávám pouze skrze Pivotal Tracker (nebudu tu rozebírat nástroj samotný, ani jak iteruju – to je každého věc :)). Důležité je, že každá story má jednoznačný identifikátor, jak je vidět na následujícím screenshotu.

Identifikátor story v Pivotal trackeru
Identifikátor story v Pivotal trackeru

V Eclipse mám PT napojený skrze mylyn-connector (návod jak na to je přímo na stránkách projektu) na Task list, kdy pracuju na aktuální vybrané úloze. Na následujícím screenshotu už je story finishnutá, takže je přeškrtnutá.

Zobrazení story v Task listu
Zobrazení story v Task listu

Když potom dané změny commitnu, nechávám jako title dané změny identifikátor a popisek, který je v PT – při commitu jsou už automaticky předvyplněné. V přehledu verzovadla je potom jednak vidět interní identifikátor daného commitu, ale v rámci popisku i identifikátor z PT.

Hotový commitnutý task v přehledu na bitbucketu
Hotový commitnutý task v přehledu na bitbucketu

A teď ono celkové propojení: když aktivuju task v IDE, změním daným identifikátorem z PT i build verzi v manifestu, společně s versionCode.

Čísla verzí v AndroidManifest.xml
Čísla verzí v AndroidManifest.xml

Když tedy následně distribuuju danou verzi testerům, tak je možné v rámci vlastností aplikace přehledně zkontrolovat verzi, kterou testují.

Zobrazení čísla verze ve vlastnostech aplikace
Zobrazení čísla verze ve vlastnostech aplikace

A pokud někdo nahlásí chybu, o které si nejsem jistý, zda byla opravená, jednoduše se vrátím ke konkrétní verzi z repozitáře a můžu zkontrolovat, o co vlastně šlo a případně si zadat novou story (bug by mohl být zadán číslem revize a build verze se neměnit).

Je mi jasné, že asi znovuobjevuju kolo, ale připadlo mi to celé tak geniálně jednoduché a praktické, že mi nedalo se o to nepodělit. Také mi nedá se nepodělit o očekávání testovacího frameworku Testflight pro Android. Věřím, že předchozí tipy skvěle doplní a posune na ještě vyšší level!

Custom ROM a Samsung Galaxy S2

Už je to rok a půl, co jsem majitelem v nadpise uvedené hračky. Mimo úvodních pár měsíců, kdy jsem byl nadšen rychlostí systému (v porovnání s G1), jsem na telefon permanentně nadával a v poslední době to gradovalo do takových extrémů (touha rozmlátit telefon, komunitu i google), že jsem se konečně přinutil věnovat odpoledne nahrání … Pokračovat ve čtení „Custom ROM a Samsung Galaxy S2“

Už je to rok a půl, co jsem majitelem v nadpise uvedené hračky. Mimo úvodních pár měsíců, kdy jsem byl nadšen rychlostí systému (v porovnání s G1), jsem na telefon permanentně nadával a v poslední době to gradovalo do takových extrémů (touha rozmlátit telefon, komunitu i google), že jsem se konečně přinutil věnovat odpoledne nahrání Custom ROMky, zprovoznění ROOTa a image s recovery. A protože jsem ne všechny potřebné informace a nástroje našel na jednom místě a navíc se potýkal s drobnými problémy, přijde mi vhod udělat tímto postem takový malý rozcestník.

Když jsem svého času přehrával systém v G1 (CyanogenMod a později SuperD), byly všechny kroky popsány pěkně pod sebou přímo na stránkách s danou ROMkou, u Toshiby se daly rozumně dohledat po fórech. Co se však týká SG2, našel jsem víceméně kulové. Všechny návody (oficiální) počítají s tím, že už požívám nějakou jinou ROMku a přestože někdy úvod a nadpis vypadá, že bude dále řeč o vanilla Samsung paskvilu androidu, nebylo tomu tak. Mimo jiné se tyto návody specializují spíše na Windows, já však potřeboval update provést z Macka (a obecně by to mělo fungovat i pro GNU Linux).

Miui
Miui

Pokusím se tyto kroky sepsat níže, spolu se zdrojem, odkud jsem čerpal. Uváděná čísla verzí (pokud je uvedu) se samozřejmě časem změní, takže v mnoha případech zůstanu pouze u uvedení názvu daného nástroje a strýček google jistě poradí. Ještě bych na úvod poznamenal, že nenesu žádné riziko, pokud si někdo následujícím postupem s telefonem něco provede.

1. Instalace obrazu zavaděče – postupoval jsem podle howto ve wiki Cyanogen modu, který mě nasměroval na Heimdal Suite, což je multiplatformní nástroj pro flashování obrazů do Samsung Galaxy S zařízení, jak tvrdí oficiální FAQ. Z výše uvedené wiki jsem se dostal i ke ClockworkMod Recovery. Heimdal jsem stáhnul ve formě instalačního balíčku a hned tady jsem měl první problém – instalátor umně skryl okno s informací o potřebě restartu pod všechna okna a já se chvíli trápil s tím, proč se mi nedaří aplikace použít. Po restartu však vše šlo jako po másle a bylo možné nahrát image do zařízení (nejprve nutno zapnout USB debugging a nastartovat telefon do „download režimu“ pomocí trojhmatu volume down, home a power):

[code]sudo heimdall flash –kernel zImage[/code]

2. Povolení superuživatele – spočívající v nahrání binárky su a aplikace Superuser. Na tuto aplikaci mě odkázal miculog, kterého jsem se ale zase tak moc nedržel a ze stránek stáhl zip archivy, které jsem nahrál na kartu, nabootoval do recovery (tentokrát volume up, home a power) a odtud je pomocí „Install zip from sdcard“ nainstaloval.

3. Provedení zálohy – stále v recovery režimu je možné provést zálohu systémových oddílů v nabídce „backup and restore“.

4. Stažení vlastního obrazu s ROM – a následné nahrání na kartu (nebo interní úložiště) v telefonu. Já se rozhodl pro MIUI ROM, takže jsem stahoval nejnovější build a snažil se postupovat podle oficiálního návodu. Bohužel jsem v rámci minulého kroku zároveň začal mazat i cache, data a další místa, takže mi telefon odmítl nabootovat a nemohl jsem tak do něj nahrát zip soubor s updatem. Po chvíli googlení jsem problém vyřešil a pomocí adb z Android SDK namountoval „ramdisk“ jako externi úložiště (telefon v recovery režimu) a nahrál tam požadovaný image:

[code]shmoula $ adb shell
# mount -t tmpfs none /sdcard
# exit
shmoula $ adb push miuiandroid_GT-I9100_ics-2.11.23.zip /sdcard/
. . . . .[/code]

5. Instalace custom ROM – po předchozím vymazání „wipe data/factory reset“, „wipe cache partition“, „advanced – Wipe Dalvik Cache“ a konečně „Mounts and Storage – Format/System“ následuje opět „Install zip from SD card“ a výběr v předchozím kroku nahraného zazipovaného image. Po chvíli akce a napětí následuje restart telefonu pomocí výběru „restart system now“ a s největší pravděpodobností naběhnutí nového systému (a drobné rozčarování z jiného rozložení a následujícího kroku).

6. Instalace Google aplikací – protože nesmějí být bundlovány s custom ROMkama. MIUI už však části těchto aplikací obsahuje, navíc má vlastní market a další drobnosti, takže je nutné tyto služby nejprve povypínat, jak radí začátek tohoto vlákna.

[code]shmoula $ adb shell
# mount -o remount,rw -t ext4 /dev/block/mmcblk0p1 /system
# mv /system/app/NetworkLocation.apk /system/app/MiuiNetworkLocation.bak
# pm disable com.miui.cloudservice
# pm disable com.xiaomi.xmsf
# exit[/code]

Teď už je možné stáhnout balíček s google apps a nahrát jej na kartu v telefonu. Instaluje se opět jako update v recovery režimu volbou „Install zip from SD card“.

7. Aktivování Mass usb režimu – od jisté verze tydlidrojdu není povolen USB Mass storage režim, takže je nutné jej aktivovat ručně:

[code]shmoula $ adb shell
# setprop persist.sys.usb.config mass_storage[/code]

8. Závěrečný restart telefonu a přihlášení se ke svému google účtu a následný komentář s pochvalou pod tímto návodem.

Android MapView a onTouch

Nemálo mě, a nejenom mě, štve absence něčeho, jako je onTouch() v mapách na Androidu, protože potřebuju vytvořit dynamické načítání overlayů. MapView sice má onTouchEvent(), ale že by bylo možné jej použít nějakým mnou zamýšleným způsobem, to zrovna ne. Zkoušel jsem pár způsobů, které tu nastíním a taky jsem zkusil jedno ne zrovna dvakrát moc … Pokračovat ve čtení „Android MapView a onTouch“

Nemálo mě, a nejenom mě, štve absence něčeho, jako je onTouch() v mapách na Androidu, protože potřebuju vytvořit dynamické načítání overlayů. MapView sice má onTouchEvent(), ale že by bylo možné jej použít nějakým mnou zamýšleným způsobem, to zrovna ne. Zkoušel jsem pár způsobů, které tu nastíním a taky jsem zkusil jedno ne zrovna dvakrát moc fér řešení. Ale zoufalí lidé se uchylují k zoufalým věcem, však to znáte… Jen to vezmu hodně hopem, protože mám moře jiných věcí na dělání, ale pár lidem jsem to slíbil a sliby se mají plnit a to nejen o Vánocích ;-).

Zatímco natahování je poměrně jasné (AsyncTask, který bude natahovat data ve čtverci, jehož směrem jsem se posunul – na základě kolize souřadnic tohoto čtverce a viewportu – aby to pořád netahalo jak blbé), taková trivialita jako je onTouch už tak jasná není.

Nejprve jsem se snažil přimět k rozumu metodu onTouchEvent() jak na MapView, tak i na Overlayi, bohužel bezúspěšně. Zajímalo by mě, jestli je tento event pozůstatek něčeho zaniklého, nebo se na něj jenom nějak zapomnělo. Tento způsob by měl fungovat nějak takto, což vypadá poměrně prakticky a použitelně – ve switchi checkovat eventy:

[sourcecode language=“java“]public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
. . .
}
return super.onTouchEvent(ev);
}[/sourcecode]

Bohužel – z nějakého důvodu tato metoda vůbec nebyla volána. Po nějakém googlení jsem narazil na blog Juriho Strumpflohnera, který řešil stejný problém a vyšpekuloval ho následovně: na mapView navěsit vlastní OnTouchListener(), čili přidat jednoduchou anonymní třídu:

[sourcecode language=“java“]mapView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent ev) {
return false;
}
});[/sourcecode]

Pokud teda dojde k eventu Touch, jsou procházeny všechny views a případné implementace listenerů v nich. V listeneru implementovaná metoda onTouch je volána nejméně jednou v závislosti na tom, zda vrátí true či false. Pokud vrací false, provede se pouze poprvé a systém je tak chytrý, že už jde příště rovnou na jistotu (aka přímo po metodě, která vrátila true), což je zároveň i kámen úrazu: je nutné zajistit, aby byla volána i metoda „níže“, protože se při true mapa nepohybuje (ale náš onTouch je volaný vždycky). Je teda nutné předat „nižšímu view“ tento event. Nejprve jsem volal dispatchTouchEvent() na view, který přišel do onTouch, ale to je blbost, protože tento view je vlastně onen MapEvent, nad kterým byl navěšen tento listener, takže došlo k zacyklení a StackOverflowError. Takže jsem třídu odanonymizoval a předávám si do ní Context, což je aktivita, uvnitř které je mapView:

[sourcecode language=“java“]public class MyTouchListener implements View.OnTouchListener {
private Activity activity;

public MyTouchListener(Activity activity) {
super();
this.activity = activity;
}

public boolean onTouch(View v, MotionEvent ev) {
activity.dispatchTouchEvent(ev);
return true;
}
}[/sourcecode]

Taky to ovšem nejede, ale oproti dispatchování do view to vydrží o chvíli déle (a pak to stejně přeteče zásobník a zdechne).

Jak už to bývá, funkční řešení bývají ta nejjednodušší, takže dneska tenhle blogpost konečně dorazím!

Ono totiž stačí rozšířit map view a přepsat v něm onTouchEvent, nebo onInterceptTouchEvent (jak je popsáno tady). V čem je zakopán pudl? V ničem, opravdu to funguje. Předchozí Class Cast Exception se dá zbavit velice jednoduše – definovat správný typ v layoutu! Pojďme tedy na to, nejprve vlastní mapView:

[sourcecode language=“java“]package cz.shmoula.pat;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

import com.google.android.maps.MapView;

public class AreaMapView extends MapView {
private Context mContext;

public AreaMapView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
mContext = context;
}

public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_MOVE) {
Log.i("!!!!!", "Pohyb!");
}
return super.onTouchEvent(ev);
}
}[/sourcecode]

Teď následuje onen výše zmíněný kámen úrazu, který dříve hulákal Class Cast výjimkou a je to docela logické, ale nevěděl jsem, že je možné vkládat do layoutů vlastní komponenty. Teď už to vím, tak je možné psát něco zhruba takového:

[sourcecode language=“xml“]<?xml version="1.0" encoding="utf-8"?>

<cz.shmoula.pat.AreaMapView
xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/areaMapView"

android:layout_width="fill_parent"
android:layout_height="fill_parent"

android:clickable="true"
android:apiKey="api_klíč"
/>
[/sourcecode]

No a v aktivitě se tato komponenta dohledá každému známým způsobem:

[sourcecode language=“java“]AreaMapView mapView = (AreaMapView) findViewById(R.id.areaMapView);[/sourcecode]

Toť vše! Opravdu, v jednoduchosti je síla. Takže teď přidat nějaké spočítání delta a pokud bylo pohnuto viewportem více, než je výše uvedený čtverec v onu stranu, tak načíst další. Na závěr se ještě musím přiznat, že toto řešení jsem nevyplodil sám, ale sprostě se nechal inspirovat OpenCachingem od Garmina, který mě nakopl na správnou cestu směrem k výsledku. Při pídění se po řešení jsem taky narazil na projekt mapview-overlay-manager, ve kterém se nachází Lazy loading a který si nechávám k dalšímu prostudování a hádám, že nebudu sám.

Dekompilace androidích aplikací

Někdo se rád pídí po tom, jak věci fungují, někdo se rád občas nechává inspirovat cizími kusy kódu a někdo prachsprostě krade (možná by se to dalo nazvat nějakou IT kleptomanií). Všem těmto skupinám by ovšem mohl přijít vhod tento blogpost, protože v několika krocích popisuje operaci jednoduchou (tu jednoduchost myslím vážně, zabývat se dedexerem, … Pokračovat ve čtení „Dekompilace androidích aplikací“

Někdo se rád pídí po tom, jak věci fungují, někdo se rád občas nechává inspirovat cizími kusy kódu a někdo prachsprostě krade (možná by se to dalo nazvat nějakou IT kleptomanií). Všem těmto skupinám by ovšem mohl přijít vhod tento blogpost, protože v několika krocích popisuje operaci jednoduchou (tu jednoduchost myslím vážně, zabývat se dedexerem, aka android disassemblerem opravdu nehodlám, z toho už jsem vyrostl a zpohodlněl) – a sice rozbalování a dekompilaci androidí aplikace.

Jak je tvořena androidí aplikace asi každý vývojář ví, takže tuto část vypustíme a přejděme blíže k věci, což by mohl být aapt, neboli Android Asset Packing Tool, který je součástí nástrojů v SDK. Tento nástroj je jednak zodpovědný za parsování všemožných souborů (včetně manifestu) uvnitř projektu a následné přetváření takových properties do R.java souboru, ale především i za proces opačný, čili dolování informací z již hotových .apk souborů a to i podepsaných a zarovnaných. Nebudeme chodit kolem horké kaše a skočme rovnou do nějakého příkladu. Půjčil jsem si APKčko z mého rozdělaného projektu a provedl nad ním následující kouzlo:

[sourcecode type=“bash“]vbalak@vbalak-desktop:~/develop/workspace.sts/PlantATree/client/target$ aapt l -a pat-client.apk
res/layout/area_map.xml
res/layout/main.xml
res/layout/tree_details.xml
res/layout/tree_list.xml
res/layout/tree_list_item.xml
res/menu/map_menu.xml
AndroidManifest.xml
resources.arsc
res/drawable-hdpi/icon.png
res/drawable-ldpi/icon.png
res/drawable-mdpi/icon.png
classes.dex
org/codehaus/jackson/map/VERSION.txt
org/codehaus/jackson/impl/VERSION.txt
META-INF/MANIFEST.MF
META-INF/CERT.SF
META-INF/CERT.RSA
[/sourcecode]

Nejprve je vidět seznam souborů nacházejících se v archivu (.apk je obyčejný zip soubor, takže je možné jej jednoduše rozbalit, v tom není žádná věda). Další část (resource table) je zajímavější – jedná se o identifikátory zdrojů, neboli to, co je v souboru R.java – to jsou ty hexa čísla uvedená bezprostředně za spec resource a dále typ tohoto zdroje – layout, string, drawable, id…

[sourcecode type=“plain“]Resource table:
Package Groups (1)
Package Group 0 id=127 packageCount=1 name=cz.shmoula.pat
Package 0 id=127 name=cz.shmoula.pat typeCount=6
type 0 configCount=0 entryCount=0
type 1 configCount=3 entryCount=1
spec resource 0x7f020000 cz.shmoula.pat:drawable/icon: flags=0x00000100
spec resource 0x7f04000a cz.shmoula.pat:string/menu_toggle_view: flags=0x00000000
. . .[/sourcecode]

Nejzajímavější je ovšem část poslední, což je sám velký manifest. Kdo měl možnost nahlédnout do AndroidManifest.xml a teď kouká na tento výpis, všímá si velké podobnosti (pokud potlačí rozdílný způsob zobrazení – tohle není xml ;-)). Jsou zde krásně vidět využívaná oprávnění, je zde vidět, jakou program používá ikonu (to je ten hexa kód, který se dá dohledat v části se zdroji – Resources Table). Jsou zde vidět jednotlivé aktivity, parametry aktivit a definované intent-filtry a také využívané knihovny – prostě kompletní manifest, akorát jinak formátovaný. Pro naše další potřeby potřebujeme vstupní bod do aplikace, což bude LAUNCHER a ten je nastaven pro třídu MainActivity, která je v balíčku cz.shmoula.pat, bude se nám hodit za chvíli. Za zmínku ještě stojí anotherMapProcess v poslední aktivitě – dvě mapy se v jedné aplikaci nesnesou, tudíž je nutné je spouštět v rámci jiného procesu, tento parametr tohle zajišťuje (taky jsem nad tím tenkrát dlouho koumal).

[sourcecode type=“plain“]Android manifest:
N: android=http://schemas.android.com/apk/res/android
E: manifest (line=2)
A: android:versionCode(0x0101021b)=(type 0x10)0x1
A: android:versionName(0x0101021c)="0.1" (Raw: "0.1")
A: package="cz.shmoula.pat" (Raw: "cz.shmoula.pat")
E: uses-permission (line=5)
A: android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
E: application (line=7)
A: android:label(0x01010001)=@0x7f040001
A: android:icon(0x01010002)=@0x7f020000
E: activity (line=8)
A: android:name(0x01010003)=".MainActivity" (Raw: ".MainActivity")
E: intent-filter (line=9)
E: action (line=10)
A: android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
E: category (line=11)
A: android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")
E: activity (line=15)
A: android:name(0x01010003)=".LookAroundActivity" (Raw: ".LookAroundActivity")
. . .
E: activity (line=16)
A: android:theme(0x01010000)=@0x1030006
A: android:name(0x01010003)=".ShowAreaMapActivity" (Raw: ".ShowAreaMapActivity")
A: android:process(0x01010011)=":anotherMapProcess" (Raw: ":anotherMapProcess")
E: uses-library (line=22)
A: android:name(0x01010003)="com.google.android.maps" (Raw: "com.google.android.maps")
[/sourcecode]

V této fázi tedy máme hrubý přehled o struktuře aplikace, bylo by vhodné se dostat ke třídám. Ty jsou zabalené uvnitř souboru classes.dex, který se nacházi uvnitř APKčka, takže bych prosil rozzipovat tento archiv. Tento soubor je víceméně nějakým způsobem rozsypaný Java ARchive a pro jeho sesypání do použitelného formátu slouží utilitka dex2jar, která se spouští jednoduchým způsobem:

[sourcecode type=“bash“]vbalak@vbalak-desktop:~/develop/stuff/dex2jar-0.0.7.8-SNAPSHOT$ ./dex2jar.sh classes.dex
version:0.0.7.8-SNAPSHOT
3 [main] INFO pxb.android.dex2jar.v3.Main – dex2jar classes.dex -&gt; classes.dex.dex2jar.jar
Done.[/sourcecode]

Získali jsme tedy opravdový archiv .jar, který je opět možné rozzipovat a koukat na jednotlivé zkompilované třídy. Také je možné udělat víc – pomocí některého java dekompileru je možné se podívat dovnitř, já volil Java Decompiler. Pomocí něj je možné otevřít získaný .jar soubor a v levé části otevřít požadovanou třídu (výše jsme zjistili, že vstupním bodem je MainActivity) a užívat pocitu vítězství, nebo začít vykrádat se začít inspirovat cizím kódem. Toď vše, pro ilustraci ještě přikládám shot JavaDecompileru.

JavaDecompiler - screenshot
JavaDecompiler - screenshot

Google I/O Extended Brno

Jsa pln dojmů z dvouhodinové návštěvy akce Brněnské GUG s promítáním live streamu z Google I/O si asi budu nucen ublognout nějaké postřehy a komentáře, i když je jasné, že zítra toho bude plné podhoubí (twittosféra je nasycena už teď). Protože ale paralelně sleduju další stream, tak to bude jen velmi stručně. Akce začala v … Pokračovat ve čtení „Google I/O Extended Brno“

Jsa pln dojmů z dvouhodinové návštěvy akce Brněnské GUG s promítáním live streamu z Google I/O si asi budu nucen ublognout nějaké postřehy a komentáře, i když je jasné, že zítra toho bude plné podhoubí (twittosféra je nasycena už teď). Protože ale paralelně sleduju další stream, tak to bude jen velmi stručně.

Akce začala v 18 hodin našeho času, dostali jsme k ní nějaké doughnuty, pitivo a heslo na wifi, pak se již mohlo začít. Nutno poznamenat, že slibovaní hosté zřejmě nedorazili (utekl jsem dřív pryč a koukám, že Jakub Čížek se nechecknul na Gowalle, spot jsem dokonce musel vytvářet já :-)).

První uvedenou novinkou byly Google Movies (pokud se to fakt tak jmenuje) – služba umožňující pronájem videa a jeho spuštění na jakémkoliv zařízení svázaném s vaším google accountem. Pronájmem mám na mysli opravdu pronájem – za pár dolarů si koupíte práva na zkouknutí filmu, na což dostanete nějakých 30 dní, po započetí sledování 24 hodin. Pro android by měla existovat nová aplikace Movies a jak se dalo čekat, je to US only.

Dalším uvedeným produktem byl Google Music, v betaverzi. Funguje stejně, jako Amazon cloud player – (pouze ve státech) nedá se nakupovat hudba, ale je možné hudbu si tam skrze aplikaci (zmíněna platforma Windows a Mac) nahrát a odtud z jakéhokoliv zařízení se stejným google účtem streamovat. Ano, streamovat – aby se prostor nedal využít k pirátským aktivitám. (pouze bych rád dodal, že doufám v brzké chcípnutí Sony, EMI a dalších ne zkostnatělých institucí, ale rovnou zkamenělých, které by si měly uvědomit, že hudbu nezabíjí kopírování, ale ony!) Pro streaming opět existuje aplikace na marketu, ale na žádném mém zařízení nejde stáhnout :-(.

US Only
US Only

Vývojáři také byli ujištěni, že svoje aplikace budou vyvíjet pouze jednou pro všechna zařízení – i Google TV bude mít android market. Znamená to teda zřejmě, že se vývoj neforkne pro tablety (a další zařízení) a telefony, jak to vypadalo doteď. Neznamená to ale samozřejmě zpětnou kompatibilitu, pořád bude samozřejmě existovat minimalSDK v manifestu.

Další peckou je vydání Accessory development kitu založeného na Arduinu (samozřejmě je vše otevřené, takže je možné očekávat boom hardwarových serepatiček, jako příklad bylo uvedeno sportovní náčiní) a vůbec větší podpora dalšího hardwaru, mimo jiné bude USB fungovat jako host, takže bude možné připojit klávesnice, gamepady a další věcičky. Demonstrace proběhla na gigantickém bludišti ovládaném tabletem s akcelerometry.

Bludiště

S tím souvisí představení dalšího produktu – Android@home – Androidu jakožto centrálního bodu pro ovládání inteligentních domů. A to počínaje nastavováním termostatu, zhášením světel, ale také multimédii – demonstrována byla výše zmíněná služba Music, kdy zakoupenou hudbu bylo možné pomocí telefonu pustit a jako výstupní zařízení nastavit hifi v obýváku a přehrávač v kuchyni.

Android@home
Android@home

To je víceméně vše, na co si z úvodu vzpomínám a teď mi běží ve streamu další android novinky a jsou to takové pecky, že se omlouvám a jdu sledovat! Jasné je, že je možné se v dohledné době těšit na obrovský boom gadgetů a všemožných více či méně praktických zařízení připojitelných k androidu a také samozřejmě S androidem. Jako třeba tablet Galaxy Tab, který dostali účastníci konference jako dárek.

PS. pomocí Moderátoru je možné pokládat otázky pro jednotlivá témata.

PlantATree

Tak nějak jsem se naučil, že nejzajímavější a nejpoužívanější jsou ty úplně nejjednodušší aplikace, o kterých by člověk při návrhu nějakého sofistikovaného systému (aka švýcarského nože) řekl, že jsou úplně na nic. Před časem jsme vymysleli úplně dokonalý tajný projekt, který měl mít množství funkcí a měl být napsaný hned a hned vržen do světa. … Pokračovat ve čtení „PlantATree“

Tak nějak jsem se naučil, že nejzajímavější a nejpoužívanější jsou ty úplně nejjednodušší aplikace, o kterých by člověk při návrhu nějakého sofistikovaného systému (aka švýcarského nože) řekl, že jsou úplně na nic. Před časem jsme vymysleli úplně dokonalý tajný projekt, který měl mít množství funkcí a měl být napsaný hned a hned vržen do světa. Napsal jsem množství prototypů, které teˇd samostatně živoří bez frontendu a defakto bez vzájemné propojenosti. Proč? Možná je toho na jednoho člověka hodně, nebo spíš je to hrozně rozprostřené do šířky a něco takového poskládat dohromady, nedejbože udržovat a upravovat…fuj! Proto jsem si to soukromě (potají) ořezal, pouze na části základní funkčnosti a už to bylo lepší, prototypy se začaly skládat dohromady jako skládačka. Ovšem lepidla je málo (času – přece jenom když člověk dělá na několika rozdílných projektech, z nichž na některé se soustředí více, na některé pouze ve zbytcích času, potom zjistí, že se ve větší míře pouze seznamuje s tím, co že to před těmi čtrnácti vlastně udělal a čím by teˇd měl pokračovat). Proto jsem to ořezal ještě více a stvořím jenom drobnost, kterou budu vidět, že funguje a na jádro budu moct postupem času lepit další kousky. Drobnost, která nebude potřebovat moc lepidla, protože nebude mít téměř žádné funkce a dohromady nebude dělat téměř nic. Takže se, drahý světe, nechej překvapit, tímhle blogpostem jsem si dal jakýsi závazek, tak to budu muset udělat :-).

A cožeto? Geolokační hříčka – nebo spíš platforma postavená na SpringMVC, která bude nabízet RESTful rozhraní pro jistý druh „checkinů“. K tomu spytlíkuju Android aplikaci a celé to poběží na CloudFoundry, které bych tímto projektem rád vyzkoušel. GAE asi definitivně hážu za hlavu díky nemožnosti ukládání polymorfů :-).