From 356065d1eea1dae90c1d56a527681006f7b06baf Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Mon, 24 Jul 2017 00:34:34 +0800 Subject: [PATCH] Rewrite SuLogAdapter --- app/build.gradle | 1 - .../com/topjohnwu/magisk/SuLogFragment.java | 11 +- .../magisk/adapters/SectionedAdapter.java | 15 ++ .../magisk/adapters/SuLogAdapter.java | 229 +++++++----------- .../magisk/database/SuDatabaseHelper.java | 49 ++-- .../magisk/superuser/SuLogEntry.java | 45 +--- 6 files changed, 144 insertions(+), 206 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 42d7c6180..c1b31bed1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,7 +53,6 @@ dependencies { implementation 'com.android.support:design:26.0.0-beta2' implementation 'com.android.support:support-v4:26.0.0-beta2' implementation 'com.jakewharton:butterknife:8.7.0' - implementation 'com.thoughtbot:expandablerecyclerview:1.4' implementation 'us.feras.mdv:markdownview:1.1.0' implementation 'org.bouncycastle:bcprov-jdk15on:1.57' implementation 'org.bouncycastle:bcpkix-jdk15on:1.57' diff --git a/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java b/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java index d060ba906..7f3e8e91b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java +++ b/app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java @@ -13,9 +13,6 @@ import android.widget.TextView; import com.topjohnwu.magisk.adapters.SuLogAdapter; import com.topjohnwu.magisk.components.Fragment; -import com.topjohnwu.magisk.superuser.SuLogEntry; - -import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; @@ -28,6 +25,7 @@ public class SuLogFragment extends Fragment { private Unbinder unbinder; private MagiskManager magiskManager; + private SuLogAdapter adapter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -48,6 +46,8 @@ public class SuLogFragment extends Fragment { View v = inflater.inflate(R.layout.fragment_su_log, container, false); unbinder = ButterKnife.bind(this, v); magiskManager = getApplication(); + adapter = new SuLogAdapter(magiskManager.suDB); + recyclerView.setAdapter(adapter); updateList(); @@ -55,13 +55,12 @@ public class SuLogFragment extends Fragment { } private void updateList() { - List logs = magiskManager.suDB.getLogList(); + adapter.notifyDBChanged(); - if (logs.size() == 0) { + if (adapter.getSectionCount() == 0) { emptyRv.setVisibility(View.VISIBLE); recyclerView.setVisibility(View.GONE); } else { - recyclerView.setAdapter(new SuLogAdapter(logs).getAdapter()); emptyRv.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java b/app/src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java index c73ff5f2f..cbfab0698 100644 --- a/app/src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java +++ b/app/src/main/java/com/topjohnwu/magisk/adapters/SectionedAdapter.java @@ -48,6 +48,21 @@ public abstract class SectionedAdapter { - private ExpandableAdapter adapter; - private Set expandList = new HashSet<>(); + private List> logEntryList; + private Set itemExpanded, sectionExpanded; + private SuDatabaseHelper suDB; + private Cursor suLogCursor = null; - public SuLogAdapter(List list) { + public SuLogAdapter(SuDatabaseHelper db) { + suDB = db; + logEntryList = Collections.emptyList(); + sectionExpanded = new HashSet<>(); + itemExpanded = new HashSet<>(); + } - // Separate the logs with date - Map> logEntryMap = new LinkedHashMap<>(); - List group; - for (SuLogEntry log : list) { - String date = log.getDateString(); - group = logEntryMap.get(date); - if (group == null) { - group = new ArrayList<>(); - logEntryMap.put(date, group); + @Override + public int getSectionCount() { + return logEntryList.size(); + } + + @Override + public int getItemCount(int section) { + return sectionExpanded.contains(section) ? logEntryList.get(section).size() : 0; + } + + @Override + public SectionHolder onCreateSectionViewHolder(ViewGroup parent) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false); + return new SectionHolder(v); + } + + @Override + public LogViewHolder onCreateItemViewHolder(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 onBindSectionViewHolder(SectionHolder holder, int section) { + suLogCursor.moveToPosition(logEntryList.get(section).get(0)); + SuLogEntry entry = new SuLogEntry(suLogCursor); + holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0); + holder.itemView.setOnClickListener(v -> { + RotateAnimation rotate; + if (sectionExpanded.contains(section)) { + holder.arrow.setRotation(0); + rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + sectionExpanded.remove(section); + notifyItemRangeRemoved(getItemPosition(section, 0), logEntryList.get(section).size()); + } else { + rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); + sectionExpanded.add(section); + notifyItemRangeChanged(getItemPosition(section, 0), logEntryList.get(section).size()); } - group.add(log); - } - - // Then format them into expandable groups - List logEntryGroups = new ArrayList<>(); - for (Map.Entry> entry : logEntryMap.entrySet()) { - logEntryGroups.add(new LogGroup(entry.getKey(), entry.getValue())); - } - adapter = new ExpandableAdapter(logEntryGroups); - + rotate.setDuration(300); + rotate.setFillAfter(true); + holder.arrow.setAnimation(rotate); + }); + holder.date.setText(entry.getDateString()); } - 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.getExpanded()) { - holder.collapse(); - expandList.remove(logEntry); - } else { - holder.expand(); - expandList.add(logEntry); - } - }); - holder.appName.setText(logEntry.appName); - holder.action.setText(context.getString(logEntry.action ? R.string.grant : 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()); - if (isGroupExpanded(flatPosition)) { + @Override + public void onBindItemViewHolder(LogViewHolder holder, int section, int position) { + int sqlPosition = logEntryList.get(section).get(position); + suLogCursor.moveToPosition(sqlPosition); + SuLogEntry entry = new SuLogEntry(suLogCursor); + holder.setExpanded(itemExpanded.contains(sqlPosition)); + holder.itemView.setOnClickListener(view -> { + if (holder.mExpanded) { + holder.collapse(); + itemExpanded.remove(sqlPosition); + } else { holder.expand(); + itemExpanded.add(sqlPosition); } - } + }); + holder.appName.setText(entry.appName); + holder.action.setText(entry.action ? R.string.grant : R.string.deny); + holder.command.setText(entry.command); + holder.fromPid.setText(String.valueOf(entry.fromPid)); + holder.toUid.setText(String.valueOf(entry.toUid)); + holder.time.setText(entry.getTimeString()); } - private class LogGroup extends ExpandableGroup { - LogGroup(String title, List items) { - super(title, items); - } + public void notifyDBChanged() { + if (suLogCursor != null) + suLogCursor.close(); + suLogCursor = suDB.getLogCursor(); + logEntryList = suDB.getLogStructure(); + itemExpanded.clear(); + sectionExpanded.clear(); + sectionExpanded.add(0); + notifyDataSetChanged(); } - static class LogGroupViewHolder extends GroupViewHolder { + static class SectionHolder extends RecyclerView.ViewHolder { @BindView(R.id.date) TextView date; @BindView(R.id.arrow) ImageView arrow; - public LogGroupViewHolder(View itemView) { + SectionHolder(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); - } } - // Wrapper class - static class LogViewHolder extends ChildViewHolder { - - private InternalViewHolder expandableViewHolder; + static class LogViewHolder extends ExpandableViewHolder { @BindView(R.id.app_name) TextView appName; @BindView(R.id.action) TextView action; @@ -162,36 +140,11 @@ public class SuLogAdapter { LogViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); - expandableViewHolder = new InternalViewHolder(itemView); } - private class InternalViewHolder extends ExpandableViewHolder { - - InternalViewHolder(View itemView) { - super(itemView); - } - - @Override - public void setExpandLayout(View itemView) { - expandLayout = itemView.findViewById(R.id.expand_layout); - } - } - - private boolean getExpanded() { - return expandableViewHolder.mExpanded; - } - - private void setExpanded(boolean expanded) { - expandableViewHolder.setExpanded(expanded); - } - - private void expand() { - expandableViewHolder.expand(); - } - - private void collapse() { - expandableViewHolder.collapse(); + @Override + public void setExpandLayout(View itemView) { + expandLayout = itemView.findViewById(R.id.expand_layout); } } - } diff --git a/app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java b/app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java index 980f46134..6931f75f6 100644 --- a/app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java +++ b/app/src/main/java/com/topjohnwu/magisk/database/SuDatabaseHelper.java @@ -4,8 +4,10 @@ import android.content.ContentValues; import android.content.Context; import android.content.pm.PackageManager; import android.database.Cursor; +import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; +import android.text.TextUtils; import com.topjohnwu.magisk.MagiskManager; import com.topjohnwu.magisk.superuser.Policy; @@ -13,8 +15,10 @@ import com.topjohnwu.magisk.superuser.SuLogEntry; import com.topjohnwu.magisk.utils.Utils; import java.io.File; +import java.text.DateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; public class SuDatabaseHelper extends SQLiteOpenHelper { @@ -179,31 +183,42 @@ public class SuDatabaseHelper extends SQLiteOpenHelper { } } - private List getLogList(SQLiteDatabase db, String selection) { - try (Cursor c = db.query(LOG_TABLE, null, selection, null, null, null, "time DESC")) { - List ret = new ArrayList<>(c.getCount()); + public List> getLogStructure() { + try (Cursor c = mDb.query(LOG_TABLE, new String[] { "time" }, null, null, null, null, "time DESC")) { + List> ret = new ArrayList<>(); + List list = null; + String dateString = null, newString; while (c.moveToNext()) { - ret.add(new SuLogEntry(c)); + Date date = new Date(c.getLong(c.getColumnIndex("time")) * 1000); + newString = DateFormat.getDateInstance(DateFormat.MEDIUM, MagiskManager.locale).format(date); + if (!TextUtils.equals(dateString, newString)) { + dateString = newString; + list = new ArrayList<>(); + ret.add(list); + } + list.add(c.getPosition()); } return ret; } } + public Cursor getLogCursor() { + return getLogCursor(mDb); + } + + public Cursor getLogCursor(SQLiteDatabase db) { + return db.query(LOG_TABLE, null, null, null, null, null, "time DESC"); + } + private void migrateLegacyLogList(File oldDB, SQLiteDatabase newDB) { - SQLiteDatabase oldDb = SQLiteDatabase.openDatabase(oldDB.getPath(), null, SQLiteDatabase.OPEN_READWRITE); - List logs = getLogList(oldDb, null); - for (SuLogEntry log : logs) { - newDB.insert(LOG_TABLE, null, log.getContentValues()); + try (SQLiteDatabase oldDb = SQLiteDatabase.openDatabase(oldDB.getPath(), null, SQLiteDatabase.OPEN_READWRITE); + Cursor c = getLogCursor(oldDb)) { + while (c.moveToNext()) { + ContentValues values = new ContentValues(); + DatabaseUtils.cursorRowToContentValues(c, values); + newDB.insert(LOG_TABLE, null, values); + } } - oldDb.close(); - } - - public List getLogList() { - return getLogList(null); - } - - public List getLogList(String selection) { - return getLogList(mDb, selection); } public void addLog(SuLogEntry log) { 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 f8a129f45..722b00822 100644 --- a/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogEntry.java +++ b/app/src/main/java/com/topjohnwu/magisk/superuser/SuLogEntry.java @@ -2,8 +2,6 @@ package com.topjohnwu.magisk.superuser; import android.content.ContentValues; import android.database.Cursor; -import android.os.Parcel; -import android.os.Parcelable; import com.topjohnwu.magisk.MagiskManager; @@ -11,7 +9,7 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; -public class SuLogEntry implements Parcelable { +public class SuLogEntry { public int fromUid, toUid, fromPid; public String packageName, appName, command; @@ -55,45 +53,4 @@ public class SuLogEntry implements Parcelable { public String getTimeString() { return new SimpleDateFormat("h:mm a", MagiskManager.locale).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()); - } }