Intent'ler - Diğer Uygulamalarla Etkileşime Geçmek

Intent'ler - Diğer Uygulamalarla Etkileşime Geçmek
Bir Android uygulaması birçok farklı Activity içerir. Her Activity yeni arayüzler gösterirken belirli bir görevi (haritanın gösterilmesi, fotoğraf çekilmesi gibi) yerine getirir. Kullanıcıyı bir Activity'den diğerine geçirmek için uygulamanızda Intent sınıfını kullanmalısınız. Uygulamanızın yapacağı herhangi bir işte "amacı" belirtmek için Intent sınıfını kullanmalısınız. Uygulamanızda startActivity() gibi bir metotla sisteme Intent geçirdiğinizde, sistem doğru uygulamayı ve eylemi belirlemek için bu Intent'i kullanır. Intent sınıfı, farklı uygulamalar tarafından kullanılan bir activity'yi çalıştırmaya da şans verir.

Bir Intent belli bir bileşeni başlatmak (örneğin: belirli bir Activity'yi) için açık (explicit) olabilir. Bununla birlikte hedeflenen amacı gerçekleştirebilecek (örn: fotoğraf çekilmesi) herhangi bir bileşeni başlatmak için örtülü (implicit) de olabilir.

Bu eğitimin devamında Intent kullanarak diğer uygulamalarla nasıl basitçe bağlantı kuracağınızı öğrenebilirsiniz. Eğitimin sonunda bir uygulamayı çağırmayı, o uygulamadan çeşitli sonuçlar almayı ve uygulamanızı başka uygulamalara tepki verebilir hale getirmeyi öğrenmiş olacaksınız.

Başka Bir Activity'yi Başlatmak

Bu eğitim içeriğinde kullanıcının bir düğmeye basmasıyla nasıl başka bir Activity'yi açacağınızı öğreneceksiniz. Esasında bu konu Android'in en önemli bileşenlerinden Intent'e giriş niteliği taşıyor.

Bu içeriği okurken uygulayarak devam etmek için öncelikle basit bir Android projesi oluşturun. Bu projenin MainActivity layout dosyasında (activity_main.xml) bir tane metin kutusu (EditText) ve düğme (Button) oluşturun. Bu belgenin sonuna geldiğinizde, bu uygulamada kullanıcının metin kutusuna girdiği değeri başka bir Activity'de gösterebileceksiniz. Öncelikle diğer Activity'ye geçmemize yarayacak düğme ile ilgili işlemleri halledelim.

Gönder düğmesine tepki verme
Uygulamanızın activity_main.xml isimli layout dosyasında bir düğme (Button) olduğunu farz edelim. Bu düğmenin tıklanma (dokunma) eylemine yanıt vermek için activity_main.xml dosyanızı açın ve android:onClick özelliğini <Button> elemanına ekleyin:

<Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button_gonder"
        android:onClick="mesajGonder" />
android:onClick özelliğinin değeri olan "mesajGonder" Activity'niz içinde, kullanıcı düğmeye bastığında sistemin çağıracağı metodun adı.

MainActivity.java dosyasını açın (/src dizinindedir) ve şu metodu MainActivity sınıfının içine ekleyin:

/** kullanıcı gönder düğmesine basınca çağırılır */
public void mesajGonder(View view) {
    // düğmeye yanıt verecek bir şeyler
}
Sistemin android:onClick'e verilen metod adıyla buradaki metod adını eşleştirebilmesi için metod imzası tıpkı burada gösterildiği gibi olmalıdır. Metod mutlaka

public olmalı
void dönüş değeri içermeli
tek parametre olarak View almalı (bu View tıklanmış olan View oluyor)
Bundan sonraki aşamada, bu metodun içini metin kutusunun içeriğini okumak ve girilen metni diğer Activity'ye göndermek için dolduracağız.

Intent oluşturma
Bir Intent, farklı bileşenler arasında (iki Activity gibi) çalışma zamanında bağlama (runtime binding) sağlayan nesnedir. Intent, bir uygulamanın "bir şeyi yapmaya niyetlenmesini" ifade eder. Intent'leri çok çeşitli görevlerde kullanabilirsiniz fakat başka bir Activity'yi başlatma işlemlerinde sıkça kullanılırlar.

mesajGonder() metodunun içinde MesajGosterActivity isimli Activity'yi başlatmak için bir Intent oluşturun:
Intent intent = new Intent(this, MesajGosterActivity.class);

Bu işlem Intent sınıfını import etmenizi gerektirir:
import android.content.Intent;

Öneri: Android Studio'dayken Alt + Enter tuşlarına basarak eksik sınıfları import edebilirsiniz.

Burada kullanılan yapılandırıcı metod iki parametre alıyor:

İlk parametre olarak Context (this kullandık çünkü Activity sınıfının, Context'in alt sınıfıdır)
Sistemin Intent'i teslim edeceği bileşenin Class ismi. (bu örnekte düğmeye tıklayınca başlayacak olan Activity'nin sınıf adı)
NOT: Henüz MesajGosterActivity isimli Activity'yi oluşturmadığınız için IDE'niz hata verecektir. İlerleyen bölümlerde oluşturacaksınız; okumaya ve uygulamaya devam edin.

Bir Intent, "ekstra veri" olarak isimlendirilen ve anahtar-değer 8key-value) veri tiplerini tutan koleksiyonları taşır. putExtra() metoduyla ilk parametre olarak anahtarı, ikinci parametre olarak değeri alır.

