mirror of
https://github.com/topjohnwu/Magisk.git
synced 2024-11-28 04:25:27 +00:00
Magisk Manager is now a SU client
1. Add request popup 2. Add su request notifications 3. Add su database helpers
This commit is contained in:
parent
cf16fd0104
commit
0bd0eb9e59
@ -8,9 +8,6 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission
|
|
||||||
android:name="android.permission.PACKAGE_USAGE_STATS"
|
|
||||||
tools:ignore="ProtectedPermissions" />
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
@ -41,6 +38,20 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".SettingsActivity"
|
android:name=".SettingsActivity"
|
||||||
android:theme="@style/AppTheme.Transparent" />
|
android:theme="@style/AppTheme.Transparent" />
|
||||||
|
<activity
|
||||||
|
android:name=".superuser.RequestActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:taskAffinity="internal.superuser"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay" />
|
||||||
|
<activity
|
||||||
|
android:name=".superuser.SuRequestActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:taskAffinity="internal.superuser"
|
||||||
|
android:theme="@style/SuRequest" />
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".superuser.SuReceiver" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name="android.support.v4.content.FileProvider"
|
android:name="android.support.v4.content.FileProvider"
|
||||||
|
36
app/src/main/java/com/topjohnwu/magisk/superuser/Policy.java
Normal file
36
app/src/main/java/com/topjohnwu/magisk/superuser/Policy.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package com.topjohnwu.magisk.superuser;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
|
||||||
|
|
||||||
|
public class Policy {
|
||||||
|
public int uid, policy;
|
||||||
|
public long until;
|
||||||
|
public boolean logging, notification;
|
||||||
|
public String packageName, appName;
|
||||||
|
|
||||||
|
public Policy() {}
|
||||||
|
|
||||||
|
public Policy(Cursor c) {
|
||||||
|
uid = c.getInt(c.getColumnIndex("uid"));
|
||||||
|
packageName = c.getString(c.getColumnIndex("package_name"));
|
||||||
|
appName = c.getString(c.getColumnIndex("app_name"));
|
||||||
|
policy = c.getInt(c.getColumnIndex("policy"));
|
||||||
|
until = c.getLong(c.getColumnIndex("until"));
|
||||||
|
logging = c.getInt(c.getColumnIndex("logging")) != 0;
|
||||||
|
notification = c.getInt(c.getColumnIndex("notification")) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContentValues getContentValues() {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("uid", uid);
|
||||||
|
values.put("package_name", packageName);
|
||||||
|
values.put("app_name", appName);
|
||||||
|
values.put("policy",policy);
|
||||||
|
values.put("until", until);
|
||||||
|
values.put("logging", logging ? 1 : 0);
|
||||||
|
values.put("notification", notification ? 1 : 0);
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.topjohnwu.magisk.superuser;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
|
||||||
|
public class RequestActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
if (intent == null) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.setClass(this, SuRequestActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.topjohnwu.magisk.superuser;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SuDatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
private static final int DATABASE_VER = 1;
|
||||||
|
private static final String TABLE_NAME = "policies";
|
||||||
|
|
||||||
|
public SuDatabaseHelper(Context context) {
|
||||||
|
super(context, "su.db", null, DATABASE_VER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL(
|
||||||
|
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
||||||
|
"(uid INT, package_name TEXT, app_name TEXT, policy INT, " +
|
||||||
|
"until INT, logging INT, notification INT, " +
|
||||||
|
"PRIMARY KEY(uid))");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
// Currently new database, no upgrading
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean deletePolicy(int uid) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
db.delete(TABLE_NAME, "uid=?", new String[] { String.valueOf(uid) });
|
||||||
|
db.close();
|
||||||
|
return getPolicy(uid) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Policy getPolicy(int uid) {
|
||||||
|
Policy policy = null;
|
||||||
|
SQLiteDatabase db = getReadableDatabase();
|
||||||
|
try (Cursor c = db.query(TABLE_NAME, null, "uid=?", new String[] { String.valueOf(uid) }, null, null, null)) {
|
||||||
|
if (c.moveToNext())
|
||||||
|
policy = new Policy(c);
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
return policy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPolicy(Policy policy) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
db.replace(TABLE_NAME, null, policy.getContentValues());
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Policy> getPolicyList(PackageManager pm) {
|
||||||
|
SQLiteDatabase db = getWritableDatabase();
|
||||||
|
ArrayList<Policy> ret = new ArrayList<>();
|
||||||
|
Policy policy;
|
||||||
|
// Clear outdated policies
|
||||||
|
db.delete(TABLE_NAME, "until > 0 and until < ?", new String[] { String.valueOf(System.currentTimeMillis()) });
|
||||||
|
try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, null)) {
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
policy = new Policy(c);
|
||||||
|
// Package is uninstalled
|
||||||
|
if (pm.getPackagesForUid(policy.uid) == null)
|
||||||
|
deletePolicy(policy.uid);
|
||||||
|
else
|
||||||
|
ret.add(policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package com.topjohnwu.magisk.superuser;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
|
||||||
|
public class SuReceiver extends BroadcastReceiver {
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
int fromUid, toUid, pid;
|
||||||
|
String command, action;
|
||||||
|
|
||||||
|
if (intent == null) return;
|
||||||
|
fromUid = intent.getIntExtra("from.uid", -1);
|
||||||
|
if (fromUid < 0) return;
|
||||||
|
action = intent.getStringExtra("action");
|
||||||
|
if (action == null) return;
|
||||||
|
SuDatabaseHelper dbHelper = new SuDatabaseHelper(context);
|
||||||
|
Policy policy = dbHelper.getPolicy(fromUid);
|
||||||
|
if (policy == null) return;
|
||||||
|
if (policy.notification) {
|
||||||
|
String message;
|
||||||
|
switch (action) {
|
||||||
|
case "allow":
|
||||||
|
message = context.getString(R.string.su_allow_toast, policy.appName);
|
||||||
|
break;
|
||||||
|
case "deny":
|
||||||
|
message = context.getString(R.string.su_deny_toast, policy.appName);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
if (policy.logging) {
|
||||||
|
toUid = intent.getIntExtra("to.uid", -1);
|
||||||
|
if (toUid < 0) return;
|
||||||
|
pid = intent.getIntExtra("pid", -1);
|
||||||
|
if (pid < 0) return;
|
||||||
|
command = intent.getStringExtra("command");
|
||||||
|
if (command == null) return;
|
||||||
|
// TODO: Place info into logs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
package com.topjohnwu.magisk.superuser;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.LocalSocket;
|
||||||
|
import android.net.LocalSocketAddress;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
|
||||||
|
public class SuRequestActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private final static int SU_PROTOCOL_PARAM_MAX = 20;
|
||||||
|
private final static int SU_PROTOCOL_NAME_MAX = 20;
|
||||||
|
private final static int SU_PROTOCOL_VALUE_MAX = 256;
|
||||||
|
|
||||||
|
@BindView(R.id.timeout) Spinner timeout;
|
||||||
|
@BindView(R.id.app_icon) ImageView appIcon;
|
||||||
|
@BindView(R.id.app_name) TextView appNameView;
|
||||||
|
@BindView(R.id.package_name) TextView packageNameView;
|
||||||
|
@BindView(R.id.grant_btn) Button grant_btn;
|
||||||
|
@BindView(R.id.deny_btn) Button deny_btn;
|
||||||
|
|
||||||
|
private String socketPath;
|
||||||
|
private LocalSocket socket;
|
||||||
|
private PackageManager pm;
|
||||||
|
private PackageInfo info;
|
||||||
|
|
||||||
|
private int uid;
|
||||||
|
private String appName, packageName;
|
||||||
|
|
||||||
|
private int[] timeoutList = {0, -1, 10, 20, 30, 60};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
|
||||||
|
pm = getPackageManager();
|
||||||
|
|
||||||
|
Intent intent = getIntent();
|
||||||
|
socketPath = intent.getStringExtra("socket");
|
||||||
|
|
||||||
|
new SocketManager().execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateUI() throws Throwable {
|
||||||
|
setContentView(R.layout.activity_request);
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.timeout, android.R.layout.simple_spinner_item);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
timeout.setAdapter(adapter);
|
||||||
|
appIcon.setImageDrawable(info.applicationInfo.loadIcon(pm));
|
||||||
|
appNameView.setText(appName);
|
||||||
|
packageNameView.setText(packageName);
|
||||||
|
|
||||||
|
grant_btn.setOnClickListener(v -> handleAction(true, timeoutList[timeout.getSelectedItemPosition()]));
|
||||||
|
deny_btn.setOnClickListener(v -> handleAction(false, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAction(boolean action, int timeout) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
socket.getOutputStream().write((action ? "socket:ALLOW" : "socket:DENY").getBytes());
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
|
||||||
|
if (timeout >= 0) {
|
||||||
|
Policy policy = new Policy();
|
||||||
|
policy.uid = uid;
|
||||||
|
policy.packageName = packageName;
|
||||||
|
policy.appName = appName;
|
||||||
|
policy.until = (timeout == 0) ? 0 : System.currentTimeMillis() + timeout * 60 * 1000;
|
||||||
|
policy.policy = action ? 2 : 1;
|
||||||
|
policy.logging = true;
|
||||||
|
policy.notification = true;
|
||||||
|
new SuDatabaseHelper(this).addPolicy(policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SocketManager extends AsyncTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
try{
|
||||||
|
socket = new LocalSocket();
|
||||||
|
socket.connect(new LocalSocketAddress(socketPath, LocalSocketAddress.Namespace.FILESYSTEM));
|
||||||
|
|
||||||
|
DataInputStream is = new DataInputStream(socket.getInputStream());
|
||||||
|
|
||||||
|
ContentValues payload = new ContentValues();
|
||||||
|
|
||||||
|
for (int i = 0; i < SU_PROTOCOL_PARAM_MAX; i++) {
|
||||||
|
|
||||||
|
int nameLen = is.readInt();
|
||||||
|
if (nameLen > SU_PROTOCOL_NAME_MAX)
|
||||||
|
throw new IllegalArgumentException("name length too long: " + nameLen);
|
||||||
|
|
||||||
|
byte[] nameBytes = new byte[nameLen];
|
||||||
|
is.readFully(nameBytes);
|
||||||
|
|
||||||
|
String name = new String(nameBytes);
|
||||||
|
|
||||||
|
int dataLen = is.readInt();
|
||||||
|
if (dataLen > SU_PROTOCOL_VALUE_MAX)
|
||||||
|
throw new IllegalArgumentException(name + " data length too long: " + dataLen);
|
||||||
|
|
||||||
|
byte[] dataBytes = new byte[dataLen];
|
||||||
|
is.readFully(dataBytes);
|
||||||
|
|
||||||
|
String data = new String(dataBytes);
|
||||||
|
|
||||||
|
payload.put(name, data);
|
||||||
|
|
||||||
|
if ("eof".equals(name))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uid = payload.getAsInteger("uid");
|
||||||
|
|
||||||
|
}catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
try {
|
||||||
|
if (!result) throw new Throwable();
|
||||||
|
String[] pkgs = pm.getPackagesForUid(uid);
|
||||||
|
if (pkgs != null && pkgs.length > 0) {
|
||||||
|
info = pm.getPackageInfo(pkgs[0], 0);
|
||||||
|
packageName = pkgs[0];
|
||||||
|
appName = info.applicationInfo.loadLabel(pm).toString();
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new Throwable();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
handleAction(false, -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
app/src/main/res/drawable/ic_su_warning.xml
Normal file
9
app/src/main/res/drawable/ic_su_warning.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="18dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFd32f2f"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
|
||||||
|
</vector>
|
104
app/src/main/res/layout/activity_request.xml
Normal file
104
app/src/main/res/layout/activity_request.xml
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:context="com.topjohnwu.playground.superuser.RequestActivity"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_width="375dp"
|
||||||
|
android:layout_gravity="center">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="@string/su_request_title"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
|
android:id="@+id/request_title"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginTop="20dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginBottom="10dp"
|
||||||
|
android:paddingStart="30dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
app:srcCompat="@mipmap/ic_launcher"
|
||||||
|
android:id="@+id/app_icon"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_marginEnd="15dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_marginStart="5dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_weight="5">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:id="@+id/app_name" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:id="@+id/package_name" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/timeout"
|
||||||
|
android:layout_margin="15dp"
|
||||||
|
android:layout_gravity="center_horizontal" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/warning"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:drawableStart="@drawable/ic_su_warning"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:text="@string/su_warning"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:drawablePadding="10dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="?android:buttonBarStyle"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:paddingLeft="30dp"
|
||||||
|
android:paddingRight="30dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="?android:buttonBarButtonStyle"
|
||||||
|
android:text="@string/deny"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/deny_btn"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="?android:buttonBarButtonStyle"
|
||||||
|
android:text="@string/grant"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/grant_btn"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -8,4 +8,12 @@
|
|||||||
<item>@string/theme_default_value</item>
|
<item>@string/theme_default_value</item>
|
||||||
<item>@string/theme_dark_value</item>
|
<item>@string/theme_dark_value</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="timeout">
|
||||||
|
<item>@string/forever</item>
|
||||||
|
<item>@string/once</item>
|
||||||
|
<item>@string/tenmin</item>
|
||||||
|
<item>@string/twentymin</item>
|
||||||
|
<item>@string/thirtymin</item>
|
||||||
|
<item>@string/sixtymin</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
@ -20,4 +20,6 @@
|
|||||||
<color name="dh_icons">#dedede</color>
|
<color name="dh_icons">#dedede</color>
|
||||||
<color name="dh_divider">#313131</color>
|
<color name="dh_divider">#313131</color>
|
||||||
|
|
||||||
|
<color name="su_request_background">#e0e0e0</color>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -143,5 +143,19 @@
|
|||||||
<string name="theme_default_value" translatable="false">default</string>
|
<string name="theme_default_value" translatable="false">default</string>
|
||||||
<string name="theme_dark_value" translatable="false">dark</string>
|
<string name="theme_dark_value" translatable="false">dark</string>
|
||||||
|
|
||||||
|
<!--Superuser-->
|
||||||
|
<string name="su_request_title">Superuser Request</string>
|
||||||
|
<string name="deny">Deny</string>
|
||||||
|
<string name="grant">Grant</string>
|
||||||
|
<string name="su_warning">Grants full access to your device.\nDeny if you\'re not sure!</string>
|
||||||
|
<string name="forever">Forever</string>
|
||||||
|
<string name="once">Once</string>
|
||||||
|
<string name="tenmin">10 min</string>
|
||||||
|
<string name="twentymin">20 min</string>
|
||||||
|
<string name="thirtymin">30 min</string>
|
||||||
|
<string name="sixtymin">60 min</string>
|
||||||
|
<string name="su_allow_toast">%1$s is granted Superuser permissions</string>
|
||||||
|
<string name="su_deny_toast">%1$s is denied Superuser permissions</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -121,4 +121,9 @@
|
|||||||
<item name="android:windowTranslucentNavigation">true</item>
|
<item name="android:windowTranslucentNavigation">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="SuRequest" parent="Theme.AppCompat.Light.Dialog">
|
||||||
|
<item name="android:windowCloseOnTouchOutside">false</item>
|
||||||
|
<item name="android:colorBackground">@color/su_request_background</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user