diff --git a/app/build.gradle b/app/build.gradle
index 904ff2a..29faa1d 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -43,6 +43,11 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
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'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ff64db1..e5e6087 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,6 +10,13 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication">
+
diff --git a/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt b/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt
index bddc079..618356e 100644
--- a/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt
+++ b/app/src/main/java/ru/karasevm/privatednstoggle/MainActivity.kt
@@ -1,39 +1,43 @@
package ru.karasevm.privatednstoggle
import android.Manifest
+import android.annotation.SuppressLint
+import android.content.Intent
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.IBinder
import android.view.Menu
import android.view.MenuItem
import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.net.Uri
+import rikka.shizuku.Shizuku
+import rikka.shizuku.ShizukuBinderWrapper
+import rikka.shizuku.ShizukuProvider
+import rikka.shizuku.SystemServiceHelper
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 binding: ActivityMainBinding
private var items = mutableListOf()
private lateinit var sharedPrefs: SharedPreferences
private lateinit var adapter: RecyclerAdapter
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ Shizuku.addRequestPermissionResultListener(this::onRequestPermissionResult)
+
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
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)
binding.recyclerView.layoutManager = linearLayoutManager
@@ -56,6 +60,38 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
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) {
R.id.add_server -> {
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()
+ }
+
+ }
}
\ No newline at end of file