diff --git a/app/build.gradle b/app/build.gradle index 7401c9b..20fa736 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,13 +36,13 @@ android { } } -ext.jabitVersion = '2.0.4' +//ext.jabitVersion = '2.0.4' +ext.jabitVersion = 'feature-extended-encoding-SNAPSHOT' dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:25.0.0' - compile 'com.android.support:support-v4:25.0.0' - compile 'com.android.support:design:25.0.0' - compile 'com.android.support.constraint:constraint-layout:1.0.0-beta1' + compile 'com.android.support:appcompat-v7:25.0.1' + compile 'com.android.support:support-v4:25.0.1' + compile 'com.android.support:design:25.0.1' compile "ch.dissem.jabit:jabit-core:$jabitVersion" compile "ch.dissem.jabit:jabit-networking:$jabitVersion" @@ -73,6 +73,7 @@ dependencies { testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19' + compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4' } idea.module { diff --git a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java index 270c6ba..2df2a68 100644 --- a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java @@ -28,6 +28,8 @@ import ch.dissem.apps.abit.service.Singleton; import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; +import static ch.dissem.bitmessage.entity.Plaintext.Encoding.EXTENDED; + /** * Compose a new message. */ @@ -37,6 +39,8 @@ public class ComposeMessageActivity extends AppCompatActivity { public static final String EXTRA_SUBJECT = "ch.dissem.abit.Message.SUBJECT"; public static final String EXTRA_CONTENT = "ch.dissem.abit.Message.CONTENT"; public static final String EXTRA_BROADCAST = "ch.dissem.abit.Message.IS_BROADCAST"; + public static final String EXTRA_ENCODING = "ch.dissem.abit.Message.ENCODING"; + public static final String EXTRA_PARENT = "ch.dissem.abit.Message.PARENT"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -69,14 +73,22 @@ public class ComposeMessageActivity extends AppCompatActivity { private static Intent getReplyIntent(Context ctx, Plaintext item) { Intent replyIntent = new Intent(ctx, ComposeMessageActivity.class); - replyIntent.putExtra(EXTRA_RECIPIENT, item.getFrom()); BitmessageAddress receivingIdentity = item.getTo(); if (receivingIdentity.isChan()) { + // reply to chan, not to the sender of the message + replyIntent.putExtra(EXTRA_RECIPIENT, receivingIdentity); // I hate when people send as chan, so it won't be the default behaviour. replyIntent.putExtra(EXTRA_IDENTITY, Singleton.getIdentity(ctx)); } else { + replyIntent.putExtra(EXTRA_RECIPIENT, item.getFrom()); replyIntent.putExtra(EXTRA_IDENTITY, receivingIdentity); } + // if the original message was sent using extended encoding, use it as well + // so features like threading can be supported + if (item.getEncoding() == EXTENDED) { + replyIntent.putExtra(EXTRA_ENCODING, EXTENDED); + replyIntent.putExtra(EXTRA_PARENT, item); + } String prefix; if (item.getSubject().length() >= 3 && item.getSubject().substring(0, 3) .equalsIgnoreCase("RE:")) { diff --git a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.java b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.java index a73a566..b3fa8e1 100644 --- a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageFragment.java @@ -16,6 +16,7 @@ package ch.dissem.apps.abit; +import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -27,18 +28,28 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.EditText; +import android.widget.Toast; import java.util.List; import ch.dissem.apps.abit.adapter.ContactAdapter; +import ch.dissem.apps.abit.dialog.SelectEncodingDialogFragment; import ch.dissem.apps.abit.service.Singleton; +import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding; +import static android.app.Activity.RESULT_OK; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_BROADCAST; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_CONTENT; +import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_ENCODING; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY; +import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_PARENT; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT; import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT; +import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST; +import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG; /** * Compose a new message. @@ -52,6 +63,8 @@ public class ComposeMessageFragment extends Fragment { private EditText subjectInput; private EditText bodyInput; private boolean broadcast; + private Plaintext.Encoding encoding; + private Plaintext parent; /** * Mandatory empty constructor for the fragment manager to instantiate the @@ -79,6 +92,14 @@ public class ComposeMessageFragment extends Fragment { if (getArguments().containsKey(EXTRA_CONTENT)) { content = getArguments().getString(EXTRA_CONTENT); } + if (getArguments().containsKey(EXTRA_ENCODING)) { + encoding = (Plaintext.Encoding) getArguments().getSerializable(EXTRA_ENCODING); + } else { + encoding = Plaintext.Encoding.SIMPLE; + } + if (getArguments().containsKey(EXTRA_PARENT)) { + parent = (Plaintext) getArguments().getSerializable(EXTRA_PARENT); + } } else { throw new RuntimeException("No identity set for ComposeMessageFragment"); } @@ -145,36 +166,83 @@ public class ComposeMessageFragment extends Fragment { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.send: - if (broadcast) { - Singleton.getBitmessageContext(getContext()).broadcast(identity, - subjectInput.getText().toString(), - bodyInput.getText().toString()); - } else { - String inputString = recipientInput.getText().toString(); - if (recipient == null || !recipient.toString().equals(inputString)) { - try { - recipient = new BitmessageAddress(inputString); - } catch (Exception e) { - List contacts = Singleton.getAddressRepository - (getContext()).getContacts(); - for (BitmessageAddress contact : contacts) { - if (inputString.equalsIgnoreCase(contact.getAlias())) { - recipient = contact; - if (inputString.equals(contact.getAlias())) - break; - } - } - } - } - Singleton.getBitmessageContext(getContext()).send(identity, recipient, - subjectInput.getText().toString(), - bodyInput.getText().toString()); - } - getActivity().finish(); + send(); + return true; + case R.id.select_encoding: + SelectEncodingDialogFragment encodingDialog = new SelectEncodingDialogFragment(); + encodingDialog.setTargetFragment(this, 0); + encodingDialog.show(getFragmentManager(), "select encoding dialog"); return true; default: return super.onOptionsItemSelected(item); } } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == 0 && resultCode == RESULT_OK) { + encoding = (Plaintext.Encoding) data.getSerializableExtra(EXTRA_ENCODING); + } else { + super.onActivityResult(requestCode, resultCode, data); + } + } + + private void send() { + Plaintext.Builder builder; + BitmessageContext bmc = Singleton.getBitmessageContext(getContext()); + if (broadcast) { + builder = new Plaintext.Builder(BROADCAST) + .from(identity); + } else { + String inputString = recipientInput.getText().toString(); + if (recipient == null || !recipient.toString().equals(inputString)) { + try { + recipient = new BitmessageAddress(inputString); + } catch (Exception e) { + List contacts = Singleton.getAddressRepository + (getContext()).getContacts(); + for (BitmessageAddress contact : contacts) { + if (inputString.equalsIgnoreCase(contact.getAlias())) { + recipient = contact; + if (inputString.equals(contact.getAlias())) + break; + } + } + } + } + builder = new Plaintext.Builder(MSG) + .from(identity) + .to(recipient); + } + switch (encoding) { + case SIMPLE: + builder.message( + subjectInput.getText().toString(), + bodyInput.getText().toString() + ); + break; + case EXTENDED: + builder.message( + new ExtendedEncoding.Builder() + .message() + .subject(subjectInput.getText().toString()) + .body(bodyInput.getText().toString()) + .build() + ); + break; + default: + Toast.makeText( + getContext(), + getContext().getString(R.string.error_unsupported_encoding, encoding), + Toast.LENGTH_LONG + ).show(); + builder.message( + subjectInput.getText().toString(), + bodyInput.getText().toString() + ); + } + bmc.send(builder.build()); + getActivity().finish(); + } } diff --git a/app/src/main/java/ch/dissem/apps/abit/dialog/SelectEncodingDialogFragment.java b/app/src/main/java/ch/dissem/apps/abit/dialog/SelectEncodingDialogFragment.java new file mode 100644 index 0000000..c9f3837 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/dialog/SelectEncodingDialogFragment.java @@ -0,0 +1,98 @@ +/* + * 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. + */ + +package ch.dissem.apps.abit.dialog; + +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatDialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RadioGroup; +import android.widget.Toast; + +import ch.dissem.apps.abit.ImportIdentityActivity; +import ch.dissem.apps.abit.MainActivity; +import ch.dissem.apps.abit.R; +import ch.dissem.bitmessage.entity.BitmessageAddress; +import ch.dissem.bitmessage.entity.Plaintext; +import ch.dissem.bitmessage.entity.payload.Pubkey; + +import static android.app.Activity.RESULT_OK; +import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_ENCODING; +import static ch.dissem.bitmessage.entity.Plaintext.Encoding.EXTENDED; +import static ch.dissem.bitmessage.entity.Plaintext.Encoding.SIMPLE; + +/** + * @author Christian Basler + */ + +public class SelectEncodingDialogFragment extends AppCompatDialogFragment { + private Plaintext.Encoding encoding; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle + savedInstanceState) { + if (getArguments() != null && getArguments().containsKey(EXTRA_ENCODING)) { + encoding = (Plaintext.Encoding) getArguments().getSerializable(EXTRA_ENCODING); + } + if (encoding == null) { + encoding = SIMPLE; + } + getDialog().setTitle(R.string.select_encoding_title); + View view = inflater.inflate(R.layout.dialog_select_message_encoding, container, false); + final RadioGroup radioGroup = (RadioGroup) view.findViewById(R.id.radioGroup); + switch (encoding) { + case SIMPLE: + radioGroup.check(R.id.simple); + break; + case EXTENDED: + radioGroup.check(R.id.extended); + break; + } + view.findViewById(R.id.ok).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + switch (radioGroup.getCheckedRadioButtonId()) { + case R.id.extended: + encoding = EXTENDED; + break; + case R.id.simple: + break; + default: + encoding = SIMPLE; + return; + } + Intent result = new Intent(); + result.putExtra(EXTRA_ENCODING, encoding); + getTargetFragment().onActivityResult(getTargetRequestCode(), RESULT_OK, result); + dismiss(); + } + }); + view.findViewById(R.id.dismiss).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + dismiss(); + } + }); + return view; + } +} 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 24cc1bb..a0d6b4d 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 @@ -199,8 +199,6 @@ public class AndroidAddressRepository implements AddressRepository { ByteArrayOutputStream out = new ByteArrayOutputStream(); address.getPubkey().writeUnencrypted(out); values.put(COLUMN_PUBLIC_KEY, out.toByteArray()); - } else { - values.put(COLUMN_PUBLIC_KEY, (byte[]) null); } if (address.getPrivateKey() != null) { values.put(COLUMN_PRIVATE_KEY, Encode.bytes(address.getPrivateKey())); diff --git a/app/src/main/res/drawable/ic_action_encoding.xml b/app/src/main/res/drawable/ic_action_encoding.xml new file mode 100644 index 0000000..92b7f03 --- /dev/null +++ b/app/src/main/res/drawable/ic_action_encoding.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/app/src/main/res/layout/dialog_select_message_encoding.xml b/app/src/main/res/layout/dialog_select_message_encoding.xml new file mode 100644 index 0000000..4a1991f --- /dev/null +++ b/app/src/main/res/layout/dialog_select_message_encoding.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + +