From cbb32f82eb901c30f695a603b06e533f1c859b3a Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sat, 28 Jan 2017 01:10:50 +0800 Subject: [PATCH] Add Superuser logging UI --- app/build.gradle | 5 +- .../java/com/topjohnwu/magisk/Global.java | 1 - .../com/topjohnwu/magisk/LogFragment.java | 42 +--- .../com/topjohnwu/magisk/MainActivity.java | 5 + .../com/topjohnwu/magisk/ModulesFragment.java | 6 +- .../com/topjohnwu/magisk/ReposFragment.java | 6 +- .../topjohnwu/magisk/SettingsActivity.java | 1 - .../com/topjohnwu/magisk/StatusFragment.java | 2 - .../com/topjohnwu/magisk/SuLogFragment.java | 38 ++- .../topjohnwu/magisk/SuperuserFragment.java | 10 +- .../magisk/adapters/PolicyAdapter.java | 3 + .../magisk/adapters/SuLogAdapter.java | 238 ++++++++++++++++++ .../magisk/adapters/TabFragmentAdapter.java | 40 +++ .../magisk/superuser/SuLogEntry.java | 66 ++++- .../magisk/superuser/SuReceiver.java | 8 +- .../com/topjohnwu/magisk/utils/Utils.java | 2 - app/src/main/res/drawable/ic_arrow.xml | 13 + app/src/main/res/layout/fragment_log.xml | 1 + app/src/main/res/layout/list_item_sulog.xml | 145 +++++++++++ .../main/res/layout/list_item_sulog_group.xml | 23 ++ app/src/main/res/values-de/strings.xml | 2 +- app/src/main/res/values-pl/strings.xml | 2 +- app/src/main/res/values/strings.xml | 6 +- 23 files changed, 594 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/com/topjohnwu/magisk/adapters/SuLogAdapter.java create mode 100644 app/src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java create mode 100644 app/src/main/res/drawable/ic_arrow.xml create mode 100644 app/src/main/res/layout/list_item_sulog.xml create mode 100644 app/src/main/res/layout/list_item_sulog_group.xml diff --git a/app/build.gradle b/app/build.gradle index 76d32e2ca..2c101b0ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -55,13 +55,14 @@ dependencies { compile 'com.android.support:design:25.1.0' compile 'com.android.support:support-v4:25.1.0' compile 'com.android.support:support-v13:25.1.0' - compile 'com.jakewharton:butterknife:8.4.0' + compile 'com.jakewharton:butterknife:8.5.1' compile 'com.google.code.gson:gson:2.8.0' compile 'com.github.clans:fab:1.6.4' + compile 'com.thoughtbot:expandablerecyclerview:1.4' compile 'com.madgag.spongycastle:core:1.54.0.0' compile 'com.madgag.spongycastle:prov:1.54.0.0' compile 'com.madgag.spongycastle:pkix:1.54.0.0' compile 'com.madgag.spongycastle:pg:1.54.0.0' compile 'com.google.android.gms:play-services-safetynet:9.0.1' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' + annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' } diff --git a/app/src/main/java/com/topjohnwu/magisk/Global.java b/app/src/main/java/com/topjohnwu/magisk/Global.java index 6440f5251..1617a7bf1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/Global.java +++ b/app/src/main/java/com/topjohnwu/magisk/Global.java @@ -3,7 +3,6 @@ package com.topjohnwu.magisk; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.text.TextUtils; import com.topjohnwu.magisk.module.Module; import com.topjohnwu.magisk.module.Repo; diff --git a/app/src/main/java/com/topjohnwu/magisk/LogFragment.java b/app/src/main/java/com/topjohnwu/magisk/LogFragment.java index 1aac9c53d..456555298 100644 --- a/app/src/main/java/com/topjohnwu/magisk/LogFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/LogFragment.java @@ -2,17 +2,14 @@ package com.topjohnwu.magisk; import android.app.Fragment; -import android.app.FragmentManager; import android.os.Bundle; import android.support.design.widget.TabLayout; -import android.support.v13.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import java.util.ArrayList; -import java.util.List; +import com.topjohnwu.magisk.adapters.TabFragmentAdapter; import butterknife.BindView; import butterknife.ButterKnife; @@ -25,7 +22,6 @@ public class LogFragment extends Fragment { @BindView(R.id.container) ViewPager viewPager; @BindView(R.id.tab) TabLayout tab; - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -33,7 +29,7 @@ public class LogFragment extends Fragment { View v = inflater.inflate(R.layout.fragment_log, container, false); unbinder = ButterKnife.bind(this, v); - ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager()); + TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager()); adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk)); @@ -54,38 +50,4 @@ public class LogFragment extends Fragment { unbinder.unbind(); } - private class ViewPagerAdapter extends FragmentPagerAdapter { - - List fragmentList; - List titleList; - - public ViewPagerAdapter(FragmentManager fm) { - super(fm); - fragmentList = new ArrayList<>(); - titleList = new ArrayList<>(); - } - - @Override - public Fragment getItem(int position) { - return fragmentList.get(position); - } - - @Override - public int getCount() { - return fragmentList.size(); - } - - @Override - public CharSequence getPageTitle(int position) { - return titleList.get(position); - } - - public void addTab(Fragment fragment, String title) { - fragmentList.add(fragment); - titleList.add(title); - } - } - - - } diff --git a/app/src/main/java/com/topjohnwu/magisk/MainActivity.java b/app/src/main/java/com/topjohnwu/magisk/MainActivity.java index b0cf4e9dd..e6ba92088 100644 --- a/app/src/main/java/com/topjohnwu/magisk/MainActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/MainActivity.java @@ -41,6 +41,7 @@ public class MainActivity extends AppCompatActivity @BindView(R.id.nav_view) public NavigationView navigationView; private int mSelectedId = R.id.status; + private float toolbarElevation; @Override protected void onCreate(final Bundle savedInstanceState) { @@ -74,6 +75,8 @@ public class MainActivity extends AppCompatActivity } }; + toolbarElevation = toolbar.getElevation(); + drawer.addDrawerListener(toggle); toggle.syncState(); @@ -158,6 +161,7 @@ public class MainActivity extends AppCompatActivity } public void navigate(int itemId, boolean now) { + toolbar.setElevation(toolbarElevation); switch (itemId) { case R.id.status: displayFragment(new StatusFragment(), "status", now); @@ -179,6 +183,7 @@ public class MainActivity extends AppCompatActivity break; case R.id.log: displayFragment(new LogFragment(), "log", now); + toolbar.setElevation(0); break; case R.id.settings: startActivity(new Intent(this, SettingsActivity.class)); diff --git a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java index 116543668..0c5a79c50 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/ModulesFragment.java @@ -35,7 +35,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi private Unbinder unbinder; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; @BindView(R.id.recyclerView) RecyclerView recyclerView; - @BindView(R.id.empty_rv) TextView emptyTv; + @BindView(R.id.empty_rv) TextView emptyRv; @BindView(R.id.fab) FloatingActionButton fabio; private List listModules = new ArrayList<>(); @@ -114,10 +114,10 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi private void updateUI() { ModuleHelper.getModuleList(listModules); if (listModules.size() == 0) { - emptyTv.setVisibility(View.VISIBLE); + emptyRv.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); } else { - emptyTv.setVisibility(View.GONE); + emptyRv.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); recyclerView.setAdapter(new ModulesAdapter(listModules)); } diff --git a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java index da4be6256..3dcfea2e0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/ReposFragment.java @@ -33,7 +33,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList private Unbinder unbinder; @BindView(R.id.recyclerView) RecyclerView recyclerView; - @BindView(R.id.empty_rv) TextView emptyTv; + @BindView(R.id.empty_rv) TextView emptyRv; @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; private List mUpdateRepos = new ArrayList<>(); @@ -138,7 +138,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList private void updateUI() { if (fUpdateRepos.size() + fInstalledRepos.size() + fOthersRepos.size() == 0) { - emptyTv.setVisibility(View.VISIBLE); + emptyRv.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); } else { List sections = new ArrayList<>(); @@ -153,7 +153,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList } SimpleSectionedRecyclerViewAdapter.Section[] array = sections.toArray(new SimpleSectionedRecyclerViewAdapter.Section[sections.size()]); mSectionedAdapter.setSections(array); - emptyTv.setVisibility(View.GONE); + emptyRv.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); } mSwipeRefreshLayout.setRefreshing(false); diff --git a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java index 00d5da9c2..441003d8e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java +++ b/app/src/main/java/com/topjohnwu/magisk/SettingsActivity.java @@ -3,7 +3,6 @@ package com.topjohnwu.magisk; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.CheckBoxPreference; -import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; diff --git a/app/src/main/java/com/topjohnwu/magisk/StatusFragment.java b/app/src/main/java/com/topjohnwu/magisk/StatusFragment.java index d4cdfdf9c..303321d6d 100644 --- a/app/src/main/java/com/topjohnwu/magisk/StatusFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/StatusFragment.java @@ -21,8 +21,6 @@ import com.topjohnwu.magisk.utils.Logger; import com.topjohnwu.magisk.utils.Shell; import com.topjohnwu.magisk.utils.Utils; -import java.util.List; - import butterknife.BindColor; import butterknife.BindView; import butterknife.ButterKnife; diff --git a/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java b/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java index 4eec98e1b..7d0366ae4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java @@ -3,18 +3,54 @@ package com.topjohnwu.magisk; import android.app.Fragment; import android.os.Bundle; +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.SuLogAdapter; +import com.topjohnwu.magisk.superuser.SuLogDatabaseHelper; +import com.topjohnwu.magisk.superuser.SuLogEntry; + +import java.util.List; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.Unbinder; public class SuLogFragment extends Fragment { + @BindView(R.id.empty_rv) TextView emptyRv; + @BindView(R.id.recyclerView) RecyclerView recyclerView; + + private Unbinder unbinder; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment - return inflater.inflate(R.layout.fragment_su_log, container, false); + View v = inflater.inflate(R.layout.fragment_su_log, container, false); + unbinder = ButterKnife.bind(this, v); + + SuLogDatabaseHelper dbHelper = new SuLogDatabaseHelper(getActivity()); + List logs = dbHelper.getLogList(); + + if (logs.size() == 0) { + emptyRv.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); + } else { + recyclerView.setAdapter(new SuLogAdapter(logs).getAdapter()); + emptyRv.setVisibility(View.GONE); + recyclerView.setVisibility(View.VISIBLE); + } + + return v; } + @Override + public void onDestroyView() { + super.onDestroyView(); + unbinder.unbind(); + } } diff --git a/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java b/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java index 710217808..16ab3d380 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/SuperuserFragment.java @@ -25,7 +25,7 @@ public class SuperuserFragment extends Fragment { private Unbinder unbinder; @BindView(R.id.recyclerView) RecyclerView recyclerView; - @BindView(R.id.empty_rv) TextView emptyTv; + @BindView(R.id.empty_rv) TextView emptyRv; @Nullable @Override @@ -38,14 +38,12 @@ public class SuperuserFragment extends Fragment { SuDatabaseHelper dbHelper = new SuDatabaseHelper(getActivity()); List policyList = dbHelper.getPolicyList(pm); - PolicyAdapter adapter = new PolicyAdapter(policyList, dbHelper, pm); - if (policyList.size() == 0) { - emptyTv.setVisibility(View.VISIBLE); + emptyRv.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); } else { - recyclerView.setAdapter(adapter); - emptyTv.setVisibility(View.GONE); + recyclerView.setAdapter(new PolicyAdapter(policyList, dbHelper, pm)); + emptyRv.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java index e85496237..9e7004d6a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/adapters/PolicyAdapter.java @@ -111,6 +111,9 @@ public class PolicyAdapter extends RecyclerView.Adapter expandList = new HashSet<>(); + + public SuLogAdapter(List list) { + + // Separate the logs with date + LinkedHashMap> logEntryMap = new LinkedHashMap<>(); + ArrayList group; + for (SuLogEntry log : list) { + String date = log.getDateString(); + group = logEntryMap.get(date); + if (group == null) { + group = new ArrayList<>(); + logEntryMap.put(date, group); + } + group.add(log); + } + + // Then format them into expandable groups + ArrayList logEntryGroups = new ArrayList<>(); + for (HashMap.Entry> entry : logEntryMap.entrySet()) { + logEntryGroups.add(new LogGroup(entry.getKey(), entry.getValue())); + } + adapter = new ExpandableAdapter(logEntryGroups); + + } + + public RecyclerView.Adapter getAdapter() { + return adapter; + } + + private class ExpandableAdapter + extends ExpandableRecyclerViewAdapter { + + ExpandableAdapter(List groups) { + super(groups); + expandableList.expandedGroupIndexes[0] = true; + } + + @Override + public LogGroupViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false); + return new LogGroupViewHolder(v); + } + + @Override + public LogViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog, parent, false); + return new LogViewHolder(v); + } + + @Override + public void onBindChildViewHolder(LogViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) { + Context context = holder.itemView.getContext(); + SuLogEntry logEntry = (SuLogEntry) group.getItems().get(childIndex); + holder.setExpanded(expandList.contains(logEntry)); + holder.itemView.setOnClickListener(view -> { + if (holder.mExpanded) { + holder.collapse(); + expandList.remove(logEntry); + } else { + holder.expand(); + expandList.add(logEntry); + } + }); + holder.appName.setText(logEntry.appName); + holder.action.setText(logEntry.action ? context.getString(R.string.grant) : context.getString(R.string.deny, "")); + holder.command.setText(logEntry.command); + holder.fromPid.setText(String.valueOf(logEntry.fromPid)); + holder.toUid.setText(String.valueOf(logEntry.toUid)); + holder.time.setText(logEntry.getTimeString()); + } + + @Override + public void onBindGroupViewHolder(LogGroupViewHolder holder, int flatPosition, ExpandableGroup group) { + holder.date.setText(group.getTitle()); + } + } + + private class LogGroup extends ExpandableGroup { + LogGroup(String title, List items) { + super(title, items); + } + } + + static class LogGroupViewHolder extends GroupViewHolder { + + @BindView(R.id.date) TextView date; + @BindView(R.id.arrow) ImageView arrow; + + public LogGroupViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + + @Override + public void expand() { + RotateAnimation rotate = + new RotateAnimation(360, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + rotate.setDuration(300); + rotate.setFillAfter(true); + arrow.setAnimation(rotate); + } + + @Override + public void collapse() { + RotateAnimation rotate = + new RotateAnimation(180, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + rotate.setDuration(300); + rotate.setFillAfter(true); + arrow.setAnimation(rotate); + } + } + + static class LogViewHolder extends ChildViewHolder { + + @BindView(R.id.app_name) TextView appName; + @BindView(R.id.action) TextView action; + @BindView(R.id.time) TextView time; + @BindView(R.id.fromPid) TextView fromPid; + @BindView(R.id.toUid) TextView toUid; + @BindView(R.id.command) TextView command; + @BindView(R.id.expand_layout) LinearLayout expandLayout; + + private ValueAnimator mAnimator; + private boolean mExpanded = false; + private static int expandHeight = 0; + + public LogViewHolder(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; + } + } + +} diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java new file mode 100644 index 000000000..fdc639034 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/adapters/TabFragmentAdapter.java @@ -0,0 +1,40 @@ +package com.topjohnwu.magisk.adapters; + +import android.app.Fragment; +import android.app.FragmentManager; +import android.support.v13.app.FragmentPagerAdapter; + +import java.util.ArrayList; +import java.util.List; + +public class TabFragmentAdapter extends FragmentPagerAdapter { + + private List fragmentList; + private List titleList; + + public TabFragmentAdapter(FragmentManager fm) { + super(fm); + fragmentList = new ArrayList<>(); + titleList = new ArrayList<>(); + } + + @Override + public Fragment getItem(int position) { + return fragmentList.get(position); + } + + @Override + public int getCount() { + return fragmentList.size(); + } + + @Override + public CharSequence getPageTitle(int position) { + return titleList.get(position); + } + + public void addTab(Fragment fragment, String title) { + fragmentList.add(fragment); + titleList.add(title); + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogEntry.java b/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogEntry.java index fe90fcd51..a0bd0427e 100644 --- a/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogEntry.java +++ b/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogEntry.java @@ -2,13 +2,20 @@ package com.topjohnwu.magisk.superuser; import android.content.ContentValues; import android.database.Cursor; +import android.os.Parcel; +import android.os.Parcelable; -public class SuLogEntry { +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +public class SuLogEntry implements Parcelable { public int fromUid, toUid, fromPid; public String packageName, appName, command; public boolean action; - public long time; + public Date date; public SuLogEntry(Policy policy) { fromUid = policy.uid; @@ -24,7 +31,7 @@ public class SuLogEntry { appName = c.getString(c.getColumnIndex("app_name")); command = c.getString(c.getColumnIndex("command")); action = c.getInt(c.getColumnIndex("action")) != 0; - time = c.getLong(c.getColumnIndex("until")); + date = new Date(c.getLong(c.getColumnIndex("time")) * 1000); } public ContentValues getContentValues() { @@ -35,8 +42,57 @@ public class SuLogEntry { values.put("from_pid", fromPid); values.put("command", command); values.put("to_uid", toUid); - values.put("action", action); - values.put("time", time); + values.put("action", action ? 1 : 0); + values.put("time", date.getTime() / 1000); return values; } + + public String getDateString() { + return DateFormat.getDateInstance(DateFormat.MEDIUM).format(date); + } + + public String getTimeString() { + return new SimpleDateFormat("h:mm a", Locale.US).format(date); + } + + + public static final Creator CREATOR = new Creator() { + @Override + public SuLogEntry createFromParcel(Parcel in) { + return new SuLogEntry(in); + } + + @Override + public SuLogEntry[] newArray(int size) { + return new SuLogEntry[size]; + } + }; + + protected SuLogEntry(Parcel in) { + fromUid = in.readInt(); + toUid = in.readInt(); + fromPid = in.readInt(); + packageName = in.readString(); + appName = in.readString(); + command = in.readString(); + action = in.readByte() != 0; + date = new Date(in.readLong()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(fromUid); + dest.writeInt(toUid); + dest.writeInt(fromPid); + dest.writeString(packageName); + dest.writeString(appName); + dest.writeString(command); + dest.writeByte((byte) (action ? 1 : 0)); + dest.writeLong(date.getTime()); + } } diff --git a/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java b/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java index 0476599d8..d242f6dfb 100644 --- a/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java +++ b/app/src/main/java/com/topjohnwu/magisk/superuser/SuReceiver.java @@ -8,6 +8,8 @@ import android.widget.Toast; import com.topjohnwu.magisk.R; +import java.util.Date; + public class SuReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -32,14 +34,18 @@ public class SuReceiver extends BroadcastReceiver { return; } + SuLogEntry log = new SuLogEntry(policy); + if (policy.notification) { String message; switch (action) { case "allow": message = context.getString(R.string.su_allow_toast, policy.appName); + log.action = true; break; case "deny": message = context.getString(R.string.su_deny_toast, policy.appName); + log.action = false; break; default: return; @@ -53,10 +59,10 @@ public class SuReceiver extends BroadcastReceiver { if (pid < 0) return; command = intent.getStringExtra("command"); if (command == null) return; - SuLogEntry log = new SuLogEntry(policy); log.toUid = toUid; log.fromPid = pid; log.command = command; + log.date = new Date(); SuLogDatabaseHelper logDbHelper = new SuLogDatabaseHelper(context); logDbHelper.addLog(log); } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java index 3848b0099..36a90e652 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java @@ -16,8 +16,6 @@ import com.topjohnwu.magisk.Global; import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.receivers.DownloadReceiver; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.File; import java.util.List; diff --git a/app/src/main/res/drawable/ic_arrow.xml b/app/src/main/res/drawable/ic_arrow.xml new file mode 100644 index 000000000..de2bcbf68 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_log.xml b/app/src/main/res/layout/fragment_log.xml index 76b375b44..370b3c64e 100644 --- a/app/src/main/res/layout/fragment_log.xml +++ b/app/src/main/res/layout/fragment_log.xml @@ -15,6 +15,7 @@ android:id="@+id/tab" app:tabPaddingEnd="20dp" app:tabPaddingStart="20dp" + android:elevation="4dp" android:visibility="gone"> diff --git a/app/src/main/res/layout/list_item_sulog.xml b/app/src/main/res/layout/list_item_sulog.xml new file mode 100644 index 000000000..dae9ec209 --- /dev/null +++ b/app/src/main/res/layout/list_item_sulog.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_item_sulog_group.xml b/app/src/main/res/layout/list_item_sulog_group.xml new file mode 100644 index 000000000..cea04576f --- /dev/null +++ b/app/src/main/res/layout/list_item_sulog_group.xml @@ -0,0 +1,23 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d98a2d8a2..b5e1b0bfa 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -138,7 +138,7 @@ Superuser-Anfrage - Gewähren + Gewähren%1$s Verweigern%1$s Gewährt vollen Zugriff auf das Gerät.\nVerweigere, wenn du dir unsicher bist! Dauerhaft diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 166199a56..e4a690a73 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -131,7 +131,7 @@ Prośba dostępu Superusera - Odmów + Odmów%1$s Przyznaj Udziela pełnego dostępu do urządzenia.\nOdmów jeśli nie jesteś pewien! Zawsze diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0422d6a25..81d3fb177 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,7 +169,9 @@ Revoke? Confirm to revoke %1$s rights? - - Hello blank fragment + + PID:\u0020 + Target UID:\u0020 + Command:\u0020