Android
Android 6.0 ve Yenilenen İzin İsteme Mekanızması
Bildiğiniz gibi uygulama içinde kullanmak istediğimiz özellikler için AndroidManifest.xml dosyasına gerekli izinleri tanımlamamız yetiyordu. Kullanıcı Google Play'den uygulamayı indiriken, istediğimiz izinler karşısında listeleniyor ve uygulamayı indirmek için bu izinleri kabul etmek zorunda kal...
Bildiğiniz gibi uygulama içinde kullanmak istediğimiz özellikler için AndroidManifest.xml dosyasına gerekli izinleri tanımlamamız yetiyordu. Kullanıcı Google Play'den uygulamayı indiriken, istediğimiz izinler karşısında listeleniyor ve uygulamayı indirmek için bu izinleri kabul etmek zorunda kalıyor. Sonrasında ise uygulama bu özellikleri tekrar izine ihtiyaç duymadan defalarca kez kullanabiliyordu. Fakat Android 6.0 ile bu izin yapısı biraz değişikliğe uğradı. Şöyle ki, Google kullanılan izinleri NORMAL İZİNLER ve TEHLİKLELİ İZİNLER olarak ikiye ayırdı. (Aslında iki değil dört kategori var fakat ilk ikisi yeterli bizim için.) Tehlikeli olarak tanımlanan türden izinler için AndroidManifest.xml'e tanımlamak yeterli olmuyor artık, uygulama çalışırken izin gereken bir iş yapmak istediğinde anında kullanıcıya sorup, her defasında izin almamız gerekiyor.
Özetle, tüm izinleri yine AndroidManifest.xml'e tanımlıyoruz. Beraberinde tehlikeli izin gerektiren işler içinse uygulama çalışırken tekrar izin istemek zorundayız. IPhone kullanan kullanıcılar bu yapıya aşinadır aslında. Çünkü IOS bu izin yapısını yıllardır başarıyla kullanıyor kendi uygulamaları içinde.
Bahsettiğimiz izin kategorilerine giren izinleri aşağıdaki gibi listeleyebiliriz.
Normal İzinler: https://developer.android.com/guide/topics/security/normal-permissions.html
Tehlikeli İzinler: https://developer.android.com/guide/topics/security/permissions.html?normal-dangerous
Örneğin internete bağlanmak normal bir izin gerektiriken, rehbere ulaşmak, SD karta dosya yazmak ya da bir numarayı aramak tehlikeli izin kategorisine giriyor.
İZİNLER İÇİN YENİ BİR DESIGN PATTERN
Gel gelelim bu izinleri nasıl isteyeceğimize. İzin istemek ve kullanıcının izin verip vermediğini kontrol etmek için yaklaşık 30 satırlık standart bir kod yazmamız gerekli. Uygulama içinde ortalama on yerde izin talebinde bulunduğumuzu düşünürsek 300 satırlık gereksiz ve kendini tekrar eden bir kod yığını oluşur. Üstelik izin istediğimiz iş ve izin isteme işlerini aynı metodda toplarsak bu Single Responsibility Prensibine de aykırı olacaktır.
Peki Çözüm Nedir?
Malum yazılım dünyasında patternlar, prensipler derken her işi yapmanın adam akıllı yolları var. Bunun için de tavsiye edilen yöntem Runtime Permissions Activity Pattern'dir. Uzun lafın kısası, abstract bir Actvitiy sınıfı yazıyoruz. Bu abstract sınıf içinde izin istemek ve izinin sonucunu kontrol etmek için gerekli metodlar bulunuyor. Yazdığımız tüm Activity sınıflarında bu abstract sınıfı extend ediyoruz ve gönül rahatlığı ile kullanabiliyoruz.
RuntimePermissionsActivity.java
public abstract class RuntimePermissionsActivity extends AppCompatActivity {
private SparseIntArray mErrorString;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mErrorString = new SparseIntArray();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
int permissionCheck = PackageManager.PERMISSION_GRANTED;
for (int permission : grantResults) {
permissionCheck = permissionCheck + permission;
}
if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) {
onPermissionsGranted(requestCode);
} else {
Snackbar.make(findViewById(android.R.id.content), mErrorString.get(requestCode),
Snackbar.LENGTH_INDEFINITE).setAction("ENABLE",
new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}
}).show();
}
}
public void requestAppPermissions(final String[] requestedPermissions,
final int stringId, final int requestCode) {
mErrorString.put(requestCode, stringId);
int permissionCheck = PackageManager.PERMISSION_GRANTED;
boolean shouldShowRequestPermissionRationale = false;
for (String permission : requestedPermissions) {
permissionCheck = permissionCheck + ContextCompat.checkSelfPermission(this, permission);
shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || ActivityCompat.shouldShowRequestPermissionRationale(this, permission);
}
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale) {
Snackbar.make(findViewById(android.R.id.content), stringId,
Snackbar.LENGTH_INDEFINITE).setAction("GRANT",
new View.OnClickListener() {
@Override
public void onClick(View v) {
ActivityCompat.requestPermissions(RuntimePermissionsActivity.this, requestedPermissions, requestCode);
}
}).show();
} else {
/*User has chosen "Never ask again." So we shouldn't request again.. DO NOTHING*/
}
} else {
onPermissionsGranted(requestCode);
}
}
public abstract void onPermissionsGranted(int requestCode);
}
RuntimePermissionsActivity.java sınıfımız içinde üç tane metod bulunuyor. İlki requestAppPermissions() isimli metod izin istemek için çağrılması gereken metoddur. Dikkat ederseniz ilk parametresi String değil String dizisidir.(String[]) Yani aynı anda birden fazla izin istemek için kullanılabilir bu metod.
İkinci metod onRequestPermissionsResult() bizim yazmadığımız, FragmentActivity.java sınıfından miras alıp override ettiğimiz metoddur. Bu metod kullanıcının bize izin verip vermediğini anlayacağımız bir callback yani geri bildirim metodudur. Eğer kullanıcı bize izin vermişse onPermissionsGranted() isimli abstract metodu çağırarak, izin isteğinin yapıldığı asıl Activity sınıfına haber veriyoruz. bu metod bu yüzden abstract olarak tanımlandı. Eğer kullanıcı izin vermemişse de elimizden gelen birşey yok. bu özelliği kullanamayacağız demektir.
Sıklıkla yapılan bir hataya dikkat çekmek isterim. Herhangi bir izin kullanıcıdan red almışsa, bu izin ikinci kez istendiğinde Android işletim Sistemi, açılan pencereye "Bir daha sorma" şeklinde bir checkbox çıkarır. Kullanıcı bu kısmı işaretlerse, bu izin tekrar istenmemelidir. Bu durum kullanıcıyı rahatsız eder. Fakat yazılım geliştiriciler genelde bu kontrolü görmezden gelerek bu ayar işaretli olsa da olmasa da izin isteme yoluna giderler. Bu durum kullanıcıyı rahatsız eder.
Kullanıcının bu izin için bir daha rahatsız edilmek istemediğini requestAppPermissions() metodu içerisindeki shouldShowRequestPermissionRationale değişkeninden anlayabiliyoruz. Eğer false gelirse kullanıcıyı rahatsız etmiyoruz.
Son olarak, bu metodları nasıl çağıracağımıza bakıyoruz. Yazacağımız her Activity sınıfında RuntimePermissionAbstractActivity.java sınıfını extend etmemiz gerekiyor.
MainActivity.java
public class MainActivity extends RuntimePermissionsActivity {
private static final int REQUEST_PERMISSION_READ_CONTACTS = 1;
private static final int REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE = 2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainActivity.super.requestAppPermissions(new
String[]{Manifest.permission.READ_CONTACTS,}, R.string
.runtime_permissions_txt, REQUEST_PERMISSION_READ_CONTACTS);
MainActivity.super.requestAppPermissions(new
String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, R.string.runtime_permissions_txt, REQUEST_PERMISSION_READ_CONTACTS);
}
@Override
public void onPermissionsGranted(final int requestCode) {
if (requestCode == REQUEST_PERMISSION_READ_CONTACTS) {
Toast.makeText(this, "Permissions Received for reading contacts.", Toast.LENGTH_LONG).show();
} else if (requestCode == REQUEST_PERMISSION_WRITE_EXTERNAL_STORAGE) {
Toast.makeText(this, "Permissions Received for writing SD card.", Toast.LENGTH_LONG).show();
}
}
}
Override ettiğimiz onPermissionsGranted() metodu, kabul edilen her izin için tetiklenecek kabul edilmeyenler içinse gereksiz gürültü yapmayacaktır.
Projenin kodlarını Github hesabımızda indirebilir, bedava uygulamanın keyfine varabilirsiniz.
Github : https://github.com/burhanaras/Android-Runtime-Permissions
Teşekkürler.
Burhan ARAS
04.08.2016
Yorumlar
Kesinlikle çok faydalı olmuş. Teşekkürler.
teşekkürler çok faydalı ve güzel anlatım
Teşekkürler.
Çok teşekkürler
Güzel anlatımınız için teşekkürler.