Android’de Content Provider Kavramı ve Kullanımı #3

Mustafa Süleyman Kınık
7 min readMay 5, 2020

--

Herkese tekrardan merhaba. Yüzdük yüzdük kuyruğuna geldik arkadaşlar. Son yazımız olacağını umduğum bu yazı ile umarım birilerine bir yardımım olmuştur. Şuradaki link üzerinden şuanda anlatımını yaptığımız uygulama/projeye ye ulaşabilirsiniz.

Main2Activity’de yapılacak işlemlerden bahsetmiştik. O zaman direk işe girişelim.

Ben Manifest içerisinde Main2Activity için şöyle bir işlem yaptım. Bu işlem ile bu activity bir dialog penceresi olarak karşımıza geliyor.

<activity android:name=".Main2Activity"
android:label="Kişi Ekle"
android:theme="@style/Theme.AppCompat.Dialog"></activity>

Bunu yaptıktan sonra aşağıdaki activity_main2.xml kodlarını direk kendi projenize uyarlayabilirsiniz.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".Main2Activity">

<Button
android:id="@+id/buttonAdd"
android:layout_width="match_parent"
android:layout_height="66dp"
android:onClick="ContactList"
android:text="Kayıtlı Kişi ekle" />

<EditText
android:id="@+id/editTextName"
android:layout_width="match_parent"
android:layout_height="48dp"
android:hint="İsim"
android:textColorHint="@android:color/darker_gray"/>

<EditText
android:id="@+id/editTextNumber"
android:layout_width="match_parent"
android:layout_height="40dp"
android:hint="Numara"
android:textColorHint="@android:color/darker_gray"/>

<RadioGroup
android:id="@+id/radioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<RadioButton
android:id="@+id/radioImportant"
android:layout_width="128dp"
android:layout_height="wrap_content"
android:text="Acil"
android:textColor="@android:color/black"></RadioButton>

<RadioButton
android:id="@+id/radioFamily"
android:layout_width="133dp"
android:layout_height="wrap_content"
android:text="Aile"
android:textColor="@android:color/black"></RadioButton>

<RadioButton
android:id="@+id/radioFriend"
android:layout_width="118dp"
android:layout_height="wrap_content"
android:text="Arkadas"
android:textColor="@android:color/black"></RadioButton>
</RadioGroup>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">

<Button
android:id="@+id/buttonUpdate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="Update"
android:layout_marginStart="9dp"
android:layout_marginEnd="20dp"
android:text="Güncelle" />

<Button
android:id="@+id/buttonDelete"
android:layout_width="100dp"
android:onClick="Delete"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:text="Sil" />

<Button
android:id="@+id/buttonSave"
android:layout_width="99dp"
android:onClick="Save"
android:layout_height="wrap_content"
android:text="Kayıt Et" />
</LinearLayout>


</LinearLayout>

Rehberimize(contacts) bağlanmak için bir Button ve olmak üzere bunun yanında güncelleme,silme,kayıt etme işlevi gören üç Button ekledik. İsim ve numarayı içinde barındıracak iki adet EditText ve bu kayıtları gruplamak için bulunan üç adet de RadioButton ekledik.

Layout işlemlerimiz bittiğine göre şimdi Main2Activity.java’nın içindeki işlemlerimize dönelim. İlk başta layout içinde oluşturduklarımızı tanımlamasını onCreate içinde yapalım. Sonrasında Main2Activity’mize ListView içinden mi yoksa OptionsMenu den mi ulaşıldığını tespitini yapalım. Eğer yeni bir kayıt eklenecekse güncelleme ve silme butonunun gözükmesi gereksiz bir durum olacağından bu işlemde gözükmemesi, ListView üzerinden ulaşırsak zaten bir kayıta ulaşıldığından kayıt işlemini yapan butonun bu sefer de gözükmemesini yapmalıyız. ListView içerisindeki bir kayıttan geldiysek, EditText’lerin içinde o kayıt ile alakalı bilgilerin gözükmesi ve bulunduğu kümenin grubun ait olduğu RadioButton’nın seçilmesi gerekli.

