/ android

Android Runtime Permission များ

Android 6.0 က စလို့ Android မှာ run time permission ဆိုပြီးပါလာပါတယ်။

ပုံမှန်အားဖြင့် Play Store ကနေ app တစ်ခုကို install လုပ်သည်ဖြစ်စေ apk file တစ်ခုကိုပဲ ဒီတိုင်း လုပ်သည်ဖြစ်စေ အဲဒီ app ရဲ့ permissions တွေကို install မလုပ်ခါနီး အခုလိုပြပါတယ်။

Android normal permissions

Image from https://whatitallboilsdownto.wordpress.com/2016/01/29/android-marshmallow-and-the-new-runtime-permissions-looking-for-best-practices

ဒါပေမယ့် android 6.0 နဲ့ အထက်မှာတော့ permission တစ်ခုခြင်းစီကို install လုပ်တဲ့အချိန်မှာမဟုတ်ပဲ ဒီ permission ကို သုံးထားတဲ့ အချိန်ကျမှပဲ တောင်းတဲ့ (တနည်းအားဖြင့် app run နေတဲ့အချိန်မှာပဲ တောင်းတဲ့ system) ကို introduce လုပ်လိုက်ပါတယ်။ ဥပမာ ကိုယ့် app ရဲ့ feature တစ်ခုက camera ကို သုံးဖို့ လိုတယ်ဆိုရင် အဲဒီ feature ကို user က သုံးတယ့်အခါမှပဲ user ဆီကနေ permission တောင်းရပါတယ်။

Android Permissions များကို normal permission နဲ့ dangerous permission များဆိုပြီး နှစ်ခု ခွဲထားပါတယ်။ Dangerous permission တွေထဲက permission တွေကိုပဲ user ဆီကနေ request လုပ်ဖို့ လိုပါတယ်။

အောက်ပါ permission များကတော့ dangerous permission များ ဖြစ်ပါတယ်။

ကိုယ့်ရဲ့ app က dangerous permission group ထဲက တစ်ခုခုကို သုံးထားတယ် နောက် Android 6.0 ( API level 23 ) ကို target လုပ်ထားတယ်ဆိုရင် လိုအပ်တဲ့ ပြင်ဆင်မှုတွေကို လုပ်ဖို့ လိုပါလိမ့်မယ်။ မဟုတ်ရင်တော့ SecurityException: Permission Denial: ဆိုတဲ့ error နဲ့ အတူ app က crash ဖြစ်သွားမှာပါ။

ဒီ post မှာ Google Samples မှာ အသုံးပြုထားတဲ့ Easy Permission ဆိုတဲ့ library နဲ့ step by step permission request လုပ်ပါမယ်။

easypermissions က Android ရဲ့ ပုံမှန် request လုပ်တဲ့ နည်းလမ်းတွေကို ပိုမို လွယ်ကူအောင် လုပ်ထားတဲ့ wrapper library ပါ။

ပထမဆုံး easypermissions ကို ကိုယ့် app မှာ dependency အနေနဲ့ add ပါတယ်။ ဒီနေရာမှာတော့ ကျနော်တို့ sample app တစ်ခု လုပ်ပြီး အဆင့်ဆင့်ကို ပြပါ့မယ်။ ပုံမှန်အတိုင်း application တစ်ခုကို Android Studio ကနေ create လုပ်ပါတယ်။ နောက် app folder အောက်က build.gradle မှာ အခုလို ထည့်ပါတယ်။

dependencies {
  // Other dependencies 

  // Easy Permissions
  compile 'pub.devrel:easypermissions:0.1.9'
}

ဒီ post မှာ single permission နှစ်ခုကို သီးခြား request လုပ်တာနဲ့ Fragment မှာ permission နှစ်ခုကို တစ်ပြိုင်တည်း request လုပ်တာကို ပြသွားပါမယ်။

Requesting Single Permissions

ပထမဆုံး Camera နဲ့ Location permission တွေကို တစ်ခုချည်းစီ request လုပ်ကြည့်ပါမယ်။

ကိုယ်လိုအပ်တဲ့ permission တွေကို AndroidManifest.xml မှာ သွားပြီး ကြေညာထားဖို့ လိုပါတယ်။

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

နောက်တစ်ဆင့်အနေနဲ့ layout မှာ Button တွေကို ထည့်ပါတယ်။

  <Button
      android:id="@+id/btnRequestCamera"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/request_camera"
      />

  <Button
      android:id="@+id/btnRequestLocation"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/request_location"
      />

  <View
      android:layout_width="wrap_content"
      android:layout_height="30dp"
      />

  <Button
      android:id="@+id/btnRequestInFragment"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/request_in_fragment"
      />

