diff options
Diffstat (limited to 'OpenKeychain/src')
24 files changed, 917 insertions, 306 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index a552e1c55..6fc0aaac6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -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; @@ -124,27 +125,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); loadHeadersFromResource(R.xml.preference_headers, 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; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java index dc203756f..f61ada84f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java @@ -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 - } - @Override - public void onEdited() { + SettingsKeyserverFragment fragment = SettingsKeyserverFragment.newInstance(keyservers); - } - - // 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(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java new file mode 100644 index 000000000..20565aa7d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java @@ -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(); + } + } + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java index 2c1714b67..321242b2f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddKeyserverDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java @@ -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); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java new file mode 100644 index 000000000..95199bcd5 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/DividerItemDecoration.java @@ -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); + } + } +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java new file mode 100644 index 000000000..c691182bf --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperAdapter.java @@ -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); +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java new file mode 100644 index 000000000..0fd24581d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperDragCallback.java @@ -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(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java new file mode 100644 index 000000000..97e70d71e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/ItemTouchHelperViewHolder.java @@ -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(); +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java new file mode 100644 index 000000000..7efcbb30c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/recyclerview/RecyclerItemClickListener.java @@ -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; + } +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..694179bd4 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_add_white_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_reorder_grey_500_24dp.png Binary files differnew file mode 100644 index 000000000..58fe2c52e --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-hdpi/ic_reorder_grey_500_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..3856041d7 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_add_white_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_reorder_grey_500_24dp.png Binary files differnew file mode 100644 index 000000000..d6abf0936 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-mdpi/ic_reorder_grey_500_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..67bb598e5 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_add_white_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-xhdpi/ic_reorder_grey_500_24dp.png Binary files differnew file mode 100644 index 000000000..0af0cb76d --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xhdpi/ic_reorder_grey_500_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..0fdced8fc --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_add_white_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_reorder_grey_500_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_reorder_grey_500_24dp.png Binary files differnew file mode 100644 index 000000000..1d9c88d10 --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_reorder_grey_500_24dp.png diff --git a/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png Binary files differnew file mode 100644 index 000000000..d64c22e9e --- /dev/null +++ b/OpenKeychain/src/main/res/drawable-xxxhdpi/ic_add_white_24dp.png diff --git a/OpenKeychain/src/main/res/layout/key_server_preference.xml b/OpenKeychain/src/main/res/layout/key_server_preference.xml index 5319a3ec0..570d57d9b 100644 --- a/OpenKeychain/src/main/res/layout/key_server_preference.xml +++ b/OpenKeychain/src/main/res/layout/key_server_preference.xml @@ -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"> - - <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"> - - <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"> - - <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" /> - - <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>
\ No newline at end of file + <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"/> + + <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"/> + + <View + style="@style/Divider"/> + + <FrameLayout + android:id="@+id/keyserver_settings_fragment_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" /> + +</LinearLayout>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/settings_keyserver_fragment.xml b/OpenKeychain/src/main/res/layout/settings_keyserver_fragment.xml new file mode 100644 index 000000000..57c81a433 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/settings_keyserver_fragment.xml @@ -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" />
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/settings_keyserver_item.xml b/OpenKeychain/src/main/res/layout/settings_keyserver_item.xml new file mode 100644 index 000000000..338104bda --- /dev/null +++ b/OpenKeychain/src/main/res/layout/settings_keyserver_item.xml @@ -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>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml b/OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml new file mode 100644 index 000000000..c4002a682 --- /dev/null +++ b/OpenKeychain/src/main/res/menu/keyserver_pref_menu.xml @@ -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>
\ No newline at end of file diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index a59b63800..619c67ee3 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -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> diff --git a/OpenKeychain/src/main/res/values/styles.xml b/OpenKeychain/src/main/res/values/styles.xml index 9ac60c397..c7b326b7a 100644 --- a/OpenKeychain/src/main/res/values/styles.xml +++ b/OpenKeychain/src/main/res/values/styles.xml @@ -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>
\ No newline at end of file |