aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRyan Hansberry <rhansby@gmail.com>2015-09-22 10:41:37 -0700
committerRyan Hansberry <rhansby@gmail.com>2015-09-22 11:59:13 -0700
commit0f293ab7927470ab3a2d78064cc3c66e2523415f (patch)
tree0f503c5f09b19df25d9ec7799ac542ea866792d4
parentd2164043d4748b0269937cb983929c6c2e8c6fff (diff)
downloadconnectbot-0f293ab7927470ab3a2d78064cc3c66e2523415f.tar.gz
connectbot-0f293ab7927470ab3a2d78064cc3c66e2523415f.tar.bz2
connectbot-0f293ab7927470ab3a2d78064cc3c66e2523415f.zip
Convert PubkeyListActivity, PortFowardListActivity, and HostListActivity to AppCompat, using AppCompatListActivity superclass
-rw-r--r--app/src/main/java/org/connectbot/AppCompatListActivity.java108
-rw-r--r--app/src/main/java/org/connectbot/HostListActivity.java321
-rw-r--r--app/src/main/java/org/connectbot/PortForwardListActivity.java375
-rw-r--r--app/src/main/java/org/connectbot/PubkeyListActivity.java504
-rw-r--r--app/src/main/res/layout/act_portforwardlist.xml6
5 files changed, 646 insertions, 668 deletions
diff --git a/app/src/main/java/org/connectbot/AppCompatListActivity.java b/app/src/main/java/org/connectbot/AppCompatListActivity.java
new file mode 100644
index 0000000..6eb406d
--- /dev/null
+++ b/app/src/main/java/org/connectbot/AppCompatListActivity.java
@@ -0,0 +1,108 @@
+/*
+ * ConnectBot: simple, powerful, open-source SSH client for Android
+ * Copyright 2015 Kenny Root, Jeffrey Sharkey
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.connectbot;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+
+public class AppCompatListActivity extends AppCompatActivity {
+ protected ItemAdapter mAdapter;
+
+ protected View mEmptyView;
+ protected RecyclerView mListView;
+
+ /**
+ * If the list is empty, hides the list and shows the empty message; otherwise, shows
+ * the list and hides the empty message.
+ */
+ protected void adjustViewVisibility() {
+ boolean isEmpty = mAdapter.getItemCount() == 0;
+ mListView.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
+ mEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
+ }
+
+ /**
+ * Item decorations for 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.
+ */
+ protected class ListItemDecoration extends RecyclerView.ItemDecoration {
+ private final int[] ATTRS = new int[] { android.R.attr.listDivider };
+
+ private final int TOP_LIST_OFFSET = 8;
+
+ private Drawable mDivider;
+
+ public ListItemDecoration(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());
+ }
+ }
+
+ protected abstract class ItemViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener, View.OnCreateContextMenuListener {
+ public ItemViewHolder(View v) {
+ super(v);
+ v.setOnClickListener(this);
+ v.setOnCreateContextMenuListener(this);
+ }
+ }
+
+ @VisibleForTesting
+ protected static abstract class ItemAdapter extends RecyclerView.Adapter<ItemViewHolder> {
+ private final LayoutInflater inflater;
+ protected final Context context;
+
+ public ItemAdapter(Context context/*, List<AbstractBean> items*/) {
+ this.context = context;
+ this.inflater = LayoutInflater.from(context);
+ }
+ }
+}
diff --git a/app/src/main/java/org/connectbot/HostListActivity.java b/app/src/main/java/org/connectbot/HostListActivity.java
index 4dfeffd..41ba62b 100644
--- a/app/src/main/java/org/connectbot/HostListActivity.java
+++ b/app/src/main/java/org/connectbot/HostListActivity.java
@@ -17,15 +17,6 @@
package org.connectbot;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.VisibleForTesting;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-
import java.util.List;
import org.connectbot.bean.HostBean;
@@ -52,6 +43,9 @@ import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.StyleRes;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
@@ -69,7 +63,7 @@ import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
-public class HostListActivity extends AppCompatActivity implements OnHostStatusChangedListener {
+public class HostListActivity extends AppCompatListActivity implements OnHostStatusChangedListener {
public final static String TAG = "CB.HostListActivity";
public static final String DISCONNECT_ACTION = "org.connectbot.action.DISCONNECT";
@@ -81,10 +75,6 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
private List<HostBean> hosts;
protected LayoutInflater inflater = null;
- private RecyclerView mHostListView;
- private HostAdapter mAdapter;
- private View mEmptyView;
-
protected boolean sortedByColor = false;
private MenuItem sortcolor;
@@ -182,10 +172,10 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
super.onCreate(icicle);
setContentView(R.layout.act_hostlist);
- mHostListView = (RecyclerView) findViewById(R.id.list);
- mHostListView.setHasFixedSize(true);
- mHostListView.setLayoutManager(new LinearLayoutManager(this));
- mHostListView.addItemDecoration(new HostListItemDecoration(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);
@@ -222,7 +212,7 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
this.sortedByColor = prefs.getBoolean(PreferenceConstants.SORT_BY_COLOR, false);
- this.registerForContextMenu(mHostListView);
+ this.registerForContextMenu(mListView);
quickconnect = (TextView) this.findViewById(R.id.front_quickconnect);
quickconnect.setVisibility(makingShortcut ? View.GONE : View.VISIBLE);
@@ -356,7 +346,6 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
}).create().show();
}
-
/**
* @return
*/
@@ -411,7 +400,7 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
}
mAdapter = new HostAdapter(this, hosts, bound);
- mHostListView.setAdapter(mAdapter);
+ mListView.setAdapter(mAdapter);
adjustViewVisibility();
}
@@ -420,136 +409,122 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
updateList();
}
- /**
- * If the host list is empty, hides the list and shows the empty message; otherwise, shows
- * the list and hides the empty message.
- */
- private void adjustViewVisibility() {
- boolean isEmpty = mAdapter.getItemCount() == 0;
- mHostListView.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
- mEmptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
- }
+ private class HostViewHolder extends ItemViewHolder {
+ public final ImageView icon;
+ public final TextView nickname;
+ public final TextView caption;
- @VisibleForTesting
- protected class HostAdapter extends RecyclerView.Adapter<HostAdapter.ViewHolder> {
- private final LayoutInflater inflater;
- private final List<HostBean> hosts;
- private final TerminalManager manager;
- private final Context context;
+ public HostBean host;
- public final static int STATE_UNKNOWN = 1, STATE_CONNECTED = 2, STATE_DISCONNECTED = 3;
+ public HostViewHolder(View v) {
+ super(v);
- class ViewHolder extends RecyclerView.ViewHolder
- implements View.OnClickListener, View.OnCreateContextMenuListener {
- public final ImageView icon;
- public final TextView nickname;
- public final TextView caption;
- public HostBean host;
-
- 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);
- }
+ 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) {
- // launch off to console details
- Uri uri = host.getUri();
+ @Override
+ public void onClick(View v) {
+ // launch off to console details
+ Uri uri = host.getUri();
- Intent contents = new Intent(Intent.ACTION_VIEW, uri);
- contents.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ Intent contents = new Intent(Intent.ACTION_VIEW, uri);
+ contents.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- if (makingShortcut) {
- // create shortcut if requested
- ShortcutIconResource icon = Intent.ShortcutIconResource.fromContext(
- HostListActivity.this, R.drawable.icon);
+ if (makingShortcut) {
+ // create shortcut if requested
+ ShortcutIconResource icon = Intent.ShortcutIconResource.fromContext(
+ HostListActivity.this, R.drawable.icon);
- Intent intent = new Intent();
- intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, contents);
- intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, host.getNickname());
- intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
+ Intent intent = new Intent();
+ intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, contents);
+ intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, host.getNickname());
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, icon);
- setResult(RESULT_OK, intent);
- finish();
+ setResult(RESULT_OK, intent);
+ finish();
- } else {
- // otherwise just launch activity to show this host
- contents.setClass(HostListActivity.this, ConsoleActivity.class);
- HostListActivity.this.startActivity(contents);
- }
+ } else {
+ // otherwise just launch activity to show this host
+ contents.setClass(HostListActivity.this, ConsoleActivity.class);
+ HostListActivity.this.startActivity(contents);
}
+ }
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- menu.setHeaderTitle(host.getNickname());
-
- // edit, disconnect, delete
- MenuItem connect = menu.add(R.string.list_host_disconnect);
- final TerminalBridge bridge = (bound == null) ? null : bound.getConnectedBridge(host);
- connect.setEnabled(bridge != null);
- connect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- bridge.dispatchDisconnect(true);
- return true;
- }
- });
-
- MenuItem edit = menu.add(R.string.list_host_edit);
- edit.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- Intent intent = new Intent(HostListActivity.this, HostEditorActivity.class);
- intent.putExtra(Intent.EXTRA_TITLE, host.getId());
- HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT);
- return true;
- }
- });
-
- MenuItem portForwards = menu.add(R.string.list_host_portforwards);
- portForwards.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- Intent intent = new Intent(HostListActivity.this, PortForwardListActivity.class);
- intent.putExtra(Intent.EXTRA_TITLE, host.getId());
- HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT);
- return true;
- }
- });
- if (!TransportFactory.canForwardPorts(host.getProtocol()))
- portForwards.setEnabled(false);
-
- MenuItem delete = menu.add(R.string.list_host_delete);
- delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- // prompt user to make sure they really want this
- new AlertDialog.Builder(HostListActivity.this)
- .setMessage(getString(R.string.delete_message, host.getNickname()))
- .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- // make sure we disconnect
- if (bridge != null)
- bridge.dispatchDisconnect(true);
-
- hostdb.deleteHost(host);
- updateList();
- }
- })
- .setNegativeButton(R.string.delete_neg, null).create().show();
-
- return true;
- }
- });
- }
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ menu.setHeaderTitle(host.getNickname());
+
+ // edit, disconnect, delete
+ MenuItem connect = menu.add(R.string.list_host_disconnect);
+ final TerminalBridge bridge = (bound == null) ? null : bound.getConnectedBridge(host);
+ connect.setEnabled(bridge != null);
+ connect.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ bridge.dispatchDisconnect(true);
+ return true;
+ }
+ });
+
+ MenuItem edit = menu.add(R.string.list_host_edit);
+ edit.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(HostListActivity.this, HostEditorActivity.class);
+ intent.putExtra(Intent.EXTRA_TITLE, host.getId());
+ HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT);
+ return true;
+ }
+ });
+
+ MenuItem portForwards = menu.add(R.string.list_host_portforwards);
+ portForwards.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ Intent intent = new Intent(HostListActivity.this, PortForwardListActivity.class);
+ intent.putExtra(Intent.EXTRA_TITLE, host.getId());
+ HostListActivity.this.startActivityForResult(intent, REQUEST_EDIT);
+ return true;
+ }
+ });
+ if (!TransportFactory.canForwardPorts(host.getProtocol()))
+ portForwards.setEnabled(false);
+
+ MenuItem delete = menu.add(R.string.list_host_delete);
+ delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ // prompt user to make sure they really want this
+ new AlertDialog.Builder(HostListActivity.this)
+ .setMessage(getString(R.string.delete_message, host.getNickname()))
+ .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ // make sure we disconnect
+ if (bridge != null)
+ bridge.dispatchDisconnect(true);
+
+ hostdb.deleteHost(host);
+ updateList();
+ }
+ })
+ .setNegativeButton(R.string.delete_neg, null).create().show();
+
+ return true;
+ }
+ });
}
+ }
+
+ @VisibleForTesting
+ private class HostAdapter extends ItemAdapter {
+ private final List<HostBean> hosts;
+ private final TerminalManager manager;
+
+ public final static int STATE_UNKNOWN = 1, STATE_CONNECTED = 2, STATE_DISCONNECTED = 3;
public HostAdapter(Context context, List<HostBean> hosts, TerminalManager manager) {
- this.context = context;
+ super(context);
+
this.hosts = hosts;
this.manager = manager;
- this.inflater = LayoutInflater.from(context);
}
/**
@@ -570,36 +545,38 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
}
@Override
- public HostAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ public HostViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_host, parent, false);
- ViewHolder vh = new ViewHolder(v);
+ HostViewHolder vh = new HostViewHolder(v);
return vh;
}
@Override
- public void onBindViewHolder(ViewHolder holder, int position) {
+ public void onBindViewHolder(ItemViewHolder holder, int position) {
+ HostViewHolder hostHolder = (HostViewHolder) holder;
+
HostBean host = hosts.get(position);
if (host == null) {
// Well, something bad happened. We can't continue.
Log.e("HostAdapter", "Host bean is null!");
- holder.nickname.setText("Error during lookup");
- holder.caption.setText("see 'adb logcat' for more");
+ hostHolder.nickname.setText("Error during lookup");
+ hostHolder.caption.setText("see 'adb logcat' for more");
}
- holder.host = host;
+ hostHolder.host = host;
- holder.nickname.setText(host.getNickname());
+ hostHolder.nickname.setText(host.getNickname());
switch (this.getConnectedState(host)) {
case STATE_UNKNOWN:
- holder.icon.setImageState(new int[] { }, true);
+ hostHolder.icon.setImageState(new int[] { }, true);
break;
case STATE_CONNECTED:
- holder.icon.setImageState(new int[] { android.R.attr.state_checked }, true);
+ hostHolder.icon.setImageState(new int[] { android.R.attr.state_checked }, true);
break;
case STATE_DISCONNECTED:
- holder.icon.setImageState(new int[] { android.R.attr.state_expanded }, true);
+ hostHolder.icon.setImageState(new int[] { android.R.attr.state_expanded }, true);
break;
}
@@ -619,19 +596,15 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
chosenStyleSecondLine = R.style.ListItemSecondLineText;
}
- holder.nickname.setTextAppearance(context, chosenStyleFirstLine);
- holder.caption.setTextAppearance(context, chosenStyleSecondLine);
+ hostHolder.nickname.setTextAppearance(context, chosenStyleFirstLine);
+ hostHolder.caption.setTextAppearance(context, chosenStyleSecondLine);
CharSequence nice = context.getString(R.string.bind_never);
if (host.getLastConnect() > 0) {
nice = DateUtils.getRelativeTimeSpanString(host.getLastConnect() * 1000);
}
- holder.caption.setText(nice);
- }
-
- public HostBean getItem(int position) {
- return hosts.get(position);
+ hostHolder.caption.setText(nice);
}
@Override
@@ -644,48 +617,4 @@ public class HostListActivity extends AppCompatActivity implements OnHostStatusC
return hosts.size();
}
}
-
- /**
- * Item decorations for host 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.
- */
- private class HostListItemDecoration extends RecyclerView.ItemDecoration {
- private final int[] ATTRS = new int[]{
- android.R.attr.listDivider
- };
-
- private final int TOP_LIST_OFFSET = 8;
-
- private Drawable mDivider;
-
- public HostListItemDecoration(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());
- }
- }
}
diff --git a/app/src/main/java/org/connectbot/PortForwardListActivity.java b/app/src/main/java/org/connectbot/PortForwardListActivity.java
index 868c611..28b4331 100644
--- a/app/src/main/java/org/connectbot/PortForwardListActivity.java
+++ b/app/src/main/java/org/connectbot/PortForwardListActivity.java
@@ -26,7 +26,6 @@ import org.connectbot.service.TerminalManager;
import org.connectbot.util.HostDatabase;
import android.app.AlertDialog;
-import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -39,23 +38,23 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View;
import android.view.ViewGroup;
-import android.view.MenuItem.OnMenuItemClickListener;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
+import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.EditText;
-import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemSelectedListener;
/**
* List all portForwards for a particular host and provide a way for users to add more portForwards,
@@ -63,7 +62,7 @@ import android.widget.AdapterView.OnItemSelectedListener;
*
* @author Kenny Root
*/
-public class PortForwardListActivity extends ListActivity {
+public class PortForwardListActivity extends AppCompatListActivity {
public final static String TAG = "CB.PortForwardListAct";
private static final int LISTENER_CYCLE_TIME = 500;
@@ -104,6 +103,13 @@ public class PortForwardListActivity extends ListActivity {
setContentView(R.layout.act_portforwardlist);
+ mListView = (RecyclerView) findViewById(R.id.list);
+ mListView.setHasFixedSize(true);
+ mListView.setLayoutManager(new LinearLayoutManager(this));
+ mListView.addItemDecoration(new ListItemDecoration(this));
+
+ mEmptyView = findViewById(R.id.empty);
+
// connect with hosts database and populate list
this.hostdb = HostDatabase.get(this);
host = hostdb.findHostById(hostId);
@@ -134,25 +140,7 @@ public class PortForwardListActivity extends ListActivity {
this.updateList();
- this.registerForContextMenu(this.getListView());
-
- this.getListView().setOnItemClickListener(new OnItemClickListener() {
- public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
- ListView lv = PortForwardListActivity.this.getListView();
- PortForwardBean pfb = (PortForwardBean) lv.getItemAtPosition(position);
-
- if (hostBridge != null) {
- if (pfb.isEnabled())
- hostBridge.disablePortForward(pfb);
- else {
- if (!hostBridge.enablePortForward(pfb))
- Toast.makeText(PortForwardListActivity.this, getString(R.string.portforward_problem), Toast.LENGTH_LONG).show();
- }
-
- updateHandler.sendEmptyMessage(-1);
- }
- }
- });
+ this.registerForContextMenu(mListView);
this.inflater = LayoutInflater.from(this);
}
@@ -200,18 +188,18 @@ public class PortForwardListActivity extends ListActivity {
break;
}
- PortForwardBean pfb = new PortForwardBean(
+ PortForwardBean portForward = new PortForwardBean(
host != null ? host.getId() : -1,
nicknameEdit.getText().toString(), type,
sourcePortEdit.getText().toString(),
destEdit.getText().toString());
if (hostBridge != null) {
- hostBridge.addPortForward(pfb);
- hostBridge.enablePortForward(pfb);
+ hostBridge.addPortForward(portForward);
+ hostBridge.enablePortForward(portForward);
}
- if (host != null && !hostdb.savePortForward(pfb)) {
+ if (host != null && !hostdb.savePortForward(portForward)) {
throw new SQLException("Could not save port forward");
}
@@ -231,188 +219,217 @@ public class PortForwardListActivity extends ListActivity {
return true;
}
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- // Create menu to handle deleting and editing port forward
- AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
- final PortForwardBean pfb = (PortForwardBean) this.getListView().getItemAtPosition(info.position);
+ protected Handler updateHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ PortForwardListActivity.this.updateList();
+ }
+ };
- menu.setHeaderTitle(pfb.getNickname());
+ protected void updateList() {
+ if (hostBridge != null) {
+ this.portForwards = hostBridge.getPortForwards();
+ } else {
+ if (this.hostdb == null) return;
+ this.portForwards = this.hostdb.getPortForwardsForHost(host);
+ }
- MenuItem edit = menu.add(R.string.portforward_edit);
- edit.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- final View editTunnelView = inflater.inflate(R.layout.dia_portforward, null, false);
-
- final Spinner typeSpinner = (Spinner) editTunnelView.findViewById(R.id.portforward_type);
- if (HostDatabase.PORTFORWARD_LOCAL.equals(pfb.getType()))
- typeSpinner.setSelection(0);
- else if (HostDatabase.PORTFORWARD_REMOTE.equals(pfb.getType()))
- typeSpinner.setSelection(1);
- else
- typeSpinner.setSelection(2);
-
- final EditText nicknameEdit = (EditText) editTunnelView.findViewById(R.id.nickname);
- nicknameEdit.setText(pfb.getNickname());
-
- final EditText sourcePortEdit = (EditText) editTunnelView.findViewById(R.id.portforward_source);
- sourcePortEdit.setText(String.valueOf(pfb.getSourcePort()));
-
- final EditText destEdit = (EditText) editTunnelView.findViewById(R.id.portforward_destination);
- if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(pfb.getType())) {
- destEdit.setEnabled(false);
- } else {
- destEdit.setText(String.format("%s:%d", pfb.getDestAddr(), pfb.getDestPort()));
- }
+ mAdapter = new PortForwardAdapter(this, portForwards);
+ mListView.setAdapter(mAdapter);
+ adjustViewVisibility();
+ }
- typeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
- public void onItemSelected(AdapterView<?> value, View view,
- int position, long id) {
- destEdit.setEnabled(position != 2);
- }
- public void onNothingSelected(AdapterView<?> arg0) {
- }
- });
+ private class PortForwardViewHolder extends ItemViewHolder {
+ public final TextView nickname;
+ public final TextView caption;
- new AlertDialog.Builder(PortForwardListActivity.this)
- .setView(editTunnelView)
- .setPositiveButton(R.string.button_change, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- try {
- if (hostBridge != null)
- hostBridge.disablePortForward(pfb);
+ public PortForwardBean portForward;
- pfb.setNickname(nicknameEdit.getText().toString());
+ public PortForwardViewHolder(View v) {
+ super(v);
- switch (typeSpinner.getSelectedItemPosition()) {
- case 0:
- pfb.setType(HostDatabase.PORTFORWARD_LOCAL);
- break;
- case 1:
- pfb.setType(HostDatabase.PORTFORWARD_REMOTE);
- break;
- case 2:
- pfb.setType(HostDatabase.PORTFORWARD_DYNAMIC5);
- break;
- }
+ nickname = (TextView) v.findViewById(android.R.id.text1);
+ caption = (TextView) v.findViewById(android.R.id.text2);
+ }
- pfb.setSourcePort(Integer.parseInt(sourcePortEdit.getText().toString()));
- pfb.setDest(destEdit.getText().toString());
+ @Override
+ public void onClick(View v) {
+ if (hostBridge != null) {
+ if (portForward.isEnabled())
+ hostBridge.disablePortForward(portForward);
+ else {
+ if (!hostBridge.enablePortForward(portForward))
+ Toast.makeText(PortForwardListActivity.this, getString(R.string.portforward_problem), Toast.LENGTH_LONG).show();
+ }
- // Use the new settings for the existing connection.
- if (hostBridge != null)
- updateHandler.postDelayed(new Runnable() {
- public void run() {
- hostBridge.enablePortForward(pfb);
- updateHandler.sendEmptyMessage(-1);
- }
- }, LISTENER_CYCLE_TIME);
+ updateHandler.sendEmptyMessage(-1);
+ }
+ }
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ // Create menu to handle deleting and editing port forward
+ AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
+
+ menu.setHeaderTitle(portForward.getNickname());
+
+ MenuItem edit = menu.add(R.string.portforward_edit);
+ edit.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ final View editTunnelView = inflater.inflate(R.layout.dia_portforward, null, false);
+
+ final Spinner typeSpinner = (Spinner) editTunnelView.findViewById(R.id.portforward_type);
+ if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType()))
+ typeSpinner.setSelection(0);
+ else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType()))
+ typeSpinner.setSelection(1);
+ else
+ typeSpinner.setSelection(2);
+
+ final EditText nicknameEdit = (EditText) editTunnelView.findViewById(R.id.nickname);
+ nicknameEdit.setText(portForward.getNickname());
+
+ final EditText sourcePortEdit = (EditText) editTunnelView.findViewById(R.id.portforward_source);
+ sourcePortEdit.setText(String.valueOf(portForward.getSourcePort()));
+
+ final EditText destEdit = (EditText) editTunnelView.findViewById(R.id.portforward_destination);
+ if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) {
+ destEdit.setEnabled(false);
+ } else {
+ destEdit.setText(String.format("%s:%d", portForward.getDestAddr(), portForward.getDestPort()));
+ }
- if (!hostdb.savePortForward(pfb)) {
- throw new SQLException("Could not save port forward");
- }
+ typeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+ public void onItemSelected(AdapterView<?> value, View view,
+ int position, long id) {
+ destEdit.setEnabled(position != 2);
+ }
- updateHandler.sendEmptyMessage(-1);
- } catch (Exception e) {
- Log.e(TAG, "Could not update port forward", e);
- // TODO Show failure dialog.
- }
+ public void onNothingSelected(AdapterView<?> arg0) {
}
- })
- .setNegativeButton(android.R.string.cancel, null).create().show();
+ });
+
+ new AlertDialog.Builder(PortForwardListActivity.this)
+ .setView(editTunnelView)
+ .setPositiveButton(R.string.button_change, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ try {
+ if (hostBridge != null)
+ hostBridge.disablePortForward(portForward);
+
+ portForward.setNickname(nicknameEdit.getText().toString());
+
+ switch (typeSpinner.getSelectedItemPosition()) {
+ case 0:
+ portForward.setType(HostDatabase.PORTFORWARD_LOCAL);
+ break;
+ case 1:
+ portForward.setType(HostDatabase.PORTFORWARD_REMOTE);
+ break;
+ case 2:
+ portForward.setType(HostDatabase.PORTFORWARD_DYNAMIC5);
+ break;
+ }
- return true;
- }
- });
+ portForward.setSourcePort(Integer.parseInt(sourcePortEdit.getText().toString()));
+ portForward.setDest(destEdit.getText().toString());
- MenuItem delete = menu.add(R.string.portforward_delete);
- delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
- public boolean onMenuItemClick(MenuItem item) {
- // prompt user to make sure they really want this
- new AlertDialog.Builder(PortForwardListActivity.this)
- .setMessage(getString(R.string.delete_message, pfb.getNickname()))
- .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- try {
- // Delete the port forward from the host if needed.
- if (hostBridge != null)
- hostBridge.removePortForward(pfb);
+ // Use the new settings for the existing connection.
+ if (hostBridge != null)
+ updateHandler.postDelayed(new Runnable() {
+ public void run() {
+ hostBridge.enablePortForward(portForward);
+ updateHandler.sendEmptyMessage(-1);
+ }
+ }, LISTENER_CYCLE_TIME);
- hostdb.deletePortForward(pfb);
- } catch (Exception e) {
- Log.e(TAG, "Could not delete port forward", e);
- }
- updateHandler.sendEmptyMessage(-1);
- }
- })
- .setNegativeButton(R.string.delete_neg, null).create().show();
+ if (!hostdb.savePortForward(portForward)) {
+ throw new SQLException("Could not save port forward");
+ }
- return true;
- }
- });
- }
+ updateHandler.sendEmptyMessage(-1);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not update port forward", e);
+ // TODO Show failure dialog.
+ }
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null).create().show();
- protected Handler updateHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- PortForwardListActivity.this.updateList();
- }
- };
+ return true;
+ }
+ });
+
+ MenuItem delete = menu.add(R.string.portforward_delete);
+ delete.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ // prompt user to make sure they really want this
+ new AlertDialog.Builder(PortForwardListActivity.this)
+ .setMessage(getString(R.string.delete_message, portForward.getNickname()))
+ .setPositiveButton(R.string.delete_pos, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ try {
+ // Delete the port forward from the host if needed.
+ if (hostBridge != null)
+ hostBridge.removePortForward(portForward);
+
+ hostdb.deletePortForward(portForward);
+ } catch (Exception e) {
+ Log.e(TAG, "Could not delete port forward", e);
+ }
+
+ updateHandler.sendEmptyMessage(-1);
+ }
+ })
+ .setNegativeButton(R.string.delete_neg, null).create().show();
- protected void updateList() {
- if (hostBridge != null) {
- this.portForwards = hostBridge.getPortForwards();
- } else {
- if (this.hostdb == null) return;
- this.portForwards = this.hostdb.getPortForwardsForHost(host);
+ return true;
+ }
+ });
}
-
- PortForwardAdapter adapter = new PortForwardAdapter(this, portForwards);
-
- this.setListAdapter(adapter);
}
- class PortForwardAdapter extends ArrayAdapter<PortForwardBean> {
- class ViewHolder {
- public TextView nickname;
- public TextView caption;
- }
-
- private List<PortForwardBean> portForwards;
+ @VisibleForTesting
+ private class PortForwardAdapter extends ItemAdapter {
+ private final List<PortForwardBean> portForwards;
public PortForwardAdapter(Context context, List<PortForwardBean> portForwards) {
- super(context, R.layout.item_portforward, portForwards);
-
+ super(context);
this.portForwards = portForwards;
}
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ViewHolder holder;
-
- if (convertView == null) {
- convertView = inflater.inflate(R.layout.item_portforward, null, false);
-
- holder = new ViewHolder();
- holder.nickname = (TextView) convertView.findViewById(android.R.id.text1);
- holder.caption = (TextView) convertView.findViewById(android.R.id.text2);
+ public PortForwardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_portforward, parent, false);
+ PortForwardViewHolder vh = new PortForwardViewHolder(v);
+ return vh;
+ }
- convertView.setTag(holder);
- } else
- holder = (ViewHolder) convertView.getTag();
+ @Override
+ public void onBindViewHolder(ItemViewHolder holder, int position) {
+ PortForwardViewHolder portForwardHolder = (PortForwardViewHolder) holder;
+ PortForwardBean portForward = portForwards.get(position);
- PortForwardBean pfb = portForwards.get(position);
- holder.nickname.setText(pfb.getNickname());
- holder.caption.setText(pfb.getDescription());
+ portForwardHolder.portForward = portForward;
+ portForwardHolder.nickname.setText(portForward.getNickname());
+ portForwardHolder.caption.setText(portForward.getDescription());
- if (hostBridge != null && !pfb.isEnabled()) {
- holder.nickname.setPaintFlags(holder.nickname.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
- holder.caption.setPaintFlags(holder.caption.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ if (hostBridge != null && !portForward.isEnabled()) {
+ portForwardHolder.nickname.setPaintFlags(portForwardHolder.nickname.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
+ portForwardHolder.caption.setPaintFlags(portForwardHolder.caption.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
+ }
- return convertView;
+ @Override
+ public long getItemId(int position) {
+ return portForwards.get(position).getId();
+ }
+
+ @Override
+ public int getItemCount() {
+ return portForwards.size();
}
}
}
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<PubkeyAdapter.ViewHolder> {
- private final LayoutInflater inflater;
- private final List<PubkeyBean> 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<PubkeyBean> pubkeys;
public PubkeyAdapter(Context context, List<PubkeyBean> 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();
}
}
}
diff --git a/app/src/main/res/layout/act_portforwardlist.xml b/app/src/main/res/layout/act_portforwardlist.xml
index 46393ae..a11bb1b 100644
--- a/app/src/main/res/layout/act_portforwardlist.xml
+++ b/app/src/main/res/layout/act_portforwardlist.xml
@@ -24,15 +24,15 @@
android:orientation="vertical"
>
- <ListView
- android:id="@android:id/list"
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/list"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1"
/>
<TextView
- android:id="@android:id/empty"
+ android:id="@+id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"