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