နောက် MainActivity မှာ camera ကို အခုလို request လုပ်ပါတယ်။ ပထမဆုံး ကိုယ့် app မှာ camera permission ရှိပြီးသားလား မရှိပြီးသားလားကို EasyPermissions.hasPermissions(this, Manifest.permission.CAMERA) ဆိုပြီး စစ်ပါတယ်။ ဒီနေရာမှာ this ဆိုတာဟာ Activity ဒါမှမဟုတ် Fragment ရဲ့ Context ဖြစ်ပါတယ်။ (သတိပြုရမယ့် အချက်တစ်ခုက permission တွေကို UI ပါတဲ့ component တွေဖြစ်တယ့် Activity သို့မဟုတ် Fragment တွေမှာပဲ request လုပ်လို့ ရပါတယ်။)

Permission မရှိသေးရင်တော့ EasyPermissions.requestPermissions(this, getString(R.string.rationale_camera), REQUEST_CODE_CAMERA, Manifest.permission.CAMERA); ဆိုပြီး request လုပ်ပါတယ်။ REQUEST_CODE_CAMERA ကို global static variable တစ်ခုအနေနဲ့ ထိပ်ဆုံးမှာ declare လုပ်ထားပါတယ်။

အလားတူပဲ Location permission ကို နောက်ထပ် static variable တစ်ခုနဲ့အတူ အပေါ်ကလိုပဲ ရေးပါတယ်။

app ကို run ကြည့်ပြီး Request Camera သို့မဟုတ် Request Location button ကို နှိပ်ကြည့်ရင် အခုလို မြင်ရမှာပါ။

ဒီနေရာမှာ Allow ဒါမှမဟုတ် Deny နှိပ်လိုက်ရင် ဘာမှ မဖြစ်သေးပါဘူး။ အဲဒါအတွက် EasyPermissions မှာ option နှစ်ခု ရှိပါတယ်။

ပထမတစ်ခုက location အတွက်ဆိုရင် enableLocation(), camera အတွက်ဆိုရင် enableCamera() method စတာတွေကို @AfterPermissionGranted annotation နဲ့ ကိုယ် request လုပ်လိုက်တဲ့ permission ရဲ့ request code နဲ့ (camera အတွက် REQUEST_CODE_CAMERA, location အတွက် REQUEST_CODE_LOCATION) annotate လုပ်ပါတယ်။ ဒါဆိုရင် Permission ကို Allow လုပ်လိုက်တာနဲ့ openCamera(), openGPS() method တွေကို အသီးသီး အလုပ်လုပ်သွားမှာပါ။ code ကတော့ ဒီလိုပါ။

ဒုတိယနည်းကတော့ ပထမနည်းထက် ပိုပြီး control ရှိပါတယ်။ ပထမဆုံး အနေနဲ့ Android ရဲ့ onRequestPermissionsResult ကို Override လုပ်ပြီး ရလာတဲ့ result တွေကို EasyPermissions.onRequestPermissionsResult နဲ့ handle လုပ်ပါတယ်။

နောက်တဆင့် အနေနဲ့EasyPermissions.PermissionCallbacks ကို implement လုပ်ပြီး
onPermissionsGranted နဲ့ onPermissionsDenied method နှစ်ခုကို အခုလို override လုပ်ထားပါမယ်။

onPermissionGranted လို့ ဆိုတဲ့အတိုင်း မှာ permission grant ဖြစ်သွားရင် request လုပ်လိုက်တဲ့ permission ရဲ့ request code အတိုင်း ဘာလုပ်မယ်ဆိုတာကို ရေးပြီး onPermissionDenied မှာ user က permission ကို deny လုပ်လိုက်ရင် ဘာဖြစ်မယ်ဆိုတာကို ရေးပါတယ်။

ကိုယ့် app မှာ သုံးထားတဲ့ တစ်ချို့ permission တွေဟာ သူတို့ မရှိရင် အဲဒီ feature က အလုပ်မလုပ်မှာ ဖြစ်တဲ့ အတွက် User က allow မလုပ်မချင်း အမြဲတမ်း တောင်းနေဖို့ လိုပါတယ်။ User က "Never Ask Again" လို့ လုပ်ထားခဲ့မယ်ဆိုရင်တော့ app ရဲ့ Settings မှာ သွားပြီး manually enable လုပ်ဖို့ လိုပါတယ်။ Setting ကိုသွားပြီး ကိုယ့် app ကို ပြန်လာတဲ့အခါမှာ permission enable ဖြစ်သွားလား မဖြစ်သွားလားကို onActivityResult မှာ စစ်ထားလို့ ရပါတယ်။

ဒါဆိုရင် android run time permission request လုပ်တာရဲ့ အခြေခံကို သဘောပေါက်မယ် ထင်ပါတယ်။

Requesting Multiple Permissions