public class Main2Activity extends AppCompatActivity {
EditText name;
EditText number;
Button save,update,delete,add;
String getName;
String getId;
String getNumber;
String getTitle;
RadioGroup radioGroup;
RadioButton r1,r2,r3;
int selectedId;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
name=findViewById(R.id.editTextName);
number=findViewById(R.id.editTextNumber);
save=findViewById(R.id.buttonSave);
update=findViewById(R.id.buttonUpdate);
delete=findViewById(R.id.buttonDelete);
add=findViewById(R.id.buttonAdd);
radioGroup=findViewById(R.id.radioGroup);
r1=findViewById(R.id.radioFamily);
r2=findViewById(R.id.radioFriend);
r3=findViewById(R.id.radioImportant);
Bundle bundle= getIntent().getExtras();
getName=bundle.getString("name");
getNumber=bundle.getString("number");
getTitle=bundle.getString("title");
getId=bundle.getString("id");
String getInfo=bundle.getString("info");
System.out.println(getInfo);
if(getInfo.matches("old"))
{
String a=r1.getText().toString();
name.setText(getName);
number.setText(getNumber);
save.setVisibility(View.INVISIBLE);
update.setVisibility(View.VISIBLE);
delete.setVisibility(View.VISIBLE);
add.setVisibility(View.INVISIBLE);
if(getTitle.matches(r1.getText().toString()))
{
r1.setChecked(true);
}
else if (getTitle.matches(r2.getText().toString()))
{
r2.setChecked(true);
}
else
{
r3.setChecked(true);
}

}
else
{
update.setVisibility(View.INVISIBLE);
delete.setVisibility(View.INVISIBLE);
save.setVisibility(View.VISIBLE);
name.setText("");
number.setText("");
}


}

Siz onCreate içindeki karmaşıklığı yapılan işlemleri başka metotlarda yazıp onCreate içinde çağırarak çözebilirsiniz. Lakin ben sizlere kodları bir bütün olarak göstermek için bu yola girişmedim.

Bu kısımları anladığımıza göre şimdi rehberimize(contacts) ulaşma işlemlerini yapalım. Rehbere ulaşmak için sadece AndroidManifest içinden bir <uses-permission> oluşturmak daha önceden de bahsettiğimiz gibi yetmemekte. Sebebi rehbere(contacts) ulaşmak “Protection level: dangerous” olarak developper.android.com sitesinde bulunan dokümantasyonlarda geçmesidir. Bu sebeple izin alma işlemini biraz daha uzun bir şekilde yapacağız.

<uses-permission android:name="android.permission.READ_CONTACTS" />

Yukarıdaki işlemleri AndroidManifest içine eklediysek sonrasında “ContactList” adında bir metot oluşturuyoruz. Hatırlarsanız bu isim “buttonAdd” id adına sahip butonun onClick niteliğiydi.

Bir if kontrolü içerisinde context ve permission tanımlaması yapılmış bir izin kontrolü yapıyoruz. İzin verilmemiş ise context ve permission 'ların bulunduğu bir dizi(array) içerisinde iznimizi istiyoruz ve son olarak bu izine bir sayı atıyoruz ki böylece bu izni aslında başka yerlerde tanımamıza yardımcı olacak bir etiketi olmuş oluyor. İzin alınmış ise Intent ile belirtilen Uri ye gidecektir. startActivity yerine startActivityForResult yapmaktayız. Çünkü aslında gideceğimiz/ işaret ettiğimiz yerden bir sonuç bekliyoruz. Bizim uygulamamızda bu sonuç bir telefon kayıtı olacaktır. Aynı izni isterken yaptığımız gibi de bu geri dönüşü etiketlemek için 2 sayısını atıyoruz.