Sıradaki Activity'nin ekstra veriyi sorgulayabilmesi(*) için Intent'inizin ekstra verisi olarak kullanmak için sabit bir anahtar (key) tanımlamalısınız. Bunun için MainActivity sınıfınızın en üstüne EXTRA_MESSAGE tanımlamasını yapabilirsiniz:

public class MainActivity extends ActionBarActivity {
    public final static String EXTRA_MESSAGE = "org.tcellgy.android.kitaplik.MESAJ";
    ...
}
Anahtarlara uygulamanızın paket adını ön ek olarak vermek iyi bir alışkanlıktır. Böylece onları eşsiz (unique) yaparsınız ve uygulamanız diğer uygulamalarla etkileşime geçebilir.

İkinci Activity'yi başlatma
Bir Activity'yi başlatmak için startActivity() metodunu çağırın ve bu metoda Intent'inizi geçirin. Sistem bu çağrıyı alacak ve Intent'te belirttiğiniz Activity'nin bir örneğini (instance) başlatacaktır.

Bu yeni kodla beraber Gönder düğmesi tarafından çağrılan mesajGonder() metodu şöyle olacaktır:

/** kullanıcı gönder düğmesine basınca çalışır */
public void mesajGonder(View view) {
    Intent intent = new Intent(this, MesajGosterActivity.class);
    EditText editText = (EditText) findViewById(R.id.edittext_mesaj);
    String mesaj = editText.getText().toString();
    intent.putExtra(EXTRA_MESSAGE, mesaj);
    startActivity(intent);
}
Bu kodun düzgün çalışması için MesajGosterActivity sınıfını oluşturmanın zamanı geldi.

İkinci Activity'yi oluşturma
Android Studio kullanarak yeni Activity oluşturmak için:

File menüsünden New > Activity > Blank Activity seçeneğini seçin.

Activity ile ilgili ayrıntıları doldurun:
Activity Name: MesajGosterActivity
Layout Name: activity_mesaj_goster (Activity Name'i yazarken kendiliğinden değişir)
Title: Mesajlar
Hierarchical Parent: gy.activityex.MainActivity
Finish'e basın.
/src dizininde oluşan MesajGosterActivity.java isimli dosya şunun gibi olacaktır:

package gy.activityex.MainActivity;


import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MesajGosterActivity extends ActionBarActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mesaj_goster);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.mesaj_goster, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Activity'nin tüm alt sınıfları onCreate() metodunu mutlaka gerçeklemelidir - implemente etmelidir. Sistem bu metodu Activity'nin yeni bir örneğini (instance) oluştururken çağırır. Bu metot setContentView() metodu ile Activity layout'unu tanımlamanız gereken yerdir. Ayrıca Activity bileşenleriyle ilgili ilk ayarları yapmanızı önerdiğimiz yerdir.

Intent'i alma
Her Activity bir Intent tarafından çağrılır - kullanıcı nerede gezinirse gezinsin. Activity'nizde getIntent() metodunu kullanarak onu başlatan Intent'i elde edebilir ve içinde taşıdığı verilere ulaşabilirsiniz.

MesajGosterActivity sınıfının içindeki onCreate() metoduna gelip, az önce MainActivity'nin teslim ettiği Intent'i ve ekstra mesajı şu şekilde alabilirsiniz:

Intent intent = getIntent();
String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);


Gelen mesajı gösterme
Artık ilk Activity'den gelen Intent'e erişebiliyoruz. Şimdi o Intent ile gelen mesajı MesajGosterActivity'de gösterelim. Bunun için MesajGosterActivity'nin layout dosyasını biraz düzenlememiz gerekiyor. Şu anki hali şöyle:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="org.tcellgy.android.kitaplik.MesajGosterActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

</RelativeLayout>

Buradaki TextView üzerinde bazı değişiklikler yapacağız: Ona ulaşabileceğimiz bir kimlik numarası (ID) vereceğiz. Şöyle güncelleyebilirsiniz:

<TextView
        android:id="@+id/text_gelenmesaj"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp" />
Buradaki android:id="@+id/text_gelenmesaj" satırına dikkat edin. Sadece bu TextView'ın ismi "text_gelenmesaj" oldu. 
TextView'a isim verdiğimize göre artık Intent'ten aldığımız mesajı gösterebiliriz. Bunun için MesajGosterActivity.java dosyanızdaki onCreate() metodunu şöyle özelleştirmek yeterli:

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mesaj_goster);
Intent intent=getIntent();
String mesaj=intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
TextView textView=(TextView) findViewById(R.id.text_gelenmesaj);
textView.setText(mesaj);
}
Uygulamanızı çalıştırdığınızda gelen metin kutusuna bir şeyler yazın ve "Göster" düğmesine basın. Sonuç aşağıdaki gibi olacaktır:

