From 9275f5ca9c100d2643708387be29f122b418f318 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Fri, 29 Jan 2016 18:05:43 +0100 Subject: [PATCH] Address related improvements - QR code is now shown in contact details and 'manage identity' view - Contacts and identities can now be deleted --- app/build.gradle | 4 + app/src/main/AndroidManifest.xml | 2 +- ...tivity.java => AddressDetailActivity.java} | 10 +- .../apps/abit/AddressDetailFragment.java | 259 ++++++++++++++++++ ...Fragment.java => AddressListFragment.java} | 4 +- .../apps/abit/ComposeMessageActivity.java | 1 + .../apps/abit/ComposeMessageFragment.java | 12 +- .../ch/dissem/apps/abit/MainActivity.java | 77 ++++-- .../apps/abit/MessageDetailFragment.java | 11 +- .../dissem/apps/abit/MessageListFragment.java | 2 +- .../apps/abit/SubscriptionDetailFragment.java | 131 --------- .../repository/AndroidAddressRepository.java | 2 +- .../dissem/apps/abit/service/Singleton.java | 6 + ...detail.xml => fragment_address_detail.xml} | 14 + ...act_list.xml => fragment_address_list.xml} | 0 app/src/main/res/menu/address.xml | 33 +++ app/src/main/res/values-de/strings.xml | 5 + app/src/main/res/values/strings.xml | 5 + 18 files changed, 403 insertions(+), 175 deletions(-) rename app/src/main/java/ch/dissem/apps/abit/{SubscriptionDetailActivity.java => AddressDetailActivity.java} (89%) create mode 100644 app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java rename app/src/main/java/ch/dissem/apps/abit/{SubscriptionListFragment.java => AddressListFragment.java} (96%) delete mode 100644 app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java rename app/src/main/res/layout/{fragment_contact_detail.xml => fragment_address_detail.xml} (86%) rename app/src/main/res/layout/{fragment_contact_list.xml => fragment_address_list.xml} (100%) create mode 100644 app/src/main/res/menu/address.xml diff --git a/app/build.gradle b/app/build.gradle index 642a542..ffa6553 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,6 +40,7 @@ dependencies { compile "ch.dissem.jabit:jabit-networking:$jabitVersion" compile "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion" compile "ch.dissem.jabit:jabit-extensions:$jabitVersion" + compile "ch.dissem.jabit:jabit-wif:$jabitVersion" compile 'org.slf4j:slf4j-android:1.7.12' @@ -51,6 +52,9 @@ dependencies { } compile 'com.mikepenz:iconics:1.6.2@aar' compile 'com.mikepenz:community-material-typeface:1.1.71@aar' + + compile 'com.journeyapps:zxing-android-embedded:3.1.0@aar' + compile 'com.google.zxing:core:3.2.0' } idea.module { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1eb392b..3f83f18 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -38,7 +38,7 @@ android:value=".MainActivity"/> diff --git a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailActivity.java b/app/src/main/java/ch/dissem/apps/abit/AddressDetailActivity.java similarity index 89% rename from app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailActivity.java rename to app/src/main/java/ch/dissem/apps/abit/AddressDetailActivity.java index cc83451..e26749a 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/AddressDetailActivity.java @@ -31,9 +31,9 @@ import android.view.MenuItem; * in a {@link MainActivity}. *

* This activity is mostly just a 'shell' activity containing nothing - * more than a {@link SubscriptionDetailFragment}. + * more than a {@link AddressDetailFragment}. */ -public class SubscriptionDetailActivity extends AppCompatActivity { +public class AddressDetailActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { @@ -59,9 +59,9 @@ public class SubscriptionDetailActivity extends AppCompatActivity { // Create the detail fragment and add it to the activity // using a fragment transaction. Bundle arguments = new Bundle(); - arguments.putSerializable(SubscriptionDetailFragment.ARG_ITEM, - getIntent().getSerializableExtra(SubscriptionDetailFragment.ARG_ITEM)); - SubscriptionDetailFragment fragment = new SubscriptionDetailFragment(); + arguments.putSerializable(AddressDetailFragment.ARG_ITEM, + getIntent().getSerializableExtra(AddressDetailFragment.ARG_ITEM)); + AddressDetailFragment fragment = new AddressDetailFragment(); fragment.setArguments(arguments); getSupportFragmentManager().beginTransaction() .add(R.id.content, fragment) diff --git a/app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java new file mode 100644 index 0000000..da08e12 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/AddressDetailFragment.java @@ -0,0 +1,259 @@ +/* + * Copyright 2015 Christian Basler + * + * 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 ch.dissem.apps.abit; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.ShareActionProvider; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.Switch; +import android.widget.TextView; + +import com.google.zxing.BarcodeFormat; +import com.google.zxing.MultiFormatWriter; +import com.google.zxing.WriterException; +import com.google.zxing.common.BitMatrix; +import com.mikepenz.google_material_typeface_library.GoogleMaterial; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.dissem.apps.abit.service.Singleton; +import ch.dissem.apps.abit.util.Drawables; +import ch.dissem.bitmessage.entity.BitmessageAddress; + +import static android.graphics.Color.BLACK; +import static android.graphics.Color.WHITE; + + +/** + * A fragment representing a single Message detail screen. + * This fragment is either contained in a {@link MainActivity} + * in two-pane mode (on tablets) or a {@link MessageDetailActivity} + * on handsets. + */ +public class AddressDetailFragment extends Fragment { + private static final Logger LOG = LoggerFactory.getLogger(AddressDetailFragment.class); + /** + * The fragment argument representing the item ID that this fragment + * represents. + */ + public static final String ARG_ITEM = "item"; + + private static final int QR_CODE_SIZE = 350; + + private ShareActionProvider shareActionProvider; + /** + * The content this fragment is presenting. + */ + private BitmessageAddress item; + + + /** + * Mandatory empty constructor for the fragment manager to instantiate the + * fragment (e.g. upon screen orientation changes). + */ + public AddressDetailFragment() { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getArguments().containsKey(ARG_ITEM)) { + // Load the dummy content specified by the fragment + // arguments. In a real-world scenario, use a Loader + // to load content from a content provider. + item = (BitmessageAddress) getArguments().getSerializable(ARG_ITEM); + } + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.address, menu); + + Drawables.addIcon(getActivity(), menu, R.id.write_message, GoogleMaterial.Icon.gmd_mail); + Drawables.addIcon(getActivity(), menu, R.id.delete, GoogleMaterial.Icon.gmd_delete); + Drawables.addIcon(getActivity(), menu, R.id.share, GoogleMaterial.Icon.gmd_share); + + shareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider( + menu.findItem(R.id.share)); + + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + final Activity ctx = getActivity(); + switch (menuItem.getItemId()) { + case R.id.write_message: + Intent intent = new Intent(ctx, ComposeMessageActivity.class); + intent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, Singleton.getIdentity(ctx)); + intent.putExtra(ComposeMessageActivity.EXTRA_RECIPIENT, item); + startActivity(intent); + return true; + case R.id.delete: + int warning; + if (item.getPrivateKey() != null) + warning = R.string.delete_identity_warning; + else + warning = R.string.delete_contact_warning; + new AlertDialog.Builder(ctx) + .setMessage(warning) + .setPositiveButton(android.R.string.yes, new + DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Singleton.getAddressRepository(ctx).remove(item); + item = null; + ctx.onBackPressed(); + } + }) + .setNegativeButton(android.R.string.no, null) + .show(); + return true; + case R.id.share: + new AlertDialog.Builder(ctx) + .setMessage("I have no fucking clue.") + .show(); + default: + return false; + } + } + + private void setShareIntent(Intent shareIntent) { + if (shareActionProvider != null) { + shareActionProvider.setShareIntent(shareIntent); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_address_detail, container, false); + + // Show the dummy content as text in a TextView. + if (item != null) { + ((ImageView) rootView.findViewById(R.id.avatar)).setImageDrawable(new Identicon(item)); + TextView name = (TextView) rootView.findViewById(R.id.name); + name.setText(item.toString()); + name.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // Nothing to do + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + // Nothing to do + } + + @Override + public void afterTextChanged(Editable s) { + item.setAlias(s.toString()); + } + }); + TextView address = (TextView) rootView.findViewById(R.id.address); + address.setText(item.getAddress()); + address.setSelected(true); + ((TextView) rootView.findViewById(R.id.stream_number)).setText(getActivity() + .getString(R.string.stream_number, item.getStream())); + if (item.getPrivateKey() == null) { + Switch active = (Switch) rootView.findViewById(R.id.active); + active.setChecked(item.isSubscribed()); + active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + item.setSubscribed(isChecked); + } + }); + + ImageView pubkeyAvailableImg = (ImageView) rootView.findViewById(R.id + .pubkey_available); + + if (item.getPubkey() == null) { + pubkeyAvailableImg.setAlpha(0.3f); + TextView pubkeyAvailableDesc = (TextView) rootView.findViewById(R.id + .pubkey_available_desc); + pubkeyAvailableDesc.setText(R.string.pubkey_not_available); + } + } else { + rootView.findViewById(R.id.active).setVisibility(View.GONE); + rootView.findViewById(R.id.pubkey_available).setVisibility(View.GONE); + rootView.findViewById(R.id.pubkey_available_desc).setVisibility(View.GONE); + } + + // QR code + ImageView qrCode = (ImageView) rootView.findViewById(R.id.qr_code); + qrCode.setImageBitmap(encodeAsBitmap(item)); + } + + return rootView; + } + + Bitmap encodeAsBitmap(BitmessageAddress address) { + StringBuilder link = new StringBuilder("bitmessage:"); + link.append(address.getAddress()); + if (address.getAlias() != null) { + link.append("?label=").append(address.getAlias()); + } + BitMatrix result; + try { + result = new MultiFormatWriter().encode(link.toString(), + BarcodeFormat.QR_CODE, QR_CODE_SIZE, QR_CODE_SIZE, null); + } catch (WriterException e) { + LOG.error(e.getMessage(), e); + return null; + } + int w = result.getWidth(); + int h = result.getHeight(); + int[] pixels = new int[w * h]; + for (int y = 0; y < h; y++) { + int offset = y * w; + for (int x = 0; x < w; x++) { + pixels[offset + x] = result.get(x, y) ? BLACK : WHITE; + } + } + Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, QR_CODE_SIZE, 0, 0, w, h); + return bitmap; + } + + @Override + public void onPause() { + if (item != null) { + Singleton.getAddressRepository(getContext()).save(item); + } + super.onPause(); + } +} diff --git a/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java b/app/src/main/java/ch/dissem/apps/abit/AddressListFragment.java similarity index 96% rename from app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java rename to app/src/main/java/ch/dissem/apps/abit/AddressListFragment.java index b84aa54..75aa930 100644 --- a/app/src/main/java/ch/dissem/apps/abit/SubscriptionListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/AddressListFragment.java @@ -38,7 +38,7 @@ import java.util.List; /** * Fragment that shows a list of all contacts, the ones we subscribed to first. */ -public class SubscriptionListFragment extends AbstractItemListFragment { +public class AddressListFragment extends AbstractItemListFragment { @Override public void onResume() { super.onResume(); @@ -113,7 +113,7 @@ public class SubscriptionListFragment extends AbstractItemListFragment contacts = Singleton.getAddressRepository(getContext()).getContacts(); + List contacts = Singleton.getAddressRepository + (getContext()).getContacts(); for (BitmessageAddress contact : contacts) { if (inputString.equalsIgnoreCase(contact.getAlias())) { recipient = contact; diff --git a/app/src/main/java/ch/dissem/apps/abit/MainActivity.java b/app/src/main/java/ch/dissem/apps/abit/MainActivity.java index f493cb3..c31a0e6 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MainActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MainActivity.java @@ -94,6 +94,7 @@ public class MainActivity extends AppCompatActivity private static final Logger LOG = LoggerFactory.getLogger(MainActivity.class); private static final int ADD_IDENTITY = 1; + private static final int MANAGE_IDENTITY = 2; /** * Whether or not the activity is in two-pane mode, i.e. running on a tablet @@ -120,7 +121,6 @@ public class MainActivity extends AppCompatActivity private Label selectedLabel; private BitmessageContext bmc; - private BitmessageAddress selectedIdentity; private AccountHeader accountHeader; @Override @@ -190,6 +190,7 @@ public class MainActivity extends AppCompatActivity profiles.add(new ProfileDrawerItem() .withIcon(new Identicon(identity)) .withName(identity.toString()) + .withNameShown(true) .withEmail(identity.getAddress()) .withTag(identity) ); @@ -216,6 +217,7 @@ public class MainActivity extends AppCompatActivity profiles.add(new ProfileSettingDrawerItem() .withName(getString(R.string.manage_identity)) .withIcon(GoogleMaterial.Icon.gmd_settings) + .withIdentifier(MANAGE_IDENTITY) ); // Create the AccountHeader accountHeader = new AccountHeaderBuilder() @@ -226,25 +228,46 @@ public class MainActivity extends AppCompatActivity @Override public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) { - if (profile.getIdentifier() == ADD_IDENTITY) { - BitmessageAddress identity = bmc.createIdentity(false); - IProfile newProfile = new ProfileDrawerItem() - .withName(identity.toString()) - .withEmail(identity.getAddress()) - .withTag(identity); - if (accountHeader.getProfiles() != null) { - // we know that there are 2 setting elements. - // Set the new profile above them ;) - accountHeader.addProfile( - newProfile, accountHeader.getProfiles().size() - 2); - } else { - accountHeader.addProfiles(newProfile); - } - } else if (profile instanceof ProfileDrawerItem) { - Object tag = ((ProfileDrawerItem) profile).getTag(); - if (tag instanceof BitmessageAddress) { - selectedIdentity = (BitmessageAddress) tag; - } + switch (profile.getIdentifier()) { + case ADD_IDENTITY: + new AlertDialog.Builder(MainActivity.this) + .setMessage(R.string.add_identity_warning) + .setPositiveButton(android.R.string.yes, new + DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + BitmessageAddress identity = bmc.createIdentity(false); + IProfile newProfile = new ProfileDrawerItem() + .withName(identity.toString()) + .withEmail(identity.getAddress()) + .withTag(identity); + if (accountHeader.getProfiles() != null) { + // we know that there are 2 setting elements. + // Set the new profile above them ;) + accountHeader.addProfile( + newProfile, accountHeader.getProfiles().size() - 2); + } else { + accountHeader.addProfiles(newProfile); + } + } + }) + .setNegativeButton(android.R.string.no, null) + .show(); + break; + case MANAGE_IDENTITY: + Intent show = new Intent(MainActivity.this, + AddressDetailActivity.class); + show.putExtra(AddressDetailFragment.ARG_ITEM, + Singleton.getIdentity(getApplicationContext())); + startActivity(show); + break; + default: + if (profile instanceof ProfileDrawerItem) { + Object tag = ((ProfileDrawerItem) profile).getTag(); + if (tag instanceof BitmessageAddress) { + Singleton.setIdentity((BitmessageAddress) tag); + } + } } // false if it should close the drawer return false; @@ -336,10 +359,10 @@ public class MainActivity extends AppCompatActivity switch (ni.getNameRes()) { case R.string.contacts_and_subscriptions: if (!(getSupportFragmentManager().findFragmentById(R.id - .item_list) instanceof SubscriptionListFragment)) { - changeList(new SubscriptionListFragment()); + .item_list) instanceof AddressListFragment)) { + changeList(new AddressListFragment()); } else { - ((SubscriptionListFragment) getSupportFragmentManager() + ((AddressListFragment) getSupportFragmentManager() .findFragmentById(R.id.item_list)).updateList(); } @@ -415,7 +438,7 @@ public class MainActivity extends AppCompatActivity if (item instanceof Plaintext) fragment = new MessageDetailFragment(); else if (item instanceof BitmessageAddress) - fragment = new SubscriptionDetailFragment(); + fragment = new AddressDetailFragment(); else throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " + "was " @@ -431,7 +454,7 @@ public class MainActivity extends AppCompatActivity if (item instanceof Plaintext) detailIntent = new Intent(this, MessageDetailActivity.class); else if (item instanceof BitmessageAddress) - detailIntent = new Intent(this, SubscriptionDetailActivity.class); + detailIntent = new Intent(this, AddressDetailActivity.class); else throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " + "was " @@ -468,8 +491,4 @@ public class MainActivity extends AppCompatActivity } super.onStop(); } - - public BitmessageAddress getSelectedIdentity() { - return selectedIdentity; - } } diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java index 21a9d3f..8296c63 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java @@ -43,6 +43,9 @@ import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.MessageRepository; import static android.text.util.Linkify.WEB_URLS; +import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY; +import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT; +import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT; import static ch.dissem.apps.abit.util.Constants.BITMESSAGE_ADDRESS_PATTERN; import static ch.dissem.apps.abit.util.Constants.BITMESSAGE_URL_SCHEMA; @@ -152,8 +155,12 @@ public class MessageDetailFragment extends Fragment { case R.id.reply: Intent replyIntent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class); - replyIntent.putExtra(ComposeMessageActivity.EXTRA_RECIPIENT, item.getFrom()); - replyIntent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, item.getTo()); + replyIntent.putExtra(EXTRA_RECIPIENT, item.getFrom()); + replyIntent.putExtra(EXTRA_IDENTITY, item.getTo()); + replyIntent.putExtra(EXTRA_SUBJECT, + (item.getSubject().substring(0, 3).equalsIgnoreCase("RE:") ? "" : "RE: ") + + item.getSubject() + ); startActivity(replyIntent); return true; case R.id.delete: diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java index c4b0d1f..71c8b20 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageListFragment.java @@ -132,7 +132,7 @@ public class MessageListFragment extends AbstractItemListFragment

{ @Override public void onClick(View view) { Intent intent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class); - intent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, ((MainActivity) getActivity()).getSelectedIdentity()); + intent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, Singleton.getIdentity(getActivity())); startActivity(intent); } }); diff --git a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java deleted file mode 100644 index 955531f..0000000 --- a/app/src/main/java/ch/dissem/apps/abit/SubscriptionDetailFragment.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2015 Christian Basler - * - * 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 ch.dissem.apps.abit; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.text.Editable; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.Switch; -import android.widget.TextView; - -import ch.dissem.apps.abit.service.Singleton; -import ch.dissem.bitmessage.entity.BitmessageAddress; - - -/** - * A fragment representing a single Message detail screen. - * This fragment is either contained in a {@link MainActivity} - * in two-pane mode (on tablets) or a {@link MessageDetailActivity} - * on handsets. - */ -public class SubscriptionDetailFragment extends Fragment { - /** - * The fragment argument representing the item ID that this fragment - * represents. - */ - public static final String ARG_ITEM = "item"; - - /** - * The content this fragment is presenting. - */ - private BitmessageAddress item; - - - /** - * Mandatory empty constructor for the fragment manager to instantiate the - * fragment (e.g. upon screen orientation changes). - */ - public SubscriptionDetailFragment() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (getArguments().containsKey(ARG_ITEM)) { - // Load the dummy content specified by the fragment - // arguments. In a real-world scenario, use a Loader - // to load content from a content provider. - item = (BitmessageAddress) getArguments().getSerializable(ARG_ITEM); - } - setHasOptionsMenu(true); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_contact_detail, container, false); - - // Show the dummy content as text in a TextView. - if (item != null) { - ((ImageView) rootView.findViewById(R.id.avatar)).setImageDrawable(new Identicon(item)); - TextView name = (TextView) rootView.findViewById(R.id.name); - name.setText(item.toString()); - name.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // Nothing to do - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - // Nothing to do - } - - @Override - public void afterTextChanged(Editable s) { - item.setAlias(s.toString()); - } - }); - TextView address = (TextView) rootView.findViewById(R.id.address); - address.setText(item.getAddress()); - address.setSelected(true); - ((TextView) rootView.findViewById(R.id.stream_number)).setText(getActivity() - .getString(R.string.stream_number, item.getStream())); - Switch active = (Switch) rootView.findViewById(R.id.active); - active.setChecked(item.isSubscribed()); - active.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - item.setSubscribed(isChecked); - } - }); - - ImageView pubkeyAvailableImg = (ImageView) rootView.findViewById(R.id.pubkey_available); - if (item.getPubkey() == null) { - pubkeyAvailableImg.setAlpha(0.3f); - TextView pubkeyAvailableDesc = (TextView) rootView.findViewById(R.id - .pubkey_available_desc); - pubkeyAvailableDesc.setText(R.string.pubkey_not_available); - } - } - - return rootView; - } - - @Override - public void onPause() { - Singleton.getAddressRepository(getContext()).save(item); - super.onPause(); - } -} diff --git a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.java b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.java index 997270b..edbdbe8 100644 --- a/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.java +++ b/app/src/main/java/ch/dissem/apps/abit/repository/AndroidAddressRepository.java @@ -239,7 +239,7 @@ public class AndroidAddressRepository implements AddressRepository { @Override public void remove(BitmessageAddress address) { SQLiteDatabase db = sql.getWritableDatabase(); - db.delete(TABLE_NAME, "address = " + address.getAddress(), null); + db.delete(TABLE_NAME, "address = ?", new String[]{address.getAddress()}); } @Override diff --git a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java index 5313927..178c8bb 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/Singleton.java @@ -124,4 +124,10 @@ public class Singleton { } return identity; } + + public static void setIdentity(BitmessageAddress identity) { + if (identity.getPrivateKey() == null) + throw new IllegalArgumentException("Identity expected, but no private key available"); + Singleton.identity = identity; + } } diff --git a/app/src/main/res/layout/fragment_contact_detail.xml b/app/src/main/res/layout/fragment_address_detail.xml similarity index 86% rename from app/src/main/res/layout/fragment_contact_detail.xml rename to app/src/main/res/layout/fragment_address_detail.xml index 662890d..f80aa59 100644 --- a/app/src/main/res/layout/fragment_contact_detail.xml +++ b/app/src/main/res/layout/fragment_address_detail.xml @@ -100,4 +100,18 @@ android:text="@string/pubkey_available" android:textAppearance="?android:attr/textAppearanceSmall"/> + <ImageView + android:id="@+id/qr_code" + tools:src="@drawable/public_key" + android:layout_width="0dp" + android:layout_height="0dp" + android:layout_below="@+id/pubkey_available" + android:layout_alignParentStart="true" + android:layout_alignParentEnd="true" + android:layout_alignParentBottom="true" + android:layout_marginTop="24dp" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:layout_marginBottom="64dp" + android:contentDescription="@string/alt_qr_code"/> </RelativeLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_contact_list.xml b/app/src/main/res/layout/fragment_address_list.xml similarity index 100% rename from app/src/main/res/layout/fragment_contact_list.xml rename to app/src/main/res/layout/fragment_address_list.xml diff --git a/app/src/main/res/menu/address.xml b/app/src/main/res/menu/address.xml new file mode 100644 index 0000000..a833cb9 --- /dev/null +++ b/app/src/main/res/menu/address.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Christian Basler + ~ + ~ 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. + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + <item + android:id="@+id/write_message" + android:title="@string/write_message" + app:showAsAction="ifRoom"/> + <item + android:id="@+id/delete" + android:title="@string/delete" + app:showAsAction="ifRoom"/> + <item + android:id="@+id/share" + android:title="@string/share" + app:actionProviderClass="android.support.v7.widget.ShareActionProvider" + app:showAsAction="always"/> +</menu> \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8a153f1..b7906bb 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -58,4 +58,9 @@ <string name="status_summary">Technische Infos</string> <string name="pubkey_available">Öffentlicher Schlüssel verfügbar</string> <string name="pubkey_not_available">Öffentlicher Schlüssel noch nicht verfügbar</string> + <string name="alt_qr_code">QR-Code</string> + <string name="add_identity_warning">Mehrere Identitäten zu haben bedeutet einen höheren Resourcenverbrauch. Sind Sie sicher?</string> + <string name="share">Teilen</string> + <string name="delete_contact_warning">Sind Sie sicher dass dieser Kontakt gelöscht werden soll?</string> + <string name="delete_identity_warning">Sind Sie sicher dass diese Identität gelöscht werden soll? Sie werden keine Nachrichten mehr empfangen können welche an diese Adresse gesendet werden, und es est nicht möglich diese Aktion rückgängig zu machen.</string> </resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 82daeff..3e37518 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,4 +61,9 @@ <string name="alias_default_identity">Me</string> <string name="pubkey_available">Public key available</string> <string name="pubkey_not_available">Public key not yet available</string> + <string name="alt_qr_code">QR code</string> + <string name="add_identity_warning">Having more identities will reequire more resources. Are you sure you want to add an identity?</string> + <string name="share">Share</string> + <string name="delete_identity_warning">Are you sure you want to delete this identity? You won\'t be able to receive any messages sent to this address and can\'t undo this operation.</string> + <string name="delete_contact_warning">Are you sure you want to delete this contact?</string> </resources>