diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index f1aeb9453..4775bcb71 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -140,10 +140,6 @@ public class KeyListFragment extends RecyclerFragment case R.id.menu_key_list_multi_delete: { long[] keyIds = getAdapter().getSelectedMasterKeyIds(); boolean hasSecret = getAdapter().isAnySecretKeySelected(); - - System.out.println(Arrays.toString(keyIds)); - System.out.println(hasSecret); - Intent intent = new Intent(getActivity(), DeleteKeyDialogActivity.class); intent.putExtra(DeleteKeyDialogActivity.EXTRA_DELETE_MASTER_KEY_IDS, keyIds); intent.putExtra(DeleteKeyDialogActivity.EXTRA_HAS_SECRET, hasSecret); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 411dac3ef..b87075b91 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -38,37 +38,35 @@ import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; +import com.tonicartos.superslim.LayoutManager; + import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogLevel; import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel; import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; +import org.sufficientlysecure.keychain.ui.adapter.NestedLogAdapter; import org.sufficientlysecure.keychain.ui.dialog.ShareLogDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerFragment; import java.io.IOException; import java.io.OutputStream; -public class LogDisplayFragment extends ListFragment implements OnItemClickListener { - - LogAdapter mAdapter; - - OperationResult mResult; +public class LogDisplayFragment extends RecyclerFragment + implements NestedLogAdapter.LogActionListener { + private OperationResult mResult; public static final String EXTRA_RESULT = "log"; - protected int mTextColor; - private Uri mLogTempFile; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mTextColor = FormattingUtils.getColorFromAttr(getActivity(), R.attr.colorText); - setHasOptionsMenu(true); } @@ -93,14 +91,11 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe return; } - mAdapter = new LogAdapter(getActivity(), mResult.getLog()); - setListAdapter(mAdapter); - - getListView().setOnItemClickListener(this); - - getListView().setFastScrollEnabled(true); - getListView().setDividerHeight(0); + NestedLogAdapter adapter = new NestedLogAdapter(getContext(), mResult.getLog()); + adapter.setListener(this); + setAdapter(adapter); + setLayoutManager(new LayoutManager(getContext())); } @Override @@ -130,7 +125,6 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe } private void shareLog() { - Activity activity = getActivity(); if (activity == null) { return; @@ -144,7 +138,7 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe try { OutputStream outputStream = activity.getContentResolver().openOutputStream(mLogTempFile); outputStream.write(log.getBytes()); - } catch (IOException e) { + } catch (IOException | NullPointerException e) { Notify.create(activity, R.string.error_log_share_internal, Style.ERROR).show(); return; } @@ -153,129 +147,12 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe ShareLogDialogFragment shareLogDialog = ShareLogDialogFragment.newInstance(mLogTempFile); shareLogDialog.show(getActivity().getSupportFragmentManager(), "shareLogDialog"); - } @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - LogEntryParcel parcel = mAdapter.getItem(position); - if ( ! (parcel instanceof SubLogEntryParcel)) { - return; - } - Intent intent = new Intent( - getActivity(), LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, ((SubLogEntryParcel) parcel).getSubResult()); + public void onSubEntryClicked(SubLogEntryParcel subLogEntryParcel) { + Intent intent = new Intent(getActivity(), LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, subLogEntryParcel.getSubResult()); startActivity(intent); } - - private class LogAdapter extends ArrayAdapter { - - private LayoutInflater mInflater; - private int dipFactor; - - public LogAdapter(Context context, OperationResult.OperationLog log) { - super(context, R.layout.log_display_item, log.toList()); - mInflater = LayoutInflater.from(getContext()); - dipFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - (float) 8, getResources().getDisplayMetrics()); - } - - private class ItemHolder { - final View mSecond; - final TextView mText, mSecondText; - final ImageView mImg, mSecondImg, mSub; - public ItemHolder(TextView text, ImageView image, ImageView sub, View second, TextView secondText, ImageView secondImg) { - mText = text; - mImg = image; - mSub = sub; - mSecond = second; - mSecondText = secondText; - mSecondImg = secondImg; - } - } - // Check if convertView.setPadding is redundant - @Override - public View getView(int position, View convertView, ViewGroup parent) { - LogEntryParcel entry = getItem(position); - ItemHolder ih; - if (convertView == null) { - convertView = mInflater.inflate(R.layout.log_display_item, parent, false); - ih = new ItemHolder( - (TextView) convertView.findViewById(R.id.log_text), - (ImageView) convertView.findViewById(R.id.log_img), - (ImageView) convertView.findViewById(R.id.log_sub), - convertView.findViewById(R.id.log_second), - (TextView) convertView.findViewById(R.id.log_second_text), - (ImageView) convertView.findViewById(R.id.log_second_img) - ); - convertView.setTag(ih); - } else { - ih = (ItemHolder) convertView.getTag(); - } - - if (entry instanceof SubLogEntryParcel) { - ih.mSub.setVisibility(View.VISIBLE); - convertView.setClickable(false); - convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); - - OperationResult result = ((SubLogEntryParcel) entry).getSubResult(); - LogEntryParcel subEntry = result.getLog().getLast(); - if (subEntry != null) { - ih.mSecond.setVisibility(View.VISIBLE); - // special case: first parameter may be a quantity - if (subEntry.mParameters != null && subEntry.mParameters.length > 0 - && subEntry.mParameters[0] instanceof Integer) { - ih.mSecondText.setText(getResources().getQuantityString(subEntry.mType.getMsgId(), - (Integer) subEntry.mParameters[0], - subEntry.mParameters)); - } else { - ih.mSecondText.setText(getResources().getString(subEntry.mType.getMsgId(), - subEntry.mParameters)); - } - ih.mSecondText.setTextColor(subEntry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : mTextColor); - switch (subEntry.mType.mLevel) { - case DEBUG: ih.mSecondImg.setBackgroundColor(Color.GRAY); break; - case INFO: ih.mSecondImg.setBackgroundColor(mTextColor); break; - case WARN: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break; - case ERROR: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; - case START: ih.mSecondImg.setBackgroundColor(mTextColor); break; - case OK: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break; - case CANCELLED: ih.mSecondImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; - } - } else { - ih.mSecond.setVisibility(View.GONE); - } - - } else { - ih.mSub.setVisibility(View.GONE); - ih.mSecond.setVisibility(View.GONE); - convertView.setClickable(true); - } - - // special case: first parameter may be a quantity - if (entry.mParameters != null && entry.mParameters.length > 0 - && entry.mParameters[0] instanceof Integer) { - ih.mText.setText(getResources().getQuantityString(entry.mType.getMsgId(), - (Integer) entry.mParameters[0], - entry.mParameters)); - } else { - ih.mText.setText(getResources().getString(entry.mType.getMsgId(), - entry.mParameters)); - } - convertView.setPadding((entry.mIndent) * dipFactor, 0, 0, 0); - ih.mText.setTextColor(entry.mType.mLevel == LogLevel.DEBUG ? Color.GRAY : mTextColor); - switch (entry.mType.mLevel) { - case DEBUG: ih.mImg.setBackgroundColor(Color.GRAY); break; - case INFO: ih.mImg.setBackgroundColor(mTextColor); break; - case WARN: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_orange_light)); break; - case ERROR: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; - case START: ih.mImg.setBackgroundColor(mTextColor); break; - case OK: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_green_light)); break; - case CANCELLED: ih.mImg.setBackgroundColor(getResources().getColor(R.color.android_red_light)); break; - } - - return convertView; - } - - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySectionedListAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySectionedListAdapter.java index c05dfdda2..4744f01dd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySectionedListAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeySectionedListAdapter.java @@ -190,7 +190,7 @@ public class KeySectionedListAdapter extends SectionCursorAdapter { + private static final int ENTRY_TYPE_REGULAR = 0; + private static final int ENTRY_TYPE_SUBLOG = 1; + + private final int mIndentFactor; + private LogActionListener mListener; + private List> mLogEntries; + + public NestedLogAdapter(Context context) { + super(); + + mIndentFactor = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + (float) 8, context.getResources().getDisplayMetrics()); + } + + public NestedLogAdapter(Context context, OperationResult.OperationLog log) { + this(context); + setLog(log); + } + + public void setListener(LogActionListener listener) { + mListener = listener; + } + + public void setLog(OperationResult.OperationLog log) { + List list = log.toList(); + + if (mLogEntries != null) { + mLogEntries.clear(); + } else { + mLogEntries = new ArrayList<>(list.size()); + } + + int lastSection = 0; + for (int i = 0; i < list.size(); i++) { + OperationResult.LogEntryParcel parcel = list.get(i); + if(parcel.mIndent < 1) { + lastSection = i; + } + + mLogEntries.add(new Pair<>(parcel, lastSection)); + } + + notifyDataSetChanged(); + } + + @Override + public int getItemCount() { + return mLogEntries != null ? mLogEntries.size() : 0; + } + + @Override + public long getItemId(int position) { + OperationResult.LogEntryParcel parcel = getItem(position); + return parcel != null ? parcel.hashCode() : -1L; + } + + public OperationResult.LogEntryParcel getItem(int position) { + return mLogEntries != null ? + mLogEntries.get(position).first : null; + } + + public int getFirstSectionPosition(int position) { + return mLogEntries != null ? + mLogEntries.get(position).second : 0; + } + + @Override + public int getItemViewType(int position) { + return (getItem(position) instanceof OperationResult.SubLogEntryParcel) ? + ENTRY_TYPE_SUBLOG : ENTRY_TYPE_REGULAR; + } + + public boolean isSection(int position) { + return mLogEntries != null && mLogEntries.get(position).second == position; + } + + @Override + public LogEntryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType) { + case ENTRY_TYPE_SUBLOG: + return new SublogEntryViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.log_display_sublog_item, parent, false)); + + case ENTRY_TYPE_REGULAR: + return new LogEntryViewHolder(LayoutInflater.from(parent.getContext()) + .inflate(R.layout.log_display_regular_item, parent, false)); + + default: + return null; + } + } + + @Override + public void onBindViewHolder(LogEntryViewHolder holder, int position) { + LayoutManager.LayoutParams layoutParams = LayoutManager.LayoutParams + .from(holder.itemView.getLayoutParams()); + + layoutParams.isHeader = isSection(position); + layoutParams.setFirstPosition(getFirstSectionPosition(position)); + + holder.bind(getItem(position), mIndentFactor); + holder.itemView.setLayoutParams(layoutParams); + } + + class LogEntryViewHolder extends RecyclerView.ViewHolder { + private TextView mLogText; + private ImageView mLogImg; + + public LogEntryViewHolder(View itemView) { + super(itemView); + + mLogText = (TextView) itemView.findViewById(R.id.log_text); + mLogImg = (ImageView) itemView.findViewById(R.id.log_img); + } + + public void bind(OperationResult.LogEntryParcel entry, int indentFactor) { + String logText; + if (entry.mParameters != null + && entry.mParameters.length > 0 + && entry.mParameters[0] instanceof Integer) { + + logText = itemView.getResources().getQuantityString(entry.mType.getMsgId(), + (int) entry.mParameters[0], entry.mParameters); + } else { + logText = itemView.getResources().getString(entry.mType.getMsgId(), + entry.mParameters); + } + + int textColor, indicatorColor; + textColor = indicatorColor = FormattingUtils.getColorFromAttr( + itemView.getContext(), R.attr.colorText); + + switch (entry.mType.mLevel) { + case DEBUG: + textColor = Color.GRAY; + indicatorColor = Color.GRAY; + break; + case WARN: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_orange_light); + break; + case ERROR: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_red_light); + break; + case OK: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_green_light); + break; + case CANCELLED: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_red_light); + break; + } + + mLogText.setText(logText); + mLogText.setTextColor(textColor); + mLogImg.setBackgroundColor(indicatorColor); + itemView.setPadding((entry.mIndent) * indentFactor, 0, 0, 0); + } + } + + class SublogEntryViewHolder extends LogEntryViewHolder implements View.OnClickListener { + private TextView mSublogText; + private ImageView mSublogImg; + + public SublogEntryViewHolder(View itemView) { + super(itemView); + + itemView.setClickable(true); + itemView.setOnClickListener(this); + + mSublogText = (TextView) itemView.findViewById(R.id.log_second_text); + mSublogImg = (ImageView) itemView.findViewById(R.id.log_second_img); + } + + @Override + public void bind(OperationResult.LogEntryParcel entry, int indentFactor) { + super.bind(entry, indentFactor); + + OperationResult.LogEntryParcel sublogEntry = ((OperationResult.SubLogEntryParcel) entry) + .getSubResult().getLog().getLast(); + + String logText; + if (sublogEntry.mParameters != null + && sublogEntry.mParameters.length > 0 + && sublogEntry.mParameters[0] instanceof Integer) { + + logText = itemView.getResources().getQuantityString(sublogEntry.mType.getMsgId(), + (int) sublogEntry.mParameters[0], sublogEntry.mParameters); + } else { + logText = itemView.getResources().getString(sublogEntry.mType.getMsgId(), + sublogEntry.mParameters); + } + + int textColor, indicatorColor; + textColor = indicatorColor = FormattingUtils.getColorFromAttr( + itemView.getContext(), R.attr.colorText); + + switch (sublogEntry.mType.mLevel) { + case DEBUG: + textColor = Color.GRAY; + indicatorColor = Color.GRAY; + break; + case WARN: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_orange_light); + break; + case ERROR: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_red_light); + break; + case OK: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_green_light); + break; + case CANCELLED: + indicatorColor = ContextCompat.getColor(itemView.getContext(), + R.color.android_red_light); + break; + } + + mSublogText.setText(logText); + mSublogText.setTextColor(textColor); + mSublogImg.setBackgroundColor(indicatorColor); + } + + @Override + public void onClick(View v) { + if (mListener != null) { + OperationResult.LogEntryParcel parcel = getItem(getAdapterPosition()); + if (parcel instanceof OperationResult.SubLogEntryParcel) { + mListener.onSubEntryClicked((OperationResult.SubLogEntryParcel) parcel); + } + } + } + } + + public interface LogActionListener { + void onSubEntryClicked(OperationResult.SubLogEntryParcel subLogEntryParcel); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/adapter/SectionCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/adapter/SectionCursorAdapter.java index d92a9ac0f..4f297ecbf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/adapter/SectionCursorAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/adapter/SectionCursorAdapter.java @@ -10,6 +10,8 @@ import android.view.ViewGroup; import com.tonicartos.superslim.LayoutManager; import org.sufficientlysecure.keychain.util.Log; +import java.util.Objects; + /** * @param section type. * @param the view holder extending {@code BaseViewHolder} that is bound to the cursor data. @@ -71,7 +73,6 @@ public abstract class SectionCursorAdapter extends Fragment { - private static final int INTERNAL_LIST_VIEW_ID = android.R.id.list; private static final int INTERNAL_EMPTY_VIEW_ID = android.R.id.empty; private static final int INTERNAL_LIST_CONTAINER_ID = android.R.id.widget_frame; @@ -104,7 +103,7 @@ public class RecyclerFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { - final Context context = parent.getContext(); + final Context context = getContext(); FrameLayout root = new FrameLayout(context); LinearLayout progressContainer = new LinearLayout(context); diff --git a/OpenKeychain/src/main/res/layout/key_list_fragment.xml b/OpenKeychain/src/main/res/layout/key_list_fragment.xml index 8f79d9c4e..3fb47d8f1 100644 --- a/OpenKeychain/src/main/res/layout/key_list_fragment.xml +++ b/OpenKeychain/src/main/res/layout/key_list_fragment.xml @@ -40,9 +40,13 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="16dp" + android:paddingStart="16dp" android:paddingRight="32dp" + android:paddingEnd="32dp" + android:paddingBottom="72dp" android:clipToPadding="false" - android:scrollbarStyle="outsideOverlay" /> + android:scrollbars="vertical" + android:scrollbarStyle="outsideOverlay"/> + + + + + + + diff --git a/OpenKeychain/src/main/res/layout/log_display_item.xml b/OpenKeychain/src/main/res/layout/log_display_sublog_item.xml similarity index 78% rename from OpenKeychain/src/main/res/layout/log_display_item.xml rename to OpenKeychain/src/main/res/layout/log_display_sublog_item.xml index 25249003f..0a75748ad 100644 --- a/OpenKeychain/src/main/res/layout/log_display_item.xml +++ b/OpenKeychain/src/main/res/layout/log_display_sublog_item.xml @@ -1,9 +1,18 @@ - + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + + super:slm_headerDisplay="sticky|inline" + super:slm_section_sectionManager="linear" + tools:ignore="ResAuto"> + android:layout_gravity="center_vertical" + tools:text="Log Entry Text" /> + android:layout_gravity="center_vertical" + tools:text="Log Entry Text" /> diff --git a/OpenKeychain/src/main/res/layout/view_key_adv_certs_header.xml b/OpenKeychain/src/main/res/layout/view_key_adv_certs_header.xml index a79b31ae9..ee8f0a196 100644 --- a/OpenKeychain/src/main/res/layout/view_key_adv_certs_header.xml +++ b/OpenKeychain/src/main/res/layout/view_key_adv_certs_header.xml @@ -1,5 +1,8 @@ - @@ -9,7 +12,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="start|left" - android:text="header text" /> + tools:text="header text" />