reworked keyserver preferences with recyclerview
@@ -34,6 +34,7 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
|
||||
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.List;
|
||||
@@ -123,27 +124,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_KEYSERVER_PREF: {
|
||||
if (resultCode == RESULT_CANCELED || data == null) {
|
||||
return;
|
||||
}
|
||||
String servers[] = data
|
||||
.getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
|
||||
sPreferences.setKeyServers(servers);
|
||||
mKeyServerPreference.setSummary(keyserverSummary(this));
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuildHeaders(List<Header> target) {
|
||||
super.onBuildHeaders(target);
|
||||
@@ -190,12 +170,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_KEYSERVER_PREF: {
|
||||
if (resultCode == RESULT_CANCELED || data == null) {
|
||||
return;
|
||||
}
|
||||
String servers[] = data
|
||||
.getStringArrayExtra(SettingsKeyServerActivity.EXTRA_KEY_SERVERS);
|
||||
sPreferences.setKeyServers(servers);
|
||||
// update preference, in case it changed
|
||||
mKeyServerPreference.setSummary(keyserverSummary(getActivity()));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,89 +17,23 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.AddKeyserverDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.widget.Editor;
|
||||
import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener;
|
||||
import org.sufficientlysecure.keychain.ui.widget.KeyServerEditor;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
public class SettingsKeyServerActivity extends BaseActivity implements OnClickListener,
|
||||
EditorListener {
|
||||
public class SettingsKeyServerActivity extends BaseActivity {
|
||||
|
||||
public static final String EXTRA_KEY_SERVERS = "key_servers";
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
private ViewGroup mEditors;
|
||||
private View mAdd;
|
||||
private View mRotate;
|
||||
private TextView mTitle;
|
||||
private TextView mSummary;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Inflate a "Done"/"Cancel" custom action bar view
|
||||
setFullScreenDialogDoneClose(R.string.btn_save,
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
okClicked();
|
||||
}
|
||||
},
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
cancelClicked();
|
||||
}
|
||||
});
|
||||
|
||||
mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
mTitle = (TextView) findViewById(R.id.title);
|
||||
mSummary = (TextView) findViewById(R.id.summary);
|
||||
mSummary.setText(getText(R.string.label_first_keyserver_is_used));
|
||||
|
||||
mTitle.setText(R.string.label_keyservers);
|
||||
|
||||
mEditors = (ViewGroup) findViewById(R.id.editors);
|
||||
mAdd = findViewById(R.id.add);
|
||||
mAdd.setOnClickListener(this);
|
||||
|
||||
mRotate = findViewById(R.id.rotate);
|
||||
mRotate.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Vector<String> servers = serverList();
|
||||
String first = servers.get(0);
|
||||
if (first != null) {
|
||||
servers.remove(0);
|
||||
servers.add(first);
|
||||
String[] dummy = {};
|
||||
makeServerList(servers.toArray(dummy));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Intent intent = getIntent();
|
||||
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS);
|
||||
makeServerList(servers);
|
||||
loadFragment(savedInstanceState, servers);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,118 +41,22 @@ public class SettingsKeyServerActivity extends BaseActivity implements OnClickLi
|
||||
setContentView(R.layout.key_server_preference);
|
||||
}
|
||||
|
||||
private void makeServerList(String[] servers) {
|
||||
if (servers != null) {
|
||||
mEditors.removeAllViews();
|
||||
for (String serv : servers) {
|
||||
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(
|
||||
R.layout.key_server_editor, mEditors, false);
|
||||
view.setEditorListener(this);
|
||||
view.setValue(serv);
|
||||
mEditors.addView(view);
|
||||
}
|
||||
private void loadFragment(Bundle savedInstanceState, String[] keyservers) {
|
||||
// However, if we're being restored from a previous state,
|
||||
// then we don't need to do anything and should return or else
|
||||
// we could end up with overlapping fragments.
|
||||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void onDeleted(Editor editor, boolean wasNewItem) {
|
||||
// nothing to do
|
||||
}
|
||||
SettingsKeyserverFragment fragment = SettingsKeyserverFragment.newInstance(keyservers);
|
||||
|
||||
@Override
|
||||
public void onEdited() {
|
||||
|
||||
}
|
||||
|
||||
// button to add keyserver clicked
|
||||
public void onClick(View v) {
|
||||
Handler returnHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
Bundle data = message.getData();
|
||||
switch (message.what) {
|
||||
case AddKeyserverDialogFragment.MESSAGE_OKAY: {
|
||||
boolean verified = data.getBoolean(AddKeyserverDialogFragment.MESSAGE_VERIFIED);
|
||||
if (verified) {
|
||||
Notify.create(SettingsKeyServerActivity.this,
|
||||
R.string.add_keyserver_verified, Notify.Style.OK).show();
|
||||
} else {
|
||||
Notify.create(SettingsKeyServerActivity.this,
|
||||
R.string.add_keyserver_without_verification,
|
||||
Notify.Style.WARN).show();
|
||||
}
|
||||
String keyserver = data.getString(AddKeyserverDialogFragment.MESSAGE_KEYSERVER);
|
||||
addKeyserver(keyserver);
|
||||
break;
|
||||
}
|
||||
case AddKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: {
|
||||
AddKeyserverDialogFragment.FailureReason failureReason =
|
||||
(AddKeyserverDialogFragment.FailureReason) data.getSerializable(
|
||||
AddKeyserverDialogFragment.MESSAGE_FAILURE_REASON);
|
||||
switch (failureReason) {
|
||||
case CONNECTION_FAILED: {
|
||||
Notify.create(SettingsKeyServerActivity.this,
|
||||
R.string.add_keyserver_connection_failed,
|
||||
Notify.Style.ERROR).show();
|
||||
break;
|
||||
}
|
||||
case INVALID_URL: {
|
||||
Notify.create(SettingsKeyServerActivity.this,
|
||||
R.string.add_keyserver_invalid_url,
|
||||
Notify.Style.ERROR).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
AddKeyserverDialogFragment dialogFragment = AddKeyserverDialogFragment
|
||||
.newInstance(messenger);
|
||||
dialogFragment.show(getSupportFragmentManager(), "addKeyserverDialog");
|
||||
}
|
||||
|
||||
public void addKeyserver(String keyserverUrl) {
|
||||
KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor,
|
||||
mEditors, false);
|
||||
view.setEditorListener(this);
|
||||
view.setValue(keyserverUrl);
|
||||
mEditors.addView(view);
|
||||
}
|
||||
|
||||
private void cancelClicked() {
|
||||
setResult(RESULT_CANCELED, null);
|
||||
finish();
|
||||
}
|
||||
|
||||
private Vector<String> serverList() {
|
||||
Vector<String> servers = new Vector<>();
|
||||
for (int i = 0; i < mEditors.getChildCount(); ++i) {
|
||||
KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
|
||||
String tmp = editor.getValue();
|
||||
if (tmp.length() > 0) {
|
||||
servers.add(tmp);
|
||||
}
|
||||
}
|
||||
return servers;
|
||||
}
|
||||
|
||||
private void okClicked() {
|
||||
Intent data = new Intent();
|
||||
Vector<String> servers = new Vector<>();
|
||||
for (int i = 0; i < mEditors.getChildCount(); ++i) {
|
||||
KeyServerEditor editor = (KeyServerEditor) mEditors.getChildAt(i);
|
||||
String tmp = editor.getValue();
|
||||
if (tmp.length() > 0) {
|
||||
servers.add(tmp);
|
||||
}
|
||||
}
|
||||
String[] dummy = new String[0];
|
||||
data.putExtra(EXTRA_KEY_SERVERS, servers.toArray(dummy));
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
// Add the fragment to the 'fragment_container' FrameLayout
|
||||
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.replace(R.id.keyserver_settings_fragment_container, fragment)
|
||||
.commitAllowingStateLoss();
|
||||
// do it immediately!
|
||||
getSupportFragmentManager().executePendingTransactions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class SettingsKeyserverFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener {
|
||||
|
||||
private static final String ARG_KEYSERVER_ARRAY = "arg_keyserver_array";
|
||||
private ItemTouchHelper mItemTouchHelper;
|
||||
|
||||
private ArrayList<String> mKeyservers;
|
||||
private KeyserverListAdapter mAdapter;
|
||||
|
||||
public static SettingsKeyserverFragment newInstance(String[] keyservers) {
|
||||
Bundle args = new Bundle();
|
||||
args.putStringArray(ARG_KEYSERVER_ARRAY, keyservers);
|
||||
|
||||
SettingsKeyserverFragment fragment = new SettingsKeyserverFragment();
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
|
||||
savedInstanceState) {
|
||||
|
||||
return inflater.inflate(R.layout.settings_keyserver_fragment, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
String keyservers[] = getArguments().getStringArray(ARG_KEYSERVER_ARRAY);
|
||||
mKeyservers = new ArrayList<>(Arrays.asList(keyservers));
|
||||
saveKeyserverList(); // in case user does not make any changes
|
||||
|
||||
mAdapter = new KeyserverListAdapter(mKeyservers);
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.keyserver_recycler_view);
|
||||
// recyclerView.setHasFixedSize(true); // the size of the first item changes
|
||||
recyclerView.setAdapter(mAdapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||
|
||||
|
||||
ItemTouchHelper.Callback callback = new ItemTouchHelperDragCallback(mAdapter);
|
||||
mItemTouchHelper = new ItemTouchHelper(callback);
|
||||
mItemTouchHelper.attachToRecyclerView(recyclerView);
|
||||
|
||||
// for clicks
|
||||
recyclerView.addOnItemTouchListener(new RecyclerItemClickListener(getActivity(), this));
|
||||
|
||||
// can't use item decoration because it doesn't move with drag and drop
|
||||
// recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
|
||||
|
||||
// We have a menu item to show in action bar.
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.keyserver_pref_menu, menu);
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
||||
case R.id.menu_add_keyserver:
|
||||
startAddKeyserverDialog();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAddKeyserverDialog() {
|
||||
// keyserver and position have no meaning
|
||||
startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction.ADD, null, -1);
|
||||
}
|
||||
|
||||
private void startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction action,
|
||||
String keyserver, final int position) {
|
||||
Handler returnHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
Bundle data = message.getData();
|
||||
switch (message.what) {
|
||||
case AddEditKeyserverDialogFragment.MESSAGE_OKAY: {
|
||||
boolean deleted =
|
||||
data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER_DELETED
|
||||
, false);
|
||||
if (deleted) {
|
||||
Notify.create(getActivity(),
|
||||
getActivity().getString(
|
||||
R.string.keyserver_deleted, mKeyservers.get(position)),
|
||||
Notify.Style.OK)
|
||||
.show();
|
||||
deleteKeyserver(position);
|
||||
return;
|
||||
}
|
||||
boolean verified =
|
||||
data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_VERIFIED);
|
||||
if (verified) {
|
||||
Notify.create(getActivity(),
|
||||
R.string.add_keyserver_verified, Notify.Style.OK).show();
|
||||
} else {
|
||||
Notify.create(getActivity(),
|
||||
R.string.add_keyserver_without_verification,
|
||||
Notify.Style.WARN).show();
|
||||
}
|
||||
String keyserver = data.getString(
|
||||
AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER);
|
||||
|
||||
AddEditKeyserverDialogFragment.DialogAction dialogAction
|
||||
= (AddEditKeyserverDialogFragment.DialogAction) data.getSerializable(
|
||||
AddEditKeyserverDialogFragment.MESSAGE_DIALOG_ACTION);
|
||||
switch (dialogAction) {
|
||||
case ADD:
|
||||
addKeyserver(keyserver);
|
||||
break;
|
||||
case EDIT:
|
||||
editKeyserver(keyserver, position);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AddEditKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: {
|
||||
AddEditKeyserverDialogFragment.FailureReason failureReason =
|
||||
(AddEditKeyserverDialogFragment.FailureReason) data.getSerializable(
|
||||
AddEditKeyserverDialogFragment.MESSAGE_FAILURE_REASON);
|
||||
switch (failureReason) {
|
||||
case CONNECTION_FAILED: {
|
||||
Notify.create(getActivity(),
|
||||
R.string.add_keyserver_connection_failed,
|
||||
Notify.Style.ERROR).show();
|
||||
break;
|
||||
}
|
||||
case INVALID_URL: {
|
||||
Notify.create(getActivity(),
|
||||
R.string.add_keyserver_invalid_url,
|
||||
Notify.Style.ERROR).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
AddEditKeyserverDialogFragment dialogFragment = AddEditKeyserverDialogFragment
|
||||
.newInstance(messenger, action, keyserver, position);
|
||||
dialogFragment.show(getFragmentManager(), "addKeyserverDialog");
|
||||
}
|
||||
|
||||
private void addKeyserver(String keyserver) {
|
||||
mKeyservers.add(keyserver);
|
||||
mAdapter.notifyItemInserted(mKeyservers.size() - 1);
|
||||
saveKeyserverList();
|
||||
}
|
||||
|
||||
private void editKeyserver(String newKeyserver, int position) {
|
||||
mKeyservers.set(position, newKeyserver);
|
||||
mAdapter.notifyItemChanged(position);
|
||||
saveKeyserverList();
|
||||
}
|
||||
|
||||
private void deleteKeyserver(int position) {
|
||||
mKeyservers.remove(position);
|
||||
// we use this
|
||||
mAdapter.notifyItemRemoved(position);
|
||||
if (position == 0 && mKeyservers.size() > 0) {
|
||||
// if we deleted the first item, we need the adapter to redraw the new first item
|
||||
mAdapter.notifyItemChanged(0);
|
||||
}
|
||||
saveKeyserverList();
|
||||
}
|
||||
|
||||
private void saveKeyserverList() {
|
||||
String servers[] = mKeyservers.toArray(new String[mKeyservers.size()]);
|
||||
Preferences.getPreferences(getActivity()).setKeyServers(servers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(View view, int position) {
|
||||
startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction.EDIT,
|
||||
mKeyservers.get(position), position);
|
||||
}
|
||||
|
||||
public class KeyserverListAdapter extends RecyclerView.Adapter<KeyserverListAdapter.ViewHolder>
|
||||
implements ItemTouchHelperAdapter {
|
||||
|
||||
// to update the ViewHolder associated with first item, for when an item is deleted
|
||||
private ViewHolder mFirstItem;
|
||||
|
||||
private final List<String> mKeyservers;
|
||||
|
||||
public KeyserverListAdapter(List<String> keyservers) {
|
||||
mKeyservers = keyservers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.settings_keyserver_item, parent, false);
|
||||
ViewHolder viewHolder = new ViewHolder(view);
|
||||
return viewHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||
if (position == 0) {
|
||||
mFirstItem = holder;
|
||||
}
|
||||
holder.keyserverUrl.setText(mKeyservers.get(position));
|
||||
|
||||
// Start a drag whenever the handle view it touched
|
||||
holder.dragHandleView.setOnTouchListener(new View.OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
|
||||
mItemTouchHelper.startDrag(holder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
selectUnselectKeyserver(holder, position);
|
||||
}
|
||||
|
||||
private void selectUnselectKeyserver(ViewHolder holder, int position) {
|
||||
|
||||
if (position == 0) {
|
||||
holder.showAsSelectedKeyserver();
|
||||
} else {
|
||||
holder.showAsUnselectedKeyserver();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target,
|
||||
int fromPosition, int toPosition) {
|
||||
Collections.swap(mKeyservers, fromPosition, toPosition);
|
||||
saveKeyserverList();
|
||||
selectUnselectKeyserver((ViewHolder) target, fromPosition);
|
||||
// we don't want source to change color while dragging, therefore we just set
|
||||
// isSelectedKeyserver instead of selectUnselectKeyserver
|
||||
((ViewHolder) source).isSelectedKeyserver = toPosition == 0;
|
||||
|
||||
notifyItemMoved(fromPosition, toPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mKeyservers.size();
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements
|
||||
ItemTouchHelperViewHolder {
|
||||
|
||||
public final ViewGroup outerLayout;
|
||||
public final TextView selectedServerLabel;
|
||||
public final TextView keyserverUrl;
|
||||
public final ImageView dragHandleView;
|
||||
|
||||
private boolean isSelectedKeyserver = false;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
outerLayout = (ViewGroup) itemView.findViewById(R.id.outer_layout);
|
||||
selectedServerLabel = (TextView) itemView.findViewById(
|
||||
R.id.selected_keyserver_title);
|
||||
keyserverUrl = (TextView) itemView.findViewById(R.id.keyserver_tv);
|
||||
dragHandleView = (ImageView) itemView.findViewById(R.id.drag_handle);
|
||||
|
||||
itemView.setClickable(true);
|
||||
}
|
||||
|
||||
public void showAsSelectedKeyserver() {
|
||||
isSelectedKeyserver = true;
|
||||
selectedServerLabel.setVisibility(View.VISIBLE);
|
||||
outerLayout.setBackgroundColor(getResources().getColor(R.color.android_green_dark));
|
||||
}
|
||||
|
||||
public void showAsUnselectedKeyserver() {
|
||||
isSelectedKeyserver = false;
|
||||
selectedServerLabel.setVisibility(View.GONE);
|
||||
outerLayout.setBackgroundColor(Color.WHITE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected() {
|
||||
selectedServerLabel.setVisibility(View.GONE);
|
||||
itemView.setBackgroundColor(Color.LTGRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClear() {
|
||||
if (isSelectedKeyserver) {
|
||||
showAsSelectedKeyserver();
|
||||
} else {
|
||||
showAsUnselectedKeyserver();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,11 @@ import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.TlsHelper;
|
||||
|
||||
public class AddKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
private static final String ARG_MESSENGER = "messenger";
|
||||
public class AddEditKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
private static final String ARG_MESSENGER = "arg_messenger";
|
||||
private static final String ARG_ACTION = "arg_dialog_action";
|
||||
private static final String ARG_POSITION = "arg_position";
|
||||
private static final String ARG_KEYSERVER = "arg_keyserver";
|
||||
|
||||
public static final int MESSAGE_OKAY = 1;
|
||||
public static final int MESSAGE_VERIFICATION_FAILED = 2;
|
||||
@@ -62,20 +65,37 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
|
||||
public static final String MESSAGE_KEYSERVER = "new_keyserver";
|
||||
public static final String MESSAGE_VERIFIED = "verified";
|
||||
public static final String MESSAGE_FAILURE_REASON = "failure_reason";
|
||||
public static final String MESSAGE_KEYSERVER_DELETED = "keyserver_deleted";
|
||||
public static final String MESSAGE_DIALOG_ACTION = "message_dialog_action";
|
||||
public static final String MESSAGE_EDIT_POSITION = "keyserver_edited_position";
|
||||
|
||||
private Messenger mMessenger;
|
||||
private DialogAction mDialogAction;
|
||||
private int mPosition;
|
||||
|
||||
private EditText mKeyserverEditText;
|
||||
private CheckBox mVerifyKeyserverCheckBox;
|
||||
|
||||
public enum DialogAction {
|
||||
ADD,
|
||||
EDIT
|
||||
}
|
||||
|
||||
public enum FailureReason {
|
||||
INVALID_URL,
|
||||
CONNECTION_FAILED
|
||||
}
|
||||
|
||||
public static AddKeyserverDialogFragment newInstance(Messenger messenger) {
|
||||
AddKeyserverDialogFragment frag = new AddKeyserverDialogFragment();
|
||||
public static AddEditKeyserverDialogFragment newInstance(Messenger messenger,
|
||||
DialogAction action,
|
||||
String keyserver,
|
||||
int position) {
|
||||
AddEditKeyserverDialogFragment frag = new AddEditKeyserverDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(ARG_MESSENGER, messenger);
|
||||
args.putSerializable(ARG_ACTION, action);
|
||||
args.putString(ARG_KEYSERVER, keyserver);
|
||||
args.putInt(ARG_POSITION, position);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
@@ -88,11 +108,11 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
|
||||
final Activity activity = getActivity();
|
||||
|
||||
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||
mDialogAction = (DialogAction) getArguments().getSerializable(ARG_ACTION);
|
||||
mPosition = getArguments().getInt(ARG_POSITION);
|
||||
|
||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
|
||||
|
||||
alert.setTitle(R.string.add_keyserver_dialog_title);
|
||||
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View view = inflater.inflate(R.layout.add_keyserver_dialog, null);
|
||||
alert.setView(view);
|
||||
@@ -100,14 +120,26 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
|
||||
mKeyserverEditText = (EditText) view.findViewById(R.id.keyserver_url_edit_text);
|
||||
mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_keyserver_checkbox);
|
||||
|
||||
// we don't want dialog to be dismissed on click, thereby requiring the hack seen below
|
||||
// and in onStart
|
||||
switch (mDialogAction) {
|
||||
case ADD: {
|
||||
alert.setTitle(R.string.add_keyserver_dialog_title);
|
||||
break;
|
||||
}
|
||||
case EDIT: {
|
||||
alert.setTitle(R.string.edit_keyserver_dialog_title);
|
||||
mKeyserverEditText.setText(getArguments().getString(ARG_KEYSERVER));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we don't want dialog to be dismissed on click for keyserver addition or edit,
|
||||
// thereby requiring the hack seen below and in onStart
|
||||
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// we need to have an empty listener to prevent errors on some devices as mentioned
|
||||
// at http://stackoverflow.com/q/13746412/3000919
|
||||
// actual listener set in onStart
|
||||
// actual listener set in onStart for adding keyservers or editing them
|
||||
}
|
||||
});
|
||||
|
||||
@@ -119,6 +151,23 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
|
||||
}
|
||||
});
|
||||
|
||||
switch (mDialogAction) {
|
||||
case EDIT: {
|
||||
alert.setNeutralButton(R.string.label_keyserver_dialog_delete,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
deleteKeyserver(mPosition);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case ADD: {
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Hack to open keyboard.
|
||||
// This is the only method that I found to work across all Android versions
|
||||
// http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
|
||||
@@ -155,25 +204,41 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
|
||||
positiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// behaviour same for edit and add
|
||||
String keyserverUrl = mKeyserverEditText.getText().toString();
|
||||
if (mVerifyKeyserverCheckBox.isChecked()) {
|
||||
verifyConnection(keyserverUrl);
|
||||
} else {
|
||||
dismiss();
|
||||
// return unverified keyserver back to activity
|
||||
addKeyserver(keyserverUrl, false);
|
||||
keyserverEdited(keyserverUrl, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void addKeyserver(String keyserver, boolean verified) {
|
||||
public void keyserverEdited(String keyserver, boolean verified) {
|
||||
dismiss();
|
||||
Bundle data = new Bundle();
|
||||
data.putSerializable(MESSAGE_DIALOG_ACTION, mDialogAction);
|
||||
data.putString(MESSAGE_KEYSERVER, keyserver);
|
||||
data.putBoolean(MESSAGE_VERIFIED, verified);
|
||||
|
||||
if (mDialogAction == DialogAction.EDIT) {
|
||||
data.putInt(MESSAGE_EDIT_POSITION, mPosition);
|
||||
}
|
||||
|
||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||
}
|
||||
|
||||
public void deleteKeyserver(int position) {
|
||||
dismiss();
|
||||
Bundle data = new Bundle();
|
||||
data.putSerializable(MESSAGE_DIALOG_ACTION, DialogAction.EDIT);
|
||||
data.putInt(MESSAGE_EDIT_POSITION, position);
|
||||
data.putBoolean(MESSAGE_KEYSERVER_DELETED, true);
|
||||
|
||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||
}
|
||||
|
||||
@@ -238,7 +303,7 @@ public class AddKeyserverDialogFragment extends DialogFragment implements OnEdit
|
||||
protected void onPostExecute(FailureReason failureReason) {
|
||||
mProgressDialog.dismiss();
|
||||
if (failureReason == null) {
|
||||
addKeyserver(mKeyserver, true);
|
||||
keyserverEdited(mKeyserver, true);
|
||||
} else {
|
||||
verificationFailed(failureReason);
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.ui.util.recyclerview;
|
||||
|
||||
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.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
|
||||
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private static final int[] ATTRS = new int[]{
|
||||
android.R.attr.listDivider
|
||||
};
|
||||
|
||||
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
|
||||
|
||||
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
|
||||
|
||||
private Drawable mDivider;
|
||||
|
||||
private int mOrientation;
|
||||
|
||||
public DividerItemDecoration(Context context, int orientation) {
|
||||
final TypedArray a = context.obtainStyledAttributes(ATTRS);
|
||||
mDivider = a.getDrawable(0);
|
||||
a.recycle();
|
||||
setOrientation(orientation);
|
||||
}
|
||||
|
||||
public void setOrientation(int orientation) {
|
||||
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
|
||||
throw new IllegalArgumentException("invalid orientation");
|
||||
}
|
||||
mOrientation = orientation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
|
||||
if (mOrientation == VERTICAL_LIST) {
|
||||
drawVertical(c, parent);
|
||||
} else {
|
||||
drawHorizontal(c, parent);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawVertical(Canvas c, RecyclerView parent) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawHorizontal(Canvas c, RecyclerView parent) {
|
||||
final int top = parent.getPaddingTop();
|
||||
final int bottom = parent.getHeight() - parent.getPaddingBottom();
|
||||
|
||||
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 left = child.getRight() + params.rightMargin;
|
||||
final int right = left + mDivider.getIntrinsicHeight();
|
||||
mDivider.setBounds(left, top, right, bottom);
|
||||
mDivider.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
|
||||
RecyclerView.State state) {
|
||||
if (mOrientation == VERTICAL_LIST) {
|
||||
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
|
||||
} else {
|
||||
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Paul Burke
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.ui.util.recyclerview;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
|
||||
/**
|
||||
* Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}.
|
||||
*/
|
||||
public interface ItemTouchHelperAdapter {
|
||||
|
||||
/**
|
||||
* Called when an item has been dragged far enough to trigger a move. This is called every time
|
||||
* an item is shifted, and <strong>not</strong> at the end of a "drop" event.<br/>
|
||||
* <br/>
|
||||
* Implementations should call {@link RecyclerView.Adapter#notifyItemMoved(int, int)} after
|
||||
* adjusting the underlying data to reflect this move.
|
||||
*
|
||||
* @param fromPosition The start position of the moved item.
|
||||
* @param toPosition Then resolved position of the moved item.
|
||||
* @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder)
|
||||
* @see RecyclerView.ViewHolder#getAdapterPosition()
|
||||
*/
|
||||
void onItemMove(RecyclerView.ViewHolder source, RecyclerView.ViewHolder target,
|
||||
int fromPosition, int toPosition);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Paul Burke
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.ui.util.recyclerview;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ItemTouchHelper.Callback} that enables basic drag & drop and
|
||||
* swipe-to-dismiss. Drag events are automatically started by an item long-press.<br/>
|
||||
* </br/>
|
||||
* Expects the <code>RecyclerView.Adapter</code> to listen for {@link
|
||||
* ItemTouchHelperAdapter} callbacks and the <code>RecyclerView.ViewHolder</code> to implement
|
||||
* {@link ItemTouchHelperViewHolder}.
|
||||
*/
|
||||
public class ItemTouchHelperDragCallback extends ItemTouchHelper.Callback {
|
||||
|
||||
private final ItemTouchHelperAdapter mAdapter;
|
||||
|
||||
public ItemTouchHelperDragCallback(ItemTouchHelperAdapter adapter) {
|
||||
mAdapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLongPressDragEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemViewSwipeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
// Enable drag and swipe in both directions
|
||||
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
|
||||
final int swipeFlags = 0;
|
||||
return makeMovementFlags(dragFlags, swipeFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source,
|
||||
RecyclerView.ViewHolder target) {
|
||||
if (source.getItemViewType() != target.getItemViewType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Notify the adapter of the move
|
||||
mAdapter.onItemMove(source, target, source.getAdapterPosition(), target.getAdapterPosition());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
|
||||
// we don't support swipe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
|
||||
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
|
||||
// Let the view holder know that this item is being moved or dragged
|
||||
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||
itemViewHolder.onItemSelected();
|
||||
}
|
||||
|
||||
super.onSelectedChanged(viewHolder, actionState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
|
||||
super.clearView(recyclerView, viewHolder);
|
||||
|
||||
// Tell the view holder it's time to restore the idle state
|
||||
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
|
||||
itemViewHolder.onItemClear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Paul Burke
|
||||
*
|
||||
* 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.sufficientlysecure.keychain.ui.util.recyclerview;
|
||||
|
||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||
|
||||
/**
|
||||
* Interface to notify an item ViewHolder of relevant callbacks from {@link
|
||||
* android.support.v7.widget.helper.ItemTouchHelper.Callback}.
|
||||
*/
|
||||
public interface ItemTouchHelperViewHolder {
|
||||
|
||||
/**
|
||||
* Called when the {@link ItemTouchHelper} first registers an item as being moved or swiped.
|
||||
* Implementations should update the item view to indicate it's active state.
|
||||
*/
|
||||
void onItemSelected();
|
||||
|
||||
|
||||
/**
|
||||
* Called when the {@link ItemTouchHelper} has completed the move or swipe, and the active item
|
||||
* state should be cleared.
|
||||
*/
|
||||
void onItemClear();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jacob Tabak
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.sufficientlysecure.keychain.ui.util.recyclerview;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* based on http://stackoverflow.com/a/26196831/3000919
|
||||
*/
|
||||
public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
|
||||
private OnItemClickListener mListener;
|
||||
private boolean mIgnoreTouch = false;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(View view, int position);
|
||||
}
|
||||
|
||||
GestureDetector mGestureDetector;
|
||||
|
||||
public RecyclerItemClickListener(Context context, OnItemClickListener listener) {
|
||||
mListener = listener;
|
||||
mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
|
||||
if (mIgnoreTouch) {
|
||||
return false;
|
||||
}
|
||||
View childView = view.findChildViewUnder(e.getX(), e.getY());
|
||||
if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
|
||||
mListener.onItemClick(childView, view.getChildAdapterPosition(childView));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) {
|
||||
// TODO: should we move mListener.onItemClick here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
mIgnoreTouch = disallowIntercept;
|
||||
}
|
||||
}
|
||||
BIN
OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.png
Normal file
|
After Width: | Height: | Size: 127 B |
|
After Width: | Height: | Size: 134 B |
BIN
OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.png
Normal file
|
After Width: | Height: | Size: 88 B |
|
After Width: | Height: | Size: 101 B |
BIN
OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.png
Normal file
|
After Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 138 B |
BIN
OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png
Normal file
|
After Width: | Height: | Size: 97 B |
|
After Width: | Height: | Size: 186 B |
BIN
OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png
Normal file
|
After Width: | Height: | Size: 102 B |
@@ -1,94 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/toolbar_include"
|
||||
layout="@layout/toolbar_standalone" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_below="@id/toolbar_include"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_marginLeft="6sp"
|
||||
android:layout_marginRight="6sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||
android:text="@string/label_keyservers_title"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/text_layout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="6sp"
|
||||
android:layout_marginRight="6sp"
|
||||
android:layout_marginBottom="6sp"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/label_keyserver_settings_hint"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="6sp"
|
||||
android:layout_marginLeft="16sp"
|
||||
android:layout_marginRight="6sp"
|
||||
android:layout_marginTop="6sp"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:drawable/menuitem_background"
|
||||
android:orientation="vertical"
|
||||
android:focusable="true">
|
||||
<View
|
||||
style="@style/Divider"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?android:attr/textAppearanceLarge" />
|
||||
<FrameLayout
|
||||
android:id="@+id/keyserver_settings_fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/rotate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="31dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginLeft="4dip"
|
||||
android:layout_marginRight="6dip"
|
||||
android:text="rotate"
|
||||
android:textColor="#ffffffff"
|
||||
android:textStyle="bold"
|
||||
android:background="@drawable/button_rounded_blue" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/add"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_margin="10dp"
|
||||
android:src="@drawable/plus"
|
||||
android:background="@drawable/button_rounded_green" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/separator"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="1dip"
|
||||
android:background="?android:attr/listDivider" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="0dip"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/editors"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/keyserver_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
46
OpenKeychain/src/main/res/layout/settings_keyserver_item.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/outer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?listPreferredItemHeight">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/keyserver_layout"
|
||||
android:padding="6sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toLeftOf="@+id/drag_handle"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/selected_keyserver_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/label_selected_keyserver_title"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/keyserver_tv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/drag_handle"
|
||||
android:layout_width="?listPreferredItemHeight"
|
||||
android:layout_height="?listPreferredItemHeight"
|
||||
android:scaleType="center"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:src="@drawable/ic_reorder_grey_500_24dp" />
|
||||
|
||||
<View
|
||||
android:layout_alignParentBottom="true"
|
||||
style="@style/Divider"/>
|
||||
|
||||
</RelativeLayout>
|
||||
10
OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_add_keyserver"
|
||||
android:title="@string/menu_search"
|
||||
android:icon="@drawable/ic_add_white_24dp"
|
||||
app:showAsAction="always" />
|
||||
</menu>
|
||||
@@ -109,6 +109,8 @@
|
||||
<string name="menu_certify_fingerprint">"Confirm via fingerprint comparison"</string>
|
||||
<string name="menu_export_log">"Export Log"</string>
|
||||
|
||||
<string name="menu_keyserver_add">"Add"</string>
|
||||
|
||||
<!-- label -->
|
||||
<string name="label_message">"Text"</string>
|
||||
<string name="label_file">"File"</string>
|
||||
@@ -153,13 +155,17 @@
|
||||
<string name="label_send_key">"Synchronize with the cloud"</string>
|
||||
<string name="label_fingerprint">"Fingerprint"</string>
|
||||
<string name="expiry_date_dialog_title">"Set expiry date"</string>
|
||||
<string name="label_first_keyserver_is_used">"(First keyserver listed is preferred)"</string>
|
||||
<string name="label_keyservers_title">"Keyservers"</string>
|
||||
<string name="label_keyserver_settings_hint">"Drag to change order, tap to edit/delete"</string>
|
||||
<string name="label_selected_keyserver_title">"Selected keyserver"</string>
|
||||
<string name="label_preferred">"preferred"</string>
|
||||
<string name="label_enable_compression">"Enable compression"</string>
|
||||
<string name="label_encrypt_filenames">"Encrypt filenames"</string>
|
||||
<string name="label_hidden_recipients">"Hide recipients"</string>
|
||||
|
||||
<string name="label_verify_keyserver">"Verify keyserver"</string>
|
||||
<string name="label_enter_keyserver_url">"Enter keyserver URL"</string>
|
||||
<string name="label_keyserver_dialog_delete">"Delete keyserver"</string>
|
||||
|
||||
<string name="pref_keyserver">"OpenPGP keyservers"</string>
|
||||
<string name="pref_keyserver_summary">"Search keys on selected OpenPGP keyservers (HKP protocol)"</string>
|
||||
@@ -672,12 +678,14 @@
|
||||
|
||||
<string name="view_key_fragment_no_system_contact">"<none>"</string>
|
||||
|
||||
<!-- Add keyserver -->
|
||||
<!-- Add/Edit keyserver -->
|
||||
<string name="add_keyserver_dialog_title">"Add keyserver"</string>
|
||||
<string name="edit_keyserver_dialog_title">"Edit keyserver"</string>
|
||||
<string name="add_keyserver_verified">"Keyserver verified!"</string>
|
||||
<string name="add_keyserver_without_verification">"Keyserver added without verification."</string>
|
||||
<string name="add_keyserver_invalid_url">"Invalid URL!"</string>
|
||||
<string name="add_keyserver_connection_failed">"Failed to connect to keyserver. Please check the URL and your internet connection."</string>
|
||||
<string name="keyserver_deleted">"%s deleted"</string>
|
||||
|
||||
<!-- Navigation Drawer -->
|
||||
<string name="nav_keys">"Keys"</string>
|
||||
|
||||
@@ -26,4 +26,12 @@
|
||||
<item name="android:textColor">@color/white</item>
|
||||
</style>
|
||||
|
||||
<!-- This style is for use with our drag and drop RecyclerView since ItemDecoration did not
|
||||
move with the drag -->
|
||||
<style name="Divider">
|
||||
<item name="android:layout_width">match_parent</item>
|
||||
<item name="android:layout_height">2dp</item>
|
||||
<item name="android:background">?android:attr/listDivider</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||