Ekran 1
Ekran 1
Örnek projeyi ekranın yukarı sol tarafındaki bölümden indirebilirsiniz.

Kullanıcıyı Farklı Bir Uygulamaya Yönlendirmek
Android'in uygulamalara sağladığı önemli özelliklerinden biri de kullanıcıyı gerçekleştirmek istedikleri "eyleme göre" başka bir uygulamaya yönlendirme olanağı sunmasıdır. Örneklemek gerekirse, çeşitli resim işleme eylemleri yaptığınız bir uygulamada fotoğraf çekmek için bir activity yazmanıza gerek yoktur. Bunun yerine fotoğraf çekilmesi için bir Intent oluşturmanız ve çalıştırmanız yeterlidir. Sistem sizi kamera uygulamasına kendiliğinden yönlendirecektir.

Uygulamanızın Activity'leri arasında geçmek için Intent kullanmalısınız. Bunu genellikle adresi belli intent'ler (explicit intent) kullanarak, çalıştırmak istediğiniz bileşenin class ismini kullanarak yaparsınız, fakat başka uygulamada bir eylem gerçekleştirmek istediğinizde, örneğin "haritayı göster" gibi, üstü kapalı intent (implicit intent) kullanmak zorundasınız.

Bu eğitim içeriği özel bir eylem için nasıl üstü kapalı (implicit) intent oluşturacağınızı ve diğer uygulamanın ilgili eylemi gerçekleştirecek Activity'sini başlatmak için o Intent'i nasıl kullanacağınız üzerinde duracaktır:

Üstü kapalı (Implicit) intent oluşturma
Intent'i karşılayacak uygulama denetimi
Intent ile bir activity başlatma
Uygulama seçim diyalogu gösterme
 
Üstü kapalı (Implicit) intent oluşturma
Üstü kapalı intent, çalıştırmak istediğiniz bileşenin class'ını çağırmak yerine gerçekleştirmek istediğiniz eylemi belirttiğiniz intent türüdür. Belirttiğiniz eylem ne yapılacağını belirtir, örneğin bir şeyi göster (view), gönder (send), getir (get) gibi. Intentler eylemle ilgili çeşitli bilgiler de taşır. Örneğin göstermek istediğiniz adres, e-posta atılacak kişi, aranacak numara gibi. Oluşturmak istediğiniz Intent'e göre bu veri Uri veya diğer veri tiplerinin biçiminde olabilir veya Intent'in hiçbir veri taşımaya ihtiyacı da olmayabilir.

Uri nedir?
Uri (uniform resource identifier) yani nizami kaynak belirteci, bir kaynağı ya da veriyi isimlendirmek için kullanılan bir standarttır. Aynı zamanda kaynağı nitelendirir. Intent kullanırken bu türden bilgiler gereklidir.

Eğer veriniz Uri şeklindeyse, basit bir Intent() yapılandırıcı metoduyla (constructor) eylemi ve verileri tanımlayabilirisiniz.

Örneğimizde bir telefon araması yaparken Uri verisini nasıl kullandığımızı görebilirsiniz.
Uri number = Uri.parse("tel:5323334455");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

Uygulamanız startActivity() ile callIntent isimli Intent'i çağırdığında Telefon uygulaması ilgili numarayı arar. 
Aşağıda çeşitli Uri verileriyle bazı uygulamaların nasıl çağrıldığını görebilirsiniz.

Bir harita göstermek

// adrese göre harita noktası
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
// ya da enlem ve boylam yerine göre konum bilgisi
// Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z parametresi yaklaşma seviyesini belirler
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
 
Bir web sayfası göstermek
Uri webpage = Uri.parse("http://www.gelecegiyazanlar.turkcell.com.tr");
Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);

Diğer türlü üstü kapalı Intent'ler, farklı veri tipleri taşıyan "ekstra" veriler gerektirebilir, örneğin String gibi. Bir veya daha fazla ekstra veriyi veri tipine özel farklı putExtra() metotları kullanarak ekleyebilirsiniz.

Bilgi: Intent'e istediğiniz veriyi ekleyemiyorsunuz. Veri tipine özel parametreler kabul eden putExtra() metotları var ve Intent'e ancak belli türden şartları kabul eden verileri ekleyebilirsiniz.

Varsayılan olarak sistem, Intent tarafından gerek duyulan bir MIME tipini o Intent'e eklenen Uri'ye göre belirler. Eğer Intent'e bir Uri ilave etmediyseniz, Intent ile ilişkilendirdiğiniz verinin tipini tanımlamak için setType() metodunu kullanmalısınız. MIME tipini tanımlamak Intent'i alması gereken Activity'lere ilave tanımlar yapmak anlamına gelir.

Aşağıda istenilen eylemi daha iyi tanımlamak için ekstra veri eklenen Intent örneklerini inceleyebilirsiniz.

