Code cleanup - most notably BitmessageService was cleaned up and doesn't use messaging anymore

This commit is contained in:
Christian Basler 2016-01-19 20:50:58 +01:00
parent 6f6a2e4e6c
commit c4d76fc8ce
58 changed files with 661 additions and 468 deletions

View File

@ -15,7 +15,7 @@
<uses-permission android:name="android.permission.WRITE_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<application <application
android:allowBackup="true" android:allowBackup="false"
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">
@ -114,7 +114,9 @@
android:exported="false" android:exported="false"
android:syncable="true"/> android:syncable="true"/>
<service android:name=".synchronization.AuthenticatorService"> <service
android:name=".synchronization.AuthenticatorService"
tools:ignore="ExportedService">
<intent-filter> <intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/> <action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter> </intent-filter>
@ -125,7 +127,8 @@
</service> </service>
<service <service
android:name=".synchronization.SyncService" android:name=".synchronization.SyncService"
android:exported="true"> android:exported="true"
tools:ignore="ExportedService">
<intent-filter> <intent-filter>
<action android:name="android.content.SyncAdapter"/> <action android:name="android.content.SyncAdapter"/>
</intent-filter> </intent-filter>

View File

@ -16,7 +16,6 @@
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.ListFragment; import android.support.v4.app.ListFragment;
@ -27,7 +26,7 @@ import ch.dissem.apps.abit.listener.ListSelectionListener;
import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
/** /**
* Created by chris on 07.09.15. * @author Christian Basler
*/ */
public abstract class AbstractItemListFragment<T> extends ListFragment { public abstract class AbstractItemListFragment<T> extends ListFragment {
/** /**
@ -39,7 +38,8 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
* A dummy implementation of the {@link ListSelectionListener} interface that does * A dummy implementation of the {@link ListSelectionListener} interface that does
* nothing. Used only when this fragment is not attached to an activity. * nothing. Used only when this fragment is not attached to an activity.
*/ */
private static ListSelectionListener<Object> dummyCallbacks = new ListSelectionListener<Object>() { private static ListSelectionListener<Object> dummyCallbacks = new
ListSelectionListener<Object>() {
@Override @Override
public void onItemSelected(Object plaintext) { public void onItemSelected(Object plaintext) {
} }
@ -84,11 +84,13 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
super.onAttach(context); super.onAttach(context);
// Activities containing this fragment must implement its callbacks. // Activities containing this fragment must implement its callbacks.
if (!(context instanceof ListSelectionListener)) { if (context instanceof ListSelectionListener) {
//noinspection unchecked
callbacks = (ListSelectionListener) context;
} else {
throw new IllegalStateException("Activity must implement fragment's callbacks."); throw new IllegalStateException("Activity must implement fragment's callbacks.");
} }
callbacks = (ListSelectionListener) context;
} }
@Override @Override
@ -105,6 +107,7 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
// Notify the active callbacks interface (the activity, if the // Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected. // fragment is attached to one) that an item has been selected.
//noinspection unchecked
callbacks.onItemSelected((T) listView.getItemAtPosition(position)); callbacks.onItemSelected((T) listView.getItemAtPosition(position));
} }

View File

