Add ability to grant permission with Shizuku

This commit is contained in:
Maksim Karasev 2023-09-11 16:35:04 +03:00
parent 4b33b2427a
commit 0107d07cec
3 changed files with 94 additions and 10 deletions

View file

@ -43,6 +43,11 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0' implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
def shizuku_version = '13.1.4'
implementation "dev.rikka.shizuku:api:$shizuku_version"
implementation "dev.rikka.shizuku:provider:$shizuku_version"
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

View file

@ -10,6 +10,13 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"> android:theme="@style/Theme.MyApplication">
<provider
android:name="rikka.shizuku.ShizukuProvider"
android:authorities="${applicationId}.shizuku"
android:multiprocess="false"
android:enabled="true"
android:exported="true"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true"> android:exported="true">

View file

@ -1,39 +1,43 @@
package ru.karasevm.privatednstoggle package ru.karasevm.privatednstoggle
import android.Manifest import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.IBinder
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.content.Intent import rikka.shizuku.Shizuku
import android.content.pm.PackageManager import rikka.shizuku.ShizukuBinderWrapper
import android.net.Uri import rikka.shizuku.ShizukuProvider
import rikka.shizuku.SystemServiceHelper
import ru.karasevm.privatednstoggle.databinding.ActivityMainBinding import ru.karasevm.privatednstoggle.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogListener, DeleteServerDialogFragment.NoticeDialogListener { class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogListener, DeleteServerDialogFragment.NoticeDialogListener, Shizuku.OnRequestPermissionResultListener {
private lateinit var linearLayoutManager: LinearLayoutManager private lateinit var linearLayoutManager: LinearLayoutManager
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private var items = mutableListOf<String>() private var items = mutableListOf<String>()
private lateinit var sharedPrefs: SharedPreferences private lateinit var sharedPrefs: SharedPreferences
private lateinit var adapter: RecyclerAdapter private lateinit var adapter: RecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Shizuku.addRequestPermissionResultListener(this::onRequestPermissionResult)
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root val view = binding.root
setContentView(view) setContentView(view)
if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/"))
startActivity(browserIntent)
finish()
}
linearLayoutManager = LinearLayoutManager(this) linearLayoutManager = LinearLayoutManager(this)
binding.recyclerView.layoutManager = linearLayoutManager binding.recyclerView.layoutManager = linearLayoutManager
@ -56,6 +60,38 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
return true return true
} }
override fun onResume() {
super.onResume()
// Check if Shizuku is available
if (Shizuku.pingBinder()) {
// check if permission is granted already
val isGranted = if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED
} else {
Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED
}
// request permission if not granted
if (!isGranted && !Shizuku.shouldShowRequestPermissionRationale()) {
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(arrayOf(ShizukuProvider.PERMISSION), 1)
} else {
Shizuku.requestPermission(1)
}
}
} else {
if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/"))
startActivity(browserIntent)
finish()
}
}
}
override fun onDestroy() {
super.onDestroy()
Shizuku.removeRequestPermissionResultListener(this::onRequestPermissionResult)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.add_server -> { R.id.add_server -> {
val newFragment = AddServerDialogFragment() val newFragment = AddServerDialogFragment()
@ -96,5 +132,41 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
} }
@SuppressLint("PrivateApi")
override fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
val isGranted = grantResult == PackageManager.PERMISSION_GRANTED
if (isGranted) {
val packageName = "ru.karasevm.privatednstoggle"
val iPmClass = Class.forName("android.content.pm.IPackageManager")
val iPmStub = Class.forName("android.content.pm.IPackageManager\$Stub")
val asInterfaceMethod = iPmStub.getMethod("asInterface", IBinder::class.java)
val grantRuntimePermissionMethod = iPmClass.getMethod(
"grantRuntimePermission",
String::class.java /* package name */,
String::class.java /* permission name */,
Int::class.java /* user ID */
)
val iPmInstance = asInterfaceMethod.invoke(
null, ShizukuBinderWrapper(
SystemServiceHelper.getSystemService("package")
)
)
grantRuntimePermissionMethod.invoke(
iPmInstance,
packageName,
Manifest.permission.WRITE_SECURE_SETTINGS,
0
)
} else if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/"))
startActivity(browserIntent)
finish()
}
}
} }