Merge branch 'feature/conversations' into develop
This commit is contained in:
commit
3ae572bcf2
@ -21,6 +21,7 @@ android {
|
|||||||
versionCode 11
|
versionCode 11
|
||||||
versionName "1.0-beta11"
|
versionName "1.0-beta11"
|
||||||
jackOptions.enabled = false
|
jackOptions.enabled = false
|
||||||
|
multiDexEnabled true
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_7
|
sourceCompatibility JavaVersion.VERSION_1_7
|
||||||
@ -38,11 +39,14 @@ android {
|
|||||||
|
|
||||||
//ext.jabitVersion = '2.0.4'
|
//ext.jabitVersion = '2.0.4'
|
||||||
ext.jabitVersion = 'feature-extended-encoding-SNAPSHOT'
|
ext.jabitVersion = 'feature-extended-encoding-SNAPSHOT'
|
||||||
|
ext.supportVersion = '25.3.1'
|
||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
compile 'com.android.support:appcompat-v7:25.1.0'
|
|
||||||
compile 'com.android.support:support-v4:25.1.0'
|
compile "com.android.support:appcompat-v7:$supportVersion"
|
||||||
compile 'com.android.support:design:25.1.0'
|
compile "com.android.support:support-v4:$supportVersion"
|
||||||
|
compile "com.android.support:design:$supportVersion"
|
||||||
|
compile "com.android.support:multidex:1.0.1"
|
||||||
|
|
||||||
compile "ch.dissem.jabit:jabit-core:$jabitVersion"
|
compile "ch.dissem.jabit:jabit-core:$jabitVersion"
|
||||||
compile "ch.dissem.jabit:jabit-networking:$jabitVersion"
|
compile "ch.dissem.jabit:jabit-networking:$jabitVersion"
|
||||||
@ -50,30 +54,32 @@ dependencies {
|
|||||||
compile "ch.dissem.jabit:jabit-extensions:$jabitVersion"
|
compile "ch.dissem.jabit:jabit-extensions:$jabitVersion"
|
||||||
compile "ch.dissem.jabit:jabit-wif:$jabitVersion"
|
compile "ch.dissem.jabit:jabit-wif:$jabitVersion"
|
||||||
|
|
||||||
compile 'org.slf4j:slf4j-android:1.7.12'
|
compile 'org.slf4j:slf4j-android:1.7.25'
|
||||||
|
|
||||||
compile 'com.mikepenz:materialize:1.0.0@aar'
|
compile 'com.mikepenz:materialize:1.0.1@aar'
|
||||||
compile('com.mikepenz:materialdrawer:5.6.0@aar') {
|
compile('com.mikepenz:materialdrawer:5.9.0@aar') {
|
||||||
transitive = true
|
transitive = true
|
||||||
}
|
}
|
||||||
compile('com.mikepenz:aboutlibraries:5.8.1@aar') {
|
compile('com.mikepenz:aboutlibraries:5.9.5@aar') {
|
||||||
transitive = true
|
transitive = true
|
||||||
}
|
}
|
||||||
compile 'com.mikepenz:iconics:1.6.2@aar'
|
compile "com.mikepenz:iconics-core:2.8.3@aar"
|
||||||
compile 'com.mikepenz:community-material-typeface:1.5.54.2@aar'
|
compile 'com.mikepenz:google-material-typeface:3.0.1.0.original@aar'
|
||||||
|
compile 'com.mikepenz:community-material-typeface:1.9.32.1@aar'
|
||||||
|
|
||||||
compile 'com.journeyapps:zxing-android-embedded:3.3.0@aar'
|
compile 'com.journeyapps:zxing-android-embedded:3.5.0@aar'
|
||||||
compile 'com.google.zxing:core:3.3.0'
|
compile 'com.google.zxing:core:3.3.0'
|
||||||
|
|
||||||
compile 'io.github.yavski:fab-speed-dial:1.0.6'
|
compile 'io.github.yavski:fab-speed-dial:1.0.6'
|
||||||
compile 'com.github.amlcurran.showcaseview:library:5.4.3'
|
compile 'com.github.amlcurran.showcaseview:library:5.4.3'
|
||||||
compile('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.9.3@aar') {
|
compile('com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:0.10.4@aar') {
|
||||||
transitive = true
|
transitive = true
|
||||||
}
|
}
|
||||||
compile 'com.github.angads25:filepicker:1.0.6'
|
compile 'com.github.angads25:filepicker:1.1.0'
|
||||||
|
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
testCompile 'org.mockito:mockito-core:2.7.22'
|
||||||
compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
idea.module {
|
idea.module {
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
|
android:name="android.support.multidex.MultiDexApplication"
|
||||||
tools:replace="android:allowBackup">
|
tools:replace="android:allowBackup">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
ALTER TABLE Message ADD COLUMN conversation BINARY[16];
|
||||||
|
|
||||||
|
CREATE TABLE Message_Parent (
|
||||||
|
parent BINARY(64) NOT NULL,
|
||||||
|
child BINARY(64) NOT NULL,
|
||||||
|
pos INT NOT NULL,
|
||||||
|
conversation BINARY[16] NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (parent, child),
|
||||||
|
FOREIGN KEY (child) REFERENCES Message (iv)
|
||||||
|
);
|
@ -20,7 +20,6 @@ import android.app.Activity;
|
|||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.app.FragmentActivity;
|
import android.support.v4.app.FragmentActivity;
|
||||||
@ -38,24 +37,14 @@ import android.widget.Switch;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.google.zxing.BarcodeFormat;
|
|
||||||
import com.google.zxing.MultiFormatWriter;
|
|
||||||
import com.google.zxing.WriterException;
|
|
||||||
import com.google.zxing.common.BitMatrix;
|
|
||||||
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
|
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
|
||||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
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.service.Singleton;
|
||||||
import ch.dissem.apps.abit.util.Drawables;
|
import ch.dissem.apps.abit.util.Drawables;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.wif.WifExporter;
|
import ch.dissem.bitmessage.wif.WifExporter;
|
||||||
|
|
||||||
import static android.graphics.Color.BLACK;
|
|
||||||
import static android.graphics.Color.WHITE;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A fragment representing a single Message detail screen.
|
* A fragment representing a single Message detail screen.
|
||||||
@ -64,7 +53,6 @@ import static android.graphics.Color.WHITE;
|
|||||||
* on handsets.
|
* on handsets.
|
||||||
*/
|
*/
|
||||||
public class AddressDetailFragment extends Fragment {
|
public class AddressDetailFragment extends Fragment {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(AddressDetailFragment.class);
|
|
||||||
/**
|
/**
|
||||||
* The fragment argument representing the item ID that this fragment
|
* The fragment argument representing the item ID that this fragment
|
||||||
* represents.
|
* represents.
|
||||||
@ -72,8 +60,6 @@ public class AddressDetailFragment extends Fragment {
|
|||||||
public static final String ARG_ITEM = "item";
|
public static final String ARG_ITEM = "item";
|
||||||
public static final String EXPORT_POSTFIX = ".keys.dat";
|
public static final String EXPORT_POSTFIX = ".keys.dat";
|
||||||
|
|
||||||
private static final int QR_CODE_SIZE = 350;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The content this fragment is presenting.
|
* The content this fragment is presenting.
|
||||||
*/
|
*/
|
||||||
@ -257,40 +243,12 @@ public class AddressDetailFragment extends Fragment {
|
|||||||
|
|
||||||
// QR code
|
// QR code
|
||||||
ImageView qrCode = (ImageView) rootView.findViewById(R.id.qr_code);
|
ImageView qrCode = (ImageView) rootView.findViewById(R.id.qr_code);
|
||||||
qrCode.setImageBitmap(qrCode(item));
|
qrCode.setImageBitmap(Drawables.qrCode(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap qrCode(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
|
@Override
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
|
@ -87,8 +87,8 @@ public class ComposeMessageActivity extends AppCompatActivity {
|
|||||||
// so features like threading can be supported
|
// so features like threading can be supported
|
||||||
if (item.getEncoding() == EXTENDED) {
|
if (item.getEncoding() == EXTENDED) {
|
||||||
replyIntent.putExtra(EXTRA_ENCODING, EXTENDED);
|
replyIntent.putExtra(EXTRA_ENCODING, EXTENDED);
|
||||||
replyIntent.putExtra(EXTRA_PARENT, item);
|
|
||||||
}
|
}
|
||||||
|
replyIntent.putExtra(EXTRA_PARENT, item);
|
||||||
String prefix;
|
String prefix;
|
||||||
if (item.getSubject().length() >= 3 && item.getSubject().substring(0, 3)
|
if (item.getSubject().length() >= 3 && item.getSubject().substring(0, 3)
|
||||||
.equalsIgnoreCase("RE:")) {
|
.equalsIgnoreCase("RE:")) {
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package ch.dissem.apps.abit;
|
package ch.dissem.apps.abit;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
@ -79,6 +80,11 @@ public class ComposeMessageFragment extends Fragment {
|
|||||||
if (getArguments() != null) {
|
if (getArguments() != null) {
|
||||||
if (getArguments().containsKey(EXTRA_IDENTITY)) {
|
if (getArguments().containsKey(EXTRA_IDENTITY)) {
|
||||||
identity = (BitmessageAddress) getArguments().getSerializable(EXTRA_IDENTITY);
|
identity = (BitmessageAddress) getArguments().getSerializable(EXTRA_IDENTITY);
|
||||||
|
if (getActivity() != null) {
|
||||||
|
if (identity == null || identity.getPrivateKey() == null) {
|
||||||
|
identity = Singleton.getIdentity(getActivity());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("No identity set for ComposeMessageFragment");
|
throw new RuntimeException("No identity set for ComposeMessageFragment");
|
||||||
}
|
}
|
||||||
@ -156,6 +162,14 @@ public class ComposeMessageFragment extends Fragment {
|
|||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (identity == null || identity.getPrivateKey() == null) {
|
||||||
|
identity = Singleton.getIdentity(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.compose, menu);
|
inflater.inflate(R.menu.compose, menu);
|
||||||
@ -170,6 +184,9 @@ public class ComposeMessageFragment extends Fragment {
|
|||||||
return true;
|
return true;
|
||||||
case R.id.select_encoding:
|
case R.id.select_encoding:
|
||||||
SelectEncodingDialogFragment encodingDialog = new SelectEncodingDialogFragment();
|
SelectEncodingDialogFragment encodingDialog = new SelectEncodingDialogFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putSerializable(EXTRA_ENCODING, encoding);
|
||||||
|
encodingDialog.setArguments(args);
|
||||||
encodingDialog.setTargetFragment(this, 0);
|
encodingDialog.setTargetFragment(this, 0);
|
||||||
encodingDialog.show(getFragmentManager(), "select encoding dialog");
|
encodingDialog.show(getFragmentManager(), "select encoding dialog");
|
||||||
return true;
|
return true;
|
||||||
|
@ -20,17 +20,32 @@ import android.app.Activity;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Base64;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Switch;
|
import android.widget.Switch;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import ch.dissem.apps.abit.service.Singleton;
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.V2Pubkey;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.V3Pubkey;
|
||||||
|
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
|
||||||
|
|
||||||
|
import static android.util.Base64.URL_SAFE;
|
||||||
|
|
||||||
public class CreateAddressActivity extends AppCompatActivity {
|
public class CreateAddressActivity extends AppCompatActivity {
|
||||||
|
private static final Pattern KEY_VALUE_PATTERN = Pattern.compile("^([a-zA-Z]+)=(.*)$");
|
||||||
|
private byte[] pubkeyBytes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -48,12 +63,21 @@ public class CreateAddressActivity extends AppCompatActivity {
|
|||||||
String addressText = getAddress(uri);
|
String addressText = getAddress(uri);
|
||||||
String[] parameters = getParameters(uri);
|
String[] parameters = getParameters(uri);
|
||||||
for (String parameter : parameters) {
|
for (String parameter : parameters) {
|
||||||
String name = parameter.substring(0, 6).toLowerCase();
|
Matcher matcher = KEY_VALUE_PATTERN.matcher(parameter);
|
||||||
if (name.startsWith("label")) {
|
if (matcher.find()) {
|
||||||
label.setText(parameter.substring(parameter.indexOf('=') + 1).trim());
|
String key = matcher.group(1).toLowerCase();
|
||||||
} else if (name.startsWith("action")) {
|
String value = matcher.group(2);
|
||||||
parameter = parameter.toLowerCase();
|
switch (key) {
|
||||||
subscribe.setChecked(parameter.contains("subscribe"));
|
case "label":
|
||||||
|
label.setText(value.trim());
|
||||||
|
break;
|
||||||
|
case "action":
|
||||||
|
subscribe.setChecked(value.trim().equalsIgnoreCase("subscribe"));
|
||||||
|
break;
|
||||||
|
case "pubkey":
|
||||||
|
pubkeyBytes = Base64.decode(value, URL_SAFE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +107,30 @@ public class CreateAddressActivity extends AppCompatActivity {
|
|||||||
if (subscribe.isChecked()) {
|
if (subscribe.isChecked()) {
|
||||||
bmc.addSubscribtion(bmAddress);
|
bmc.addSubscribtion(bmAddress);
|
||||||
}
|
}
|
||||||
|
if (pubkeyBytes != null) {
|
||||||
|
try {
|
||||||
|
final Pubkey pubkey;
|
||||||
|
InputStream pubkeyStream = new ByteArrayInputStream(pubkeyBytes);
|
||||||
|
long stream = bmAddress.getStream();
|
||||||
|
switch ((int) bmAddress.getVersion()) {
|
||||||
|
case 2:
|
||||||
|
pubkey = V2Pubkey.read(pubkeyStream, stream);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
pubkey = V3Pubkey.read(pubkeyStream, stream);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
pubkey = new V4Pubkey(V3Pubkey.read(pubkeyStream, stream));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pubkey = null;
|
||||||
|
}
|
||||||
|
if (pubkey != null) {
|
||||||
|
bmAddress.setPubkey(pubkey);
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setResult(Activity.RESULT_OK);
|
setResult(Activity.RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
|
@ -16,16 +16,23 @@
|
|||||||
|
|
||||||
package ch.dissem.apps.abit;
|
package ch.dissem.apps.abit;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Display;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
import android.widget.CompoundButton;
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@ -48,9 +55,6 @@ import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
|
|||||||
import com.mikepenz.materialdrawer.model.interfaces.IProfile;
|
import com.mikepenz.materialdrawer.model.interfaces.IProfile;
|
||||||
import com.mikepenz.materialdrawer.model.interfaces.Nameable;
|
import com.mikepenz.materialdrawer.model.interfaces.Nameable;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -64,6 +68,8 @@ import ch.dissem.apps.abit.repository.AndroidMessageRepository;
|
|||||||
import ch.dissem.apps.abit.service.BitmessageService;
|
import ch.dissem.apps.abit.service.BitmessageService;
|
||||||
import ch.dissem.apps.abit.service.Singleton;
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
import ch.dissem.apps.abit.synchronization.SyncAdapter;
|
import ch.dissem.apps.abit.synchronization.SyncAdapter;
|
||||||
|
import ch.dissem.apps.abit.util.Drawables;
|
||||||
|
import ch.dissem.apps.abit.util.Labels;
|
||||||
import ch.dissem.apps.abit.util.Preferences;
|
import ch.dissem.apps.abit.util.Preferences;
|
||||||
import ch.dissem.bitmessage.BitmessageContext;
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
@ -72,6 +78,7 @@ import ch.dissem.bitmessage.entity.valueobject.Label;
|
|||||||
|
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static ch.dissem.apps.abit.ComposeMessageActivity.launchReplyTo;
|
import static ch.dissem.apps.abit.ComposeMessageActivity.launchReplyTo;
|
||||||
|
import static ch.dissem.apps.abit.repository.AndroidMessageRepository.LABEL_ARCHIVE;
|
||||||
import static ch.dissem.apps.abit.service.BitmessageService.isRunning;
|
import static ch.dissem.apps.abit.service.BitmessageService.isRunning;
|
||||||
|
|
||||||
|
|
||||||
@ -99,7 +106,6 @@ public class MainActivity extends AppCompatActivity
|
|||||||
public static final String EXTRA_REPLY_TO_MESSAGE = "ch.dissem.abit.ReplyToMessage";
|
public static final String EXTRA_REPLY_TO_MESSAGE = "ch.dissem.abit.ReplyToMessage";
|
||||||
public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox";
|
public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox";
|
||||||
|
|
||||||
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;
|
private static final int MANAGE_IDENTITY = 2;
|
||||||
|
|
||||||
@ -233,6 +239,50 @@ public class MainActivity extends AppCompatActivity
|
|||||||
.withActivity(this)
|
.withActivity(this)
|
||||||
.withHeaderBackground(R.drawable.header)
|
.withHeaderBackground(R.drawable.header)
|
||||||
.withProfiles(profiles)
|
.withProfiles(profiles)
|
||||||
|
.withOnAccountHeaderProfileImageListener(new AccountHeader.OnAccountHeaderProfileImageListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onProfileImageClick(View view, IProfile profile, boolean current) {
|
||||||
|
if (current) {
|
||||||
|
// Show QR code in modal dialog
|
||||||
|
final Dialog dialog = new Dialog(MainActivity.this);
|
||||||
|
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
|
||||||
|
ImageView imageView = new ImageView(MainActivity.this);
|
||||||
|
imageView.setImageBitmap(Drawables.qrCode(Singleton.getIdentity(MainActivity.this)));
|
||||||
|
imageView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dialog.addContentView(imageView, new RelativeLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
Window window = dialog.getWindow();
|
||||||
|
if (window != null) {
|
||||||
|
Display display = window.getWindowManager().getDefaultDisplay();
|
||||||
|
Point size = new Point();
|
||||||
|
display.getSize(size);
|
||||||
|
int dim = size.x < size.y ? size.x : size.y;
|
||||||
|
|
||||||
|
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
|
||||||
|
lp.copyFrom(window.getAttributes());
|
||||||
|
lp.width = dim;
|
||||||
|
lp.height = dim;
|
||||||
|
|
||||||
|
window.setAttributes(lp);
|
||||||
|
}
|
||||||
|
dialog.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onProfileImageLongClick(View view, IProfile iProfile, boolean b) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
.withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
|
.withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onProfileChanged(View view, IProfile profile, boolean current) {
|
public boolean onProfileChanged(View view, IProfile profile, boolean current) {
|
||||||
@ -272,7 +322,7 @@ public class MainActivity extends AppCompatActivity
|
|||||||
final ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
|
final ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
|
||||||
drawerItems.add(new PrimaryDrawerItem()
|
drawerItems.add(new PrimaryDrawerItem()
|
||||||
.withName(R.string.archive)
|
.withName(R.string.archive)
|
||||||
.withTag(AndroidMessageRepository.LABEL_ARCHIVE)
|
.withTag(LABEL_ARCHIVE)
|
||||||
.withIcon(CommunityMaterial.Icon.cmd_archive)
|
.withIcon(CommunityMaterial.Icon.cmd_archive)
|
||||||
);
|
);
|
||||||
drawerItems.add(new DividerDrawerItem());
|
drawerItems.add(new DividerDrawerItem());
|
||||||
@ -311,7 +361,15 @@ public class MainActivity extends AppCompatActivity
|
|||||||
public boolean onItemClick(View view, int position, IDrawerItem item) {
|
public boolean onItemClick(View view, int position, IDrawerItem item) {
|
||||||
if (item.getTag() instanceof Label) {
|
if (item.getTag() instanceof Label) {
|
||||||
selectedLabel = (Label) item.getTag();
|
selectedLabel = (Label) item.getTag();
|
||||||
showSelectedLabel();
|
if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof
|
||||||
|
MessageListFragment) {
|
||||||
|
((MessageListFragment) getSupportFragmentManager()
|
||||||
|
.findFragmentById(R.id.item_list)).updateList(selectedLabel);
|
||||||
|
} else {
|
||||||
|
MessageListFragment listFragment = new MessageListFragment();
|
||||||
|
changeList(listFragment);
|
||||||
|
listFragment.updateList(selectedLabel);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
} else if (item instanceof Nameable<?>) {
|
} else if (item instanceof Nameable<?>) {
|
||||||
Nameable<?> ni = (Nameable<?>) item;
|
Nameable<?> ni = (Nameable<?>) item;
|
||||||
@ -374,7 +432,10 @@ public class MainActivity extends AppCompatActivity
|
|||||||
for (Label label : labels) {
|
for (Label label : labels) {
|
||||||
addLabelEntry(label);
|
addLabelEntry(label);
|
||||||
}
|
}
|
||||||
showSelectedLabel();
|
IDrawerItem selectedDrawerItem = drawer.getDrawerItem(selectedLabel);
|
||||||
|
if (selectedDrawerItem != null) {
|
||||||
|
drawer.setSelection(selectedDrawerItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
@ -389,7 +450,11 @@ public class MainActivity extends AppCompatActivity
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||||
selectedLabel = (Label) savedInstanceState.getSerializable("selectedLabel");
|
selectedLabel = (Label) savedInstanceState.getSerializable("selectedLabel");
|
||||||
showSelectedLabel();
|
|
||||||
|
IDrawerItem selectedItem = drawer.getDrawerItem(selectedLabel);
|
||||||
|
if (selectedItem != null) {
|
||||||
|
drawer.setSelection(selectedItem);
|
||||||
|
}
|
||||||
super.onRestoreInstanceState(savedInstanceState);
|
super.onRestoreInstanceState(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,37 +491,9 @@ public class MainActivity extends AppCompatActivity
|
|||||||
public void addLabelEntry(Label label) {
|
public void addLabelEntry(Label label) {
|
||||||
PrimaryDrawerItem item = new PrimaryDrawerItem()
|
PrimaryDrawerItem item = new PrimaryDrawerItem()
|
||||||
.withName(label.toString())
|
.withName(label.toString())
|
||||||
.withTag(label);
|
.withTag(label)
|
||||||
if (label.getType() == null) {
|
.withIcon(Labels.getIcon(label))
|
||||||
item.withIcon(CommunityMaterial.Icon.cmd_label)
|
.withIconColor(Labels.getColor(label));
|
||||||
.withIconColor(label.getColor());
|
|
||||||
} else {
|
|
||||||
switch (label.getType()) {
|
|
||||||
case INBOX:
|
|
||||||
item.withIcon(GoogleMaterial.Icon.gmd_inbox);
|
|
||||||
break;
|
|
||||||
case DRAFT:
|
|
||||||
item.withIcon(CommunityMaterial.Icon.cmd_file);
|
|
||||||
break;
|
|
||||||
case OUTBOX:
|
|
||||||
item.withIcon(CommunityMaterial.Icon.cmd_outbox);
|
|
||||||
break;
|
|
||||||
case SENT:
|
|
||||||
item.withIcon(CommunityMaterial.Icon.cmd_send);
|
|
||||||
break;
|
|
||||||
case BROADCAST:
|
|
||||||
item.withIcon(CommunityMaterial.Icon.cmd_rss);
|
|
||||||
break;
|
|
||||||
case UNREAD:
|
|
||||||
item.withIcon(GoogleMaterial.Icon.gmd_markunread_mailbox);
|
|
||||||
break;
|
|
||||||
case TRASH:
|
|
||||||
item.withIcon(GoogleMaterial.Icon.gmd_delete);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
item.withIcon(CommunityMaterial.Icon.cmd_label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
drawer.addItemAtPosition(item, drawer.getDrawerItems().size() - 3);
|
drawer.addItemAtPosition(item, drawer.getDrawerItems().size() - 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -497,13 +534,15 @@ public class MainActivity extends AppCompatActivity
|
|||||||
for (IDrawerItem item : drawer.getDrawerItems()) {
|
for (IDrawerItem item : drawer.getDrawerItems()) {
|
||||||
if (item.getTag() instanceof Label) {
|
if (item.getTag() instanceof Label) {
|
||||||
Label label = (Label) item.getTag();
|
Label label = (Label) item.getTag();
|
||||||
int unread = bmc.messages().countUnread(label);
|
if (label != LABEL_ARCHIVE) {
|
||||||
if (unread > 0) {
|
int unread = bmc.messages().countUnread(label);
|
||||||
((PrimaryDrawerItem) item).withBadge(String.valueOf(unread));
|
if (unread > 0) {
|
||||||
} else {
|
((PrimaryDrawerItem) item).withBadge(String.valueOf(unread));
|
||||||
((PrimaryDrawerItem) item).withBadge((String) null);
|
} else {
|
||||||
|
((PrimaryDrawerItem) item).withBadge((String) null);
|
||||||
|
}
|
||||||
|
drawer.updateItem(item);
|
||||||
}
|
}
|
||||||
drawer.updateItem(item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -521,18 +560,6 @@ public class MainActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showSelectedLabel() {
|
|
||||||
if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof
|
|
||||||
MessageListFragment) {
|
|
||||||
((MessageListFragment) getSupportFragmentManager()
|
|
||||||
.findFragmentById(R.id.item_list)).updateList(selectedLabel);
|
|
||||||
} else {
|
|
||||||
MessageListFragment listFragment = new MessageListFragment();
|
|
||||||
changeList(listFragment);
|
|
||||||
listFragment.updateList(selectedLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback method from {@link ListSelectionListener}
|
* Callback method from {@link ListSelectionListener}
|
||||||
* indicating that the item with the given ID was selected.
|
* indicating that the item with the given ID was selected.
|
||||||
|
@ -16,8 +16,14 @@
|
|||||||
|
|
||||||
package ch.dissem.apps.abit;
|
package ch.dissem.apps.abit;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.IdRes;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.util.Linkify;
|
import android.text.util.Linkify;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@ -29,22 +35,29 @@ import android.widget.ImageView;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
||||||
|
import com.mikepenz.iconics.view.IconicsImageView;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
|
||||||
import ch.dissem.apps.abit.listener.ActionBarListener;
|
import ch.dissem.apps.abit.listener.ActionBarListener;
|
||||||
import ch.dissem.apps.abit.service.Singleton;
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
import ch.dissem.apps.abit.util.Assets;
|
import ch.dissem.apps.abit.util.Assets;
|
||||||
import ch.dissem.apps.abit.util.Drawables;
|
import ch.dissem.apps.abit.util.Drawables;
|
||||||
|
import ch.dissem.apps.abit.util.Labels;
|
||||||
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
import ch.dissem.bitmessage.entity.Plaintext;
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
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.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;
|
||||||
|
import static ch.dissem.apps.abit.util.Strings.normalizeWhitespaces;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,6 +119,11 @@ public class MessageDetailFragment extends Fragment {
|
|||||||
} else if (item.getType() == Plaintext.Type.BROADCAST) {
|
} else if (item.getType() == Plaintext.Type.BROADCAST) {
|
||||||
((TextView) rootView.findViewById(R.id.recipient)).setText(R.string.broadcast);
|
((TextView) rootView.findViewById(R.id.recipient)).setText(R.string.broadcast);
|
||||||
}
|
}
|
||||||
|
RecyclerView labelView = (RecyclerView) rootView.findViewById(R.id.labels);
|
||||||
|
LabelAdapter labelAdapter = new LabelAdapter(getActivity(), item.getLabels());
|
||||||
|
labelView.setAdapter(labelAdapter);
|
||||||
|
labelView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
|
||||||
|
|
||||||
TextView messageBody = (TextView) rootView.findViewById(R.id.text);
|
TextView messageBody = (TextView) rootView.findViewById(R.id.text);
|
||||||
messageBody.setText(item.getText());
|
messageBody.setText(item.getText());
|
||||||
|
|
||||||
@ -130,16 +148,33 @@ public class MessageDetailFragment extends Fragment {
|
|||||||
removed = true;
|
removed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MessageRepository messageRepo = Singleton.getMessageRepository(inflater.getContext());
|
||||||
if (removed) {
|
if (removed) {
|
||||||
if (getActivity() instanceof ActionBarListener) {
|
if (getActivity() instanceof ActionBarListener) {
|
||||||
((ActionBarListener) getActivity()).updateUnread();
|
((ActionBarListener) getActivity()).updateUnread();
|
||||||
}
|
}
|
||||||
Singleton.getMessageRepository(inflater.getContext()).save(item);
|
messageRepo.save(item);
|
||||||
}
|
}
|
||||||
|
List<Plaintext> parents = new ArrayList<>(item.getParents().size());
|
||||||
|
for (InventoryVector parentIV : item.getParents()) {
|
||||||
|
Plaintext parent = messageRepo.getMessage(parentIV);
|
||||||
|
if (parent != null) {
|
||||||
|
parents.add(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
showRelatedMessages(rootView, R.id.parents, parents);
|
||||||
|
showRelatedMessages(rootView, R.id.responses, messageRepo.findResponses(item));
|
||||||
}
|
}
|
||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void showRelatedMessages(View rootView, @IdRes int id, List<Plaintext> messages) {
|
||||||
|
RecyclerView recyclerView = (RecyclerView) rootView.findViewById(id);
|
||||||
|
RelatedMessageAdapter adapter = new RelatedMessageAdapter(getActivity(), messages);
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.message, menu);
|
inflater.inflate(R.menu.message, menu);
|
||||||
@ -197,4 +232,136 @@ public class MessageDetailFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class RelatedMessageAdapter extends RecyclerView.Adapter<RelatedMessageAdapter.ViewHolder> {
|
||||||
|
private final List<Plaintext> messages;
|
||||||
|
private final Context ctx;
|
||||||
|
|
||||||
|
private RelatedMessageAdapter(Context ctx, List<Plaintext> messages) {
|
||||||
|
this.messages = messages;
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RelatedMessageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
Context context = parent.getContext();
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
|
|
||||||
|
// Inflate the custom layout
|
||||||
|
View contactView = inflater.inflate(R.layout.item_message_minimized, parent, false);
|
||||||
|
|
||||||
|
// Return a new holder instance
|
||||||
|
return new RelatedMessageAdapter.ViewHolder(contactView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Involves populating data into the item through holder
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(RelatedMessageAdapter.ViewHolder viewHolder, int position) {
|
||||||
|
// Get the data model based on position
|
||||||
|
Plaintext message = messages.get(position);
|
||||||
|
|
||||||
|
viewHolder.avatar.setImageDrawable(new Identicon(message.getFrom()));
|
||||||
|
viewHolder.status.setImageResource(Assets.getStatusDrawable(message.getStatus()));
|
||||||
|
viewHolder.sender.setText(message.getFrom().toString());
|
||||||
|
viewHolder.extract.setText(normalizeWhitespaces(message.getText()));
|
||||||
|
viewHolder.item = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the total count of items in the list
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return messages.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
private final ImageView avatar;
|
||||||
|
private final ImageView status;
|
||||||
|
private final TextView sender;
|
||||||
|
private final TextView extract;
|
||||||
|
private Plaintext item;
|
||||||
|
|
||||||
|
ViewHolder(final View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
avatar = (ImageView) itemView.findViewById(R.id.avatar);
|
||||||
|
status = (ImageView) itemView.findViewById(R.id.status);
|
||||||
|
sender = (TextView) itemView.findViewById(R.id.sender);
|
||||||
|
extract = (TextView) itemView.findViewById(R.id.text);
|
||||||
|
itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (ctx instanceof MainActivity) {
|
||||||
|
((MainActivity) ctx).onItemSelected(item);
|
||||||
|
} else {
|
||||||
|
Intent detailIntent;
|
||||||
|
detailIntent = new Intent(ctx, MessageDetailActivity.class);
|
||||||
|
detailIntent.putExtra(MessageDetailFragment.ARG_ITEM, item);
|
||||||
|
ctx.startActivity(detailIntent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LabelAdapter extends
|
||||||
|
RecyclerView.Adapter<LabelAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private final List<Label> labels;
|
||||||
|
private final Context ctx;
|
||||||
|
|
||||||
|
private LabelAdapter(Context ctx, Set<Label> labels) {
|
||||||
|
this.labels = new ArrayList<>(labels);
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LabelAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
Context context = parent.getContext();
|
||||||
|
LayoutInflater inflater = LayoutInflater.from(context);
|
||||||
|
|
||||||
|
// Inflate the custom layout
|
||||||
|
View contactView = inflater.inflate(R.layout.item_label, parent, false);
|
||||||
|
|
||||||
|
// Return a new holder instance
|
||||||
|
return new ViewHolder(contactView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Involves populating data into the item through holder
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(LabelAdapter.ViewHolder viewHolder, int position) {
|
||||||
|
// Get the data model based on position
|
||||||
|
Label label = labels.get(position);
|
||||||
|
|
||||||
|
viewHolder.icon.setColor(Labels.getColor(label));
|
||||||
|
viewHolder.icon.setIcon(Labels.getIcon(label));
|
||||||
|
viewHolder.label.setText(Labels.getText(label, ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the total count of items in the list
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return labels.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide a direct reference to each of the views within a data item
|
||||||
|
// Used to cache the views within the item layout for fast access
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
// Your holder should contain a member variable
|
||||||
|
// for any view that will be set as you render a row
|
||||||
|
public IconicsImageView icon;
|
||||||
|
public TextView label;
|
||||||
|
|
||||||
|
// We also create a constructor that accepts the entire item row
|
||||||
|
// and does the view lookups to find each subview
|
||||||
|
ViewHolder(View itemView) {
|
||||||
|
// Stores the itemView in a public final member variable that can be used
|
||||||
|
// to access the context from any ViewHolder instance.
|
||||||
|
super(itemView);
|
||||||
|
|
||||||
|
icon = (IconicsImageView) itemView.findViewById(R.id.icon);
|
||||||
|
label = (TextView) itemView.findViewById(R.id.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,10 @@ import android.widget.Toast;
|
|||||||
import com.mikepenz.aboutlibraries.Libs;
|
import com.mikepenz.aboutlibraries.Libs;
|
||||||
import com.mikepenz.aboutlibraries.LibsBuilder;
|
import com.mikepenz.aboutlibraries.LibsBuilder;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.repository.AndroidNodeRegistry;
|
||||||
import ch.dissem.apps.abit.service.Singleton;
|
import ch.dissem.apps.abit.service.Singleton;
|
||||||
import ch.dissem.apps.abit.synchronization.SyncAdapter;
|
import ch.dissem.apps.abit.synchronization.SyncAdapter;
|
||||||
|
import ch.dissem.bitmessage.BitmessageContext;
|
||||||
|
|
||||||
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
|
||||||
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
|
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_TRUSTED_NODE;
|
||||||
@ -78,7 +80,9 @@ public class SettingsFragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
Singleton.getBitmessageContext(ctx).cleanup();
|
BitmessageContext bmc = Singleton.getBitmessageContext(ctx);
|
||||||
|
bmc.cleanup();
|
||||||
|
bmc.internals().getNodeRegistry().clear();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,8 +46,7 @@ public class SelectEncodingDialogFragment extends AppCompatDialogFragment {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
savedInstanceState) {
|
|
||||||
if (getArguments() != null && getArguments().containsKey(EXTRA_ENCODING)) {
|
if (getArguments() != null && getArguments().containsKey(EXTRA_ENCODING)) {
|
||||||
encoding = (Plaintext.Encoding) getArguments().getSerializable(EXTRA_ENCODING);
|
encoding = (Plaintext.Encoding) getArguments().getSerializable(EXTRA_ENCODING);
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,10 @@ import java.io.IOException;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import ch.dissem.apps.abit.R;
|
import ch.dissem.apps.abit.util.Labels;
|
||||||
|
import ch.dissem.apps.abit.util.UuidUtils;
|
||||||
import ch.dissem.bitmessage.entity.Plaintext;
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
@ -40,6 +42,8 @@ import ch.dissem.bitmessage.ports.AbstractMessageRepository;
|
|||||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
import ch.dissem.bitmessage.utils.Encode;
|
import ch.dissem.bitmessage.utils.Encode;
|
||||||
|
|
||||||
|
import static ch.dissem.apps.abit.util.UuidUtils.asUuid;
|
||||||
|
import static ch.dissem.bitmessage.utils.Strings.hex;
|
||||||
import static java.lang.String.valueOf;
|
import static java.lang.String.valueOf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,6 +69,9 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
private static final String COLUMN_RETRIES = "retries";
|
private static final String COLUMN_RETRIES = "retries";
|
||||||
private static final String COLUMN_NEXT_TRY = "next_try";
|
private static final String COLUMN_NEXT_TRY = "next_try";
|
||||||
private static final String COLUMN_INITIAL_HASH = "initial_hash";
|
private static final String COLUMN_INITIAL_HASH = "initial_hash";
|
||||||
|
private static final String COLUMN_CONVERSATION = "conversation";
|
||||||
|
|
||||||
|
private static final String PARENTS_TABLE_NAME = "Message_Parent";
|
||||||
|
|
||||||
private static final String JOIN_TABLE_NAME = "Message_Label";
|
private static final String JOIN_TABLE_NAME = "Message_Label";
|
||||||
private static final String JT_COLUMN_MESSAGE = "message_id";
|
private static final String JT_COLUMN_MESSAGE = "message_id";
|
||||||
@ -122,35 +129,9 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
private Label getLabel(Cursor c) {
|
private Label getLabel(Cursor c) {
|
||||||
String typeName = c.getString(c.getColumnIndex(LBL_COLUMN_TYPE));
|
String typeName = c.getString(c.getColumnIndex(LBL_COLUMN_TYPE));
|
||||||
Label.Type type = typeName == null ? null : Label.Type.valueOf(typeName);
|
Label.Type type = typeName == null ? null : Label.Type.valueOf(typeName);
|
||||||
String text;
|
String text = Labels.getText(type, null, context);
|
||||||
if (type == null) {
|
if (text == null) {
|
||||||
text = c.getString(c.getColumnIndex(LBL_COLUMN_LABEL));
|
text = c.getString(c.getColumnIndex(LBL_COLUMN_LABEL));
|
||||||
} else {
|
|
||||||
switch (type) {
|
|
||||||
case INBOX:
|
|
||||||
text = context.getString(R.string.inbox);
|
|
||||||
break;
|
|
||||||
case DRAFT:
|
|
||||||
text = context.getString(R.string.draft);
|
|
||||||
break;
|
|
||||||
case OUTBOX:
|
|
||||||
text = context.getString(R.string.outbox);
|
|
||||||
break;
|
|
||||||
case SENT:
|
|
||||||
text = context.getString(R.string.sent);
|
|
||||||
break;
|
|
||||||
case UNREAD:
|
|
||||||
text = context.getString(R.string.unread);
|
|
||||||
break;
|
|
||||||
case TRASH:
|
|
||||||
text = context.getString(R.string.trash);
|
|
||||||
break;
|
|
||||||
case BROADCAST:
|
|
||||||
text = context.getString(R.string.broadcasts);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text = c.getString(c.getColumnIndex(LBL_COLUMN_LABEL));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Label label = new Label(
|
Label label = new Label(
|
||||||
text,
|
text,
|
||||||
@ -164,7 +145,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
public int countUnread(Label label) {
|
public int countUnread(Label label) {
|
||||||
String[] args;
|
String[] args;
|
||||||
String where;
|
String where;
|
||||||
if (label == null){
|
if (label == null) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (label == LABEL_ARCHIVE) {
|
if (label == LABEL_ARCHIVE) {
|
||||||
@ -187,6 +168,72 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UUID> findConversations(Label label) {
|
||||||
|
String[] projection = {
|
||||||
|
COLUMN_CONVERSATION,
|
||||||
|
};
|
||||||
|
|
||||||
|
String where;
|
||||||
|
if (label == null) {
|
||||||
|
where = "id NOT IN (SELECT message_id FROM Message_Label)";
|
||||||
|
} else {
|
||||||
|
where = "id IN (SELECT message_id FROM Message_Label WHERE label_id=" + label.getId() + ")";
|
||||||
|
}
|
||||||
|
List<UUID> result = new LinkedList<>();
|
||||||
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
|
try (Cursor c = db.query(
|
||||||
|
TABLE_NAME, projection,
|
||||||
|
where,
|
||||||
|
null, null, null, null
|
||||||
|
)) {
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
byte[] uuidBytes = c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION));
|
||||||
|
result.add(asUuid(uuidBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void updateParents(SQLiteDatabase db, Plaintext message) {
|
||||||
|
if (message.getInventoryVector() == null || message.getParents().isEmpty()) {
|
||||||
|
// There are no parents to save yet (they are saved in the extended data, that's enough for now)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
byte[] childIV = message.getInventoryVector().getHash();
|
||||||
|
db.delete(PARENTS_TABLE_NAME, "child=?", new String[]{hex(childIV).toString()});
|
||||||
|
|
||||||
|
// save new parents
|
||||||
|
int order = 0;
|
||||||
|
for (InventoryVector parentIV : message.getParents()) {
|
||||||
|
Plaintext parent = getMessage(parentIV);
|
||||||
|
mergeConversations(db, parent.getConversationId(), message.getConversationId());
|
||||||
|
order++;
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("parent", parentIV.getHash());
|
||||||
|
values.put("child", childIV);
|
||||||
|
values.put("pos", order);
|
||||||
|
values.put("conversation", UuidUtils.asBytes(message.getConversationId()));
|
||||||
|
db.insertOrThrow(PARENTS_TABLE_NAME, null, values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces every occurrence of the source conversation ID with the target ID
|
||||||
|
*
|
||||||
|
* @param db is used to keep everything within one transaction
|
||||||
|
* @param source ID of the conversation to be merged
|
||||||
|
* @param target ID of the merge target
|
||||||
|
*/
|
||||||
|
private void mergeConversations(SQLiteDatabase db, UUID source, UUID target) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put("conversation", UuidUtils.asBytes(target));
|
||||||
|
String[] whereArgs = {hex(UuidUtils.asBytes(source)).toString()};
|
||||||
|
db.update(TABLE_NAME, values, "conversation=?", whereArgs);
|
||||||
|
db.update(PARENTS_TABLE_NAME, values, "conversation=?", whereArgs);
|
||||||
|
}
|
||||||
|
|
||||||
protected List<Plaintext> find(String where) {
|
protected List<Plaintext> find(String where) {
|
||||||
List<Plaintext> result = new LinkedList<>();
|
List<Plaintext> result = new LinkedList<>();
|
||||||
|
|
||||||
@ -205,7 +252,8 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
COLUMN_STATUS,
|
COLUMN_STATUS,
|
||||||
COLUMN_TTL,
|
COLUMN_TTL,
|
||||||
COLUMN_RETRIES,
|
COLUMN_RETRIES,
|
||||||
COLUMN_NEXT_TRY
|
COLUMN_NEXT_TRY,
|
||||||
|
COLUMN_CONVERSATION
|
||||||
};
|
};
|
||||||
|
|
||||||
SQLiteDatabase db = sql.getReadableDatabase();
|
SQLiteDatabase db = sql.getReadableDatabase();
|
||||||
@ -220,8 +268,8 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
byte[] data = c.getBlob(c.getColumnIndex(COLUMN_DATA));
|
||||||
Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex
|
Plaintext.Type type = Plaintext.Type.valueOf(c.getString(c.getColumnIndex
|
||||||
(COLUMN_TYPE)));
|
(COLUMN_TYPE)));
|
||||||
Plaintext.Builder builder = Plaintext.readWithoutSignature(type, new
|
Plaintext.Builder builder = Plaintext.readWithoutSignature(type,
|
||||||
ByteArrayInputStream(data));
|
new ByteArrayInputStream(data));
|
||||||
long id = c.getLong(c.getColumnIndex(COLUMN_ID));
|
long id = c.getLong(c.getColumnIndex(COLUMN_ID));
|
||||||
builder.id(id);
|
builder.id(id);
|
||||||
builder.IV(new InventoryVector(iv));
|
builder.IV(new InventoryVector(iv));
|
||||||
@ -240,6 +288,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
if (!c.isNull(nextTryColumn)) {
|
if (!c.isNull(nextTryColumn)) {
|
||||||
builder.nextTry(c.getLong(nextTryColumn));
|
builder.nextTry(c.getLong(nextTryColumn));
|
||||||
}
|
}
|
||||||
|
builder.conversation(asUuid(c.getBlob(c.getColumnIndex(COLUMN_CONVERSATION))));
|
||||||
builder.labels(findLabels(id));
|
builder.labels(findLabels(id));
|
||||||
result.add(builder.build());
|
result.add(builder.build());
|
||||||
}
|
}
|
||||||
@ -268,6 +317,8 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
update(db, message);
|
update(db, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateParents(db, message);
|
||||||
|
|
||||||
// remove existing labels
|
// remove existing labels
|
||||||
db.delete(JOIN_TABLE_NAME, "message_id=?", new String[]{valueOf(message.getId())});
|
db.delete(JOIN_TABLE_NAME, "message_id=?", new String[]{valueOf(message.getId())});
|
||||||
|
|
||||||
@ -302,6 +353,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
values.put(COLUMN_TTL, message.getTTL());
|
values.put(COLUMN_TTL, message.getTTL());
|
||||||
values.put(COLUMN_RETRIES, message.getRetries());
|
values.put(COLUMN_RETRIES, message.getRetries());
|
||||||
values.put(COLUMN_NEXT_TRY, message.getNextTry());
|
values.put(COLUMN_NEXT_TRY, message.getNextTry());
|
||||||
|
values.put(COLUMN_CONVERSATION, UuidUtils.asBytes(message.getConversationId()));
|
||||||
long id = db.insertOrThrow(TABLE_NAME, null, values);
|
long id = db.insertOrThrow(TABLE_NAME, null, values);
|
||||||
message.setId(id);
|
message.setId(id);
|
||||||
}
|
}
|
||||||
@ -322,6 +374,7 @@ public class AndroidMessageRepository extends AbstractMessageRepository {
|
|||||||
values.put(COLUMN_TTL, message.getTTL());
|
values.put(COLUMN_TTL, message.getTTL());
|
||||||
values.put(COLUMN_RETRIES, message.getRetries());
|
values.put(COLUMN_RETRIES, message.getRetries());
|
||||||
values.put(COLUMN_NEXT_TRY, message.getNextTry());
|
values.put(COLUMN_NEXT_TRY, message.getNextTry());
|
||||||
|
values.put(COLUMN_CONVERSATION, UuidUtils.asBytes(message.getConversationId()));
|
||||||
db.update(TABLE_NAME, values, "id = " + message.getId(), null);
|
db.update(TABLE_NAME, values, "id = " + message.getId(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import android.database.sqlite.SQLiteStatement;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -56,6 +57,12 @@ public class AndroidNodeRegistry implements NodeRegistry {
|
|||||||
db.delete(TABLE_NAME, "time < ?", new String[]{valueOf(now(-28 * DAY))});
|
db.delete(TABLE_NAME, "time < ?", new String[]{valueOf(now(-28 * DAY))});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
SQLiteDatabase db = sql.getWritableDatabase();
|
||||||
|
db.delete(TABLE_NAME, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
private Long loadExistingTime(NetworkAddress node) {
|
private Long loadExistingTime(NetworkAddress node) {
|
||||||
SQLiteStatement statement = loadExistingStatement.get();
|
SQLiteStatement statement = loadExistingStatement.get();
|
||||||
if (statement == null) {
|
if (statement == null) {
|
||||||
|
@ -27,7 +27,7 @@ import ch.dissem.apps.abit.util.Assets;
|
|||||||
*/
|
*/
|
||||||
public class SqlHelper extends SQLiteOpenHelper {
|
public class SqlHelper extends SQLiteOpenHelper {
|
||||||
// If you change the database schema, you must increment the database version.
|
// If you change the database schema, you must increment the database version.
|
||||||
private static final int DATABASE_VERSION = 6;
|
private static final int DATABASE_VERSION = 7;
|
||||||
private static final String DATABASE_NAME = "jabit.db";
|
private static final String DATABASE_NAME = "jabit.db";
|
||||||
|
|
||||||
private final Context ctx;
|
private final Context ctx;
|
||||||
@ -61,6 +61,8 @@ public class SqlHelper extends SQLiteOpenHelper {
|
|||||||
executeMigration(db, "V3.3__Create_table_node");
|
executeMigration(db, "V3.3__Create_table_node");
|
||||||
case 5:
|
case 5:
|
||||||
executeMigration(db, "V3.4__Add_label_outbox");
|
executeMigration(db, "V3.4__Add_label_outbox");
|
||||||
|
case 6:
|
||||||
|
executeMigration(db, "V4.0__Create_table_message_parent");
|
||||||
default:
|
default:
|
||||||
// Nothing to do. Let's assume we won't upgrade from a version that's newer than
|
// Nothing to do. Let's assume we won't upgrade from a version that's newer than
|
||||||
// DATABASE_VERSION.
|
// DATABASE_VERSION.
|
||||||
@ -73,7 +75,7 @@ public class SqlHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StringBuilder join(long... numbers) {
|
static StringBuilder join(long... numbers) {
|
||||||
StringBuilder streamList = new StringBuilder();
|
StringBuilder streamList = new StringBuilder();
|
||||||
for (int i = 0; i < numbers.length; i++) {
|
for (int i = 0; i < numbers.length; i++) {
|
||||||
if (i > 0) streamList.append(", ");
|
if (i > 0) streamList.append(", ");
|
||||||
@ -82,7 +84,7 @@ public class SqlHelper extends SQLiteOpenHelper {
|
|||||||
return streamList;
|
return streamList;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StringBuilder join(Enum<?>... types) {
|
static StringBuilder join(Enum<?>... types) {
|
||||||
StringBuilder streamList = new StringBuilder();
|
StringBuilder streamList = new StringBuilder();
|
||||||
for (int i = 0; i < types.length; i++) {
|
for (int i = 0; i < types.length; i++) {
|
||||||
if (i > 0) streamList.append(", ");
|
if (i > 0) streamList.append(", ");
|
||||||
|
@ -18,6 +18,7 @@ package ch.dissem.apps.abit.service;
|
|||||||
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
@ -38,6 +39,17 @@ public class BitmessageService extends Service {
|
|||||||
|
|
||||||
private NetworkNotification notification = null;
|
private NetworkNotification notification = null;
|
||||||
|
|
||||||
|
private final Handler cleanupHandler = new Handler();
|
||||||
|
private final Runnable cleanupTask = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
bmc.cleanup();
|
||||||
|
if (isRunning()) {
|
||||||
|
cleanupHandler.postDelayed(this, 24 * 60 * 60 * 1000L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static boolean isRunning() {
|
public static boolean isRunning() {
|
||||||
return running && bmc.isRunning();
|
return running && bmc.isRunning();
|
||||||
}
|
}
|
||||||
@ -61,6 +73,7 @@ public class BitmessageService extends Service {
|
|||||||
bmc.startup();
|
bmc.startup();
|
||||||
}
|
}
|
||||||
notification.show();
|
notification.show();
|
||||||
|
cleanupHandler.postDelayed(cleanupTask, 24 * 60 * 60 * 1000L);
|
||||||
}
|
}
|
||||||
return Service.START_STICKY;
|
return Service.START_STICKY;
|
||||||
}
|
}
|
||||||
@ -72,6 +85,8 @@ public class BitmessageService extends Service {
|
|||||||
}
|
}
|
||||||
running = false;
|
running = false;
|
||||||
notification.showShutdown();
|
notification.showShutdown();
|
||||||
|
cleanupHandler.removeCallbacks(cleanupTask);
|
||||||
|
bmc.cleanup();
|
||||||
stopSelf();
|
stopSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ import ch.dissem.bitmessage.networking.nio.NioNetworkHandler;
|
|||||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
import ch.dissem.bitmessage.ports.ProofOfWorkRepository;
|
||||||
|
import ch.dissem.bitmessage.utils.ConversationService;
|
||||||
import ch.dissem.bitmessage.utils.TTL;
|
import ch.dissem.bitmessage.utils.TTL;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
||||||
@ -51,6 +52,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
|||||||
*/
|
*/
|
||||||
public class Singleton {
|
public class Singleton {
|
||||||
private static BitmessageContext bitmessageContext;
|
private static BitmessageContext bitmessageContext;
|
||||||
|
private static ConversationService conversationService;
|
||||||
private static MessageListener messageListener;
|
private static MessageListener messageListener;
|
||||||
private static BitmessageAddress identity;
|
private static BitmessageAddress identity;
|
||||||
private static AndroidProofOfWorkRepository powRepo;
|
private static AndroidProofOfWorkRepository powRepo;
|
||||||
@ -160,4 +162,16 @@ public class Singleton {
|
|||||||
throw new IllegalArgumentException("Identity expected, but no private key available");
|
throw new IllegalArgumentException("Identity expected, but no private key available");
|
||||||
Singleton.identity = identity;
|
Singleton.identity = identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ConversationService getConversationService(Context ctx) {
|
||||||
|
if (conversationService == null) {
|
||||||
|
final BitmessageContext bmc = getBitmessageContext(ctx);
|
||||||
|
synchronized (Singleton.class) {
|
||||||
|
if (conversationService == null) {
|
||||||
|
conversationService = new ConversationService(bmc.messages());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return conversationService;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,19 +19,41 @@ package ch.dissem.apps.abit.util;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
|
import android.util.Base64;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.MultiFormatWriter;
|
||||||
|
import com.google.zxing.WriterException;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
import com.mikepenz.iconics.IconicsDrawable;
|
import com.mikepenz.iconics.IconicsDrawable;
|
||||||
import com.mikepenz.iconics.typeface.IIcon;
|
import com.mikepenz.iconics.typeface.IIcon;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import ch.dissem.apps.abit.Identicon;
|
import ch.dissem.apps.abit.Identicon;
|
||||||
import ch.dissem.apps.abit.R;
|
import ch.dissem.apps.abit.R;
|
||||||
|
import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||||
|
import ch.dissem.bitmessage.exception.ApplicationException;
|
||||||
|
|
||||||
|
import static android.graphics.Color.BLACK;
|
||||||
|
import static android.graphics.Color.WHITE;
|
||||||
|
import static android.util.Base64.NO_WRAP;
|
||||||
|
import static android.util.Base64.URL_SAFE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some helper methods to work with drawables.
|
* Some helper methods to work with drawables.
|
||||||
*/
|
*/
|
||||||
public class Drawables {
|
public class Drawables {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(Drawables.class);
|
||||||
|
|
||||||
|
private static final int QR_CODE_SIZE = 350;
|
||||||
|
|
||||||
public static MenuItem addIcon(Context ctx, Menu menu, int menuItem, IIcon icon) {
|
public static MenuItem addIcon(Context ctx, Menu menu, int menuItem, IIcon icon) {
|
||||||
MenuItem item = menu.findItem(menuItem);
|
MenuItem item = menu.findItem(menuItem);
|
||||||
item.setIcon(new IconicsDrawable(ctx, icon).colorRes(R.color.colorPrimaryDarkText).actionBar());
|
item.setIcon(new IconicsDrawable(ctx, icon).colorRes(R.color.colorPrimaryDarkText).actionBar());
|
||||||
@ -49,4 +71,42 @@ public class Drawables {
|
|||||||
identicon.draw(canvas);
|
identicon.draw(canvas);
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Bitmap qrCode(BitmessageAddress address) {
|
||||||
|
StringBuilder link = new StringBuilder("bitmessage:");
|
||||||
|
link.append(address.getAddress());
|
||||||
|
if (address.getAlias() != null) {
|
||||||
|
link.append("?label=").append(address.getAlias());
|
||||||
|
}
|
||||||
|
if (address.getPubkey() != null) {
|
||||||
|
link.append(address.getAlias() == null ? '?' : '&');
|
||||||
|
ByteArrayOutputStream pubkey = new ByteArrayOutputStream();
|
||||||
|
try {
|
||||||
|
address.getPubkey().writeUnencrypted(pubkey);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ApplicationException(e);
|
||||||
|
}
|
||||||
|
link.append("pubkey=").append(Base64.encodeToString(pubkey.toByteArray(), URL_SAFE | NO_WRAP));
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
77
app/src/main/java/ch/dissem/apps/abit/util/Labels.java
Normal file
77
app/src/main/java/ch/dissem/apps/abit/util/Labels.java
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package ch.dissem.apps.abit.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.ColorInt;
|
||||||
|
|
||||||
|
import com.mikepenz.community_material_typeface_library.CommunityMaterial;
|
||||||
|
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
|
||||||
|
import com.mikepenz.iconics.typeface.IIcon;
|
||||||
|
|
||||||
|
import ch.dissem.apps.abit.R;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class to help with translating the default labels, getting label colors and so on.
|
||||||
|
*/
|
||||||
|
public class Labels {
|
||||||
|
public static String getText(Label label, Context ctx) {
|
||||||
|
return getText(label.getType(), label.toString(), ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getText(Label.Type type, String alternative, Context ctx) {
|
||||||
|
if (type == null) {
|
||||||
|
return alternative;
|
||||||
|
} else {
|
||||||
|
switch (type) {
|
||||||
|
case INBOX:
|
||||||
|
return ctx.getString(R.string.inbox);
|
||||||
|
case DRAFT:
|
||||||
|
return ctx.getString(R.string.draft);
|
||||||
|
case OUTBOX:
|
||||||
|
return ctx.getString(R.string.outbox);
|
||||||
|
case SENT:
|
||||||
|
return ctx.getString(R.string.sent);
|
||||||
|
case UNREAD:
|
||||||
|
return ctx.getString(R.string.unread);
|
||||||
|
case TRASH:
|
||||||
|
return ctx.getString(R.string.trash);
|
||||||
|
case BROADCAST:
|
||||||
|
return ctx.getString(R.string.broadcasts);
|
||||||
|
default:
|
||||||
|
return alternative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IIcon getIcon(Label label) {
|
||||||
|
if (label.getType() == null) {
|
||||||
|
return CommunityMaterial.Icon.cmd_label;
|
||||||
|
}
|
||||||
|
switch (label.getType()) {
|
||||||
|
case INBOX:
|
||||||
|
return GoogleMaterial.Icon.gmd_inbox;
|
||||||
|
case DRAFT:
|
||||||
|
return CommunityMaterial.Icon.cmd_file;
|
||||||
|
case OUTBOX:
|
||||||
|
return CommunityMaterial.Icon.cmd_inbox_arrow_up;
|
||||||
|
case SENT:
|
||||||
|
return CommunityMaterial.Icon.cmd_send;
|
||||||
|
case BROADCAST:
|
||||||
|
return CommunityMaterial.Icon.cmd_rss;
|
||||||
|
case UNREAD:
|
||||||
|
return GoogleMaterial.Icon.gmd_markunread_mailbox;
|
||||||
|
case TRASH:
|
||||||
|
return GoogleMaterial.Icon.gmd_delete;
|
||||||
|
default:
|
||||||
|
return CommunityMaterial.Icon.cmd_label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
public static int getColor(Label label) {
|
||||||
|
if (label.getType() == null) {
|
||||||
|
return label.getColor();
|
||||||
|
}
|
||||||
|
return 0xFF000000;
|
||||||
|
}
|
||||||
|
}
|
37
app/src/main/java/ch/dissem/apps/abit/util/UuidUtils.java
Normal file
37
app/src/main/java/ch/dissem/apps/abit/util/UuidUtils.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package ch.dissem.apps.abit.util;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQLite has no UUID data type, and UUIDs are therefore best saved as BINARY[16]. This class
|
||||||
|
* takes care of conversion between byte[16] and UUID.
|
||||||
|
* <p>
|
||||||
|
* Thanks to Brice Roncace on
|
||||||
|
* <a href="http://stackoverflow.com/questions/17893609/convert-uuid-to-byte-that-works-when-using-uuid-nameuuidfrombytesb">
|
||||||
|
* Stack Overflow
|
||||||
|
* </a>
|
||||||
|
* for providing the UUID <-> byte[] conversions.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public class UuidUtils {
|
||||||
|
public static UUID asUuid(byte[] bytes) {
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(bytes);
|
||||||
|
long firstLong = bb.getLong();
|
||||||
|
long secondLong = bb.getLong();
|
||||||
|
return new UUID(firstLong, secondLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] asBytes(UUID uuid) {
|
||||||
|
if (uuid == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
|
||||||
|
bb.putLong(uuid.getMostSignificantBits());
|
||||||
|
bb.putLong(uuid.getLeastSignificantBits());
|
||||||
|
return bb.array();
|
||||||
|
}
|
||||||
|
}
|
10
app/src/main/res/drawable/border_bottom.xml
Normal file
10
app/src/main/res/drawable/border_bottom.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||||
|
<item android:top="1dp" android:bottom="1dp">
|
||||||
|
<shape
|
||||||
|
android:shape="rectangle">
|
||||||
|
<stroke android:width="1dp" android:color="#FFDDDDDD" />
|
||||||
|
<solid android:color="#00000000" />
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
@ -15,7 +15,7 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<RelativeLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@ -31,10 +31,9 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/select_encoding_warning"
|
android:text="@string/select_encoding_warning"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
android:layout_alignParentTop="true"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
android:layout_alignParentStart="true"
|
||||||
tools:layout_constraintLeft_creator="1"
|
android:layout_alignParentEnd="true"/>
|
||||||
tools:layout_constraintTop_creator="1"/>
|
|
||||||
|
|
||||||
|
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
@ -43,9 +42,9 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingBottom="24dp"
|
android:paddingBottom="24dp"
|
||||||
android:paddingTop="24dp"
|
android:paddingTop="24dp"
|
||||||
app:layout_constraintLeft_toLeftOf="@+id/description"
|
android:layout_alignStart="@+id/description"
|
||||||
app:layout_constraintRight_toRightOf="@+id/description"
|
android:layout_alignEnd="@+id/description"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/description">
|
android:layout_below="@+id/description">
|
||||||
|
|
||||||
<RadioButton
|
<RadioButton
|
||||||
android:id="@+id/simple"
|
android:id="@+id/simple"
|
||||||
@ -72,10 +71,8 @@
|
|||||||
android:text="@string/ok"
|
android:text="@string/ok"
|
||||||
android:textColor="@color/colorAccent"
|
android:textColor="@color/colorAccent"
|
||||||
app:layout_constraintHorizontal_bias="1.0"
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
app:layout_constraintRight_toRightOf="@+id/radioGroup"
|
android:layout_alignRight="@+id/radioGroup"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/radioGroup"
|
android:layout_below="@+id/radioGroup"/>
|
||||||
tools:layout_constraintRight_creator="1"
|
|
||||||
tools:layout_constraintTop_creator="1"/>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/dismiss"
|
android:id="@+id/dismiss"
|
||||||
@ -84,7 +81,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/cancel"
|
android:text="@string/cancel"
|
||||||
android:textColor="@color/colorAccent"
|
android:textColor="@color/colorAccent"
|
||||||
app:layout_constraintRight_toLeftOf="@+id/ok"
|
android:layout_toLeftOf="@+id/ok"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/radioGroup"/>
|
android:layout_below="@+id/radioGroup"/>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</RelativeLayout>
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:focusableInTouchMode="true"
|
android:focusableInTouchMode="true"
|
||||||
|
android:paddingBottom="64dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -24,7 +25,7 @@
|
|||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
android:textAppearance="?android:attr/textAppearanceLarge"
|
android:textAppearance="?android:attr/textAppearanceLarge"
|
||||||
tools:ignore="UnusedAttribute"
|
tools:ignore="UnusedAttribute"
|
||||||
tools:text="Subject"/>
|
tools:text="Subject" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/status"
|
android:id="@+id/status"
|
||||||
@ -32,17 +33,17 @@
|
|||||||
android:layout_height="60dp"
|
android:layout_height="60dp"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:tint="@color/colorAccent"
|
|
||||||
tools:src="@drawable/ic_notification_proof_of_work"
|
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
tools:ignore="ContentDescription"/>
|
android:tint="@color/colorAccent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@drawable/ic_notification_proof_of_work" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="2dip"
|
android:layout_height="2dip"
|
||||||
android:layout_below="@id/subject"
|
android:layout_below="@id/subject"
|
||||||
android:background="@color/divider"/>
|
android:background="@color/divider" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
@ -52,7 +53,7 @@
|
|||||||
android:layout_below="@+id/divider"
|
android:layout_below="@+id/divider"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
android:src="@color/colorAccent"
|
android:src="@color/colorAccent"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sender"
|
android:id="@+id/sender"
|
||||||
@ -64,7 +65,7 @@
|
|||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="Sender"/>
|
tools:text="Sender" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/recipient"
|
android:id="@+id/recipient"
|
||||||
@ -75,19 +76,44 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
tools:text="Recipient"/>
|
tools:text="Recipient" />
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/parents"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/avatar"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_below="@+id/avatar"
|
android:layout_below="@+id/parents"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:layout_marginTop="32dp"
|
android:layout_marginTop="32dp"
|
||||||
android:paddingBottom="64dp"
|
android:paddingBottom="16dp"
|
||||||
android:text="New Text"
|
tools:text="Message Body"
|
||||||
android:textIsSelectable="true"/>
|
android:textIsSelectable="true" />
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/labels"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/text"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:id="@+id/responses"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/labels"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
28
app/src/main/res/layout/item_label.xml
Normal file
28
app/src/main/res/layout/item_label.xml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.mikepenz.iconics.view.IconicsImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="16dp"
|
||||||
|
android:layout_height="16dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
app:iiv_color="@android:color/black"
|
||||||
|
app:iiv_icon="cmd-label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="24dp"
|
||||||
|
tools:text="Label"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_toEndOf="@+id/icon" />
|
||||||
|
</RelativeLayout>
|
62
app/src/main/res/layout/item_message_minimized.xml
Normal file
62
app/src/main/res/layout/item_message_minimized.xml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:background="@drawable/border_bottom">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginTop="5dp"
|
||||||
|
android:src="@color/colorPrimaryDark"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/sender"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_toEndOf="@+id/avatar"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:lines="1"
|
||||||
|
android:paddingBottom="0dp"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
tools:text="Sender" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_below="@+id/sender"
|
||||||
|
android:layout_toEndOf="@+id/avatar"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:lines="1"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
|
tools:text="Text" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/status"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBottom="@id/avatar"
|
||||||
|
android:layout_alignEnd="@+id/avatar"
|
||||||
|
android:layout_marginBottom="-8dp"
|
||||||
|
android:layout_marginEnd="-8dp"
|
||||||
|
android:tint="@color/colorAccent"
|
||||||
|
tools:ignore="ContentDescription"
|
||||||
|
tools:src="@drawable/ic_notification_proof_of_work" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
@ -1,5 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
~ Copyright 2015 Christian Basler
|
~ Copyright 2015 Christian Basler
|
||||||
~
|
~
|
||||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -15,8 +14,7 @@
|
|||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -31,11 +29,10 @@
|
|||||||
android:foreground="?attr/selectableItemBackground"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
tools:ignore="UselessParent">
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:background="?attr/selectableItemBackground">
|
||||||
android:background="?attr/selectableItemBackground">
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/avatar"
|
android:id="@+id/avatar"
|
||||||
@ -45,7 +42,7 @@
|
|||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
android:src="@color/colorPrimaryDark"
|
android:src="@color/colorPrimaryDark"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sender"
|
android:id="@+id/sender"
|
||||||
@ -63,8 +60,7 @@
|
|||||||
android:paddingTop="0dp"
|
android:paddingTop="0dp"
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
tools:text="Sender"
|
tools:text="Sender" />
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/subject"
|
android:id="@+id/subject"
|
||||||
@ -78,7 +74,7 @@
|
|||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
tools:text="Subject"/>
|
tools:text="Subject" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text"
|
android:id="@+id/text"
|
||||||
@ -94,7 +90,7 @@
|
|||||||
android:paddingLeft="8dp"
|
android:paddingLeft="8dp"
|
||||||
android:paddingRight="8dp"
|
android:paddingRight="8dp"
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||||
tools:text="Text"/>
|
tools:text="Text" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/status"
|
android:id="@+id/status"
|
||||||
@ -106,7 +102,7 @@
|
|||||||
android:layout_marginEnd="-8dp"
|
android:layout_marginEnd="-8dp"
|
||||||
android:tint="@color/colorAccent"
|
android:tint="@color/colorAccent"
|
||||||
tools:ignore="ContentDescription"
|
tools:ignore="ContentDescription"
|
||||||
tools:src="@drawable/ic_notification_proof_of_work"/>
|
tools:src="@drawable/ic_notification_proof_of_work" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
<item name="android:textColor">@color/colorAccent</item>
|
<item name="android:textColor">@color/colorAccent</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="FixedDialog" parent="Theme.AppCompat.Light.Dialog">
|
<style name="FixedDialog" parent="Theme.AppCompat.Light.Dialog.MinWidth">
|
||||||
<item name="windowNoTitle">false</item>
|
<item name="windowNoTitle">false</item>
|
||||||
</style>
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -9,7 +9,8 @@ buildscript {
|
|||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:2.2.3'
|
classpath 'com.android.tools.build:gradle:2.3.1'
|
||||||
|
classpath 'com.github.ben-manes:gradle-versions-plugin:0.14.0'
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
@ -17,6 +18,8 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
|
apply plugin: 'com.github.ben-manes.versions'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
#Fri Aug 12 22:10:25 CEST 2016
|
#Tue Apr 04 00:02:32 CEST 2017
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-all.zip
|
||||||
|
22
gradlew
vendored
22
gradlew
vendored
@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
@ -154,11 +154,19 @@ if $cygwin ; then
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
# Escape application args
|
||||||
function splitJvmOpts() {
|
save ( ) {
|
||||||
JVM_OPTS=("$@")
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
}
|
}
|
||||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
APP_ARGS=$(save "$@")
|
||||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
|
||||||
|
|
||||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||||
|
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
||||||
|
6
gradlew.bat
vendored
6
gradlew.bat
vendored
@ -49,7 +49,6 @@ goto fail
|
|||||||
@rem Get command-line arguments, handling Windows variants
|
@rem Get command-line arguments, handling Windows variants
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
|
||||||
|
|
||||||
:win9xME_args
|
:win9xME_args
|
||||||
@rem Slurp the command line arguments.
|
@rem Slurp the command line arguments.
|
||||||
@ -60,11 +59,6 @@ set _SKIP=2
|
|||||||
if "x%~1" == "x" goto execute
|
if "x%~1" == "x" goto execute
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
set CMD_LINE_ARGS=%*
|
||||||
goto execute
|
|
||||||
|
|
||||||
:4NT_args
|
|
||||||
@rem Get arguments from the 4NT Shell from JP Software
|
|
||||||
set CMD_LINE_ARGS=%$
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
Loading…
Reference in New Issue
Block a user