public void ContactList(View view)
{
if(ContextCompat.checkSelfPermission(Main2Activity.this, Manifest.permission.READ_CONTACTS)!= PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(Main2Activity.this,new String[] {Manifest.permission.READ_CONTACTS},1);
}
else
{
Intent intent= new Intent(Intent.ACTION_PICK,ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(intent,2);
}

}
İşlem öncesi uygulamamızdan istenilecek izin

Fark ettiyseniz izni aldıktan sonra aslında herhangi bir işlem gerçekleşmiyor. Yani biz istiyoruz ki izni aldıktan sonra tekrardan butona basmadan kendisi bizi karşı tarafa o anda bir defaya mahsus iletsin. Zaten izni verdikten sonra bu metot tekrar tetiklendiğinde otomatik olarak else kısmı çalışacağından bizi direk işaret ettiğimiz kısıma götürecektir.

onRequestPermissionsResult içerisinde ilk başta bizim requestCode 1 etiketine eşit olup olmadığını ve bunla birlikte grantResults.length ile grantResult 'ın dolu olup olmadığını ve dizisinin ilk elemanında izin verilmiş anlamına gelen PackageManager.PERMISSION_GRANTED eşitliyoruz. Bu koşullar sağlanırsa Intent gerçekleşiyor.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==1&&grantResults.length>0 && grantResults[0]==PackageManager.PERMISSION_GRANTED)
{
Intent intent= new Intent(Intent.ACTION_PICK,ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(intent,2);
}
}

Şimdi ise yapacağımız işlem rehberimizden bir kayıt seçtikten sonra olanlar ile ilgili olacaktır.

onActivityResult metodunu Override ettikten sonra bir if ile koşullu ifademizi oluşturuyoruz. İçerisinde requestCode resultCode ve data ile ilgili karşılaştırmalar eşitlik işlemleri bulunmakta.

requestCode: startActivityForResult ile aynı değere eşitlenerek bizim gititğimiz yerin Intent’i ile senkronize ediyor.

resultCode :Sonucun tamamlanması ile ilgilenen bir eşitlikte kullanılıyor.

data : Yapılan işlemler sonucu geriye kullanmamız için değer döndüren kısım.

Buraya kadar anlatılanları anladıysak şimdi data.getData() oluşturulan bir Uri içine atarak Cursor ile bu oluşturulan Uri’nin içinde arama işlemi yapılacağını belirtmiş oluyoruz. Eğer içi boş değilse while döngüsü ile bir sonraki adıma gitmesi ve bu adımlar yapılırken getName ve getNumber' a dönen değerleri atıyoruz/eşitliyoruz.

cursor.getColumnIndex ve cursor.getString hakkında konuşmak gerekirse, kısaca cursor.getColumnIndex içine verdiğimiz sütun(column) isminden bize index’ini döndürüyor ve cursor.getString ile de onu String bir değer olarak alabilmemizi sağlıyor. Şuradan daha fazla bilgiye ulaşabilirsiniz.

Kodlarda ilginizi çeken bir husus olabileceğini düşünüyorum. Kendim de bu konuyu ilk öğrenirken garipsemiştim doğrusu. Bu ilgi çekici kısım örneğimizdeki getNumber için tekrardan bir Cursor oluşturup tekrardan döngü içerisinden arama yapmaktır. Sebebi ise bir kaydı alırken kayıt ismi ile kayıttaki numarayı aynı yerden alamıyor olmamızdan kaynaklı. Kendimde bu uygulamayı yazarken bunu fark etmem sonucu stackoverflow gibi sitelerden bununla ilgili yazılar okudum ve ulaştığım sonuç şuydu. Basitçe anlatmak gerekirse bizler bir telefonun rehberine ulaşıp bilgileri çekerken aynı Cursor içinde tanımladığımız Uri üzerinden hem rehberdeki kayıtlı bilginin adına hemde numarasına ulaşamamaktayız. Bu sebeple basit bir tabir ile iki aşamalı işlem yapacağız. İlk olarak ContactsContract.Contacts._ID index’inden gelen sonucu String tipinde id isimli bir değere atacağız. Bir if sorgusu içinde telefon numarasının varlığını sorgulayan işlemi yapacağız. Eğer var ise oluşturacağımız yeni bir Cursor ile zaten var olduğundan emin olduğumuz telefon numarasını alabilmek için oluşturduğumuz String tipinde id değerini yeni Cursor umuzun selection kısmında ContactsContract.CommonDataKinds.Phone.CONTACT_ID ye eşitleyerek, aslında bu id’ ye eşit olanı Cursor ile tarayacaksın demiş olacağız ve altında da zaten bir önceki işlemlerde yaptığımızın aynısı olan işlemler ile numaraya erişeceğiz.

@Override
protected void onActivityResult (int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==2&&resultCode==RESULT_OK&&data!=null)
{
Uri uri=data.getData();
Cursor cursor=getContentResolver().query(uri,null,null,null,null);
if(cursor!=null)
{
while(cursor.moveToNext())
{
getName=cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String id=cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0)
{

Cursor findNumber=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ id,null,null);

while(findNumber.moveToNext())
{
getNumber=findNumber.getString(findNumber.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));

}
findNumber.close();
}
}

}
cursor.close();

