From 0f293ab7927470ab3a2d78064cc3c66e2523415f Mon Sep 17 00:00:00 2001 From: Ryan Hansberry Date: Tue, 22 Sep 2015 10:41:37 -0700 Subject: Convert PubkeyListActivity, PortFowardListActivity, and HostListActivity to AppCompat, using AppCompatListActivity superclass --- .../java/org/connectbot/PubkeyListActivity.java | 504 +++++++++------------ 1 file changed, 214 insertions(+), 290 deletions(-) (limited to 'app/src/main/java/org/connectbot/PubkeyListActivity.java') diff --git a/app/src/main/java/org/connectbot/PubkeyListActivity.java b/app/src/main/java/org/connectbot/PubkeyListActivity.java index 3aea1bb..d25411f 100644 --- a/app/src/main/java/org/connectbot/PubkeyListActivity.java +++ b/app/src/main/java/org/connectbot/PubkeyListActivity.java @@ -37,6 +37,10 @@ import org.connectbot.util.PubkeyDatabase; import org.connectbot.util.PubkeyUtils; import org.openintents.intents.FileManagerIntents; +import com.trilead.ssh2.crypto.Base64; +import com.trilead.ssh2.crypto.PEMDecoder; +import com.trilead.ssh2.crypto.PEMStructure; + import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -45,16 +49,11 @@ import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.ServiceConnection; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.support.annotation.VisibleForTesting; -import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.ClipboardManager; @@ -73,17 +72,13 @@ import android.widget.TableRow; import android.widget.TextView; import android.widget.Toast; -import com.trilead.ssh2.crypto.Base64; -import com.trilead.ssh2.crypto.PEMDecoder; -import com.trilead.ssh2.crypto.PEMStructure; - /** * List public keys in database by nickname and describe their properties. Allow users to import, * generate, rename, and delete key pairs. * * @author Kenny Root */ -public class PubkeyListActivity extends AppCompatActivity implements EventListener { +public class PubkeyListActivity extends AppCompatListActivity implements EventListener { public final static String TAG = "CB.PubkeyListActivity"; private static final int MAX_KEYFILE_SIZE = 8192; @@ -99,10 +94,6 @@ public class PubkeyListActivity extends AppCompatActivity implements EventListen protected LayoutInflater inflater = null; - private View mEmptyView; - private RecyclerView mPubkeyListView; - private PubkeyAdapter mAdapter; - protected TerminalManager bound = null; private MenuItem onstartToggle = null; @@ -143,14 +134,14 @@ public class PubkeyListActivity extends AppCompatActivity implements EventListen super.onCreate(icicle); setContentView(R.layout.act_pubkeylist); - mPubkeyListView = (RecyclerView) findViewById(R.id.list); - mPubkeyListView.setHasFixedSize(true); - mPubkeyListView.setLayoutManager(new LinearLayoutManager(this)); - mPubkeyListView.addItemDecoration(new PubkeyListItemDecoration(this)); + mListView = (RecyclerView) findViewById(R.id.list); + mListView.setHasFixedSize(true); + mListView.setLayoutManager(new LinearLayoutManager(this)); + mListView.addItemDecoration(new ListItemDecoration(this)); mEmptyView = findViewById(R.id.empty); - registerForContextMenu(mPubkeyListView); + registerForContextMenu(mListView); clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); @@ -278,28 +269,15 @@ public class PubkeyListActivity extends AppCompatActivity implements EventListen updateList(); } - - protected void updateList() { PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); pubkeys = pubkeyDb.allPubkeys(); mAdapter = new PubkeyAdapter(this, pubkeys); - mPubkeyListView.setAdapter(mAdapter); + mListView.setAdapter(mAdapter); adjustViewVisibility(); } - /** - * If the pubkey list is empty, hides the list and shows the empty message; otherwise, shows - * the list and hides the empty message. - */ - // TODO: this method is being duplicated but could be part of a superclass - private void adjustViewVisibility() { - boolean isEmpty = mAdapter.getItemCount() == 0; - mPubkeyListView.setVisibility(isEmpty ? View.GONE : View.VISIBLE); - mEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE); - } - @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); @@ -430,242 +408,237 @@ public class PubkeyListActivity extends AppCompatActivity implements EventListen .setNegativeButton(android.R.string.cancel, null).create().show(); } - @VisibleForTesting - protected class PubkeyAdapter extends RecyclerView.Adapter { - private final LayoutInflater inflater; - private final List pubkeys; - private final Context context; - - // KLUDGE: duplication of HostListActivity? - class ViewHolder extends RecyclerView.ViewHolder - implements View.OnClickListener, View.OnCreateContextMenuListener { - public final ImageView icon; - public final TextView nickname; - public final TextView caption; - public PubkeyBean pubkey; - - public ViewHolder(View v) { - super(v); - v.setOnClickListener(this); - v.setOnCreateContextMenuListener(this); - - icon = (ImageView) v.findViewById(android.R.id.icon); - nickname = (TextView) v.findViewById(android.R.id.text1); - caption = (TextView) v.findViewById(android.R.id.text2); - } + private class PubkeyViewHolder extends ItemViewHolder { + public final ImageView icon; + public final TextView nickname; + public final TextView caption; - @Override - public void onClick(View v) { - boolean loaded = bound != null && bound.isKeyLoaded(pubkey.getNickname()); + public PubkeyBean pubkey; - // handle toggling key in-memory on/off - if (loaded) { - bound.removeKey(pubkey.getNickname()); - updateList(); - } else { - handleAddKey(pubkey); - } + public PubkeyViewHolder(View v) { + super(v); + + icon = (ImageView) v.findViewById(android.R.id.icon); + nickname = (TextView) v.findViewById(android.R.id.text1); + caption = (TextView) v.findViewById(android.R.id.text2); + } + + @Override + public void onClick(View v) { + boolean loaded = bound != null && bound.isKeyLoaded(pubkey.getNickname()); + + // handle toggling key in-memory on/off + if (loaded) { + bound.removeKey(pubkey.getNickname()); + updateList(); + } else { + handleAddKey(pubkey); } + } - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - // Create menu to handle deleting and editing pubkey - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - - menu.setHeaderTitle(pubkey.getNickname()); - - // TODO: option load/unload key from in-memory list - // prompt for password as needed for passworded keys - - // cant change password or clipboard imported keys - final boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType()); - final boolean loaded = bound != null && bound.isKeyLoaded(pubkey.getNickname()); - - MenuItem load = menu.add(loaded ? R.string.pubkey_memory_unload : R.string.pubkey_memory_load); - load.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - if (loaded) { - bound.removeKey(pubkey.getNickname()); - updateList(); - } else { - handleAddKey(pubkey); - //bound.addKey(nickname, trileadKey); - } - return true; - } - }); - - onstartToggle = menu.add(R.string.pubkey_load_on_start); - onstartToggle.setEnabled(!pubkey.isEncrypted()); - onstartToggle.setCheckable(true); - onstartToggle.setChecked(pubkey.isStartup()); - onstartToggle.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - // toggle onstart status - pubkey.setStartup(!pubkey.isStartup()); - PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); - pubkeyDb.savePubkey(pubkey); + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + // Create menu to handle deleting and editing pubkey + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + + menu.setHeaderTitle(pubkey.getNickname()); + + // TODO: option load/unload key from in-memory list + // prompt for password as needed for passworded keys + + // cant change password or clipboard imported keys + final boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType()); + final boolean loaded = bound != null && bound.isKeyLoaded(pubkey.getNickname()); + + MenuItem load = menu.add(loaded ? R.string.pubkey_memory_unload : R.string.pubkey_memory_load); + load.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + if (loaded) { + bound.removeKey(pubkey.getNickname()); updateList(); - return true; + } else { + handleAddKey(pubkey); + //bound.addKey(nickname, trileadKey); } - }); - - MenuItem copyPublicToClipboard = menu.add(R.string.pubkey_copy_public); - copyPublicToClipboard.setEnabled(!imported); - copyPublicToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - try { - PublicKey pk = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType()); - String openSSHPubkey = PubkeyUtils.convertToOpenSSHFormat(pk, pubkey.getNickname()); - - clipboard.setText(openSSHPubkey); - } catch (Exception e) { - e.printStackTrace(); - } - return true; + return true; + } + }); + + onstartToggle = menu.add(R.string.pubkey_load_on_start); + onstartToggle.setEnabled(!pubkey.isEncrypted()); + onstartToggle.setCheckable(true); + onstartToggle.setChecked(pubkey.isStartup()); + onstartToggle.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // toggle onstart status + pubkey.setStartup(!pubkey.isStartup()); + PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); + pubkeyDb.savePubkey(pubkey); + updateList(); + return true; + } + }); + + MenuItem copyPublicToClipboard = menu.add(R.string.pubkey_copy_public); + copyPublicToClipboard.setEnabled(!imported); + copyPublicToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + try { + PublicKey pk = PubkeyUtils.decodePublic(pubkey.getPublicKey(), pubkey.getType()); + String openSSHPubkey = PubkeyUtils.convertToOpenSSHFormat(pk, pubkey.getNickname()); + + clipboard.setText(openSSHPubkey); + } catch (Exception e) { + e.printStackTrace(); } - }); - - MenuItem copyPrivateToClipboard = menu.add(R.string.pubkey_copy_private); - copyPrivateToClipboard.setEnabled(!pubkey.isEncrypted() || imported); - copyPrivateToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - try { - String data = null; - - if (imported) - data = new String(pubkey.getPrivateKey()); - else { - PrivateKey pk = PubkeyUtils.decodePrivate(pubkey.getPrivateKey(), pubkey.getType()); - data = PubkeyUtils.exportPEM(pk, null); - } - - clipboard.setText(data); - } catch (Exception e) { - e.printStackTrace(); + return true; + } + }); + + MenuItem copyPrivateToClipboard = menu.add(R.string.pubkey_copy_private); + copyPrivateToClipboard.setEnabled(!pubkey.isEncrypted() || imported); + copyPrivateToClipboard.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + try { + String data = null; + + if (imported) + data = new String(pubkey.getPrivateKey()); + else { + PrivateKey pk = PubkeyUtils.decodePrivate(pubkey.getPrivateKey(), pubkey.getType()); + data = PubkeyUtils.exportPEM(pk, null); } - return true; + + clipboard.setText(data); + } catch (Exception e) { + e.printStackTrace(); } - }); - - MenuItem changePassword = menu.add(R.string.pubkey_change_password); - changePassword.setEnabled(!imported); - changePassword.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - final View changePasswordView = inflater.inflate(R.layout.dia_changepassword, null, false); - ((TableRow) changePasswordView.findViewById(R.id.old_password_prompt)) - .setVisibility(pubkey.isEncrypted() ? View.VISIBLE : View.GONE); - new AlertDialog.Builder(PubkeyListActivity.this) - .setView(changePasswordView) - .setPositiveButton(R.string.button_change, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - String oldPassword = ((EditText) changePasswordView.findViewById(R.id.old_password)).getText().toString(); - String password1 = ((EditText) changePasswordView.findViewById(R.id.password1)).getText().toString(); - String password2 = ((EditText) changePasswordView.findViewById(R.id.password2)).getText().toString(); - - if (!password1.equals(password2)) { - new AlertDialog.Builder(PubkeyListActivity.this) - .setMessage(R.string.alert_passwords_do_not_match_msg) - .setPositiveButton(android.R.string.ok, null) - .create().show(); - return; - } + return true; + } + }); + + MenuItem changePassword = menu.add(R.string.pubkey_change_password); + changePassword.setEnabled(!imported); + changePassword.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + final View changePasswordView = inflater.inflate(R.layout.dia_changepassword, null, false); + ((TableRow) changePasswordView.findViewById(R.id.old_password_prompt)) + .setVisibility(pubkey.isEncrypted() ? View.VISIBLE : View.GONE); + new AlertDialog.Builder(PubkeyListActivity.this) + .setView(changePasswordView) + .setPositiveButton(R.string.button_change, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String oldPassword = ((EditText) changePasswordView.findViewById(R.id.old_password)).getText().toString(); + String password1 = ((EditText) changePasswordView.findViewById(R.id.password1)).getText().toString(); + String password2 = ((EditText) changePasswordView.findViewById(R.id.password2)).getText().toString(); + + if (!password1.equals(password2)) { + new AlertDialog.Builder(PubkeyListActivity.this) + .setMessage(R.string.alert_passwords_do_not_match_msg) + .setPositiveButton(android.R.string.ok, null) + .create().show(); + return; + } - try { - if (!pubkey.changePassword(oldPassword, password1)) - new AlertDialog.Builder(PubkeyListActivity.this) - .setMessage(R.string.alert_wrong_password_msg) - .setPositiveButton(android.R.string.ok, null) - .create().show(); - else { - PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); - pubkeyDb.savePubkey(pubkey); - updateList(); - } - } catch (Exception e) { - Log.e(TAG, "Could not change private key password", e); + try { + if (!pubkey.changePassword(oldPassword, password1)) new AlertDialog.Builder(PubkeyListActivity.this) - .setMessage(R.string.alert_key_corrupted_msg) + .setMessage(R.string.alert_wrong_password_msg) .setPositiveButton(android.R.string.ok, null) .create().show(); + else { + PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); + pubkeyDb.savePubkey(pubkey); + updateList(); } + } catch (Exception e) { + Log.e(TAG, "Could not change private key password", e); + new AlertDialog.Builder(PubkeyListActivity.this) + .setMessage(R.string.alert_key_corrupted_msg) + .setPositiveButton(android.R.string.ok, null) + .create().show(); } - }) - .setNegativeButton(android.R.string.cancel, null).create().show(); - - return true; - } - }); - - confirmUse = menu.add(R.string.pubkey_confirm_use); - confirmUse.setCheckable(true); - confirmUse.setChecked(pubkey.isConfirmUse()); - confirmUse.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - // toggle confirm use - pubkey.setConfirmUse(!pubkey.isConfirmUse()); - PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); - pubkeyDb.savePubkey(pubkey); - updateList(); - return true; - } - }); - - MenuItem delete = menu.add(R.string.pubkey_delete); - delete.setOnMenuItemClickListener(new OnMenuItemClickListener() { - public boolean onMenuItemClick(MenuItem item) { - // prompt user to make sure they really want this - new AlertDialog.Builder(PubkeyListActivity.this) - .setMessage(getString(R.string.delete_message, pubkey.getNickname())) - .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - - // dont forget to remove from in-memory - if (loaded) { - bound.removeKey(pubkey.getNickname()); - } + } + }) + .setNegativeButton(android.R.string.cancel, null).create().show(); - // delete from backend database and update gui - PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); - pubkeyDb.deletePubkey(pubkey); - updateList(); + return true; + } + }); + + confirmUse = menu.add(R.string.pubkey_confirm_use); + confirmUse.setCheckable(true); + confirmUse.setChecked(pubkey.isConfirmUse()); + confirmUse.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // toggle confirm use + pubkey.setConfirmUse(!pubkey.isConfirmUse()); + PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); + pubkeyDb.savePubkey(pubkey); + updateList(); + return true; + } + }); + + MenuItem delete = menu.add(R.string.pubkey_delete); + delete.setOnMenuItemClickListener(new OnMenuItemClickListener() { + public boolean onMenuItemClick(MenuItem item) { + // prompt user to make sure they really want this + new AlertDialog.Builder(PubkeyListActivity.this) + .setMessage(getString(R.string.delete_message, pubkey.getNickname())) + .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + + // dont forget to remove from in-memory + if (loaded) { + bound.removeKey(pubkey.getNickname()); } - }) - .setNegativeButton(R.string.delete_neg, null).create().show(); - return true; - } - }); - } + // delete from backend database and update gui + PubkeyDatabase pubkeyDb = PubkeyDatabase.get(PubkeyListActivity.this); + pubkeyDb.deletePubkey(pubkey); + updateList(); + } + }) + .setNegativeButton(R.string.delete_neg, null).create().show(); + + return true; + } + }); } + } + + @VisibleForTesting + private class PubkeyAdapter extends ItemAdapter { + private final List pubkeys; public PubkeyAdapter(Context context, List pubkeys) { - this.context = context; + super(context); this.pubkeys = pubkeys; - this.inflater = LayoutInflater.from(context); } @Override - public PubkeyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + public PubkeyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_pubkey, parent, false); - ViewHolder vh = new ViewHolder(v); + PubkeyViewHolder vh = new PubkeyViewHolder(v); return vh; } - @Override - public void onBindViewHolder(ViewHolder holder, int position) { + public void onBindViewHolder(ItemViewHolder holder, int position) { + PubkeyViewHolder pubkeyHolder = (PubkeyViewHolder) holder; + PubkeyBean pubkey = pubkeys.get(position); if (pubkey == null) { // Well, something bad happened. We can't continue. Log.e("PubkeyAdapter", "Pubkey bean is null!"); - holder.nickname.setText("Error during lookup"); - holder.caption.setText("see 'adb logcat' for more"); + pubkeyHolder.nickname.setText("Error during lookup"); + pubkeyHolder.caption.setText("see 'adb logcat' for more"); } - holder.pubkey = pubkey; + pubkeyHolder.pubkey = pubkey; - holder.nickname.setText(pubkey.getNickname()); + pubkeyHolder.nickname.setText(pubkey.getNickname()); boolean imported = PubkeyDatabase.KEY_TYPE_IMPORTED.equals(pubkey.getType()); @@ -673,88 +646,39 @@ public class PubkeyListActivity extends AppCompatActivity implements EventListen try { PEMStructure struct = PEMDecoder.parsePEM(new String(pubkey.getPrivateKey()).toCharArray()); String type = (struct.pemType == PEMDecoder.PEM_RSA_PRIVATE_KEY) ? "RSA" : "DSA"; - holder.caption.setText(String.format("%s unknown-bit", type)); + pubkeyHolder.caption.setText(String.format("%s unknown-bit", type)); } catch (IOException e) { Log.e(TAG, "Error decoding IMPORTED public key at " + pubkey.getId(), e); } } else { try { - holder.caption.setText(pubkey.getDescription()); + pubkeyHolder.caption.setText(pubkey.getDescription()); } catch (Exception e) { Log.e(TAG, "Error decoding public key at " + pubkey.getId(), e); - holder.caption.setText(R.string.pubkey_unknown_format); + pubkeyHolder.caption.setText(R.string.pubkey_unknown_format); } } if (bound == null) { - holder.icon.setVisibility(View.GONE); + pubkeyHolder.icon.setVisibility(View.GONE); } else { - holder.icon.setVisibility(View.VISIBLE); + pubkeyHolder.icon.setVisibility(View.VISIBLE); if (bound.isKeyLoaded(pubkey.getNickname())) - holder.icon.setImageState(new int[] { android.R.attr.state_checked }, true); + pubkeyHolder.icon.setImageState(new int[] { android.R.attr.state_checked }, true); else - holder.icon.setImageState(new int[] { }, true); + pubkeyHolder.icon.setImageState(new int[] { }, true); } } - public PubkeyBean getItem(int position) { - return pubkeys.get(position); - } - - @Override - public long getItemId(int position) { - return pubkeys.get(position).getId(); - } - @Override public int getItemCount() { return pubkeys.size(); } - } - - /** - * Item decorations for pubkey list items, which adds a divider between items and leaves a - * small offset at the top of the list to adhere to the Material Design spec. - */ - // KLUDGE: duplication - private class PubkeyListItemDecoration extends RecyclerView.ItemDecoration { - private final int[] ATTRS = new int[]{ - android.R.attr.listDivider - }; - - private final int TOP_LIST_OFFSET = 8; - - private Drawable mDivider; - - public PubkeyListItemDecoration(Context c) { - final TypedArray a = c.obtainStyledAttributes(ATTRS); - mDivider = a.getDrawable(0); - a.recycle(); - } @Override - public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { - final int left = parent.getPaddingLeft(); - final int right = parent.getWidth() - parent.getPaddingRight(); - - final int childCount = parent.getChildCount(); - for (int i = 0; i < childCount; i++) { - final View child = parent.getChildAt(i); - final RecyclerView.LayoutParams params = - (RecyclerView.LayoutParams) child.getLayoutParams(); - final int top = child.getBottom() + params.bottomMargin; - final int bottom = top + mDivider.getIntrinsicHeight(); - mDivider.setBounds(left, top, right, bottom); - mDivider.draw(c); - } - } - - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, - RecyclerView.State state) { - int top = parent.getChildAdapterPosition(view) == 0 ? TOP_LIST_OFFSET : 0; - outRect.set(0, top, 0, mDivider.getIntrinsicHeight()); + public long getItemId(int position) { + return pubkeys.get(position).getId(); } } } -- cgit v1.2.3