Zdieľanie dát je skvelé! Fotku môžete vďaka zdieľaniu rovno poslať na Facebook. SMSku do kalendárika. Webovú stránku do Pocketu. Na rozdiel od iných platforiem nemusíte implementovať prepojenie appky so všetkými ostatnými, pretože sharing v Androide je univerzálny a flexibilný.
A na rozdiel od strašidelného API fotoaparátu je zdieľaní dát medzi aplikáciami pohodlná záležitosť.
V Androide existuje jednoduchý mechanizmus výmeny dát medzi aplikáciami pomocou implicitných intentov.
Implicitný intent neobsahuje názov komponentu (aktivity, služby), ktorý sa má spustiť. Namiesto neho obsahuje len názov akcie, a cieľový komponent sa určí dynamicky.
Implicitný intent sa odošle do systému, ktorý preverí všetky komponenty a zistí, ktoré z nich ho dokážu spracovať. Overenie je efektívne vďaka intent filtrom, ktoré dokážu ihneď rozhodnúť, či intent prijať alebo nie.
Čo ak intent dokáže spracovať viacero komponentov? V takom prípade Android ponúkne dialógové okno, kde používateľ ručne určí cieľový komponent.
Zdieľanie dát teda spočíva vo vytvorení implicitného intentu a jeho odoslaniu do systému pomocou metódy startActivity()
. Intent určený na zdieľanie dát medzi aplikáciami má nasledovné atribúty:
ACTION_SEND
.Uri
reprezentujúce adresu dát, s ktorými sa pracuje. Môže napríklad obsahovať Uri
k dátam v content providerovi (ak zdieľame databázové dáta) alebo cestu k súboru v súborovom systéme (ak zdieľame súbor).text/plain
, obrázky image/*
.Obohaťme fotoaplikáciu o zdieľanie fotiek!
Dodajme do lišty akcií tlačidlo:
<item android:id="@+id/shareAction"
android:title="Share"
android:showAsAction="ifRoom"
/>
Predpokladáme, že appka používa API pre Android 4 a novšie a nespolieha sa na knižnicu kompatibility. To je dôvod, prečo atribút showAsAction
patrí do menného priestoru android
a nie do app
určeného pre atribúty z knižnice kompatibility.
Obslužný kód je klasika: nafúkneme lištu akcií z layoutu a kliknutie obslúžime megaswitchom. Kliknutie na zdieľanie bude presmerované na metódu sharePhoto()
:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.shareAction:
sharePhoto();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void sharePhoto() {
}
Základný kód pre odoslanie je jednoduchý: vyplníme akciu, nastavíme MIME typ a odošleme intent:
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setDataAndType(this.photoUri, "image/*");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Fotr Photo");
startActivity(shareIntent);
Ak chcete nastaviť dáta i typ, použite metódu setDataAndType()
. Metóda setData()
totiž resetuje typ a naopak, setType()
resetuje dáta.
Ak vytvoríme fotku a necháme ju nazdieľať, Android zistí, že ju dokáže spracovať viacero aktivít:
Ak existuje v systéme len jeden komponent, ktorý vie obslúžiť intent, priamo sa otvorí bez výzvy používateľa. Dialóg pre výber sa tiež nezobrazí, ak používateľ prednastavil aktivitu pre spracovanie konkrétneho typu intentu pomocou tlačidla Always.
A čo ak intent nevie spracovať žiaden komponent? Aplikácia spadne.
Predtým než odošleme intent musíme skontrolovať, či existuje v systéme aspoň jedna aktivita, ktorá ho spracuje:
if (shareIntent.resolveActivity(getPackageManager()) != null) {
startActivity(shareIntent);
}
Pri vyhodnocovaní vhodných systémových komponentov, ktoré môžu spracovať intent, sa do úvahy berú nasledovné vlastnosti:
Uri
a MIME typomPozor, dáta uvedené v extras sa pri vyhodnocovaní neberú do úvahy.
Vyhodnocovanie intentov je podrobne, vrátane algoritmu, popísané v oficiálnom sprievodcovi na portáli Androidu..
Ak chceme vždy vyvolať dialógové okno, môžeme intent obaliť do výberového intentu:
Intent chooseSharingTarget = Intent.createChooser(shareIntent, title);
if (chooseSharingTarget.resolveActivity(getPackageManager()) != null) {
startActivity(chooseSharingTarget);
}
Vďaka zdieľaniu môžete využiť zobrazovanie polohy na mape. Stačí vytvoriť intent s nasledovnými vlastnosťami:
ACTION_VIEW
Uri
, ktoré v sebe ponesie dáta, napríklad Uri uri = Uri.parse("geo:37.7749,-122.4194")
setPackage("com.google.android.apps.maps")
, ktorý obmedzí výber cieľových aktivít pre spracovanie intentuOdosielanie fotiek do Instagramu je tiež jednoduché. Nastavíte:
image/*
Intent.EXTRA_STREAM
Intent.EXTRA_TEXT
Na prijímanie intentov si vytvorme samostatný projekt Dropage, ktorý bude vedieť prijímať fotky. Aktivita dokáže prijímať implicitné intenty, ak:
<intent-filter>
V elemente <activity>
definujme <intent-filter>
:
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
Potrebujeme definovať:
*
, ktorý deklaruje podporu ľubovoľného formátu (napr. image/jpeg
alebo image/png
).Všimnime si, že naša aktivita už definuje jeden implicitný intentový filter a to pre jej spustenie z launchera, resp. z domovskej obrazovky.
Získanie intentu môžeme dosiahnuť v metóde onCreate()
aktivity:
Intent intent = getIntent();
if(Intent.ACTION_SEND.equals(intent.getAction())
&& intent.getType().startsWith("image/")) {
Uri photoUri = intent.getData();
showExifData(photoUri);
}
Pomocou metódy getIntent()
získame intent, vďaka ktorému sa spustila aktivita a následne sa rozhodneme, o aký konkrétny intent ide. Aktivita totiž môže byť spustená nielen zdieľaním, ale i z hlavnej obrazovky či launchera, čo musíme zobrať do úvahy. V podmienke teda hľadáme správnu akciu a správny typ MIME a ak uspejeme, z dát vytiahneme Uri
adresu, ktorú pošleme do pomocnej metódy.
Niektoré aplikácie neposielajú cestu k súboru v dátach ako Uri
, ale v špeciálnom extra s kľúčom EXTRA_STREAM
, kde pošlú adresu so schémou content://
, ktorú treba vyhodnotiť. Takéto intenty nebudeme podporovať, pretože vyžadujú špinavú prácu prevodu URI na cestu k súboru. V praktických aplikáciách je možné pozrieť napríklad do githubového projektu iPaulPro/aFileChooser
.
V Androide existuje pomocná trieda ExifInterface
, ktorá vie načítať EXIFové informácie z obrázka. Akurát, že obrázok musí byť reprezentovaný súborom. Cestu k súboru vieme vytiahnuť z Uri
adresy pomocou metódy getPath()
, ktorý následne prepošleme do ExifInterface
.
private void showExifData(Uri photoUri) {
try {
if(photoUri == null) {
return;
}
ExifInterface exifInterface = new ExifInterface(photoUri.getPath());
int imageHeight = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, -1);
int imageWidth = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, -1);
imageWidthTextView.setText(Integer.toString(imageWidth));
imageHeightTextView.setText(Integer.toString(imageHeight));
} catch (IOException e) {
Log.e(MainActivity.class.getName(), "Error while reading EXIF data", e);
}
}
Zistiť, čo všetko sa deje s intentom, môže byť náročné. Ak však nastavíte intentu príznak pre logovanie, budete ho vidieť v LogCate.
intent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
Logovanie prebieha na úrovni VERBOSE
, a na to, aby ho bolo vidieť, je treba v LogCate vypnúť štandardný filter správ, ktorý je obmedzený na balíček aplikácie.
Celý projekt pre prijímanie dát nájdete na GitHube v repozitári novotnyr/android-exifdropage-2015
.
Zdieľanie dát môžete uľahčiť ešte viac, ak použijete zabudované tlačidlo Easy Share na lište akcií. Do layoutu lišty akcií stačí dodať novú položku:
<item android:id="@+id/easyShareAction"
android:actionProviderClass="android.widget.ShareActionProvider"
android:title="Share"
android:showAsAction="ifRoom"
/>
Rozdiel oproti tradičnej položke spočíva v atribúte actionProviderClass
, ktorý udáva názov triedy zodpovedajúcej za správanie tlačidla.
Do kódu potom dodáme špeciálnu obsluhu pre získanie inštanciu ShareActionProvider
a, ktorá bude zodpovedná za vyhľadávanie appiek podporujúcich zdieľanie.
private ShareActionProvider shareActionProvider;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
MenuItem item = menu.findItem(R.id.easyShareAction);
shareActionProvider = (ShareActionProvider) item.getActionProvider();
return true;
}
Keďže naša aplikácia nepodporuje knižnicu kompatibility, importujeme triedu android.widget.ShareActionProvider
.
Následne refaktorujeme kód: tvorbu intentu na zdieľanie vytiahneme do samostatnej metódy:
private Intent getShareIntent() {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setDataAndType(this.photoUri, "image/*");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Fotr Photo");
shareIntent.addFlags(Intent.FLAG_DEBUG_LOG_RESOLUTION);
return shareIntent;
}
Náš poskytovateľ zdieľania share action provider prepojíme s intentou v metóde, ktorú vytvoríme.
private void updateEasyShare() {
if(this.shareActionProvider != null) {
this.shareActionProvider.setShareIntent(getShareIntent());
}
}
V poslednom kroku upravíme metódu onPhotoUriFound()
, kde na konci aktualizujeme intent zdieľania a prepojíme ho s poskytovateľom akcie zdieľania:
@Override
protected void onPhotoUriFound() {
...
updateEasyShare();
}