Ekiyle birlikte e-posta göndermek

Intent emailIntent = new Intent(Intent.ACTION_SEND);
//bu Intent'in bir URI'si yok. O halde MIME tipini "text/plain" tanımlayalım
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"destek@gelecegiyazanlar.org", "ali@site.com"}); // alıcılar
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "E-posta başlığı");
emailIntent.putExtra(Intent.EXTRA_TEXT, "Merhaba, uygulama üzerinden e-posta gönderiyorum");
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://epostaya/ekleyeceginiz/dosyanin/adresi"));
// daha fazla eki, Uri'lerden oluşan ArrayList geçirerek de ekleyebilirsiniz.

Takvim etkinliği yaratmak

Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
Calendar baslamaZamani = Calendar.getInstance().set(2014, 3, 23, 9, 30);
Calendar bitisZamani = Calendar.getInstance().set(2014, 3, 23, 23, 30);
calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, baslamaZamani.getTimeInMillis());
calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, bitisZamani.getTimeInMillis());
calendarIntent.putExtra(Events.TITLE, "Bayram kutlaması");
calendarIntent.putExtra(Events.EVENT_LOCATION, "Kasaba meydanı");
Not: Bu Intent kullanımı API seviyesi olarak 14 ve üstlerinde geçerlidir.

