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"/>
<application
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
@ -114,7 +114,9 @@
android:exported="false"
android:syncable="true"/>
<service android:name=".synchronization.AuthenticatorService">
<service
android:name=".synchronization.AuthenticatorService"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator"/>
</intent-filter>
@ -125,7 +127,8 @@
</service>
<service
android:name=".synchronization.SyncService"
android:exported="true">
android:exported="true"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter"/>
</intent-filter>

View File

@ -16,7 +16,6 @@
package ch.dissem.apps.abit;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
@ -27,7 +26,7 @@ import ch.dissem.apps.abit.listener.ListSelectionListener;
import ch.dissem.bitmessage.entity.valueobject.Label;
/**
* Created by chris on 07.09.15.
* @author Christian Basler
*/
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
* 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
public void onItemSelected(Object plaintext) {
}
@ -84,11 +84,13 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
super.onAttach(context);
// 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.");
}
callbacks = (ListSelectionListener) context;
}
@Override
@ -105,6 +107,7 @@ public abstract class AbstractItemListFragment<T> extends ListFragment {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
//noinspection unchecked
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;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import ch.dissem.bitmessage.entity.BitmessageAddress;
/**
* Compose a new message.
@ -20,6 +35,7 @@ public class ComposeMessageActivity extends AppCompatActivity {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_action_close);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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;
import android.os.Bundle;
@ -8,7 +24,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.AdapterView;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;

View File

@ -16,13 +16,12 @@
package ch.dissem.apps.abit;
import android.graphics.*;
import android.graphics.drawable.Drawable;
import ch.dissem.bitmessage.entity.BitmessageAddress;
/**
*
* @author Christian Basler
*/
public class Identicon extends Drawable {
private static final int SIZE = 9;
@ -34,7 +33,6 @@ public class Identicon extends Drawable {
private float cellWidth;
private float cellHeight;
private byte[] hash;
private int color;
private int background;
private boolean[][] fields;
@ -44,7 +42,7 @@ public class Identicon extends Drawable {
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
hash = input.getRipe();
byte[] hash = input.getRipe();
fields = new boolean[SIZE][SIZE];
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;
import android.app.AlertDialog;
@ -7,11 +23,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
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.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
@ -39,7 +51,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
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.ListSelectionListener;
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.synchronization.SyncAdapter;
import ch.dissem.apps.abit.util.Preferences;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext;
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.MSG_START_NODE;
import static ch.dissem.apps.abit.service.BitmessageService.MSG_STOP_NODE;
import static ch.dissem.apps.abit.service.BitmessageService.isRunning;
/**
@ -92,14 +101,12 @@ public class MainActivity extends AppCompatActivity
*/
private boolean twoPane;
private static IncomingHandler incomingHandler = new IncomingHandler();
private static Messenger messenger = new Messenger(incomingHandler);
private static Messenger service;
private static BitmessageBinder service;
private static boolean bound;
private static ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MainActivity.service = new Messenger(service);
MainActivity.service = (BitmessageBinder) service;
MainActivity.bound = true;
}
@ -112,16 +119,15 @@ public class MainActivity extends AppCompatActivity
private Label selectedLabel;
private MessageRepository messageRepo;
private AddressRepository addressRepo;
private BitmessageContext bmc;
private BitmessageAddress selectedIdentity;
private AccountHeader accountHeader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
messageRepo = Singleton.getMessageRepository(this);
addressRepo = Singleton.getAddressRepository(this);
List<Label> labels = messageRepo.getLabels();
bmc = Singleton.getBitmessageContext(this);
List<Label> labels = bmc.messages().getLabels();
if (selectedLabel == null) {
selectedLabel = labels.get(0);
}
@ -179,7 +185,7 @@ public class MainActivity extends AppCompatActivity
private void createDrawer(Toolbar toolbar, Collection<Label> labels) {
final ArrayList<IProfile> profiles = new ArrayList<>();
for (BitmessageAddress identity : addressRepo.getIdentities()) {
for (BitmessageAddress identity : bmc.addresses().getIdentities()) {
LOG.info("Adding identity " + identity.getAddress());
profiles.add(new ProfileDrawerItem()
.withIcon(new Identicon(identity))
@ -212,7 +218,7 @@ public class MainActivity extends AppCompatActivity
.withIcon(GoogleMaterial.Icon.gmd_settings)
);
// Create the AccountHeader
AccountHeader accountHeader = new AccountHeaderBuilder()
accountHeader = new AccountHeaderBuilder()
.withActivity(this)
.withHeaderBackground(R.drawable.header)
.withProfiles(profiles)
@ -221,13 +227,18 @@ public class MainActivity extends AppCompatActivity
public boolean onProfileChanged(View view, IProfile profile, boolean
currentProfile) {
if (profile.getIdentifier() == ADD_IDENTITY) {
try {
Message message = Message.obtain(null, BitmessageService
.MSG_CREATE_IDENTITY);
message.replyTo = messenger;
service.send(message);
} catch (RemoteException e) {
LOG.error(e.getMessage(), e);
BitmessageAddress identity = bmc.createIdentity(false);
IProfile newProfile = new ProfileDrawerItem()
.withName(identity.toString())
.withEmail(identity.getAddress())
.withTag(identity);
if (accountHeader.getProfiles() != null) {
// we know that there are 2 setting elements.
// Set the new profile above them ;)
accountHeader.addProfile(
newProfile, accountHeader.getProfiles().size() - 2);
} else {
accountHeader.addProfiles(newProfile);
}
} else if (profile instanceof ProfileDrawerItem) {
Object tag = ((ProfileDrawerItem) profile).getTag();
@ -243,7 +254,6 @@ public class MainActivity extends AppCompatActivity
if (profiles.size() > 2) { // There's always the add and manage identity items
accountHeader.setActiveProfile(profiles.get(0), true);
}
incomingHandler.updateAccountHeader(accountHeader);
ArrayList<IDrawerItem> drawerItems = new ArrayList<>();
for (Label label : labels) {
@ -299,23 +309,16 @@ public class MainActivity extends AppCompatActivity
new SwitchDrawerItem()
.withName(R.string.full_node)
.withIcon(CommunityMaterial.Icon.cmd_cloud_outline)
.withChecked(BitmessageService.isRunning())
.withChecked(isRunning())
.withOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(IDrawerItem drawerItem,
CompoundButton buttonView,
boolean isChecked) {
if (messenger != null) {
if (isChecked) {
checkAndStartNode(buttonView);
} else {
try {
service.send(Message.obtain(null,
MSG_STOP_NODE));
} catch (RemoteException e) {
LOG.error(e.getMessage(), e);
}
}
service.shutdownNode();
}
}
})
@ -361,15 +364,17 @@ public class MainActivity extends AppCompatActivity
}
private void checkAndStartNode(final CompoundButton buttonView) {
if (service == null) return;
if (Preferences.isConnectionAllowed(MainActivity.this)) {
forceStartNode();
service.startupNode();
} else {
new AlertDialog.Builder(MainActivity.this)
.setMessage(R.string.full_node_warning)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
forceStartNode();
service.startupNode();
}
})
.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() {
if (getSupportFragmentManager().findFragmentById(R.id.item_list) instanceof
MessageListFragment) {
@ -476,46 +472,4 @@ public class MainActivity extends AppCompatActivity
public BitmessageAddress getSelectedIdentity() {
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);
setSupportActionBar(toolbar);
// Show the Up button in the action bar.
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// 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;
import android.content.Intent;
@ -79,7 +95,8 @@ public class MessageDetailFragment extends Fragment {
if (item != null) {
((TextView) rootView.findViewById(R.id.subject)).setText(item.getSubject());
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());
if (item.getTo() != null) {
((TextView) rootView.findViewById(R.id.recipient)).setText(item.getTo().toString());
@ -99,7 +116,6 @@ public class MessageDetailFragment extends Fragment {
messageBody.setLinksClickable(true);
messageBody.setTextIsSelectable(true);
}
boolean removed = false;
Iterator<Label> labels = item.getLabels().iterator();
@ -112,6 +128,7 @@ public class MessageDetailFragment extends Fragment {
if (removed) {
Singleton.getMessageRepository(inflater.getContext()).save(item);
}
}
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.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);
super.onCreateOptionsMenu(menu, inflater);
@ -132,7 +150,8 @@ public class MessageDetailFragment extends Fragment {
MessageRepository messageRepo = Singleton.getMessageRepository(getContext());
switch (menuItem.getItemId()) {
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_IDENTITY, item.getTo());
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;
import android.content.Intent;

View File

@ -17,16 +17,8 @@
package ch.dissem.apps.abit;
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.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.view.View;
import android.widget.Button;
@ -34,35 +26,11 @@ import android.widget.EditText;
import android.widget.Switch;
import android.widget.TextView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ch.dissem.apps.abit.service.BitmessageService;
import ch.dissem.apps.abit.service.Singleton;
import ch.dissem.bitmessage.BitmessageContext;
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 {
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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -103,20 +71,11 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
BitmessageAddress bmAddress = new BitmessageAddress(address);
bmAddress.setAlias(label.getText().toString());
final int what;
if (subscribe.isChecked())
what = MSG_SUBSCRIBE;
else
what = MSG_ADD_CONTACT;
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);
BitmessageContext bmc = Singleton.getBitmessageContext(OpenBitmessageLinkActivity
.this);
bmc.addContact(bmAddress);
if (subscribe.isChecked()) {
bmc.addSubscribtion(bmAddress);
}
setResult(Activity.RESULT_OK);
@ -150,20 +109,4 @@ public class OpenBitmessageLinkActivity extends AppCompatActivity {
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);
setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
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;
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;
import android.os.Bundle;
import android.app.Activity;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.widget.TextView;
@ -20,6 +35,7 @@ public class StatusActivity extends AppCompatActivity {
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(false);

View File

@ -43,6 +43,7 @@ public class SubscriptionDetailActivity extends AppCompatActivity {
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// Show the Up button in the action bar.
//noinspection ConstantConditions
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
// 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;
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.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.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

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;
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;
import android.content.Context;
@ -6,12 +22,9 @@ import android.preference.PreferenceManager;
import java.util.Arrays;
import ch.dissem.apps.abit.util.Preferences;
import ch.dissem.bitmessage.InternalContext;
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.
*

View File

@ -17,7 +17,7 @@
package ch.dissem.apps.abit.listener;
/**
* Created by chris on 06.09.15.
* @author Christian Basler
*/
public interface ActionBarListener {
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");
* you may not use this file except in compliance with the License.

View File

@ -16,60 +16,32 @@
package ch.dissem.apps.abit.listener;
import android.annotation.TargetApi;
import android.app.NotificationManager;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.support.v7.app.NotificationCompat;
import java.util.Deque;
import java.util.LinkedList;
import ch.dissem.apps.abit.notification.NewMessageNotification;
import ch.dissem.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.Plaintext;
import java.util.LinkedList;
/**
* Listens for decrypted Bitmessage messages. Does show a notification.
* <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.
* </p>
*/
public class MessageListener implements BitmessageContext.Listener {
private final Context ctx;
private final NotificationManager manager;
private final LinkedList<Plaintext> unacknowledged = new LinkedList<>();
private final Deque<Plaintext> unacknowledged = new LinkedList<>();
private int numberOfUnacknowledgedMessages = 0;
private final NewMessageNotification notification;
public MessageListener(Context ctx) {
this.ctx = ctx.getApplicationContext();
this.manager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
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
public void receive(final Plaintext plaintext) {
synchronized (unacknowledged) {
@ -80,7 +52,6 @@ public class MessageListener implements BitmessageContext.Listener {
}
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
if (numberOfUnacknowledgedMessages == 1) {
notification.singleNotification(plaintext);
} 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;
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;
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;
import android.content.Context;
@ -7,7 +23,10 @@ import android.support.v7.app.NotificationCompat;
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 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;
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;
import android.app.PendingIntent;
@ -10,7 +26,7 @@ import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.StyleSpan;
import java.util.LinkedList;
import java.util.Collection;
import ch.dissem.apps.abit.Identicon;
import ch.dissem.apps.abit.MainActivity;
@ -29,8 +45,10 @@ public class NewMessageNotification extends AbstractNotification {
public NewMessageNotification singleNotification(Plaintext plaintext) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx);
Spannable bigText = new SpannableString(plaintext.getSubject() + "\n" + plaintext.getText());
bigText.setSpan(SPAN_EMPHASIS, 0, plaintext.getSubject().length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
Spannable bigText = new SpannableString(plaintext.getSubject() + "\n" + plaintext.getText
());
bigText.setSpan(SPAN_EMPHASIS, 0, plaintext.getSubject().length(), Spanned
.SPAN_INCLUSIVE_EXCLUSIVE);
builder.setSmallIcon(R.drawable.ic_notification_new_message)
.setLargeIcon(toBitmap(new Identicon(plaintext.getFrom()), 192))
.setContentTitle(plaintext.getFrom().toString())
@ -40,27 +58,38 @@ public class NewMessageNotification extends AbstractNotification {
Intent showMessageIntent = new Intent(ctx, MainActivity.class);
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.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();
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);
builder.setSmallIcon(R.drawable.ic_notification_new_message)
.setContentTitle(ctx.getString(R.string.n_new_messages, unacknowledged.size()))
.setContentText(ctx.getString(R.string.app_name));
NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
//noinspection SynchronizationOnLocalVariableOrMethodParameter
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) {
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);
}
}

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;
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;
import android.content.Context;

View File

@ -21,6 +21,17 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
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.payload.ObjectType;
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.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.bitmessage.utils.UnixTime.MINUTE;
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;
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;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Binder;
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.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.bitmessage.BitmessageContext;
import ch.dissem.bitmessage.entity.BitmessageAddress;
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 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
private static final Object lock = new Object();
@ -53,8 +45,6 @@ public class BitmessageService extends Service {
private static volatile boolean running = false;
private static Messenger messenger;
public static boolean isRunning() {
return running && bmc.isRunning();
}
@ -65,7 +55,6 @@ public class BitmessageService extends Service {
if (bmc == null) {
bmc = Singleton.getBitmessageContext(this);
notification = new NetworkNotification(this, bmc);
messenger = new Messenger(new IncomingHandler(this));
}
}
}
@ -81,101 +70,34 @@ public class BitmessageService extends Service {
running = false;
}
/**
* Return an object that allows the system to invoke
* the sync adapter.
*/
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
return new BitmessageBinder();
}
private static class IncomingHandler extends Handler {
private WeakReference<BitmessageService> service;
private IncomingHandler(BitmessageService service) {
this.service = new WeakReference<>(service);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
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));
public class BitmessageBinder extends Binder {
public void startupNode() {
startService(new Intent(BitmessageService.this, BitmessageService.class));
running = true;
service.get().startForeground(ONGOING_NOTIFICATION_ID, notification
.getNotification());
startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification());
if (!bmc.isRunning()) {
bmc.startup();
}
notification.show();
break;
case MSG_STOP_NODE:
}
public void shutdownNode() {
if (bmc.isRunning()) {
bmc.shutdown();
}
running = false;
service.get().stopForeground(false);
service.get().stopSelf();
break;
default:
super.handleMessage(msg);
}
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;
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;
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;
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;
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;
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;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
/*
* Define an implementation of ContentProvider that stubs out
@ -25,7 +42,7 @@ public class StubProvider extends ContentProvider {
* Return no type for MIME type
*/
@Override
public String getType(Uri uri) {
public String getType(@NonNull Uri uri) {
return null;
}
@ -35,7 +52,7 @@ public class StubProvider extends ContentProvider {
*/
@Override
public Cursor query(
Uri uri,
@NonNull Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
@ -47,7 +64,7 @@ public class StubProvider extends ContentProvider {
* insert() always returns null (no URI)
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
public Uri insert(@NonNull Uri uri, ContentValues values) {
return null;
}
@ -55,7 +72,7 @@ public class StubProvider extends ContentProvider {
* delete() always returns "no rows affected" (0)
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@ -63,7 +80,7 @@ public class StubProvider extends ContentProvider {
* update() always returns "no rows affected" (0)
*/
public int update(
Uri uri,
@NonNull Uri uri,
ContentValues values,
String selection,
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;
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;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Define a Service that returns an IBinder for the
* sync adapter class, allowing the sync adapter framework to call
* onPerformSync().
*/
public class SyncService extends Service {
private static final Logger LOG = LoggerFactory.getLogger(SyncService.class);
// Storage for an instance of the sync adapter
private static SyncAdapter syncAdapter = null;
// Object to use as a thread-safe lock

View File

@ -28,15 +28,6 @@ import java.util.Scanner;
* Helper class to work with 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) {
try {
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;
import java.util.regex.Pattern;

View File

@ -32,7 +32,7 @@ import com.mikepenz.iconics.IconicsDrawable;
*/
public class Drawables {
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) {

View File

@ -1,4 +1,3 @@
package ch.dissem.apps.abit.util;
/*
* This software is provided 'as-is', without any express or implied
* 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.
*/
package ch.dissem.apps.abit.util;
import android.os.Build;
import android.os.Process;
import android.util.Log;
@ -78,7 +79,7 @@ public final class PRNGFixes {
// Mix in the device- and invocation-specific seed.
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, generateSeed());
.invoke(null, (Object) generateSeed());
// Mix output of Linux PRNG into OpenSSL's PRNG
int bytesRead = (Integer) Class.forName(
@ -169,6 +170,7 @@ public final class PRNGFixes {
* {@link SecureRandomSpi} which passes all requests to the Linux PRNG
* ({@code /dev/urandom}).
*/
@SuppressWarnings("JavaDoc")
public static class LinuxPRNGSecureRandom extends SecureRandomSpi {
/*
@ -241,6 +243,7 @@ public final class PRNGFixes {
synchronized (sLock) {
in = getUrandomInputStream();
}
//noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (in) {
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;
import android.content.Context;

View File

@ -1,5 +1,6 @@
<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:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -11,7 +12,8 @@
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
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"
xmlns:tools="http://schemas.android.com/tools"

View File

@ -1,5 +1,6 @@
<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:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -11,7 +12,8 @@
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
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"
xmlns:tools="http://schemas.android.com/tools"

View File

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

View File

@ -33,7 +33,8 @@
android:title="@string/status"
app:title="@string/status"
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>

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -13,15 +14,15 @@
android:id="@+id/subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:elegantTextHeight="false"
android:enabled="false"
android:gravity="center_vertical"
android:padding="16dp"
android:text="Subject"
android:textAppearance="?android:attr/textAppearanceLarge" />
tools:text="Subject"
android:textAppearance="?android:attr/textAppearanceLarge"
tools:ignore="UnusedAttribute"/>
<View
android:id="@+id/divider"
@ -34,11 +35,11 @@
android:id="@+id/avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/divider"
android:layout_margin="16dp"
android:src="@color/colorAccent" />
android:src="@color/colorAccent"
tools:ignore="ContentDescription"/>
<TextView
android:id="@+id/sender"
@ -46,11 +47,10 @@
android:layout_height="20dp"
android:layout_alignTop="@+id/avatar"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:text="Sender"
tools:text="Sender"
android:textStyle="bold" />
<TextView
@ -59,17 +59,15 @@
android:layout_height="20dp"
android:layout_alignBottom="@+id/avatar"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:gravity="center_vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:text="Recipient" />
tools:text="Recipient" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/avatar"
android:layout_marginLeft="16dp"

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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_height="match_parent"
android:fitsSystemWindows="true">
@ -27,7 +28,8 @@
android:elevation="4dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
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>

View File

@ -5,5 +5,5 @@
android:id="@+id/send"
app:showAsAction="always"
android:icon="@drawable/ic_action_send"
android:title="@string/send"/>`
android:title="@string/send"/>
</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"?>
<resources>
<string name="define_jabit" translatable="false"></string>
<string name="define_jabit" translatable="false"/>
<!-- Author section -->
<string name="library_jabit_author" translatable="false">Christian Basler</string>
<string name="library_jabit_authorWebsite" translatable="false">dissem.ch</string>