Add Superuser management UI

This commit is contained in:
topjohnwu
2017-01-26 01:13:23 +08:00
parent 3681177be4
commit 67c9e2ead6
16 changed files with 515 additions and 16 deletions

View File

@ -165,6 +165,9 @@ public class MainActivity extends AppCompatActivity
case R.id.install:
displayFragment(new InstallFragment(), "install", now);
break;
case R.id.superuser:
displayFragment(new SuperuserFragment(), "superuser", now);
break;
case R.id.modules:
displayFragment(new ModulesFragment(), "modules", now);
break;

View File

@ -1,6 +1,5 @@
package com.topjohnwu.magisk;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;

View File

@ -0,0 +1,67 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuperuserFragment extends Fragment {
private Unbinder unbinder;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyTv;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
unbinder = ButterKnife.bind(this, view);
PackageManager pm = getActivity().getPackageManager();
SuDatabaseHelper dbHelper = new SuDatabaseHelper(getActivity());
List<Policy> policyList = dbHelper.getPolicyList(pm);
PolicyAdapter adapter = new PolicyAdapter(policyList, dbHelper, pm);
if (policyList.size() == 0) {
emptyTv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
recyclerView.setAdapter(adapter);
emptyTv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
return view;
}
@Override
public void onStart() {
super.onStart();
getActivity().setTitle(getString(R.string.superuser));
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@ -0,0 +1,200 @@
package com.topjohnwu.magisk.adapters;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.pm.PackageManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
import java.util.HashSet;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
private List<Policy> policyList;
private SuDatabaseHelper dbHelper;
private PackageManager pm;
private HashSet<Policy> expandList = new HashSet<>();
public PolicyAdapter(List<Policy> list, SuDatabaseHelper db, PackageManager pm) {
policyList = list;
dbHelper = db;
this.pm = pm;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
try {
Policy policy = policyList.get(position);
holder.setExpanded(expandList.contains(policy));
holder.itemView.setOnClickListener(view -> {
if (holder.mExpanded) {
holder.collapse();
expandList.remove(policy);
} else {
holder.expand();
expandList.add(policy);
}
});
holder.appName.setText(policy.appName);
holder.packageName.setText(policy.packageName);
holder.appIcon.setImageDrawable(pm.getPackageInfo(policy.packageName, 0).applicationInfo.loadIcon(pm));
holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked && policy.policy == Policy.DENY) {
policy.policy = Policy.ALLOW;
dbHelper.addPolicy(policy);
} else if (!isChecked && policy.policy == Policy.ALLOW) {
policy.policy = Policy.DENY;
dbHelper.addPolicy(policy);
}
});
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked && !policy.notification) {
policy.notification = true;
dbHelper.addPolicy(policy);
} else if (!isChecked && policy.notification) {
policy.notification = false;
dbHelper.addPolicy(policy);
}
});
holder.loggingSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked && !policy.logging) {
policy.logging = true;
dbHelper.addPolicy(policy);
} else if (!isChecked && policy.logging) {
policy.logging = false;
dbHelper.addPolicy(policy);
}
});
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
} catch (PackageManager.NameNotFoundException e) {
policyList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, policyList.size());
onBindViewHolder(holder, position);
}
}
@Override
public int getItemCount() {
return policyList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.package_name) TextView packageName;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.master_switch) Switch masterSwitch;
@BindView(R.id.notification_switch) Switch notificationSwitch;
@BindView(R.id.logging_switch) Switch loggingSwitch;
@BindView(R.id.more_info) ImageView moreInfo;
private ValueAnimator mAnimator;
private boolean mExpanded = false;
private static int expandHeight = 0;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
expandLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (expandHeight == 0) {
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
expandLayout.measure(widthSpec, heightSpec);
expandHeight = expandLayout.getMeasuredHeight();
}
expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
expandLayout.setVisibility(View.GONE);
mAnimator = slideAnimator(0, expandHeight);
return true;
}
});
}
private void setExpanded(boolean expanded) {
mExpanded = expanded;
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = expanded ? expandHeight : 0;
expandLayout.setLayoutParams(layoutParams);
expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
}
private void expand() {
expandLayout.setVisibility(View.VISIBLE);
mAnimator.start();
mExpanded = true;
}
private void collapse() {
if (!mExpanded) return;
int finalHeight = expandLayout.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
expandLayout.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {}
@Override
public void onAnimationCancel(Animator animator) {}
@Override
public void onAnimationRepeat(Animator animator) {}
});
mAnimator.start();
mExpanded = false;
}
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(valueAnimator -> {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = value;
expandLayout.setLayoutParams(layoutParams);
});
return animator;
}
}
}

View File

@ -7,6 +7,11 @@ import android.database.Cursor;
public class Policy {
public static final int INTERACTIVE = 0;
public static final int DENY = 1;
public static final int ALLOW = 2;
public int uid, policy;
public long until;
public boolean logging, notification;

View File

@ -57,19 +57,20 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
}
public List<Policy> getPolicyList(PackageManager pm) {
List<Policy> ret = new ArrayList<>();
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)) {
try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, "app_name")) {
while (c.moveToNext()) {
policy = new Policy(c);
// Package is uninstalled
if (pm.getPackagesForUid(policy.uid) == null)
if (pm.getPackagesForUid(policy.uid) == null) {
deletePolicy(policy.uid);
else
} else {
ret.add(policy);
}
}
}
db.close();

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.superuser;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@ -11,10 +10,7 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
@ -117,7 +113,7 @@ public class SuRequestActivity extends AppCompatActivity {
policy.packageName = packageName;
policy.appName = appName;
policy.until = (timeout == 0) ? 0 : (System.currentTimeMillis() / 1000 + timeout * 60);
policy.policy = action ? 2 : 1;
policy.policy = action ? Policy.ALLOW : Policy.DENY;
policy.logging = true;
policy.notification = true;
new SuDatabaseHelper(this).addPolicy(policy);

View File

@ -8,7 +8,6 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.OpenableColumns;
import android.util.Log;
import android.widget.Toast;
import com.topjohnwu.magisk.Global;