Address related improvements

- QR code is now shown in contact details and 'manage identity' view
- Contacts and identities can now be deleted
This commit is contained in:
Christian Basler 2016-01-29 18:05:43 +01:00
parent adfb3a920a
commit 9275f5ca9c
18 changed files with 403 additions and 175 deletions

View File

@ -40,6 +40,7 @@ dependencies {
compile "ch.dissem.jabit:jabit-networking:$jabitVersion" compile "ch.dissem.jabit:jabit-networking:$jabitVersion"
compile "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion" compile "ch.dissem.jabit:jabit-cryptography-spongy:$jabitVersion"
compile "ch.dissem.jabit:jabit-extensions:$jabitVersion" compile "ch.dissem.jabit:jabit-extensions:$jabitVersion"
compile "ch.dissem.jabit:jabit-wif:$jabitVersion"
compile 'org.slf4j:slf4j-android:1.7.12' compile 'org.slf4j:slf4j-android:1.7.12'
@ -51,6 +52,9 @@ dependencies {
} }
compile 'com.mikepenz:iconics:1.6.2@aar' compile 'com.mikepenz:iconics:1.6.2@aar'
compile 'com.mikepenz:community-material-typeface:1.1.71@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 { idea.module {

View File

@ -38,7 +38,7 @@
android:value=".MainActivity"/> android:value=".MainActivity"/>
</activity> </activity>
<activity <activity
android:name=".SubscriptionDetailActivity" android:name=".AddressDetailActivity"
android:label="@string/title_subscription_detail" android:label="@string/title_subscription_detail"
android:parentActivityName=".MainActivity" android:parentActivityName=".MainActivity"
tools:ignore="UnusedAttribute"> tools:ignore="UnusedAttribute">

View File

@ -31,9 +31,9 @@ import android.view.MenuItem;
* in a {@link MainActivity}. * in a {@link MainActivity}.
* <p/> * <p/>
* This activity is mostly just a 'shell' activity containing nothing * 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 @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -59,9 +59,9 @@ public class SubscriptionDetailActivity extends AppCompatActivity {
// Create the detail fragment and add it to the activity // Create the detail fragment and add it to the activity
// using a fragment transaction. // using a fragment transaction.
Bundle arguments = new Bundle(); Bundle arguments = new Bundle();
arguments.putSerializable(SubscriptionDetailFragment.ARG_ITEM, arguments.putSerializable(AddressDetailFragment.ARG_ITEM,
getIntent().getSerializableExtra(SubscriptionDetailFragment.ARG_ITEM)); getIntent().getSerializableExtra(AddressDetailFragment.ARG_ITEM));
SubscriptionDetailFragment fragment = new SubscriptionDetailFragment(); AddressDetailFragment fragment = new AddressDetailFragment();
fragment.setArguments(arguments); fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.content, fragment) .add(R.id.content, fragment)

View File

@ -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();
}
}

View File

@ -38,7 +38,7 @@ import java.util.List;
/** /**
* Fragment that shows a list of all contacts, the ones we subscribed to first. * Fragment that shows a list of all contacts, the ones we subscribed to first.
*/ */
public class SubscriptionListFragment extends AbstractItemListFragment<BitmessageAddress> { public class AddressListFragment extends AbstractItemListFragment<BitmessageAddress> {
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
@ -113,7 +113,7 @@ public class SubscriptionListFragment extends AbstractItemListFragment<Bitmessag
@Nullable @Nullable
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_contact_list, container, false); return inflater.inflate(R.layout.fragment_address_list, container, false);
} }
@Override @Override

View File