ကိုယ့် app ရဲ့ တစ်ချို့ feature တွေဟာ permission တစ်ခုထက်မက လိုအပ်တာကို ကြုံရနိုင်ပါတယ်။ ဥပမာ camera ကိုသုံးပြီး ဓါတ်ပုံရိုက်တယ် နောက်ရလာတဲ့ ပုံကို external storage မှာ save လုပ်တယ်ဆိုရင် camera permission နဲ့ write external storage permission နှစ်ခု လိုမှာ ဖြစ်ပါတယ်။ နှစ်ခုတည်းက တစ်ခုပဲ user က allow လုပ်ပြီး တစ်ခုကို မလုပ်ဘူးဆိုရင် feature က အပြည့်အဝ အလုပ်လုပ်မှာ မဟုတ်ပါဘူး။

easypermissions မှာ multiple permissions တွေကို string array တစ်ခုထဲထည့်ပြီး request လုပ်လို့ရပါတယ်။

UI မှာဆိုရင်တော့ permission နှစ်ခုကို dialog တစ်ခုတည်းမှာပဲ တစ်ခုပြီး တစ်ခု request လုပ်ပါတယ်။

ကျန်တာကတော့ single permission request လုပ်တာနဲ့ အတူတူပါပဲ။

အခုရေးသွားတဲ့ sample app ရဲ့ code တွေကို GitHub မှာ ယူနိုင်ပါတယ်။ (Download source code zip)

sample app apk ကတော့ ဒီမှာ ပါ။

Tips and Good practices

  • ကိုယ့် app မှာ dangerous permission တွေထဲက တတ်နိုင်သမျှ မလိုအပ်ပဲ မသုံးတာ ကောင်းပါတယ်။ ဆိုပါတော့ ကိုယ့် app မှာ ဖုန်းခေါ်တဲ့ feature ပါတယ်ဆိုရင် ပုံမှန်အားဖြင့် android.permission.CALL_PHONE ကို သုံးကြမှာပါ။ Java code ကတော့ ဒီလိုပါ။
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:1234567890"))
startActivity(intent);

CALL_PHONE permission ဟာ dangerous permission မှာ ပါတဲ့အတွက် အထက်ကလို အဆင့်ဆင့် permission request လုပ်ရပါလိမ့်မယ်။ အဲဒီအစား အခုလို ပြောင်းလဲလိုက်မယ်ဆိုရင် permission request လုပ်စရာမလိုတော့သလို Manifest မှာပါ declare လုပ်စရာ လိုမှာ မဟုတ်တော့ပါဘူး။

Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:1234567890"))
startActivity(intent);

ခြားနားချက်ကတော့ INTENT.ACTION_CALL က တိုက်ရိုက် ဖုန်းခေါ်တာ ဖြစ်ပြီး INTENT.ACTION_DIAL ကတော့ Dialer ကို အရင်သွားပြီးမှာ user က call ကို နှိပ်ပြီးခေါ်တာ ဖြစ်ပါတယ်။ User action မပါပဲ ဖုန်းခေါ်တာထက် dialer ကနေ ဖုန်းခေါ်တာ ပိုပြီးသင့်တော်တဲ့ နည်းလမ်းလို့ ထင်ပါတယ်။ ဒါ့အပြင် dangerous permission ကို သုံးစရာ မလိုတော့တဲ့အတွက် permission request လုပ်ရတဲ့ code တွေ ရှင်းသွားပါတယ်။ ရေးရတဲ့ code ပိုနည်းလေ bug နည်းလေ maintain လုပ်ရတာ ပိုလွယ်ကူလေပါ။

  • Permission တစ်ခုချင်းစီအတွက် REQUEST_CODE တွေဟာ မတူပဲ unique ဖြစ်နေဖို့ အရေးကြီးပါတယ်။ မဟုတ်ပဲ permission နှစ်ခုက request code value တစ်ခုတည်းကိုပဲ သုံးနေမယ်ဆိုရင် ကိုယ်လိုချင်တဲ့ result ကို ရမှာ မဟုတ်ပါဘူး။

  • New runtime permission system ကို လုံး၀ပြောင်းလဲဖို့ အဆင်သင့် မဖြစ်သေးဘူးဆိုရင် ကိုယ့် app ရဲ့ targetSdk ကို API 22 မှာပဲ ထားထားလို့ ရပါတယ်။ ဒါပေမယ့် အနှေးနဲ့ အမြန်တော့ ပြောင်းရမှာ ဖြစ်ပါတယ်။ Android 6.0 (M) ထွက်ရှိတာ တစ်နှစ်ကျော်ပြီ ဖြစ်တဲ့အပြင် Android N ပါ မကြာမှီ ထွက်လာတော့မှာ ဖြစ်တဲ့အတွက် ပြောင်းလဲဖို့ တိုက်တွန်းပါတယ်။ မဟုတ်ရင်တော့ အသစ်ထွက်ရှိလာတဲ့ Android version တွေမှာ ပါလာမယ့် API အသစ်တွေကို အသုံးပြုလို့ ရမှာ မဟုတ်ပါဘူး။

References :