@ -1,9 +1,24 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.os.Bundle; import android.os.Bundle;
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 ch.dissem.bitmessage.entity.BitmessageAddress;
/** /**
* Compose a new message. * Compose a new message.
@ -20,6 +35,7 @@ public class ComposeMessageActivity extends AppCompatActivity {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_action_close); getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_action_close);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(false); getSupportActionBar().setHomeButtonEnabled(false);

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.os.Bundle; import android.os.Bundle;
@ -8,7 +24,6 @@ import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.AutoCompleteTextView; import android.widget.AutoCompleteTextView;
import android.widget.EditText; import android.widget.EditText;

View File

@ -16,13 +16,12 @@
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.graphics.*; import android.graphics.*;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
/** /**
* * @author Christian Basler
*/ */
public class Identicon extends Drawable { public class Identicon extends Drawable {
private static final int SIZE = 9; private static final int SIZE = 9;
@ -34,7 +33,6 @@ public class Identicon extends Drawable {
private float cellWidth; private float cellWidth;
private float cellHeight; private float cellHeight;
private byte[] hash;
private int color; private int color;
private int background; private int background;
private boolean[][] fields; private boolean[][] fields;
@ -44,7 +42,7 @@ public class Identicon extends Drawable {
paint.setStyle(Paint.Style.FILL); paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true); paint.setAntiAlias(true);
hash = input.getRipe(); byte[] hash = input.getRipe();
fields = new boolean[SIZE][SIZE]; fields = new boolean[SIZE][SIZE];
color = Color.HSVToColor(new float[]{Math.abs(hash[0] * hash[1] + hash[2]) % 360, 0.8f, 1.0f}); color = Color.HSVToColor(new float[]{Math.abs(hash[0] * hash[1] + hash[2]) % 360, 0.8f, 1.0f});

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.app.AlertDialog; import android.app.AlertDialog;
@ -7,11 +23,7 @@ import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
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;
@ -39,7 +51,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.Serializable; import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -47,18 +58,16 @@ import java.util.List;
import ch.dissem.apps.abit.listener.ActionBarListener; import ch.dissem.apps.abit.listener.ActionBarListener;
import ch.dissem.apps.abit.listener.ListSelectionListener; import ch.dissem.apps.abit.listener.ListSelectionListener;
import ch.dissem.apps.abit.service.BitmessageService; import ch.dissem.apps.abit.service.BitmessageService;
import ch.dissem.apps.abit.service.BitmessageService.BitmessageBinder;
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.Preferences; import ch.dissem.apps.abit.util.Preferences;
import ch.dissem.bitmessage.BitmessageContext;
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.Label; import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.ports.AddressRepository;
import ch.dissem.bitmessage.ports.MessageRepository;
import static ch.dissem.apps.abit.service.BitmessageService.DATA_FIELD_IDENTITY; import static ch.dissem.apps.abit.service.BitmessageService.isRunning;
import static ch.dissem.apps.abit.service.BitmessageService.MSG_START_NODE;
import static ch.dissem.apps.abit.service.BitmessageService.MSG_STOP_NODE;
/** /**
@ -92,14 +101,12 @@ public class MainActivity extends AppCompatActivity
*/ */
private boolean twoPane; private boolean twoPane;
private static IncomingHandler incomingHandler = new IncomingHandler(); private static BitmessageBinder service;
private static Messenger messenger = new Messenger(incomingHandler);
private static Messenger service;
private static boolean bound; private static boolean bound;
private static ServiceConnection connection = new ServiceConnection() { private static ServiceConnection connection = new ServiceConnection() {
@Override @Override
public void onServiceConnected(ComponentName name, IBinder service) { public void onServiceConnected(ComponentName name, IBinder service) {
MainActivity.service = new Messenger(service); MainActivity.service = (BitmessageBinder) service;
MainActivity.bound = true; MainActivity.bound = true;
} }
@ -112,16 +119,15 @@ public class MainActivity extends AppCompatActivity
private Label selectedLabel; private Label selectedLabel;
private MessageRepository messageRepo; private BitmessageContext bmc;
private AddressRepository addressRepo;
private BitmessageAddress selectedIdentity; private BitmessageAddress selectedIdentity;
private AccountHeader accountHeader;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
messageRepo = Singleton.getMessageRepository(this); bmc = Singleton.getBitmessageContext(this);
addressRepo = Singleton.getAddressRepository(this); List<Label> labels = bmc.messages().getLabels();
List<Label> labels = messageRepo.getLabels();
if (selectedLabel == null) { if (selectedLabel == null) {
selectedLabel = labels.get(0); selectedLabel = labels.get(0);
} }
@ -179,7 +185,7 @@ public class MainActivity extends AppCompatActivity
private void createDrawer(Toolbar toolbar, Collection<Label> labels) { private void createDrawer(Toolbar toolbar, Collection<Label> labels) {
final ArrayList<IProfile> profiles = new ArrayList<>(); final ArrayList<IProfile> profiles = new ArrayList<>();
for (BitmessageAddress identity : addressRepo.getIdentities()) { for (BitmessageAddress identity : bmc.addresses().getIdentities()) {
LOG.info("Adding identity " + identity.getAddress()); LOG.info("Adding identity " + identity.getAddress());
profiles.add(new ProfileDrawerItem() profiles.add(new ProfileDrawerItem()
.withIcon(new Identicon(identity)) .withIcon(new Identicon(identity))
@ -188,7 +194,7 @@ public class MainActivity extends AppCompatActivity
.withTag(identity) .withTag(identity)
); );
} }
if (profiles.isEmpty()){ if (profiles.isEmpty()) {
// Create an initial identity // Create an initial identity
BitmessageAddress identity = Singleton.getIdentity(this); BitmessageAddress identity = Singleton.getIdentity(this);
profiles.add(new ProfileDrawerItem() profiles.add(new ProfileDrawerItem()
@ -212,7 +218,7 @@ public class MainActivity extends AppCompatActivity
.withIcon(GoogleMaterial.Icon.gmd_settings) .withIcon(GoogleMaterial.Icon.gmd_settings)
); );
// Create the AccountHeader // Create the AccountHeader
AccountHeader accountHeader = new AccountHeaderBuilder() accountHeader = new AccountHeaderBuilder()
.withActivity(this) .withActivity(this)
.withHeaderBackground(R.drawable.header) .withHeaderBackground(R.drawable.header)
.withProfiles(profiles) .withProfiles(profiles)
@ -221,13 +227,18 @@ public class MainActivity extends AppCompatActivity
public boolean onProfileChanged(View view, IProfile profile, boolean public boolean onProfileChanged(View view, IProfile profile, boolean
currentProfile) { currentProfile) {
if (profile.getIdentifier() == ADD_IDENTITY) { if (profile.getIdentifier() == ADD_IDENTITY) {
try { BitmessageAddress identity = bmc.createIdentity(false);
Message message = Message.obtain(null, BitmessageService IProfile newProfile = new ProfileDrawerItem()
.MSG_CREATE_IDENTITY); .withName(identity.toString())
message.replyTo = messenger; .withEmail(identity.getAddress())
service.send(message); .withTag(identity);
} catch (RemoteException e) { if (accountHeader.getProfiles() != null) {
LOG.error(e.getMessage(), e); // we know that there are 2 setting elements.
// Set the new profile above them ;)
accountHeader.addProfile(
newProfile, accountHeader.getProfiles().size() - 2);
} else {
accountHeader.addProfiles(newProfile);
} }
} else if (profile instanceof ProfileDrawerItem) { } else if (profile instanceof ProfileDrawerItem) {
Object tag = ((ProfileDrawerItem) profile).getTag(); Object tag = ((ProfileDrawerItem) profile).getTag();
@ -243,7 +254,6 @@ public class MainActivity extends AppCompatActivity
if (profiles.size() > 2) { // There's always the add and manage identity items if (profiles.size() > 2) { // There's always the add and manage identity items
accountHeader.setActiveProfile(profiles.get(0), true); accountHeader.setActiveProfile(profiles.get(0), true);
} }
incomingHandler.updateAccountHeader(accountHeader);
ArrayList<IDrawerItem> drawerItems = new ArrayList<>(); ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
for (Label label : labels) { for (Label label : labels) {
@ -299,23 +309,16 @@ public class MainActivity extends AppCompatActivity
new SwitchDrawerItem() new SwitchDrawerItem()
.withName(R.string.full_node) .withName(R.string.full_node)
.withIcon(CommunityMaterial.Icon.cmd_cloud_outline) .withIcon(CommunityMaterial.Icon.cmd_cloud_outline)
.withChecked(BitmessageService.isRunning()) .withChecked(isRunning())
.withOnCheckedChangeListener(new OnCheckedChangeListener() { .withOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(IDrawerItem drawerItem, public void onCheckedChanged(IDrawerItem drawerItem,
CompoundButton buttonView, CompoundButton buttonView,
boolean isChecked) { boolean isChecked) {
if (messenger != null) { if (isChecked) {
if (isChecked) { checkAndStartNode(buttonView);
checkAndStartNode(buttonView); } else {
} else { service.shutdownNode();
try {
service.send(Message.obtain(null,
MSG_STOP_NODE));
} catch (RemoteException e) {
LOG.error(e.getMessage(), e);
}
}
} }
} }
}) })
@ -361,15 +364,17 @@ public class MainActivity extends AppCompatActivity
} }
private void checkAndStartNode(final CompoundButton buttonView) { private void checkAndStartNode(final CompoundButton buttonView) {
if (service == null) return;
if (Preferences.isConnectionAllowed(MainActivity.this)) { if (Preferences.isConnectionAllowed(MainActivity.this)) {
forceStartNode(); service.startupNode();
} else { } else {
new AlertDialog.Builder(MainActivity.this) new AlertDialog.Builder(MainActivity.this)
.setMessage(R.string.full_node_warning) .setMessage(R.string.full_node_warning)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
forceStartNode(); service.startupNode();
} }
}) })
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@ -382,15 +387,6 @@ public class MainActivity extends AppCompatActivity
} }
} }
private void forceStartNode() {
try {
service.send(Message.obtain(null,
MSG_START_NODE));
} catch (RemoteException e) {
LOG.error(e.getMessage(), e);
}
}
private void showSelectedLabel() { private void showSelectedLabel() {
if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof
MessageListFragment) { MessageListFragment) {
@ -476,46 +472,4 @@ public class MainActivity extends AppCompatActivity
public BitmessageAddress getSelectedIdentity() { public BitmessageAddress getSelectedIdentity() {
return selectedIdentity; return selectedIdentity;
} }
private static class IncomingHandler extends Handler {
private WeakReference<AccountHeader> accountHeaderRef;
private IncomingHandler() {
accountHeaderRef = new WeakReference<>(null);
}
public void updateAccountHeader(AccountHeader accountHeader) {
accountHeaderRef = new WeakReference<>(accountHeader);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BitmessageService.MSG_CREATE_IDENTITY: {
AccountHeader accountHeader = accountHeaderRef.get();
if (accountHeader == null) break;
Serializable data = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
if (data instanceof BitmessageAddress) {
BitmessageAddress identity = (BitmessageAddress) data;
IProfile newProfile = new ProfileDrawerItem()
.withName(identity.toString())
.withEmail(identity.getAddress())
.withTag(identity);
if (accountHeader.getProfiles() != null) {
//we know that there are 2 setting elements. set the new profile
// above them ;)
accountHeader.addProfile(newProfile, accountHeader.getProfiles().size
() - 2);
} else {
accountHeader.addProfiles(newProfile);
}
}
break;
}
default:
super.handleMessage(msg);
}
}
}
} }

View File

@ -27,6 +27,7 @@ public class MessageDetailActivity extends AppCompatActivity {
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
// Show the Up button in the action bar. // Show the Up button in the action bar.
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// savedInstanceState is non-null when there is fragment state // savedInstanceState is non-null when there is fragment state

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.content.Intent; import android.content.Intent;
@ -79,7 +95,8 @@ public class MessageDetailFragment extends Fragment {
if (item != null) { if (item != null) {
((TextView) rootView.findViewById(R.id.subject)).setText(item.getSubject()); ((TextView) rootView.findViewById(R.id.subject)).setText(item.getSubject());
BitmessageAddress sender = item.getFrom(); BitmessageAddress sender = item.getFrom();
((ImageView) rootView.findViewById(R.id.avatar)).setImageDrawable(new Identicon(sender)); ((ImageView) rootView.findViewById(R.id.avatar)).setImageDrawable(new Identicon
(sender));
((TextView) rootView.findViewById(R.id.sender)).setText(sender.toString()); ((TextView) rootView.findViewById(R.id.sender)).setText(sender.toString());
if (item.getTo() != null) { if (item.getTo() != null) {
((TextView) rootView.findViewById(R.id.recipient)).setText(item.getTo().toString()); ((TextView) rootView.findViewById(R.id.recipient)).setText(item.getTo().toString());
@ -99,18 +116,18 @@ public class MessageDetailFragment extends Fragment {
messageBody.setLinksClickable(true); messageBody.setLinksClickable(true);
messageBody.setTextIsSelectable(true); messageBody.setTextIsSelectable(true);
}
boolean removed = false; boolean removed = false;
Iterator<Label> labels = item.getLabels().iterator(); Iterator<Label> labels = item.getLabels().iterator();
while (labels.hasNext()) { while (labels.hasNext()) {
if (labels.next().getType() == Label.Type.UNREAD) { if (labels.next().getType() == Label.Type.UNREAD) {
labels.remove(); labels.remove();
removed = true; removed = true;
}
}
if (removed) {
Singleton.getMessageRepository(inflater.getContext()).save(item);
} }
}
if (removed) {
Singleton.getMessageRepository(inflater.getContext()).save(item);
} }
return rootView; return rootView;
} }
@ -121,7 +138,8 @@ public class MessageDetailFragment extends Fragment {
Drawables.addIcon(getActivity(), menu, R.id.reply, GoogleMaterial.Icon.gmd_reply); Drawables.addIcon(getActivity(), menu, R.id.reply, GoogleMaterial.Icon.gmd_reply);
Drawables.addIcon(getActivity(), menu, R.id.delete, GoogleMaterial.Icon.gmd_delete); Drawables.addIcon(getActivity(), menu, R.id.delete, GoogleMaterial.Icon.gmd_delete);
Drawables.addIcon(getActivity(), menu, R.id.mark_unread, GoogleMaterial.Icon.gmd_markunread); Drawables.addIcon(getActivity(), menu, R.id.mark_unread, GoogleMaterial.Icon
.gmd_markunread);
Drawables.addIcon(getActivity(), menu, R.id.archive, GoogleMaterial.Icon.gmd_archive); Drawables.addIcon(getActivity(), menu, R.id.archive, GoogleMaterial.Icon.gmd_archive);
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
@ -132,7 +150,8 @@ public class MessageDetailFragment extends Fragment {
MessageRepository messageRepo = Singleton.getMessageRepository(getContext()); MessageRepository messageRepo = Singleton.getMessageRepository(getContext());
switch (menuItem.getItemId()) { switch (menuItem.getItemId()) {
case R.id.reply: case R.id.reply:
Intent replyIntent = new Intent(getActivity().getApplicationContext(), ComposeMessageActivity.class); Intent replyIntent = new Intent(getActivity().getApplicationContext(),
ComposeMessageActivity.class);
replyIntent.putExtra(ComposeMessageActivity.EXTRA_RECIPIENT, item.getFrom()); replyIntent.putExtra(ComposeMessageActivity.EXTRA_RECIPIENT, item.getFrom());
replyIntent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, item.getTo()); replyIntent.putExtra(ComposeMessageActivity.EXTRA_IDENTITY, item.getTo());
startActivity(replyIntent); startActivity(replyIntent);

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.content.Intent; import android.content.Intent;

View File

@ -17,16 +17,8 @@
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.app.Activity; import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
@ -34,35 +26,11 @@ import android.widget.EditText;
import android.widget.Switch; import android.widget.Switch;
import android.widget.TextView; import android.widget.TextView;
import org.slf4j.Logger; import ch.dissem.apps.abit.service.Singleton;
import org.slf4j.LoggerFactory; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.apps.abit.service.BitmessageService;
import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.BitmessageAddress;
import static ch.dissem.apps.abit.service.BitmessageService.DATA_FIELD_ADDRESS;
import static ch.dissem.apps.abit.service.BitmessageService.MSG_ADD_CONTACT;
import static ch.dissem.apps.abit.service.BitmessageService.MSG_SUBSCRIBE;
public class OpenBitmessageLinkActivity extends AppCompatActivity { public class OpenBitmessageLinkActivity extends AppCompatActivity {
private static final Logger LOG = LoggerFactory.getLogger(OpenBitmessageLinkActivity.class);
private Messenger service;
private boolean bound;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
OpenBitmessageLinkActivity.this.service = new Messenger(service);
OpenBitmessageLinkActivity.this.bound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
service = null;
bound = false;
}
};
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -103,20 +71,11 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
BitmessageAddress bmAddress = new BitmessageAddress(address); BitmessageAddress bmAddress = new BitmessageAddress(address);
bmAddress.setAlias(label.getText().toString()); bmAddress.setAlias(label.getText().toString());
final int what; BitmessageContext bmc = Singleton.getBitmessageContext(OpenBitmessageLinkActivity
if (subscribe.isChecked()) .this);
what = MSG_SUBSCRIBE; bmc.addContact(bmAddress);
else if (subscribe.isChecked()) {
what = MSG_ADD_CONTACT; bmc.addSubscribtion(bmAddress);
try {
Message message = Message.obtain(null, what);
Bundle bundle = new Bundle();
bundle.putSerializable(DATA_FIELD_ADDRESS, bmAddress);
message.setData(bundle);
service.send(message);
} catch (RemoteException e) {
LOG.error(e.getMessage(), e);
} }
setResult(Activity.RESULT_OK); setResult(Activity.RESULT_OK);
@ -150,20 +109,4 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
return new String[0]; return new String[0];
} }
} }
@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, BitmessageService.class), connection, Context
.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
if (bound) {
unbindService(connection);
bound = false;
}
super.onStop();
}
} }

View File

@ -16,6 +16,7 @@ public class SettingsActivity extends AppCompatActivity {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(false); getSupportActionBar().setHomeButtonEnabled(false);

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.content.Context; import android.content.Context;

View File

@ -1,7 +1,22 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit; package ch.dissem.apps.abit;
import android.os.Bundle; import android.os.Bundle;
import android.app.Activity;
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.widget.TextView; import android.widget.TextView;
@ -20,6 +35,7 @@ public class StatusActivity extends AppCompatActivity {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(false); getSupportActionBar().setHomeButtonEnabled(false);

View File

@ -43,6 +43,7 @@ public class SubscriptionDetailActivity extends AppCompatActivity {
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
// Show the Up button in the action bar. // Show the Up button in the action bar.
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// savedInstanceState is non-null when there is fragment state // savedInstanceState is non-null when there is fragment state

View File

@ -1,28 +1,23 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.adapter; package ch.dissem.apps.abit.adapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import ch.dissem.apps.abit.util.PRNGFixes; import ch.dissem.apps.abit.util.PRNGFixes;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.PlaintextHolder;
import ch.dissem.bitmessage.entity.payload.Broadcast;
import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography; import ch.dissem.bitmessage.cryptography.sc.SpongyCryptography;
import ch.dissem.bitmessage.utils.UnixTime;
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT;
import static ch.dissem.bitmessage.entity.Plaintext.Type.BROADCAST;
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
/** /**
* @author Christian Basler * @author Christian Basler

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.adapter; package ch.dissem.apps.abit.adapter;
import android.content.Context; import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.adapter; package ch.dissem.apps.abit.adapter;
import android.content.Context; import android.content.Context;
@ -6,12 +22,9 @@ import android.preference.PreferenceManager;
import java.util.Arrays; import java.util.Arrays;
import ch.dissem.apps.abit.util.Preferences;
import ch.dissem.bitmessage.InternalContext; import ch.dissem.bitmessage.InternalContext;
import ch.dissem.bitmessage.ports.ProofOfWorkEngine; import ch.dissem.bitmessage.ports.ProofOfWorkEngine;
import static ch.dissem.apps.abit.util.Constants.PREFERENCE_SERVER_POW;
/** /**
* Switches between two {@link ProofOfWorkEngine}s depending on the configuration. * Switches between two {@link ProofOfWorkEngine}s depending on the configuration.
* *

View File

@ -17,7 +17,7 @@
package ch.dissem.apps.abit.listener; package ch.dissem.apps.abit.listener;
/** /**
* Created by chris on 06.09.15. * @author Christian Basler
*/ */
public interface ActionBarListener { public interface ActionBarListener {
void updateTitle(CharSequence title); void updateTitle(CharSequence title);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2015 Christian Basler * Copyright 2016 Christian Basler
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

View File

@ -16,60 +16,32 @@
package ch.dissem.apps.abit.listener; package ch.dissem.apps.abit.listener;
import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.net.Uri; import java.util.Deque;
import android.os.Build; import java.util.LinkedList;
import android.provider.ContactsContract;
import android.support.v7.app.NotificationCompat;
import ch.dissem.apps.abit.notification.NewMessageNotification; import ch.dissem.apps.abit.notification.NewMessageNotification;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.Plaintext;
import java.util.LinkedList;
/** /**
* Listens for decrypted Bitmessage messages. Does show a notification. * Listens for decrypted Bitmessage messages. Does show a notification.
* <p> * <p>
* Should show a notification when the app isn't running, but update the message list when it is. Also, * Should show a notification when the app isn't running, but update the message list when it is.
* Also,
* notifications should be combined. * notifications should be combined.
* </p> * </p>
*/ */
public class MessageListener implements BitmessageContext.Listener { public class MessageListener implements BitmessageContext.Listener {
private final Context ctx; private final Deque<Plaintext> unacknowledged = new LinkedList<>();
private final NotificationManager manager;
private final LinkedList<Plaintext> unacknowledged = new LinkedList<>();
private int numberOfUnacknowledgedMessages = 0; private int numberOfUnacknowledgedMessages = 0;
private final NewMessageNotification notification; private final NewMessageNotification notification;
public MessageListener(Context ctx) { public MessageListener(Context ctx) {
this.ctx = ctx.getApplicationContext();
this.manager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
this.notification = new NewMessageNotification(ctx); this.notification = new NewMessageNotification(ctx);
} }
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static int getMaxContactPhotoSize(final Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// Note that this URI is safe to call on the UI thread.
final Uri uri = ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI;
final String[] projection = new String[]{ContactsContract.DisplayPhoto.DISPLAY_MAX_DIM};
final Cursor c = context.getContentResolver().query(uri, projection, null, null, null);
try {
c.moveToFirst();
return c.getInt(0);
} finally {
c.close();
}
}
// fallback: 96x96 is the max contact photo size for pre-ICS versions
return 96;
}
@Override @Override
public void receive(final Plaintext plaintext) { public void receive(final Plaintext plaintext) {
synchronized (unacknowledged) { synchronized (unacknowledged) {
@ -80,7 +52,6 @@ public class MessageListener implements BitmessageContext.Listener {
} }
} }
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
if (numberOfUnacknowledgedMessages == 1) { if (numberOfUnacknowledgedMessages == 1) {
notification.singleNotification(plaintext); notification.singleNotification(plaintext);
} else { } else {

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.listener; package ch.dissem.apps.abit.listener;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.notification; package ch.dissem.apps.abit.notification;
import android.app.Notification; import android.app.Notification;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.notification; package ch.dissem.apps.abit.notification;
import android.content.Context; import android.content.Context;
@ -7,7 +23,10 @@ import android.support.v7.app.NotificationCompat;
import ch.dissem.apps.abit.R; import ch.dissem.apps.abit.R;
/** /**
* Created by chrigu on 29.10.15. * Easily create notifications with error messages. Use carefully, users probably won't like them.
* (But they are useful during development/testing)
*
* @author Christian Basler
*/ */
public class ErrorNotification extends AbstractNotification { public class ErrorNotification extends AbstractNotification {
public static final int ERROR_NOTIFICATION_ID = 4; public static final int ERROR_NOTIFICATION_ID = 4;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.notification; package ch.dissem.apps.abit.notification;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.notification; package ch.dissem.apps.abit.notification;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -10,7 +26,7 @@ import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import java.util.LinkedList; import java.util.Collection;
import ch.dissem.apps.abit.Identicon; import ch.dissem.apps.abit.Identicon;
import ch.dissem.apps.abit.MainActivity; import ch.dissem.apps.abit.MainActivity;
@ -29,8 +45,10 @@ public class NewMessageNotification extends AbstractNotification {
public NewMessageNotification singleNotification(Plaintext plaintext) { public NewMessageNotification singleNotification(Plaintext plaintext) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx); NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
Spannable bigText = new SpannableString(plaintext.getSubject() + "\n" + plaintext.getText()); Spannable bigText = new SpannableString(plaintext.getSubject() + "\n" + plaintext.getText
bigText.setSpan(SPAN_EMPHASIS, 0, plaintext.getSubject().length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); ());
bigText.setSpan(SPAN_EMPHASIS, 0, plaintext.getSubject().length(), Spanned
.SPAN_INCLUSIVE_EXCLUSIVE);
builder.setSmallIcon(R.drawable.ic_notification_new_message) builder.setSmallIcon(R.drawable.ic_notification_new_message)
.setLargeIcon(toBitmap(new Identicon(plaintext.getFrom()), 192)) .setLargeIcon(toBitmap(new Identicon(plaintext.getFrom()), 192))
.setContentTitle(plaintext.getFrom().toString()) .setContentTitle(plaintext.getFrom().toString())
@ -40,27 +58,38 @@ public class NewMessageNotification extends AbstractNotification {
Intent showMessageIntent = new Intent(ctx, MainActivity.class); Intent showMessageIntent = new Intent(ctx, MainActivity.class);
showMessageIntent.putExtra(MainActivity.EXTRA_SHOW_MESSAGE, plaintext); showMessageIntent.putExtra(MainActivity.EXTRA_SHOW_MESSAGE, plaintext);
PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent, PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0, showMessageIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent); builder.setContentIntent(pendingIntent);
builder.addAction(R.drawable.ic_action_reply, ctx.getString(R.string.reply), pendingIntent); builder.addAction(R.drawable.ic_action_reply, ctx.getString(R.string.reply), pendingIntent);
builder.addAction(R.drawable.ic_action_delete, ctx.getString(R.string.delete), pendingIntent); builder.addAction(R.drawable.ic_action_delete, ctx.getString(R.string.delete),
pendingIntent);
notification = builder.build(); notification = builder.build();
return this; return this;
} }
public NewMessageNotification multiNotification(LinkedList<Plaintext> unacknowledged, int numberOfUnacknowledgedMessages) { /**
* @param unacknowledged will be accessed from different threads, so make sure wherever it's
* accessed it will be in a <code>synchronized(unacknowledged)
* {}</code> block
*/
public NewMessageNotification multiNotification(Collection<Plaintext> unacknowledged, int
numberOfUnacknowledgedMessages) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx); NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
builder.setSmallIcon(R.drawable.ic_notification_new_message) builder.setSmallIcon(R.drawable.ic_notification_new_message)
.setContentTitle(ctx.getString(R.string.n_new_messages, unacknowledged.size())) .setContentTitle(ctx.getString(R.string.n_new_messages, unacknowledged.size()))
.setContentText(ctx.getString(R.string.app_name)); .setContentText(ctx.getString(R.string.app_name));
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (unacknowledged) { synchronized (unacknowledged) {
inboxStyle.setBigContentTitle(ctx.getString(R.string.n_new_messages, numberOfUnacknowledgedMessages)); inboxStyle.setBigContentTitle(ctx.getString(R.string.n_new_messages,
numberOfUnacknowledgedMessages));
for (Plaintext msg : unacknowledged) { for (Plaintext msg : unacknowledged) {
Spannable sb = new SpannableString(msg.getFrom() + " " + msg.getSubject()); Spannable sb = new SpannableString(msg.getFrom() + " " + msg.getSubject());
sb.setSpan(SPAN_EMPHASIS, 0, String.valueOf(msg.getFrom()).length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); sb.setSpan(SPAN_EMPHASIS, 0, String.valueOf(msg.getFrom()).length(), Spannable
.SPAN_INCLUSIVE_EXCLUSIVE);
inboxStyle.addLine(sb); inboxStyle.addLine(sb);
} }
} }

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.notification; package ch.dissem.apps.abit.notification;
import android.app.PendingIntent; import android.app.PendingIntent;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.pow; package ch.dissem.apps.abit.pow;
import android.content.Context; import android.content.Context;

View File

@ -21,6 +21,17 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException; import android.database.sqlite.SQLiteConstraintException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import ch.dissem.bitmessage.entity.ObjectMessage; import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.payload.ObjectType; import ch.dissem.bitmessage.entity.payload.ObjectType;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
@ -28,21 +39,6 @@ import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.ports.Inventory; import ch.dissem.bitmessage.ports.Inventory;
import ch.dissem.bitmessage.utils.Encode; import ch.dissem.bitmessage.utils.Encode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static ch.dissem.apps.abit.repository.SqlHelper.join; import static ch.dissem.apps.abit.repository.SqlHelper.join;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE; import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static ch.dissem.bitmessage.utils.UnixTime.now; import static ch.dissem.bitmessage.utils.UnixTime.now;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.repository; package ch.dissem.apps.abit.repository;
import android.content.ContentValues; import android.content.ContentValues;

View File

@ -1,26 +1,31 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.service; package ch.dissem.apps.abit.service;
import android.app.Service; import android.app.Service;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Binder;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.widget.Toast;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import ch.dissem.apps.abit.R;
import ch.dissem.apps.abit.notification.NetworkNotification; import ch.dissem.apps.abit.notification.NetworkNotification;
import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID; import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID;
@ -32,19 +37,6 @@ import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIF
public class BitmessageService extends Service { public class BitmessageService extends Service {
public static final Logger LOG = LoggerFactory.getLogger(BitmessageService.class); public static final Logger LOG = LoggerFactory.getLogger(BitmessageService.class);
public static final int MSG_CREATE_IDENTITY = 10;
public static final int MSG_SUBSCRIBE = 20;
public static final int MSG_ADD_CONTACT = 21;
public static final int MSG_SEND_MESSAGE = 30;
public static final int MSG_SEND_BROADCAST = 31;
public static final int MSG_START_NODE = 100;
public static final int MSG_STOP_NODE = 101;
public static final String DATA_FIELD_IDENTITY = "identity";
public static final String DATA_FIELD_ADDRESS = "address";
public static final String DATA_FIELD_SUBJECT = "subject";
public static final String DATA_FIELD_MESSAGE = "message";
// Object to use as a thread-safe lock // Object to use as a thread-safe lock
private static final Object lock = new Object(); private static final Object lock = new Object();
@ -53,8 +45,6 @@ public class BitmessageService extends Service {
private static volatile boolean running = false; private static volatile boolean running = false;
private static Messenger messenger;
public static boolean isRunning() { public static boolean isRunning() {
return running && bmc.isRunning(); return running && bmc.isRunning();
} }
@ -65,7 +55,6 @@ public class BitmessageService extends Service {
if (bmc == null) { if (bmc == null) {
bmc = Singleton.getBitmessageContext(this); bmc = Singleton.getBitmessageContext(this);
notification = new NetworkNotification(this, bmc); notification = new NetworkNotification(this, bmc);
messenger = new Messenger(new IncomingHandler(this));
} }
} }
} }
@ -81,101 +70,34 @@ public class BitmessageService extends Service {
running = false; running = false;
} }
/** /**
* Return an object that allows the system to invoke * Return an object that allows the system to invoke
* the sync adapter. * the sync adapter.
*/ */
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return messenger.getBinder(); return new BitmessageBinder();
} }
private static class IncomingHandler extends Handler { public class BitmessageBinder extends Binder {
private WeakReference<BitmessageService> service; public void startupNode() {
startService(new Intent(BitmessageService.this, BitmessageService.class));
private IncomingHandler(BitmessageService service) { running = true;
this.service = new WeakReference<>(service); startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
if (!bmc.isRunning()) {
bmc.startup();
}
notification.show();
} }
@Override public void shutdownNode() {
public void handleMessage(Message msg) { if (bmc.isRunning()) {
switch (msg.what) { bmc.shutdown();
case MSG_CREATE_IDENTITY: {
BitmessageAddress identity = bmc.createIdentity(false);
if (msg.replyTo != null) {
try {
Message message = Message.obtain(this, MSG_CREATE_IDENTITY);
Bundle bundle = new Bundle();
bundle.putSerializable(DATA_FIELD_IDENTITY, identity);
message.setData(bundle);
msg.replyTo.send(message);
} catch (RemoteException e) {
LOG.debug(e.getMessage(), e);
}
}
break;
}
case MSG_SUBSCRIBE: {
Serializable data = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
if (data instanceof BitmessageAddress) {
bmc.addSubscribtion((BitmessageAddress) data);
}
break;
}
case MSG_ADD_CONTACT: {
Serializable data = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
if (data instanceof BitmessageAddress) {
bmc.addContact((BitmessageAddress) data);
}
break;
}
case MSG_SEND_MESSAGE: {
Serializable identity = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
Serializable address = msg.getData().getSerializable(DATA_FIELD_ADDRESS);
if (identity instanceof BitmessageAddress
&& address instanceof BitmessageAddress) {
String subject = msg.getData().getString(DATA_FIELD_SUBJECT);
String message = msg.getData().getString(DATA_FIELD_MESSAGE);
bmc.send((BitmessageAddress) identity, (BitmessageAddress) address,
subject, message);
} else {
Context ctx = service.get();
Toast.makeText(ctx, "Could not send", Toast.LENGTH_LONG);
}
break;
}
case MSG_SEND_BROADCAST: {
Serializable data = msg.getData().getSerializable(DATA_FIELD_IDENTITY);
if (data instanceof BitmessageAddress) {
String subject = msg.getData().getString(DATA_FIELD_SUBJECT);
String message = msg.getData().getString(DATA_FIELD_MESSAGE);
bmc.broadcast((BitmessageAddress) data, subject, message);
}
break;
}
case MSG_START_NODE:
// TODO: warn user, option to restrict to WiFi
// (I'm not quite sure this can be done here, though)
service.get().startService(new Intent(service.get(), BitmessageService.class));
running = true;
service.get().startForeground(ONGOING_NOTIFICATION_ID, notification
.getNotification());
if (!bmc.isRunning()) {
bmc.startup();
}
notification.show();
break;
case MSG_STOP_NODE:
if (bmc.isRunning()) {
bmc.shutdown();
}
running = false;
service.get().stopForeground(false);
service.get().stopSelf();
break;
default:
super.handleMessage(msg);
} }
running = false;
stopForeground(false);
stopSelf();
} }
} }
} }

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.service; package ch.dissem.apps.abit.service;
import android.app.Service; import android.app.Service;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.service; package ch.dissem.apps.abit.service;
import android.content.ComponentName; import android.content.ComponentName;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.service; package ch.dissem.apps.abit.service;
import android.content.Context; import android.content.Context;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.synchronization; package ch.dissem.apps.abit.synchronization;
import android.accounts.AbstractAccountAuthenticator; import android.accounts.AbstractAccountAuthenticator;

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.synchronization; package ch.dissem.apps.abit.synchronization;
import android.app.Service; import android.app.Service;

View File

@ -1,9 +1,26 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.synchronization; package ch.dissem.apps.abit.synchronization;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentValues; import android.content.ContentValues;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull;
/* /*
* Define an implementation of ContentProvider that stubs out * Define an implementation of ContentProvider that stubs out
@ -25,7 +42,7 @@ public class StubProvider extends ContentProvider {
* Return no type for MIME type * Return no type for MIME type
*/ */
@Override @Override
public String getType(Uri uri) { public String getType(@NonNull Uri uri) {
return null; return null;
} }
@ -35,7 +52,7 @@ public class StubProvider extends ContentProvider {
*/ */
@Override @Override
public Cursor query( public Cursor query(
Uri uri, @NonNull Uri uri,
String[] projection, String[] projection,
String selection, String selection,
String[] selectionArgs, String[] selectionArgs,
@ -47,7 +64,7 @@ public class StubProvider extends ContentProvider {
* insert() always returns null (no URI) * insert() always returns null (no URI)
*/ */
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(@NonNull Uri uri, ContentValues values) {
return null; return null;
} }
@ -55,7 +72,7 @@ public class StubProvider extends ContentProvider {
* delete() always returns "no rows affected" (0) * delete() always returns "no rows affected" (0)
*/ */
@Override @Override
public int delete(Uri uri, String selection, String[] selectionArgs) { public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return 0; return 0;
} }
@ -63,7 +80,7 @@ public class StubProvider extends ContentProvider {
* update() always returns "no rows affected" (0) * update() always returns "no rows affected" (0)
*/ */
public int update( public int update(
Uri uri, @NonNull Uri uri,
ContentValues values, ContentValues values,
String selection, String selection,
String[] selectionArgs) { String[] selectionArgs) {

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.synchronization; package ch.dissem.apps.abit.synchronization;
import android.accounts.Account; import android.accounts.Account;

View File

@ -1,19 +1,31 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.synchronization; package ch.dissem.apps.abit.synchronization;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Define a Service that returns an IBinder for the * Define a Service that returns an IBinder for the
* sync adapter class, allowing the sync adapter framework to call * sync adapter class, allowing the sync adapter framework to call
* onPerformSync(). * onPerformSync().
*/ */
public class SyncService extends Service { public class SyncService extends Service {
private static final Logger LOG = LoggerFactory.getLogger(SyncService.class);
// Storage for an instance of the sync adapter // Storage for an instance of the sync adapter
private static SyncAdapter syncAdapter = null; private static SyncAdapter syncAdapter = null;
// Object to use as a thread-safe lock // Object to use as a thread-safe lock

View File

@ -28,15 +28,6 @@ import java.util.Scanner;
* Helper class to work with Assets. * Helper class to work with Assets.
*/ */
public class Assets { public class Assets {
public static String readToString(Context ctx, String name) {
try {
InputStream in = ctx.getAssets().open(name);
return new Scanner(in, "UTF-8").useDelimiter("\\A").next();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static List<String> readSqlStatements(Context ctx, String name) { public static List<String> readSqlStatements(Context ctx, String name) {
try { try {
InputStream in = ctx.getAssets().open(name); InputStream in = ctx.getAssets().open(name);

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.util; package ch.dissem.apps.abit.util;
import java.util.regex.Pattern; import java.util.regex.Pattern;

View File

@ -32,7 +32,7 @@ import com.mikepenz.iconics.IconicsDrawable;
*/ */
public class Drawables { public class Drawables {
public static void addIcon(Context ctx, Menu menu, int menuItem, GoogleMaterial.Icon icon) { public static void addIcon(Context ctx, Menu menu, int menuItem, GoogleMaterial.Icon icon) {
menu.findItem(menuItem).setIcon(new IconicsDrawable(ctx, icon).colorRes(R.color.primary_text_default_material_dark).actionBar()); menu.findItem(menuItem).setIcon(new IconicsDrawable(ctx, icon).colorRes(R.color.colorPrimaryDarkText).actionBar());
} }
public static Bitmap toBitmap(Identicon identicon, int size) { public static Bitmap toBitmap(Identicon identicon, int size) {

View File

@ -1,4 +1,3 @@
package ch.dissem.apps.abit.util;
/* /*
* This software is provided 'as-is', without any express or implied * This software is provided 'as-is', without any express or implied
* warranty. In no event will Google be held liable for any damages * warranty. In no event will Google be held liable for any damages
@ -9,6 +8,8 @@ package ch.dissem.apps.abit.util;
* freely, as long as the origin is not misrepresented. * freely, as long as the origin is not misrepresented.
*/ */
package ch.dissem.apps.abit.util;
import android.os.Build; import android.os.Build;
import android.os.Process; import android.os.Process;
import android.util.Log; import android.util.Log;
@ -78,7 +79,7 @@ public final class PRNGFixes {
// Mix in the device- and invocation-specific seed. // Mix in the device- and invocation-specific seed.
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto") Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class) .getMethod("RAND_seed", byte[].class)
.invoke(null, generateSeed()); .invoke(null, (Object) generateSeed());
// Mix output of Linux PRNG into OpenSSL's PRNG // Mix output of Linux PRNG into OpenSSL's PRNG
int bytesRead = (Integer) Class.forName( int bytesRead = (Integer) Class.forName(
@ -169,6 +170,7 @@ public final class PRNGFixes {
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG * {@link SecureRandomSpi} which passes all requests to the Linux PRNG
* ({@code /dev/urandom}). * ({@code /dev/urandom}).
*/ */
@SuppressWarnings("JavaDoc")
public static class LinuxPRNGSecureRandom extends SecureRandomSpi { public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
/* /*
@ -241,6 +243,7 @@ public final class PRNGFixes {
synchronized (sLock) { synchronized (sLock) {
in = getUrandomInputStream(); in = getUrandomInputStream();
} }
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (in) { synchronized (in) {
in.readFully(bytes); in.readFully(bytes);
} }

View File

@ -1,3 +1,19 @@
/*
* Copyright 2016 Christian Basler
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.dissem.apps.abit.util; package ch.dissem.apps.abit.util;
import android.content.Context; import android.content.Context;

View File

@ -1,5 +1,6 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout 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"
android:gravity="center" android:gravity="center"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -11,7 +12,8 @@
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:elevation="4dp" /> android:elevation="4dp"
tools:ignore="UnusedAttribute"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

@ -1,5 +1,6 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout 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"
android:gravity="center" android:gravity="center"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -11,7 +12,8 @@
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:elevation="4dp" /> android:elevation="4dp"
tools:ignore="UnusedAttribute"/>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"

View File

@ -9,9 +9,8 @@
android:id="@+id/address" android:id="@+id/address"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:text="BM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" tools:text="BM-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
android:textSize="10dp" android:textSize="10dp"
tools:ignore="SpUsage" /> tools:ignore="SpUsage" />
@ -19,7 +18,6 @@
android:id="@+id/label_wrapper" android:id="@+id/label_wrapper"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignLeft="@+id/address"
android:layout_alignStart="@+id/address" android:layout_alignStart="@+id/address"
android:layout_below="@+id/address" android:layout_below="@+id/address"
android:layout_marginTop="16dp"> android:layout_marginTop="16dp">
@ -37,7 +35,6 @@
android:id="@+id/subscribe" android:id="@+id/subscribe"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignLeft="@+id/address"
android:layout_alignStart="@+id/address" android:layout_alignStart="@+id/address"
android:layout_below="@+id/label_wrapper" android:layout_below="@+id/label_wrapper"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
@ -50,7 +47,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/subscribe" android:layout_below="@+id/subscribe"
android:layout_marginBottom="12dp" android:layout_marginBottom="12dp"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
@ -62,7 +58,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignTop="@+id/do_import" android:layout_alignTop="@+id/do_import"
android:layout_toLeftOf="@+id/do_import"
android:layout_toStartOf="@+id/do_import" android:layout_toStartOf="@+id/do_import"
android:text="@string/cancel" /> android:text="@string/cancel" />

View File

@ -33,7 +33,8 @@
android:title="@string/status" android:title="@string/status"
app:title="@string/status" app:title="@string/status"
app:layout_scrollFlags="scroll|enterAlways" app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/> app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute"/>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>

View File

@ -16,7 +16,7 @@
--> -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <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="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -25,41 +25,38 @@
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:src="@color/colorAccent" android:src="@color/colorAccent"
android:layout_margin="16dp"/> android:layout_margin="16dp"
tools:ignore="ContentDescription"/>
<TextView <TextView
android:id="@+id/name" android:id="@+id/name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Name" tools:text="Name"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignTop="@+id/avatar" android:layout_alignTop="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar" android:layout_toEndOf="@+id/avatar"
android:paddingTop="0dp" android:paddingTop="0dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:paddingBottom="0dp" android:paddingBottom="0dp"
android:textStyle="bold" android:textStyle="bold"/>
/>
<TextView <TextView
android:id="@+id/address" android:id="@+id/address"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="BM-2cW0000000000000000000000000000000" tools:text="BM-2cW0000000000000000000000000000000"
android:lines="1" android:lines="1"
android:ellipsize="marquee" android:ellipsize="marquee"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:layout_alignBottom="@+id/avatar" android:layout_alignBottom="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar"/> android:layout_toEndOf="@+id/avatar"/>
</RelativeLayout> </RelativeLayout>

View File

@ -50,10 +50,9 @@
android:lines="1" android:lines="1"
android:paddingLeft="16dp" android:paddingLeft="16dp"
android:paddingRight="16dp" android:paddingRight="16dp"
android:text="BM-XyYxXyYxXyYxXyYxXyYx" tools:text="BM-XyYxXyYxXyYxXyYxXyYx"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:textStyle="bold" android:textStyle="bold"/>
tools:ignore="HardcodedText"/>
<TextView <TextView
android:id="@+id/stream_number" android:id="@+id/stream_number"
@ -64,9 +63,8 @@
android:lines="1" android:lines="1"
android:paddingLeft="16dp" android:paddingLeft="16dp"
android:paddingRight="16dp" android:paddingRight="16dp"
android:text="Stream #" tools:text="Stream #"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"/>
tools:ignore="HardcodedText"/>
<Switch <Switch
android:id="@+id/active" android:id="@+id/active"

View File

@ -14,7 +14,6 @@
android:scrollbarStyle="outsideOverlay" android:scrollbarStyle="outsideOverlay"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"/> android:layout_alignParentBottom="true"/>
@ -26,6 +25,5 @@
app:elevation="8dp" app:elevation="8dp"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_margin="16dp"/> android:layout_margin="16dp"/>
</RelativeLayout> </RelativeLayout>

View File

@ -1,6 +1,7 @@
<?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"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<RelativeLayout <RelativeLayout
@ -13,15 +14,15 @@
android:id="@+id/subject" android:id="@+id/subject"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:elegantTextHeight="false" android:elegantTextHeight="false"
android:enabled="false" android:enabled="false"
android:gravity="center_vertical" android:gravity="center_vertical"
android:padding="16dp" android:padding="16dp"
android:text="Subject" tools:text="Subject"
android:textAppearance="?android:attr/textAppearanceLarge" /> android:textAppearance="?android:attr/textAppearanceLarge"
tools:ignore="UnusedAttribute"/>
<View <View
android:id="@+id/divider" android:id="@+id/divider"
@ -34,11 +35,11 @@
android:id="@+id/avatar" android:id="@+id/avatar"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
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"/>
<TextView <TextView
android:id="@+id/sender" android:id="@+id/sender"
@ -46,11 +47,10 @@
android:layout_height="20dp" android:layout_height="20dp"
android:layout_alignTop="@+id/avatar" android:layout_alignTop="@+id/avatar"
android:layout_toEndOf="@+id/avatar" android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:text="Sender" tools:text="Sender"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView <TextView
@ -59,17 +59,15 @@
android:layout_height="20dp" android:layout_height="20dp"
android:layout_alignBottom="@+id/avatar" android:layout_alignBottom="@+id/avatar"
android:layout_toEndOf="@+id/avatar" android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:gravity="center_vertical" android:gravity="center_vertical"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:text="Recipient" /> tools:text="Recipient" />
<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_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_below="@+id/avatar" android:layout_below="@+id/avatar"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"

View File

@ -14,7 +14,6 @@
android:scrollbarStyle="outsideOverlay" android:scrollbarStyle="outsideOverlay"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_alignParentBottom="true"/> android:layout_alignParentBottom="true"/>
@ -26,6 +25,5 @@
app:elevation="8dp" app:elevation="8dp"
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_margin="16dp"/> android:layout_margin="16dp"/>
</RelativeLayout> </RelativeLayout>

View File

@ -16,6 +16,7 @@
--> -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
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">
<ImageView <ImageView
@ -23,21 +24,20 @@
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:src="@color/colorAccent" android:src="@color/colorAccent"
android:layout_margin="16dp"/> android:layout_margin="16dp"
tools:ignore="ContentDescription"/>
<TextView <TextView
android:id="@+id/sender" android:id="@+id/sender"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Sender" tools:text="Sender"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignTop="@+id/avatar" android:layout_alignTop="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar" android:layout_toEndOf="@+id/avatar"
android:layout_marginTop="-5dp" android:layout_marginTop="-5dp"
android:paddingTop="0dp" android:paddingTop="0dp"
@ -51,21 +51,20 @@
android:id="@+id/subject" android:id="@+id/subject"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Subject" tools:text="Subject"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:layout_below="@+id/sender" android:layout_below="@+id/sender"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar"/> android:layout_toEndOf="@+id/avatar"/>
<TextView <TextView
android:id="@+id/text" android:id="@+id/text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Text" tools:text="Text"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
@ -74,7 +73,6 @@
android:paddingRight="8dp" android:paddingRight="8dp"
android:paddingBottom="8dp" android:paddingBottom="8dp"
android:layout_below="@+id/subject" android:layout_below="@+id/subject"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar"/> android:layout_toEndOf="@+id/avatar"/>
</RelativeLayout> </RelativeLayout>

View File

@ -17,6 +17,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout 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"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -25,21 +26,20 @@
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:src="@color/colorAccent" android:src="@color/colorAccent"
android:layout_margin="16dp"/> android:layout_margin="16dp"
tools:ignore="ContentDescription"/>
<TextView <TextView
android:id="@+id/name" android:id="@+id/name"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Name" tools:text="Name"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_alignTop="@+id/avatar" android:layout_alignTop="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar" android:layout_toEndOf="@+id/avatar"
android:paddingTop="0dp" android:paddingTop="0dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
@ -52,14 +52,13 @@
android:id="@+id/stream_number" android:id="@+id/stream_number"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Stream #" tools:text="Stream #"
android:lines="1" android:lines="1"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall" android:textAppearance="?android:attr/textAppearanceSmall"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:layout_alignBottom="@+id/avatar" android:layout_alignBottom="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:layout_toEndOf="@+id/avatar"/> android:layout_toEndOf="@+id/avatar"/>
<com.mikepenz.iconics.view.IconicsImageView <com.mikepenz.iconics.view.IconicsImageView
@ -69,8 +68,7 @@
app:iiv_color="@android:color/black" app:iiv_color="@android:color/black"
app:iiv_icon="cmd-rss" app:iiv_icon="cmd-rss"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginRight="16dp"/> android:layout_marginEnd="16dp"/>
</RelativeLayout> </RelativeLayout>

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.design.widget.CoordinatorLayout 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"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true"> android:fitsSystemWindows="true">
@ -27,7 +28,8 @@
android:elevation="4dp" android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_scrollFlags="scroll|enterAlways" app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute"/>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>

View File

@ -5,5 +5,5 @@
android:id="@+id/send" android:id="@+id/send"
app:showAsAction="always" app:showAsAction="always"
android:icon="@drawable/ic_action_send" android:icon="@drawable/ic_action_send"
android:title="@string/send"/>` android:title="@string/send"/>
</menu> </menu>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
</menu>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="define_jabit" translatable="false"></string> <string name="define_jabit" translatable="false"/>
<!-- Author section --> <!-- Author section -->
<string name="library_jabit_author" translatable="false">Christian Basler</string> <string name="library_jabit_author" translatable="false">Christian Basler</string>
<string name="library_jabit_authorWebsite" translatable="false">dissem.ch</string> <string name="library_jabit_authorWebsite" translatable="false">dissem.ch</string>