From 6a8648ca288a14029b7115d13611b14e461239e7 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Thu, 20 Oct 2016 12:53:35 +0200 Subject: [PATCH] Added actions to notifications (this required a slight detour) --- app/src/main/AndroidManifest.xml | 16 +++- .../apps/abit/ComposeMessageActivity.java | 36 +++++++- .../ch/dissem/apps/abit/MainActivity.java | 85 +++++-------------- .../apps/abit/MessageDetailFragment.java | 31 +------ .../abit/dialog/FullNodeDialogActivity.java | 44 ++++++++++ .../notification/NetworkNotification.java | 50 ++++++++--- .../notification/NewMessageNotification.java | 55 +++++++----- .../abit/service/BitmessageIntentService.java | 72 ++++++++++++++++ .../apps/abit/service/BitmessageService.java | 60 +++++-------- .../drawable/ic_notification_node_start.xml | 25 ++++++ .../drawable/ic_notification_node_stop.xml | 25 ++++++ .../main/res/layout/dialog_add_identity.xml | 5 +- app/src/main/res/layout/dialog_full_node.xml | 60 +++++++++++++ app/src/main/res/values-de/strings.xml | 3 + app/src/main/res/values/strings.xml | 3 + 15 files changed, 405 insertions(+), 165 deletions(-) create mode 100644 app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.java create mode 100644 app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.java create mode 100644 app/src/main/res/drawable/ic_notification_node_start.xml create mode 100644 app/src/main/res/drawable/ic_notification_node_stop.xml create mode 100644 app/src/main/res/layout/dialog_full_node.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 90f7d6f..a7a2f74 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -47,6 +47,10 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".MainActivity"/> + - - + + @@ -158,6 +167,9 @@ android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter"/> + diff --git a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java index c5f2032..97a18c1 100644 --- a/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/ComposeMessageActivity.java @@ -16,10 +16,16 @@ package ch.dissem.apps.abit; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; +import ch.dissem.bitmessage.entity.Plaintext; + /** * Compose a new message. */ @@ -46,7 +52,33 @@ public class ComposeMessageActivity extends AppCompatActivity { ComposeMessageFragment fragment = new ComposeMessageFragment(); fragment.setArguments(getIntent().getExtras()); getSupportFragmentManager().beginTransaction() - .replace(R.id.content, fragment) - .commit(); + .replace(R.id.content, fragment) + .commit(); + } + + public static void launchReplyTo(Fragment fragment, Plaintext item) { + fragment.startActivity(getReplyIntent(fragment.getActivity(), item)); + } + + public static void launchReplyTo(Activity activity, Plaintext item) { + activity.startActivity(getReplyIntent(activity, item)); + } + + private static Intent getReplyIntent(Context ctx, Plaintext item) { + Intent replyIntent = new Intent(ctx, ComposeMessageActivity.class); + replyIntent.putExtra(EXTRA_RECIPIENT, item.getFrom()); + replyIntent.putExtra(EXTRA_IDENTITY, item.getTo()); + String prefix; + if (item.getSubject().length() >= 3 && item.getSubject().substring(0, 3) + .equalsIgnoreCase("RE:")) { + prefix = ""; + } else { + prefix = "RE: "; + } + replyIntent.putExtra(EXTRA_SUBJECT, prefix + item.getSubject()); + replyIntent.putExtra(EXTRA_CONTENT, + "\n\n------------------------------------------------------\n" + + item.getText()); + return replyIntent; } } diff --git a/app/src/main/java/ch/dissem/apps/abit/MainActivity.java b/app/src/main/java/ch/dissem/apps/abit/MainActivity.java index 10dc12f..7e418c2 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MainActivity.java +++ b/app/src/main/java/ch/dissem/apps/abit/MainActivity.java @@ -17,19 +17,14 @@ package ch.dissem.apps.abit; import android.app.AlertDialog; -import android.content.ComponentName; -import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.graphics.Point; import android.os.Bundle; -import android.os.IBinder; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.ViewGroup; -import android.widget.CompoundButton; import android.widget.RelativeLayout; import com.github.amlcurran.showcaseview.ShowcaseView; @@ -59,10 +54,10 @@ import java.util.Collection; import java.util.List; import ch.dissem.apps.abit.dialog.AddIdentityDialogFragment; +import ch.dissem.apps.abit.dialog.FullNodeDialogActivity; 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; @@ -71,6 +66,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress; import ch.dissem.bitmessage.entity.Plaintext; import ch.dissem.bitmessage.entity.valueobject.Label; +import static ch.dissem.apps.abit.ComposeMessageActivity.launchReplyTo; import static ch.dissem.apps.abit.service.BitmessageService.isRunning; @@ -94,6 +90,7 @@ import static ch.dissem.apps.abit.service.BitmessageService.isRunning; public class MainActivity extends AppCompatActivity implements ListSelectionListener, ActionBarListener { public static final String EXTRA_SHOW_MESSAGE = "ch.dissem.abit.ShowMessage"; + public static final String EXTRA_REPLY_TO_MESSAGE = "ch.dissem.abit.ReplyToMessage"; public static final String ACTION_SHOW_INBOX = "ch.dissem.abit.ShowInbox"; private static final Logger LOG = LoggerFactory.getLogger(MainActivity.class); @@ -110,22 +107,6 @@ public class MainActivity extends AppCompatActivity */ private boolean twoPane; - private static BitmessageBinder service; - private static boolean bound; - private static ServiceConnection connection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - MainActivity.service = (BitmessageBinder) service; - MainActivity.bound = true; - } - - @Override - public void onServiceDisconnected(ComponentName name) { - service = null; - bound = false; - } - }; - private Label selectedLabel; private BitmessageContext bmc; @@ -172,8 +153,13 @@ public class MainActivity extends AppCompatActivity Singleton.getMessageListener(this).resetNotification(); // handle intents - if (getIntent().hasExtra(EXTRA_SHOW_MESSAGE)) { - onItemSelected(getIntent().getSerializableExtra(EXTRA_SHOW_MESSAGE)); + Intent intent = getIntent(); + if (intent.hasExtra(EXTRA_SHOW_MESSAGE)) { + onItemSelected(intent.getSerializableExtra(EXTRA_SHOW_MESSAGE)); + } + if (intent.hasExtra(EXTRA_REPLY_TO_MESSAGE)) { + Plaintext item = (Plaintext) intent.getSerializableExtra(EXTRA_REPLY_TO_MESSAGE); + launchReplyTo(this, item); } if (Preferences.useTrustedNode(this)) { @@ -345,9 +331,9 @@ public class MainActivity extends AppCompatActivity .withChecked(isRunning()) .withOnCheckedChangeListener((drawerItem, buttonView, isChecked) -> { if (isChecked) { - checkAndStartNode(buttonView); + checkAndStartNode(); } else { - service.shutdownNode(); + stopService(new Intent(this, BitmessageService.class)); } }); @@ -448,23 +434,11 @@ public class MainActivity extends AppCompatActivity } } - private void checkAndStartNode(final CompoundButton buttonView) { - if (service == null) return; - + private void checkAndStartNode() { if (Preferences.isConnectionAllowed(MainActivity.this)) { - service.startupNode(); + startService(new Intent(this, BitmessageService.class)); } else { - new AlertDialog.Builder(MainActivity.this) - .setMessage(R.string.full_node_warning) - .setPositiveButton( - android.R.string.yes, - (dialog, which) -> service.startupNode() - ) - .setNegativeButton( - android.R.string.no, - (dialog, which) -> updateNodeSwitch() - ) - .show(); + startActivity(new Intent(this, FullNodeDialogActivity.class)); } } @@ -483,11 +457,14 @@ public class MainActivity extends AppCompatActivity } } - public void updateNodeSwitch() { - runOnUiThread(() -> { - nodeSwitch.withChecked(bmc.isRunning()); - drawer.updateStickyFooterItem(nodeSwitch); - }); + public static void updateNodeSwitch() { + MainActivity i = getInstance(); + if (i != null) { + i.runOnUiThread(() -> { + i.nodeSwitch.withChecked(i.bmc.isRunning()); + i.drawer.updateStickyFooterItem(i.nodeSwitch); + }); + } } private void showSelectedLabel() { @@ -556,22 +533,6 @@ public class MainActivity extends AppCompatActivity return selectedLabel; } - @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(); - } - public static MainActivity getInstance() { if (instance == null) return null; return instance.get(); diff --git a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java index bce2795..d3d2689 100644 --- a/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java +++ b/app/src/main/java/ch/dissem/apps/abit/MessageDetailFragment.java @@ -16,11 +16,9 @@ package ch.dissem.apps.abit; -import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.util.Linkify; -import android.text.util.Linkify.TransformFilter; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -33,7 +31,6 @@ import android.widget.TextView; import com.mikepenz.google_material_typeface_library.GoogleMaterial; import java.util.Iterator; -import java.util.regex.Matcher; import ch.dissem.apps.abit.listener.ActionBarListener; import ch.dissem.apps.abit.service.Singleton; @@ -44,10 +41,6 @@ import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.ports.MessageRepository; import static android.text.util.Linkify.WEB_URLS; -import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_CONTENT; -import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_IDENTITY; -import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_RECIPIENT; -import static ch.dissem.apps.abit.ComposeMessageActivity.EXTRA_SUBJECT; import static ch.dissem.apps.abit.util.Constants.BITMESSAGE_ADDRESS_PATTERN; import static ch.dissem.apps.abit.util.Constants.BITMESSAGE_URL_SCHEMA; @@ -113,11 +106,8 @@ public class MessageDetailFragment extends Fragment { Linkify.addLinks(messageBody, WEB_URLS); Linkify.addLinks(messageBody, BITMESSAGE_ADDRESS_PATTERN, BITMESSAGE_URL_SCHEMA, null, - new TransformFilter() { - public final String transformUrl(final Matcher match, String url) { - return match.group(); - } - }); + (match, url) -> match.group() + ); messageBody.setLinksClickable(true); messageBody.setTextIsSelectable(true); @@ -158,22 +148,7 @@ 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); - replyIntent.putExtra(EXTRA_RECIPIENT, item.getFrom()); - replyIntent.putExtra(EXTRA_IDENTITY, item.getTo()); - String prefix; - if (item.getSubject().length() >= 3 && item.getSubject().substring(0, 3) - .equalsIgnoreCase("RE:")) { - prefix = ""; - } else { - prefix = "RE: "; - } - replyIntent.putExtra(EXTRA_SUBJECT, prefix + item.getSubject()); - replyIntent.putExtra(EXTRA_CONTENT, - "\n\n------------------------------------------------------\n" - + item.getText()); - startActivity(replyIntent); + ComposeMessageActivity.launchReplyTo(this, item); return true; case R.id.delete: if (isInTrash(item)) { diff --git a/app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.java b/app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.java new file mode 100644 index 0000000..2f573b2 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/dialog/FullNodeDialogActivity.java @@ -0,0 +1,44 @@ +/* + * Copyright 2016 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.apps.abit.dialog; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +import ch.dissem.apps.abit.R; +import ch.dissem.apps.abit.service.BitmessageService; + +import static ch.dissem.apps.abit.MainActivity.updateNodeSwitch; + +/** + * @author Christian Basler + */ + +public class FullNodeDialogActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.dialog_full_node); + findViewById(R.id.ok).setOnClickListener(v -> { + startService(new Intent(this, BitmessageService.class)); + updateNodeSwitch(); + finish(); + }); + findViewById(R.id.dismiss).setOnClickListener(v -> finish()); + } +} diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java index 1377e1f..606c389 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NetworkNotification.java @@ -27,21 +27,26 @@ import java.util.TimerTask; import ch.dissem.apps.abit.MainActivity; import ch.dissem.apps.abit.R; +import ch.dissem.apps.abit.service.BitmessageIntentService; import ch.dissem.apps.abit.service.BitmessageService; import ch.dissem.bitmessage.utils.Property; +import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; +import static ch.dissem.apps.abit.MainActivity.updateNodeSwitch; + /** * Shows the network status (as long as the client is connected as a full node) */ public class NetworkNotification extends AbstractNotification { - public static final int ONGOING_NOTIFICATION_ID = 2; + public static final int NETWORK_NOTIFICATION_ID = 2; - private NotificationCompat.Builder builder; + private final NotificationCompat.Builder builder; + private Timer timer; public NetworkNotification(Context ctx) { super(ctx); - Intent showMessageIntent = new Intent(ctx, MainActivity.class); - PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 1, showMessageIntent, 0); + Intent showAppIntent = new Intent(ctx, MainActivity.class); + PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 1, showAppIntent, 0); builder = new NotificationCompat.Builder(ctx); builder.setSmallIcon(R.drawable.ic_notification_full_node) .setContentTitle(ctx.getString(R.string.bitmessage_full_node)) @@ -58,10 +63,7 @@ public class NetworkNotification extends AbstractNotification { Property connections = BitmessageService.getStatus().getProperty("network", "connections"); if (!running) { builder.setContentText(ctx.getString(R.string.connection_info_disconnected)); - MainActivity mainActivity = MainActivity.getInstance(); - if (mainActivity != null) { - mainActivity.updateNodeSwitch(); - } + updateNodeSwitch(); } else if (connections.getProperties().length == 0) { builder.setContentText(ctx.getString(R.string.connection_info_pending)); } else { @@ -80,6 +82,19 @@ public class NetworkNotification extends AbstractNotification { } builder.setContentText(info); } + builder.mActions.clear(); + Intent intent = new Intent(ctx, BitmessageIntentService.class); + if (running) { + intent.putExtra(BitmessageIntentService.EXTRA_SHUTDOWN_NODE, true); + builder.addAction(R.drawable.ic_notification_node_stop, + ctx.getString(R.string.full_node_stop), + PendingIntent.getService(ctx, 0, intent, FLAG_UPDATE_CURRENT)); + } else { + intent.putExtra(BitmessageIntentService.EXTRA_STARTUP_NODE, true); + builder.addAction(R.drawable.ic_notification_node_start, + ctx.getString(R.string.full_node_restart), + PendingIntent.getService(ctx, 1, intent, FLAG_UPDATE_CURRENT)); + } notification = builder.build(); return running; } @@ -88,7 +103,8 @@ public class NetworkNotification extends AbstractNotification { public void show() { super.show(); - new Timer().schedule(new TimerTask() { + timer = new Timer(); + timer.schedule(new TimerTask() { @Override public void run() { if (!update()) { @@ -99,14 +115,28 @@ public class NetworkNotification extends AbstractNotification { }, 10_000, 10_000); } + public void showShutdown() { + if (timer != null) { + timer.cancel(); + } + update(); + super.show(); + } + @Override protected int getNotificationId() { - return ONGOING_NOTIFICATION_ID; + return NETWORK_NOTIFICATION_ID; } public void connecting() { builder.setOngoing(true); builder.setContentText(ctx.getString(R.string.connection_info_pending)); + Intent intent = new Intent(ctx, BitmessageIntentService.class); + intent.putExtra(BitmessageIntentService.EXTRA_SHUTDOWN_NODE, true); + builder.mActions.clear(); + builder.addAction(R.drawable.ic_notification_node_stop, + ctx.getString(R.string.full_node_stop), + PendingIntent.getService(ctx, 0, intent, FLAG_UPDATE_CURRENT)); notification = builder.build(); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java index 80ff371..06e4c02 100644 --- a/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java +++ b/app/src/main/java/ch/dissem/apps/abit/notification/NewMessageNotification.java @@ -31,8 +31,13 @@ import java.util.Collection; import ch.dissem.apps.abit.Identicon; import ch.dissem.apps.abit.MainActivity; import ch.dissem.apps.abit.R; +import ch.dissem.apps.abit.service.BitmessageIntentService; import ch.dissem.bitmessage.entity.Plaintext; +import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; +import static ch.dissem.apps.abit.MainActivity.EXTRA_REPLY_TO_MESSAGE; +import static ch.dissem.apps.abit.MainActivity.EXTRA_SHOW_MESSAGE; +import static ch.dissem.apps.abit.service.BitmessageIntentService.EXTRA_DELETE_MESSAGE; import static ch.dissem.apps.abit.util.Drawables.toBitmap; public class NewMessageNotification extends AbstractNotification { @@ -46,51 +51,59 @@ 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); + .SPAN_INCLUSIVE_EXCLUSIVE); builder.setSmallIcon(R.drawable.ic_notification_new_message) - .setLargeIcon(toBitmap(new Identicon(plaintext.getFrom()), 192)) - .setContentTitle(plaintext.getFrom().toString()) - .setContentText(plaintext.getSubject()) - .setStyle(new NotificationCompat.BigTextStyle().bigText(bigText)) - .setContentInfo("Info"); + .setLargeIcon(toBitmap(new Identicon(plaintext.getFrom()), 192)) + .setContentTitle(plaintext.getFrom().toString()) + .setContentText(plaintext.getSubject()) + .setStyle(new NotificationCompat.BigTextStyle().bigText(bigText)) + .setContentInfo("Info"); - 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); - builder.setContentIntent(pendingIntent); - - // TODO: add proper intents to reply/delete - builder.addAction(R.drawable.ic_action_reply, ctx.getString(R.string.reply), pendingIntent); + builder.setContentIntent( + createActivityIntent(EXTRA_SHOW_MESSAGE, plaintext)); + builder.addAction(R.drawable.ic_action_reply, ctx.getString(R.string.reply), + createActivityIntent(EXTRA_REPLY_TO_MESSAGE, plaintext)); builder.addAction(R.drawable.ic_action_delete, ctx.getString(R.string.delete), - pendingIntent); + createServiceIntent(ctx, EXTRA_DELETE_MESSAGE, plaintext)); notification = builder.build(); return this; } + private PendingIntent createActivityIntent(String action, Plaintext message) { + Intent intent = new Intent(ctx, MainActivity.class); + intent.putExtra(action, message); + return PendingIntent.getActivity(ctx, action.hashCode(), intent, FLAG_UPDATE_CURRENT); + } + + private PendingIntent createServiceIntent(Context ctx, String action, Plaintext message) { + Intent intent = new Intent(ctx, BitmessageIntentService.class); + intent.putExtra(action, message); + return PendingIntent.getService(ctx, action.hashCode(), intent, FLAG_UPDATE_CURRENT); + } + /** * @param unacknowledged will be accessed from different threads, so make sure wherever it's * accessed it will be in a synchronized(unacknowledged) * {} block */ public NewMessageNotification multiNotification(Collection unacknowledged, int - numberOfUnacknowledgedMessages) { + 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)); + .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)); + 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); + .SPAN_INCLUSIVE_EXCLUSIVE); inboxStyle.addLine(sb); } } diff --git a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.java b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.java new file mode 100644 index 0000000..ecce911 --- /dev/null +++ b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageIntentService.java @@ -0,0 +1,72 @@ +/* + * 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.IntentService; +import android.content.Intent; + +import ch.dissem.apps.abit.dialog.FullNodeDialogActivity; +import ch.dissem.apps.abit.util.Preferences; +import ch.dissem.bitmessage.BitmessageContext; +import ch.dissem.bitmessage.entity.Plaintext; + +import static ch.dissem.apps.abit.MainActivity.updateNodeSwitch; + +/** + * @author Christian Basler + */ + +public class BitmessageIntentService extends IntentService { + public static final String EXTRA_DELETE_MESSAGE = "ch.dissem.abit.DeleteMessage"; + public static final String EXTRA_STARTUP_NODE = "ch.dissem.abit.StartFullNode"; + public static final String EXTRA_SHUTDOWN_NODE = "ch.dissem.abit.StopFullNode"; + + private BitmessageContext bmc; + + public BitmessageIntentService() { + super("BitmessageIntentService"); + } + + @Override + public void onCreate() { + super.onCreate(); + bmc = Singleton.getBitmessageContext(this); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (intent.hasExtra(EXTRA_DELETE_MESSAGE)) { + Plaintext item = (Plaintext) intent.getSerializableExtra(EXTRA_DELETE_MESSAGE); + bmc.labeler().delete(item); + bmc.messages().save(item); + } + if (intent.hasExtra(EXTRA_STARTUP_NODE)) { + if (Preferences.isConnectionAllowed(this)) { + startService(new Intent(this, BitmessageService.class)); + updateNodeSwitch(); + } else { + Intent dialogIntent = new Intent(this, FullNodeDialogActivity.class); + dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(dialogIntent); + sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } + } + if (intent.hasExtra(EXTRA_SHUTDOWN_NODE)) { + stopService(new Intent(this, BitmessageService.class)); + } + } +} diff --git a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java index 799aa07..1b8c645 100644 --- a/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java +++ b/app/src/main/java/ch/dissem/apps/abit/service/BitmessageService.java @@ -18,14 +18,14 @@ package ch.dissem.apps.abit.service; import android.app.Service; import android.content.Intent; -import android.os.Binder; import android.os.IBinder; +import android.support.annotation.Nullable; import ch.dissem.apps.abit.notification.NetworkNotification; import ch.dissem.bitmessage.BitmessageContext; import ch.dissem.bitmessage.utils.Property; -import static ch.dissem.apps.abit.notification.NetworkNotification.ONGOING_NOTIFICATION_ID; +import static ch.dissem.apps.abit.notification.NetworkNotification.NETWORK_NOTIFICATION_ID; /** * Define a Service that returns an IBinder for the @@ -44,55 +44,41 @@ public class BitmessageService extends Service { @Override public void onCreate() { - synchronized (BitmessageService.class) { - if (bmc == null) { - bmc = Singleton.getBitmessageContext(this); - } - notification = new NetworkNotification(this); + if (bmc == null) { + bmc = Singleton.getBitmessageContext(this); } + notification = new NetworkNotification(this); + running = false; } @Override public int onStartCommand(Intent intent, int flags, int startId) { - return Service.START_STICKY; - } - - @Override - public void onDestroy() { - if (bmc.isRunning()) bmc.shutdown(); - running = false; - } - - /** - * Return an object that allows the system to invoke - * the sync adapter. - */ - @Override - public IBinder onBind(Intent intent) { - return new BitmessageBinder(); - } - - public class BitmessageBinder extends Binder { - public void startupNode() { - startService(new Intent(BitmessageService.this, BitmessageService.class)); + if (!isRunning()) { running = true; notification.connecting(); - startForeground(ONGOING_NOTIFICATION_ID, notification.getNotification()); + startForeground(NETWORK_NOTIFICATION_ID, notification.getNotification()); if (!bmc.isRunning()) { bmc.startup(); } notification.show(); } + return Service.START_STICKY; + } - public void shutdownNode() { - if (bmc.isRunning()) { - bmc.shutdown(); - } - running = false; - stopForeground(true); - notification.show(); - stopSelf(); + @Override + public void onDestroy() { + if (bmc.isRunning()) { + bmc.shutdown(); } + running = false; + notification.showShutdown(); + stopSelf(); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; } public static Property getStatus() { diff --git a/app/src/main/res/drawable/ic_notification_node_start.xml b/app/src/main/res/drawable/ic_notification_node_start.xml new file mode 100644 index 0000000..4b219f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_notification_node_start.xml @@ -0,0 +1,25 @@ +<!-- + ~ 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M8,5v14l11,-7z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_notification_node_stop.xml b/app/src/main/res/drawable/ic_notification_node_stop.xml new file mode 100644 index 0000000..302844d --- /dev/null +++ b/app/src/main/res/drawable/ic_notification_node_stop.xml @@ -0,0 +1,25 @@ +<!-- + ~ 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/> +</vector> diff --git a/app/src/main/res/layout/dialog_add_identity.xml b/app/src/main/res/layout/dialog_add_identity.xml index 720b1fd..cae15ba 100644 --- a/app/src/main/res/layout/dialog_add_identity.xml +++ b/app/src/main/res/layout/dialog_add_identity.xml @@ -28,7 +28,7 @@ <TextView android:id="@+id/description" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:text="@string/add_identity_warning" app:layout_constraintLeft_toLeftOf="parent" @@ -39,7 +39,7 @@ <RadioGroup android:id="@+id/radioGroup" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:paddingBottom="24dp" android:paddingTop="24dp" @@ -86,7 +86,6 @@ app:layout_constraintHorizontal_bias="1.0" app:layout_constraintRight_toRightOf="@+id/radioGroup" app:layout_constraintTop_toBottomOf="@+id/radioGroup" - tools:layout_constraintLeft_creator="1" tools:layout_constraintRight_creator="1" tools:layout_constraintTop_creator="1"/> diff --git a/app/src/main/res/layout/dialog_full_node.xml b/app/src/main/res/layout/dialog_full_node.xml new file mode 100644 index 0000000..9c8edd9 --- /dev/null +++ b/app/src/main/res/layout/dialog_full_node.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright 2016 Christian Basler + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<android.support.constraint.ConstraintLayout + 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="336dp" + android:layout_height="wrap_content"> + + <TextView + android:id="@+id/description" + android:layout_width="320dp" + android:layout_height="wrap_content" + android:padding="24dp" + android:text="@string/full_node_warning" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:layout_editor_absoluteX="0dp"/> + + <Button + android:id="@+id/ok" + style="?android:attr/borderlessButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="8dp" + android:text="@string/startup_node" + android:textColor="@color/colorAccent" + app:layout_constraintEnd_toEndOf="@+id/description" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintTop_toBottomOf="@+id/description" + tools:layout_editor_absoluteX="184dp"/> + + <Button + android:id="@+id/dismiss" + style="?android:attr/borderlessButtonStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="8dp" + android:text="@string/cancel" + android:textColor="@color/colorAccent" + app:layout_constraintEnd_toStartOf="@+id/ok" + app:layout_constraintTop_toBottomOf="@+id/description" + tools:ignore="RtlSymmetry" + tools:layout_editor_absoluteX="87dp"/> +</android.support.constraint.ConstraintLayout> diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ae37f4a..a72fb64 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -96,4 +96,7 @@ Als Alternative kann in den Einstellungen ein vertrauenswürdiger Knoten konfigu <string name="select_file_title">Datei auswählen</string> <string name="select_identities_to_import">Bitte wähle die zu importierenden Identitäten:</string> <string name="import_input_description">Du kannst einfach den Inhalt eines Exports oder einer ‘keys.dat’-Datei einfügen</string> + <string name="full_node_restart">Knoten starten</string> + <string name="full_node_stop">Knoten beenden</string> + <string name="startup_node">Knoten starten</string> </resources> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dd448d9..4fb16d4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -95,4 +95,7 @@ As an alternative you could configure a trusted node in the settings, but as of <string name="select_file_title">Select a File</string> <string name="select_identities_to_import">Please select the identities you want to import:</string> <string name="import_input_description">You can just paste the contents of an export or a ‘keys.dat’ file</string> + <string name="full_node_stop">shutdown node</string> + <string name="full_node_restart">restart node</string> + <string name="startup_node">Startup node</string> </resources>