@ -26,6 +26,7 @@ import android.support.v7.widget.Toolbar;
public class ComposeMessageActivity extends AppCompatActivity { public class ComposeMessageActivity extends AppCompatActivity {
public static final String EXTRA_IDENTITY = "ch.dissem.abit.Message.SENDER"; public static final String EXTRA_IDENTITY = "ch.dissem.abit.Message.SENDER";
public static final String EXTRA_RECIPIENT = "ch.dissem.abit.Message.RECIPIENT"; public static final String EXTRA_RECIPIENT = "ch.dissem.abit.Message.RECIPIENT";
public static final String EXTRA_SUBJECT = "ch.dissem.abit.Message.SUBJECT";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {

View File

@ -36,6 +36,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY; 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_RECIPIENT;
import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT;
/** /**
* Compose a new message. * Compose a new message.
@ -43,6 +44,7 @@ import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT;
public class ComposeMessageFragment extends Fragment { public class ComposeMessageFragment extends Fragment {
private BitmessageAddress identity; private BitmessageAddress identity;
private BitmessageAddress recipient; private BitmessageAddress recipient;
private String subject;
private AutoCompleteTextView recipientInput; private AutoCompleteTextView recipientInput;
private EditText subjectInput; private EditText subjectInput;
private EditText bodyInput; private EditText bodyInput;
@ -66,6 +68,9 @@ public class ComposeMessageFragment extends Fragment {
if (getArguments().containsKey(EXTRA_RECIPIENT)) { if (getArguments().containsKey(EXTRA_RECIPIENT)) {
recipient = (BitmessageAddress) getArguments().getSerializable(EXTRA_RECIPIENT); recipient = (BitmessageAddress) getArguments().getSerializable(EXTRA_RECIPIENT);
} }
if (getArguments().containsKey(EXTRA_SUBJECT)) {
subject = getArguments().getString(EXTRA_SUBJECT);
}
} else { } else {
throw new RuntimeException("No identity set for ComposeMessageFragment"); throw new RuntimeException("No identity set for ComposeMessageFragment");
} }
@ -99,9 +104,9 @@ public class ComposeMessageFragment extends Fragment {
recipientInput.setText(recipient.toString()); recipientInput.setText(recipient.toString());
} }
subjectInput = (EditText) rootView.findViewById(R.id.subject); subjectInput = (EditText) rootView.findViewById(R.id.subject);
subjectInput.setText(subject);
bodyInput = (EditText) rootView.findViewById(R.id.body); bodyInput = (EditText) rootView.findViewById(R.id.body);
// bodyInput.setInputType(EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE);
// bodyInput.setImeOptions(EditorInfo.IME_ACTION_SEND | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
return rootView; return rootView;
} }
@ -120,7 +125,8 @@ public class ComposeMessageFragment extends Fragment {
try { try {
recipient = new BitmessageAddress(inputString); recipient = new BitmessageAddress(inputString);
} catch (Exception e) { } catch (Exception e) {
List<BitmessageAddress> contacts = Singleton.getAddressRepository(getContext()).getContacts(); List<BitmessageAddress> contacts = Singleton.getAddressRepository
(getContext()).getContacts();
for (BitmessageAddress contact : contacts) { for (BitmessageAddress contact : contacts) {
if (inputString.equalsIgnoreCase(contact.getAlias())) { if (inputString.equalsIgnoreCase(contact.getAlias())) {
recipient = contact; recipient = contact;

View File

@ -94,6 +94,7 @@ public class MainActivity extends AppCompatActivity
private static final Logger LOG = LoggerFactory.getLogger(MainActivity.class); private static final Logger LOG = LoggerFactory.getLogger(MainActivity.class);
private static final int ADD_IDENTITY = 1; 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 * 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 Label selectedLabel;
private BitmessageContext bmc; private BitmessageContext bmc;
private BitmessageAddress selectedIdentity;
private AccountHeader accountHeader; private AccountHeader accountHeader;
@Override @Override
@ -190,6 +190,7 @@ public class MainActivity extends AppCompatActivity
profiles.add(new ProfileDrawerItem() profiles.add(new ProfileDrawerItem()
.withIcon(new Identicon(identity)) .withIcon(new Identicon(identity))
.withName(identity.toString()) .withName(identity.toString())
.withNameShown(true)
.withEmail(identity.getAddress()) .withEmail(identity.getAddress())
.withTag(identity) .withTag(identity)
); );
@ -216,6 +217,7 @@ public class MainActivity extends AppCompatActivity
profiles.add(new ProfileSettingDrawerItem() profiles.add(new ProfileSettingDrawerItem()
.withName(getString(R.string.manage_identity)) .withName(getString(R.string.manage_identity))
.withIcon(GoogleMaterial.Icon.gmd_settings) .withIcon(GoogleMaterial.Icon.gmd_settings)
.withIdentifier(MANAGE_IDENTITY)
); );
// Create the AccountHeader // Create the AccountHeader
accountHeader = new AccountHeaderBuilder() accountHeader = new AccountHeaderBuilder()
@ -226,25 +228,46 @@ public class MainActivity extends AppCompatActivity
@Override @Override
public boolean onProfileChanged(View view, IProfile profile, boolean public boolean onProfileChanged(View view, IProfile profile, boolean
currentProfile) { currentProfile) {
if (profile.getIdentifier() == ADD_IDENTITY) { switch (profile.getIdentifier()) {
BitmessageAddress identity = bmc.createIdentity(false); case ADD_IDENTITY:
IProfile newProfile = new ProfileDrawerItem() new AlertDialog.Builder(MainActivity.this)
.withName(identity.toString()) .setMessage(R.string.add_identity_warning)
.withEmail(identity.getAddress()) .setPositiveButton(android.R.string.yes, new
.withTag(identity); DialogInterface.OnClickListener() {
if (accountHeader.getProfiles() != null) { @Override
// we know that there are 2 setting elements. public void onClick(DialogInterface dialog, int which) {
// Set the new profile above them ;) BitmessageAddress identity = bmc.createIdentity(false);
accountHeader.addProfile( IProfile newProfile = new ProfileDrawerItem()
newProfile, accountHeader.getProfiles().size() - 2); .withName(identity.toString())
} else { .withEmail(identity.getAddress())
accountHeader.addProfiles(newProfile); .withTag(identity);
} if (accountHeader.getProfiles() != null) {
} else if (profile instanceof ProfileDrawerItem) { // we know that there are 2 setting elements.
Object tag = ((ProfileDrawerItem) profile).getTag(); // Set the new profile above them ;)
if (tag instanceof BitmessageAddress) { accountHeader.addProfile(
selectedIdentity = (BitmessageAddress) tag; 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 // false if it should close the drawer
return false; return false;
@ -336,10 +359,10 @@ public class MainActivity extends AppCompatActivity
switch (ni.getNameRes()) { switch (ni.getNameRes()) {
case R.string.contacts_and_subscriptions: case R.string.contacts_and_subscriptions:
if (!(getSupportFragmentManager().findFragmentById(R.id if (!(getSupportFragmentManager().findFragmentById(R.id
.item_list) instanceof SubscriptionListFragment)) { .item_list) instanceof AddressListFragment)) {
changeList(new SubscriptionListFragment()); changeList(new AddressListFragment());
} else { } else {
((SubscriptionListFragment) getSupportFragmentManager() ((AddressListFragment) getSupportFragmentManager()
.findFragmentById(R.id.item_list)).updateList(); .findFragmentById(R.id.item_list)).updateList();
} }
@ -415,7 +438,7 @@ public class MainActivity extends AppCompatActivity
if (item instanceof Plaintext) if (item instanceof Plaintext)
fragment = new MessageDetailFragment(); fragment = new MessageDetailFragment();
else if (item instanceof BitmessageAddress) else if (item instanceof BitmessageAddress)
fragment = new SubscriptionDetailFragment(); fragment = new AddressDetailFragment();
else else
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " + throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " +
"was " "was "
@ -431,7 +454,7 @@ public class MainActivity extends AppCompatActivity
if (item instanceof Plaintext) if (item instanceof Plaintext)
detailIntent = new Intent(this, MessageDetailActivity.class); detailIntent = new Intent(this, MessageDetailActivity.class);
else if (item instanceof BitmessageAddress) else if (item instanceof BitmessageAddress)
detailIntent = new Intent(this, SubscriptionDetailActivity.class); detailIntent = new Intent(this, AddressDetailActivity.class);
else else
throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " + throw new IllegalArgumentException("Plaintext or BitmessageAddress expected, but " +
"was " "was "
@ -468,8 +491,4 @@ public class MainActivity extends AppCompatActivity
} }
super.onStop(); super.onStop();
} }
public BitmessageAddress getSelectedIdentity() {
return selectedIdentity;
}
} }

View File

@ -43,6 +43,9 @@ import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.ports.MessageRepository; import ch.dissem.bitmessage.ports.MessageRepository;
import static android.text.util.Linkify.WEB_URLS; 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_ADDRESS_PATTERN;
import static ch.dissem.apps.abit.util.Constants.BITMESSAGE_URL_SCHEMA; import static ch.dissem.apps.abit.util.Constants.BITMESSAGE_URL_SCHEMA;
@ -152,8 +155,12 @@ public class MessageDetailFragment extends Fragment {
case R.id.reply: case R.id.reply:
Intent replyIntent = new Intent(getActivity().getApplicationContext(), Intent replyIntent = new Intent(getActivity().getApplicationContext(),
ComposeMessageActivity.class); ComposeMessageActivity.class);
replyIntent.putExtra(ComposeMessageActivity.EXTRA_RECIPIENT, item.getFrom()); replyIntent.putExtra(EXTRA_RECIPIENT, item.getFrom());
replyIntent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, item.getTo()); replyIntent.putExtra(EXTRA_IDENTITY, item.getTo());
replyIntent.putExtra(EXTRA_SUBJECT,
(item.getSubject().substring(0, 3).equalsIgnoreCase("RE:") ? "" : "RE: ")
+ item.getSubject()
);
startActivity(replyIntent); startActivity(replyIntent);
return true; return true;
case R.id.delete: case R.id.delete:

View File

@ -132,7 +132,7 @@ public class MessageListFragment extends AbstractItemListFragment<Plaintext> {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Intent intent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class); 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); startActivity(intent);
} }
}); });

View File

@ -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();
}
}

View File

@ -239,7 +239,7 @@ public class AndroidAddressRepository implements AddressRepository {
@Override @Override
public void remove(BitmessageAddress address) { public void remove(BitmessageAddress address) {
SQLiteDatabase db = sql.getWritableDatabase(); SQLiteDatabase db = sql.getWritableDatabase();
db.delete(TABLE_NAME, "address = " + address.getAddress(), null); db.delete(TABLE_NAME, "address = ?", new String[]{address.getAddress()});
} }
@Override @Override

View File

@ -124,4 +124,10 @@ public class Singleton {
} }
return identity; 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;
}
} }

View File

@ -100,4 +100,18 @@
android:text="@string/pubkey_available" android:text="@string/pubkey_available"
android:textAppearance="?android:attr/textAppearanceSmall"/> 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> </RelativeLayout>

View File

@ -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>

View File

@ -58,4 +58,9 @@
<string name="status_summary">Technische Infos</string> <string name="status_summary">Technische Infos</string>
<string name="pubkey_available">Öffentlicher Schlüssel verfügbar</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="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> </resources>

View File

@ -61,4 +61,9 @@
<string name="alias_default_identity">Me</string> <string name="alias_default_identity">Me</string>
<string name="pubkey_available">Public key available</string> <string name="pubkey_available">Public key available</string>
<string name="pubkey_not_available">Public key not yet 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> </resources>