number.setText(getNumber);
name.setText(getName);
}
}

Yazı dizimizin son bölümlerine geldik. Kayıt etme, güncelleme ve silme.

Kayıt ve güncelleme işlemleri birbirlerinin neredeyse kopyası durumumda.

İlk olarak kayıtları gruplandırmak için RadioButton aracılığıyla seçim yapıyoruz. Seçim işlemi sonrası ContentValues sınıfından contentValues isimli oluşturduğumuz nesne(object) ile put metodu çektiğimiz isim ve numarayı ve bunlara karşılık gelecek olan sütun adlarını ekliyoruz ve bu contentvalues ve Uri ismimizi, insert class’ının içine yazıp ekleme işlemini tamamlıyoruz. Böylece kayıt işlemi sonlanmış oluyor. Güncelleme işleminin tek farkı bir kayıta, değiştirilmiş değerlerin o kaydın id’si yardımı ile güncellenmesi işlemidir.

public void Save(View view)
{
selectedId=radioGroup.getCheckedRadioButtonId();
switch (selectedId)
{
case R.id.radioFamily:
getTitle=r1.getText().toString();
break;
case R.id.radioFriend:
getTitle=r2.getText().toString();
break;
case R.id.radioImportant:
getTitle=r3.getText().toString();
break;
default:
break;

}
ContentValues contentValues=new ContentValues();
contentValues.put(AppProvider.NAME,name.getText().toString());
contentValues.put(AppProvider.NUMBER,number.getText().toString());
contentValues.put(AppProvider.TITLE,getTitle);
getContentResolver().insert(AppProvider.CONTENT_URI,contentValues);
finish();
}
public void Update(View view)
{
selectedId=radioGroup.getCheckedRadioButtonId();
switch (selectedId)
{
case R.id.radioFamily:
getTitle=r1.getText().toString();
break;
case R.id.radioFriend:
getTitle=r2.getText().toString();
break;
case R.id.radioImportant:
getTitle=r3.getText().toString();
break;
default:
break;

}
ContentValues contentValues=new ContentValues();
contentValues.put(AppProvider.NAME,name.getText().toString());
contentValues.put(AppProvider.NUMBER,number.getText().toString());
contentValues.put(AppProvider.TITLE,getTitle);
getContentResolver().update(AppProvider.CONTENT_URI,contentValues,"id=?",new String[]{getId});
finish();

}

Silme işlemi ise varlığı ile yokluğu belli olmayacak bir uzunlukta. Silme işlemi de aslında güncelleme işleminin son kısmıyla çok benzerdir. Aynı mantıkla çalışır ama bu sefer var olan id’yi silme işlemini yapar.

public void Delete (View view)
{
getContentResolver().delete(AppProvider.CONTENT_URI,"id=? ",new String[]{getId});
finish();

}

Serimizin sonuna gelmiş bulunmaktayız hatalarım olduysa kesinlikle bana ulaşın aynı zamanda konunun anlatımı zor olduğu için biz öğrenci arkadaşlara daha çok hitap edeceğini düşündüğümden fazla teknik bir dil yerine zihinde daha iyi anlaşılması üzerine cümlelerimi kurdum. Umarım ihtiyaçlarına yardımcı olmuşumdur. Herkese güzel sağlıklı günler dilerim.

Not: Her şeyi yaptıktan sonra, sadece manuel kendiniz girdi olarak eklemek yerine “biz o kadar rehbere ulaşmak için kod yazık biraz da oradan kayıt çekip ekleyelim ” derseniz emülatörünüzün rehberine bu iş öncesinde kayıt eklemeyi unutmayın :D

--

--