Not: Intent'i mümkün olduğunca kullanıma uygun tanımlamanız önemlidir. Örneğin ACTION_VIEW Intent'ini kullanarak bir resim göstermek istiyorsanız, MIME tipini image/* olarak tanımlamalısınız. Bunu yaptığınızda Intent tarafından tetiklenmiş olan, diğer veri tiplerini gösterebilen (view) uygulamaların (harita uygulaması gibi) önüne geçersiniz.

Bunu biraz daha açıklayalım: Bir resim görüntülemek istediğini varsayalım. Bu resmi görüntüleyecek bir uygulama açmaya çalışırken Intent'inizi ACTION_VIEW eylemiyle yapılandırırsınız ancak ACTION_VIEW eylemi çok genel bir ifade. VIEW edebilen yani bir şeyler gösterebilen birçok uygulama olabilir. Eğer oluşturduğunuz Intent'in MIME tipini image/* olarak belirtmezseniz, sadece görselleri gösterebilen uygulamaları hedeflemiş olursunuz.

Intent'i karşılayacak uygulama denetimi
Android platformu belli Intent'lerin yerleşik uygulamalarca (Telefon, e-posta veya takvim uygulaması gibi) karşılık bulacağını garanti ediyor olsa da siz bir Intent'i çağırmadan önce mutlaka bir doğrulama adımı eklemelisiniz.

Uyarı: Eğer Intent'i çağırdığınızda cihazda belirttiğiniz işlemi yapacak bir uygulama yoksa, uygulamanız çökecektir.

Intent'e yanıt verecek bir Activity'nin olup olmadığını doğrulamak için, queryIntentActivites() metodunu çağırarak Intent'inizi karşılama yeteneği olan Activity'lerin listesini alabilirsiniz. Dönen List boş değilse Intent'inizi güvenle kullanabilirsiniz. Örneğin:
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;

Eğer isIntentSafe değeri true ise en az bir uygulama Intent'e cevap verecektir. Eğer dönen cevap false ise almak istediğiniz eyleminizi gerçekleştirebilecek bir uygulama bulunmamaktadır. 

NOT: Activity'niz ilk başladığında bu kontrolü yapmanız, belki de Intent kullanmaya çalışırken hata vermeden önce kapatmanız gereken özellikler olabileceği için makul bir işlem olabilir. Böyle bir durumda, yapmak istediğini eylemi yapan uygulamalara bağlantı verebilirsiniz. (Google Play'de uygulamanıza bağlantı vermek belgesine bakabilirsiniz.)

Intent ile bir activity başlatma
Bir kere Intent'inizi oluşturup ekstra verileri ayarladığınızda startActivity() metodunu çağırarak bunu sisteme gönderebilirsiniz. Sistem işlemi gerçekleştirebilecek birden fazla Activity bulursa, kullanacağı uygulamayı kullanıcıya açtığı diyalog penceresinde gösterir. (Bkz Resim 1)
Resim 1: Intent'i karşılayabilecek birden fazla uygulama olduğunda çıkan seçim diyalogu

Eğer uygun sadece bir Activity varsa, sistem hemen şu metodu çalıştırır:
startActivity(intent);

Aşağıda bir uygulamada harita gösteriminin nasıl olacağını, ilgili uygulamanın doğrulamasını nasıl yapacağınızı gösteren kodu görebilirsiniz.


//Intent'i oluşturalım
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

// Karşılanacağının doğrulamasını yapalım
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;

// güvenli ise bir activity başlatalım
if (isIntentSafe) {
    startActivity(mapIntent);
}
 

Uygulama seçim diyalogu gösterme
Bir Activity'yi oluşturduğunuz Intent'i startActivity() metoduna geçirerek başlattığınızda ve Intent'inize yanıt verebilecek birden fazla uygulama olduğunda kullanıcı varsayılan olarak kullanılacak uygulamayı seçebilir. (Diyalog pencereciğinin altındaki seçenek kutusunu seçerek; Bkz: Resim 1) Bu durumlarda oluşan pencereden kullanıcı her zaman kullanacağı uygulamayı seçebilir. Web sitesi açılışlarında kullandığı web tarayıcıyı veya fotoğraf çekmek için kullandığı kamera uygulamasını insanlar genelde değiştirmek istemezler. Bu gibi durumlar için hoş bir özelliktir.
Resim 2: Seçim diyalogu

Bunun yanında bazı uygulamalarda bu tercih sık sık değişebilir, örneğin paylaşma aksiyonunu bazen Bluetooth, bazen e-posta bazen mesaj tercih edilebilir. Bunun gibi değişkenlik gösteren isteklere karşı bir seçim ekranı kullanmanız ve her zaman kullanmanız gereklidir.

Seçim pencereciğini göstermek için createChooser() metodunu kullanarak bir Intent oluşturup, startActivity()'ye geçirebilirsiniz. Örneğin,

Intent intent = new Intent(Intent.ACTION_SEND);

...

// arayüz metinleri için her zaman string kaynakları kullanın
// örneğin burada title değişkenine "Fotoğrafı şununla paylaş"
// metnini atamış oluyoruz
String title = getResources().getString(R.string.secici_basligi);
// seçici ekranı göstermek için Intent oluşturuyoruz
Intent chooser = Intent.createChooser(intent, title);

// Intent'in en az bir Activity çözümleneceğini doğruluyoruz
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}
Bu kodla createChooser() metoduna geçirilmiş Intent'e karşılık gelebilecek uygulamaların listesini gösteren bir diyalog pencereciğinde gösterilir ve R.string.secici_basligi değişkeninden sağlanan metin diyalog başlığı olarak kullanılır.
Bir Uygulamadan Sonuç Almak
Başka bir Activity'yi çalıştırma işlemi her zaman tek taraflı-karşılıksız olmak zorunda değildir. Başlattıktan sonra ondan sonuç döndürebilirsiniz. Sonuç döndürmek için (startActivity() metodunu çağırmak yerine) startActivityForResult() metodunu çağırmanız yeterli olacaktır.

Örneğin Kamera uygulamasını başlatabilir ve kullanıcının çektiği fotoğrafı "sonuç" olarak alabilirsiniz. Aynı şekilde, Kişiler uygulamasını başlatıp kulllanıcının seçtiği kişinin ayrıntılarını "sonuç" olarak alabilirsiniz.

Elbette Activity'nin bir "sonuç" döndürecek şekilde tasarlanmış olması gerekir. Activity bunu yaptığında, "sonucu" bir Intent nesnesi şekilde döndürür ve siz de Activity'nizdeki onActivityResult() callback metoduyla bu sonucu alırsınız.

NOT: startActivityForResult() metodunu çağıracağınız zaman açık (explicit) veya örtülü (implicit) Intent kullanabilirsiniz (Bu metodun parametre olarak Intent aldığını hatırlatalım). Kendi Activity'lerinizden birini sonuç almak için başlattığınızda, beklediğiniz sonucu aldığınızdan emin olmak için açık Intent kullanmalısınız.

Activity'yi başlatmak
Bir Activity'yi sonuç alma işlemi için başlatırken Intent nesnesi kullanıyoruz. Activity'yi başlatırken kullandığınız bu Intent nesnesiyle ilgili özel bir şey yapmanıza gerek olmasa da, startActivityForResult() metoduna parametre olarak geçmek için ayrıca integer bir argüman gerekiyor.

Buradaki integer argüman, isteğinizi kimliklendirmeye ve diğer isteklerden ayırmaya yarayan "istek kodu"nu tanımlar. Kimlik numarası gibi. Her isteğin bir kodu olmalıdır. Sonuç Intent'ini aldığınızda ilgili callback metodu (aşağıda açıklanacak) aynı "istek kodu"nu tanımlar. Böylece siz de sonucu doğru tanımlar ve ona göre işlemeye başlarsınız.

Örneğin, kullanıcının kişi listesinden birini seçmesini sağlayan Activity'yi nasıl başlatacağımıza bakalım:
static final int PICK_CONTACT_REQUEST = 1;  // istek kodu
...
private void pickContact() {
    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
    pickContactIntent.setType(Phone.CONTENT_TYPE); // sadece telefon numarası olan kullanıcıları göstersin
    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}
 
Sonucu almak
Kullanıcıya işlemini yaptırtmak için açılan sonraki Activity'de kullanıcının işi bitip geri döndüğünde, sistem bu açma işlemini yaptırdığınız asıl Activity'nizin onActivityResult() metodunu çağırır. Bu metodun üç argümanı vardır:

startActivityForResult() metoduna geçirdiğiniz "istek kodu".
Sonraki Activity tarafından tanımlanan "sonuç kodu". Eğer işlem başarılıysa RESULT_OK, kullanıcı geri tuşuna basmış ya da farklı bir hatayla karşılaşılmışsa RESULT_CANCELED değerini alacaktır.
Sonuç verisini taşıyan bir Intent.
Aşağıda, "bir kişi seçme" yani "pick a contact" Intent'inin sonucunu nasıl işleyebileceğinizi dair bir örnek var:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // metoda gelen isteğin, yanıt vereceğimiz istek olup olmadığını kontrol edelim
    if (requestCode == PICK_CONTACT_REQUEST) {
        // istek başarılı mı diye bakalım
        if (resultCode == RESULT_OK) {
            // Kullanıcımız bir kontakı başarıyla seçmiş.
            // 'Intent data' parametresiyle seçili kontak hakkında daha fazla bilgi alabilirsiniz
            // Kontak ile ilgili bir şeyler yapabilirsiniz (aşağıda daha geniş bir örnek bulabilirsiniz)
        }
    }
}
Bu örnekte, Android'in Kişiler (Contacts) uygulamasının döndürdüğü sonuç Intent'i, kullanıcının seçtiği kişiyi/kişi bilgisini tanımlayan bir Uri içeriğidir.

Sonuçları başarılı bir şekilde yönetmek için sonuç Intent'inin formatının ne olacağını mutlaka anlamış olmalısınız. Sonuç döndüren Activity, kendi Activity'lerinizden biri olduğunda bu kolaydır. Karmaşık sonuç verileri döndüren, güvenebileceğiniz ve Android platformunun kendi API'sini sağladığı uygulamalar da buna dâhildir. Örneklemek gerekirse, Kişiler/Rehber uygulaması her zaman seçilen kişinin bilgilerini Uri ile döndürür ve Kamera uygulaması ise ekstra "data" olarak Bitmap değer döndürür.

Bonus: Kişi (Contact) verisini okumak
Kişiler uygulamasından nasıl sonuç alacağınızı gösteren yukarıdaki kod örneği, İçerik Sağlayıcılar (Content Providers) hakkında ileri seviye tartışmaları gerektirdiği için aslında sonuç verisini nasıl okuyacağınızın ayrıntılarını göstermiyor. Meraklı biriyseniz, aşağıdaki örnekte seçilen kişinin telefon numarasını gösteren şu kod ilginizi çekecektir:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // metoda gelen isteğin, yanıt vereceğimiz istek olup olmadığını kontrol edelim
    if (requestCode == PICK_CONTACT_REQUEST) {
        // isteğin başarılı sonuçlandırıldığını doğrulayalım
        if (resultCode == RESULT_OK) {
            // seçilen kişiye işaret eden URI'yi alalım
            Uri contactUri = data.getData();
            // sadece NUMBER sütununa ihtiyacımız var
            String[] projection = {Phone.NUMBER};

            // NUMBER sütununu alarak kişi üzerinde sorgulama yapalım
            // Bir seçim ya da sıralama işine gerek yok (gelen URI'de sadece bir tane sonuç var)
            // DİKKAT: query() metodunu uygulama arayüzünüzü (UI) bloklamamak için
            // farklı bir thread üzerinde çağırmalısınız. Örneğin basitliğini korumak için o
            // işlemi burada yapmıyoruz
            // sorguyu gerçekleştirenin CursorLoader olduğu gözünüzden kaçmasın
            Cursor cursor = getContentResolver()
                    .query(contactUri, projection, null, null, null);
            cursor.moveToFirst();

            // telefon numarasını NUMBER sütunundan alalım
            int column = cursor.getColumnIndex(Phone.NUMBER);
            String number = cursor.getString(column);

            // telefon numarasıyla ilgili işlemle...
        }
    }
}
NOT: Android 2.3'ten (API 9) önce Kişi Veri Sağlayıcısı (Contacts Provider) üzerinde sorgu yaparken (bir örneğini yukarıda görüyorsunuz), uygulamanız için READ_CONTACTS iznini almanız gerekiyor. Android 2.3 ile başlamak üzere Kişiler/Rehber uygulaması, Contacts Provider'dan size döndüreceği sonucu okuyabilesiniz diye uygulamanıza geçici bir izin verir. Bu geçici izin sadece belli bir kişi isteği yaptığınızda geçerlidir. Bu yüzden READ_CONTACTS iznini uygulamanız için almadıktan sonra, Intent'in Uri değerinde tanımladığınızdan başka bir kişi için sorgulama yapamazsınız.
Diğer Uygulamaların Sizin Activity'nizi Başlatmasına İzin Vermek
Bundan önceki eğitim içeriklerinde genelde şunu yapıyorduk: kendi uygulamamızda başka uygulamaların Activity'sini başlatıyorduk. Uygulamanız başka bir uygulama için kullanışlı olabilecek bir eylemi gerçekleştiriyorsa, onu başka uygulamalardan gelen isteklere yanıt verecek şekilde hazırlamanız gerekir. Örneğin kullanıcının arkadaşlarıyla mesaj veya fotoğraf paylaştığı bir sosyal ağ uygulaması yapıyorsanız, ACTION_SEND Intent'ini desteklemelisiniz ki, kullanıcılar başka bir uygulamadaki "paylaş" eylemini kullandıklarında bu tür eylemleri uygulamanız üzerinden gerçekleştirebilsinler.

Diğer uygulamaların Activity'nizi başlatmasını sağlamak için manifest dosyasında uygun <activity> elementinin içine <intent-filter> elementini koymalısınız.

Uygulamanız cihaza yüklendiğinde, sistem, uygulamanızın Intent filter'ına bakarak buradan ürettiği bilgileri kurulu uygulamaların Intent'lerinden oluşan dâhili bir kataloğa ekler. Başka bir uygulama startActivity() ya da startActivityForResult() metotlarını örtülü (implicit) Intent ile çağırdığında, sistem hangi Activity'lerin çağrıyı cevaplayabileceğini bu kataloğa bakarak bulur. 

Intent filter ekleme
Activity'nizin karşılayabileceği Intent'leri belirtmek için her birine (Activity'nin kabul ettiği veri ve eyleme özel şekilde) özel intent filter'ı uygun şekilde mümkün mertebe spesifik tanımlamalısınız.

Sistem, eğer Activity'nin aşağıda verilen kriterlere tam uyan intent filter'ı varsa o Activity'ye o anki Intent'i gönderecektir:

Action
Gerçekleşecek eylem için string türünden bir isimlendirme. Bunlar genelde platform tarafından gelen ACTION_SEND, ACTION_VIEW gibi değerlerdir.

Bunu intent filter'ınızın içinde <action> elementi ile tanımlayabilirsiniz. Elementin içinde tanımlayacağınız bu değerin API'deki sabitler (aşağıda örneklerini göreceksiniz) yerine, eylemle ilgili tam bir string isim olması gerekiyor.

Data
Intent ile ilişkilendirilmiş verinin tanımlaması.

Bunu intent filter'ınızın içinde <data> elementi ile tanımlayabilirsiniz. Bu elementte bir veya birden fazla öznitelik (attribute) kullanacağınızda sadece MIME tipi, URI ön eki, URI şeması veya tüm bunların kombinasyonunu tanımlayabilir ve kabul edilen veri tipini belirtebilirsiniz.

NOT: Eğer veri Uri'si ile ilgili detayları ilan etmeniz gerekmiyorsa, Activity'nizin karşılayabileceği verinin tipini, text/plain ya da image/jpeg gibi sadece android:mimeType özniteliğini kullanarak tanımlamalısınız.

Category

Activity'nin karşılayacağı Intent'i karakterize etmek için (genellikle kullanıcının el hareketi veya başlatıldığı yerle ilgili) ek bilgi sağlar. Sistem tarafından desteklenen birkaç farklı kategori de vardır fakat çoğu çok nadiren kullanılır. Bununla beraber varsayılan olarak tüm örtülü (implicit) Intent'ler CATEGORY_DEFAULT ile tanımlanır.

Bunu intent filter'ınızın içine <category> elementi ile tanımlayabilirsiniz.

 

Activity'nizin hangi kriterleri kabul ettiğini <intent-filter> elementinin içinde uygun XML elementleriyle iç içe ilan etmelisiniz.

Aşağıdaki örnekte, ACTION_SEND Intent'ine, veri tipi metin veya resim oldukça karşılık verebilen Activity için tanımlanmış intent filter'ını görüyorsunuz:

<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
        <data android:mimeType="image/*"/>
    </intent-filter>
</activity>

Uygulamaya gelecek her Intent bir eylem ve bir veri tipiyle gelir. Fakat tanımladığınız her <intent-filter>'ın içinde <action>, <category> ve <data> elementlerinin örneklerini birden fazla ilan edebilirsiniz ki, bu sorun oluşturmayacaktır.

Herhangi iki eylem ve veri çifti birbiriyle çakışıyorsa, onları kabul ettikleri veri tiplerine göre iki ayrı intent filter olarak ayırmalısınız.

Örneğin, Activity'nizin ACTION_SEND ve ACTION_SENDTO Intent'leriyle metin ve resme karşılık verebildiğini varsayın. Böyle bir senaryoda iki ayrı intent filter tanımlamalısınız çünkü ACTION_SENDTO Intent'i send veya sendto Uri şemasını kullanarak, alıcının ismini tanımlarken Uri verisini kullanmaya ihtiyaç duyar. Örneğimizi inceleyelim:

<activity android:name="ShareActivity">
    <!-- metin göndermeye uygun intent-filter; SENDTO eylemini sms URI şemalarıyla kabul eder -->
    <intent-filter>
        <action android:name="android.intent.action.SENDTO"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="sms" />
        <data android:scheme="smsto" />
    </intent-filter>
    
    <!-- metin veya resim göndermek için intent-filter; SEND eylemini metin veya resim verisiyle kabul eder -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

NOT: Üstü kapalı (implicit) Intent'leri alabilmek için intent filter'ınıza mutlaka CATEGORY_DEFAULT kategorisini eklemelisiniz. startActivity() ve startActivityForResult() metotları, parametre olarak alacağı Intent'ler ancak CATEGORY_DEFAULT kategorisiyle ilan edilmiş olduğunda işleme koyar. Böyle bir ekleme yapmazsanız üstü kapalı Intent'ler Activity'nizi çözümleyemezler - başka uygulamaların "Paylaş" eylemlerinde sizin uygulamanız listelenmez.

Sosyal paylaşım davranışları gerçekleştirmenize yarayan ACTION_SEND Intent'lerini göndermek ve almak hakkında daha fazla bilgi edinmek istiyorsanız "Diğer Uygulamalardan Veri Almak (Receiving Simple Data from Other Apps)" içeriğine bakınız.

Activity içerisinde Intent'in kullanılması
Activity'niz içinde alınacak eyleme karar vermek için öncelikle onu başlatacak olan Intent'i okuyabilmelisiniz. Bunu yapabilmek için Activity'niz başlarken getIntent() metodunu çağırarak onu başlatan Intent'i alabilirsiniz. Bu işlemi Activity'nin herhangi bir yaşam döngüsü olayında yapabilirsiniz fakat genellikle onCreate() ve onStart() gibi ilk sıralarda çalışan callback'ler sırasında yapmanızı öneririz.

Bir örnek:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    // bu activity'yi başlatan intent'i alalım
    Intent intent = getIntent();
    Uri data = intent.getData();

    //intent tipipne göre neler yapabileceğimize bakalım
    if (intent.getType().indexOf("image/") != -1) {
        // image verisiyle intent'i kullanalım
    } else if (intent.getType().equals("text/plain")) {
        // metin verisiyle intent'i işleyelim
    }
}
 

Sonuç döndürme
Oluşturduğu Intent ile Activity'nizi çağıran Activity'ye bir sonuç dönmek isterseniz basitçe setResult() metodunu çağırabilir ve sonuç koduyla birlikte sonuç Intent'ini tanımlayabilirsiniz. Bu, şu anlama geliyor: Döneceğiniz sonuç da bir Intent nesnesi ve her sonuca uygun onu diğerlerinden ayırmaya yarayan bir sonuç kodu var.

Sonuç döndürme işlemi tamamlandığında ve kullanıcının orijinal Activity'ye geri dönmesi gerektiğinde finish() metodunu çağırarak Activity'nizi kapatmalısınız. Örneğin:

//sonuç verisi olarak teslim edilecek bir Intent oluşturalım
Intent result = new Intent("org.tcellgy.ornek.RESULT_ACTION", Uri.parse("content://result_uri");
setResult(Activity.RESULT_OK, result);
finish();
Sonuç döndürmek için mutlaka bir "sonuç kodu" tanımlamalısınız. Bu genelde RESULT_OK ya da RESULT_CANCELED gibi bir değer alır ki bunlar Activity sınıfından gelen sabit değerlerdir. Eğer gerekiyorsa bir Intent ile ekstra bilgi sağlayabilirsiniz.

NOT: Sonuç varsayılan olarak RESULT_CANCELED olarak ayarlıdır. Böylece kullanıcı siz sonucu ayarlamadan ve eylemi tamamlamadan önce Geri düğmesine basacak olursa orijinal Activity'ye "iptal edildi" sonucu gönderilir.

Eğer sonuç kodu olarak bir integer değeri döndürmek istiyorsanız, 0'dan büyük herhangi bir değeri kullanabilirsiniz. Sonuç kodu olarak integer kullanacağınızdan, sonuç döndürürken içine Intent'i koymanıza gerek kalmaz; basitçe sonuç kodunu parametre geçerek setResult() metodunu çağırabilirsiniz. Örneğin:
setResult(RESULT_COLOR_RED); finish();
 
Böyle yapmayı seçtiğinizde elinizde bir avuç olası sonuç olacak, o da sınıfınızda yerel olarak tanımladığınız integer değer. Böylesi bir yaklaşım, kendi uygulamanızın bir Activity'sine sonuç döndürdüğünüzde gayet iyi çalışacaktır çünkü Activity'niz, sonuç kodlarını belirlemek için kullandığınız public sabit değerleri referans alabilen, kendi uygulamanızın Activity'si.

NOT: Activity'nizin startActivity() ile mi yoksa startActivityForResult() ile mi başlatıldığını doğrulamanıza gerek yoktur. Eğer Activity'nizi başlatan sonuç olarak bir Intent bekliyorsa bekliyorsa basitçe setResult() metodunu çağırabilirsiniz. Eğer meydana getiren Activity startActivityForResult() metodunu çağırmışsa sistem setResult() ile vereceğiniz sonucu ona teslim edecektir. Öteki türlü sonuç göz ardı edilir.

Yorumlar

Bu blogdaki popüler yayınlar

İç İçe Döngüler

CSS Bir Elemanın Genişliği ve Yüksekliği

JavaScript Dilinde Fonksiyon Çağırma Teknikleri