diff --git a/AndroidManifest.xml b/AndroidManifest.xml index e35d020ee..eaf0d1b66 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -31,6 +31,7 @@ + Delta Chat requires the Microphone permission in order to send audio messages, but it has been permanently denied. Please continue to app settings, select \"Permissions\", and enable \"Microphone\". Delta Chat requires the Storage permission in order to attach or export photos, videos, or audio, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Storage\". Delta Chat requires Location permission in order to attach a location, but it has been permanently denied. Please continue to the app settings menu, select \"Permissions\", and enable \"Location\". + Tap here to receive messages while Delta Chat is in background. + Delta Chat uses minimal resources and takes care not to drain your battery. Belgian diff --git a/src/org/thoughtcrime/securesms/ConversationListFragment.java b/src/org/thoughtcrime/securesms/ConversationListFragment.java index a5e778a5a..49cb885fc 100644 --- a/src/org/thoughtcrime/securesms/ConversationListFragment.java +++ b/src/org/thoughtcrime/securesms/ConversationListFragment.java @@ -58,6 +58,7 @@ import com.b44t.messenger.DcEventCenter; import org.thoughtcrime.securesms.ConversationListAdapter.ItemClickListener; import org.thoughtcrime.securesms.components.recyclerview.DeleteItemAnimator; import org.thoughtcrime.securesms.components.registration.PulsingFloatingActionButton; +import org.thoughtcrime.securesms.components.reminder.DozeReminder; import org.thoughtcrime.securesms.components.reminder.Reminder; import org.thoughtcrime.securesms.components.reminder.ReminderView; import org.thoughtcrime.securesms.connect.ApplicationDcContext; @@ -186,11 +187,13 @@ public class ConversationListFragment extends Fragment // return Optional.of(new ExpiredBuildReminder(context)); // } else if (OutdatedBuildReminder.isEligible()) { // return Optional.of(new OutdatedBuildReminder(context)); -// } else if (DozeReminder.isEligible(context)) { -// return Optional.of(new DozeReminder(context)); -// } else { - return Optional.absent(); -// } +// } else + if (DozeReminder.isEligible(context)) { + return Optional.of(new DozeReminder(context)); + } + else { + return Optional.absent(); + } } @Override diff --git a/src/org/thoughtcrime/securesms/LogViewFragment.java b/src/org/thoughtcrime/securesms/LogViewFragment.java index 3d51a3460..cd67b41dc 100644 --- a/src/org/thoughtcrime/securesms/LogViewFragment.java +++ b/src/org/thoughtcrime/securesms/LogViewFragment.java @@ -26,6 +26,7 @@ import android.os.Build; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.PowerManager; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.util.Log; @@ -160,21 +161,24 @@ public class LogViewFragment extends Fragment { } private static String buildDescription(Context context) { + + PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); final PackageManager pm = context.getPackageManager(); final StringBuilder builder = new StringBuilder(); - builder.append("Device : ") + builder.append("device=") .append(Build.MANUFACTURER).append(" ") .append(Build.MODEL).append(" (") .append(Build.PRODUCT).append(")\n"); - builder.append("Android : ").append(VERSION.RELEASE).append(" (") + builder.append("android=").append(VERSION.RELEASE).append(" (") .append(VERSION.INCREMENTAL).append(", ") .append(Build.DISPLAY).append(")\n"); - builder.append("Memory : ").append(getMemoryUsage(context)).append("\n"); - builder.append("Memclass: ").append(getMemoryClass(context)).append("\n"); - builder.append("OS Host : ").append(Build.HOST).append("\n"); - builder.append("App : "); + builder.append("sdk=").append(Build.VERSION.SDK_INT).append("\n"); + builder.append("memory=").append(getMemoryUsage(context)).append("\n"); + builder.append("memoryClass=").append(getMemoryClass(context)).append("\n"); + builder.append("host=").append(Build.HOST).append("\n"); + builder.append("app="); try { builder.append(pm.getApplicationLabel(pm.getApplicationInfo(context.getPackageName(), 0))) .append(" ") @@ -183,7 +187,11 @@ public class LogViewFragment extends Fragment { .append(BuildConfig.FLAVOR) .append(BuildConfig.DEBUG? "-debug" : "") .append("\n"); - } catch (PackageManager.NameNotFoundException nnfe) { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + builder.append("ignoreBatteryOptimizations=").append( + powerManager.isIgnoringBatteryOptimizations(context.getPackageName())).append("\n"); + } + } catch (Exception e) { builder.append("Unknown\n"); } diff --git a/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java b/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java index 909465354..9efc77e0a 100644 --- a/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java +++ b/src/org/thoughtcrime/securesms/components/reminder/DozeReminder.java @@ -1,15 +1,18 @@ package org.thoughtcrime.securesms.components.reminder; +import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.PowerManager; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.annotation.RequiresApi; +import android.support.v4.content.ContextCompat; import android.view.View; import org.thoughtcrime.securesms.R; @@ -20,32 +23,44 @@ public class DozeReminder extends Reminder { @RequiresApi(api = Build.VERSION_CODES.M) public DozeReminder(@NonNull final Context context) { - super("R.string.DozeReminder_optimize_for_missing_play_services", - "R.string.DozeReminder_this_device_does_not_support_play_services_tap_to_disable_system_battery"); + super(context.getString(R.string.perm_enable_bg_reminder_title), + context.getString(R.string.perm_enable_bg_reminder_text)); setOkListener(v -> { - Prefs.setPromptedOptimizeDoze(context, true); - Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, - Uri.parse("package:" + context.getPackageName())); - context.startActivity(intent); + if(ContextCompat.checkSelfPermission(context, Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)==PackageManager.PERMISSION_GRANTED) { + Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, + Uri.parse("package:" + context.getPackageName())); + context.startActivity(intent); + } + else { + Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); + context.startActivity(intent); + } }); - setDismissListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Prefs.setPromptedOptimizeDoze(context, true); - } + setDismissListener(v -> { + Prefs. setPromptedOptimizeDoze(context, true); }); } public static boolean isEligible(Context context) { - if(context!=null) { // condition is always true, disable reminder for now + if(context==null) { return false; } - return !Prefs.hasPromptedOptimizeDoze(context) && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - !((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isIgnoringBatteryOptimizations(context.getPackageName()); - } + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return false; + } + if(Prefs.hasPromptedOptimizeDoze(context)) { + return false; + } + + PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + if(pm.isIgnoringBatteryOptimizations(context.getPackageName())) { + return false; + } + + return true; // yip, asking for disabling battery optimisations makes sense + } } diff --git a/src/org/thoughtcrime/securesms/connect/KeepAliveService.java b/src/org/thoughtcrime/securesms/connect/KeepAliveService.java index c00f2e26b..02860b687 100644 --- a/src/org/thoughtcrime/securesms/connect/KeepAliveService.java +++ b/src/org/thoughtcrime/securesms/connect/KeepAliveService.java @@ -1,12 +1,16 @@ package org.thoughtcrime.securesms.connect; +import android.annotation.TargetApi; import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.IBinder; +import android.os.PowerManager; import android.support.v4.app.NotificationCompat; import android.util.Log; @@ -20,6 +24,18 @@ public class KeepAliveService extends Service { public static void startSelf(Context context) { try { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + if(powerManager.isIgnoringBatteryOptimizations(context.getPackageName())) { + return; // fine, the user has disabled the battery optimisations for us + } + } + else { + return; // android <= lollipop does not have a doze mode + } + + // if we're here, we're on a system with doze mode and battery optimisations enabled. + // add foreground notification to stay alive. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // the started service has to call startForeground() within 5 seconds, // see https://developer.android.com/about/versions/oreo/android-8.0-changes @@ -93,7 +109,25 @@ public class KeepAliveService extends Service { } builder.setWhen(0); builder.setContentIntent(contentIntent); - builder.setSmallIcon(R.drawable.icon_notification); + builder.setSmallIcon(R.drawable.notification_permanent); + if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) { + createFgNotificationChannel(this); + builder.setChannelId(FG_CHANNEL_ID); + } return builder.build(); } + + private static boolean ch_created = false; + private static final String FG_CHANNEL_ID = "dc_foreground_notification_ch"; + @TargetApi(Build.VERSION_CODES.O) + static private void createFgNotificationChannel(Context context) { + if(!ch_created) { + ch_created = true; + NotificationChannel channel = new NotificationChannel(FG_CHANNEL_ID, + "Receive messages in background.", NotificationManager.IMPORTANCE_MIN); // IMPORTANCE_DEFAULT will play a sound + channel.setDescription("Ensure reliable message receiving."); + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + } + } } diff --git a/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java index d13a0b6b8..71fb00a37 100644 --- a/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java +++ b/src/org/thoughtcrime/securesms/notifications/AbstractNotificationBuilder.java @@ -1,9 +1,13 @@ package org.thoughtcrime.securesms.notifications; +import android.annotation.TargetApi; import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.content.Context; import android.graphics.Color; import android.net.Uri; +import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; @@ -86,4 +90,18 @@ public abstract class AbstractNotificationBuilder extends NotificationCompat.Bui return blinkPattern.split(","); } + + private static boolean ch_created = false; + protected static final String MSG_CHANNEL_ID = "dc_message_notification_ch"; + @TargetApi(Build.VERSION_CODES.O) + protected void createMsgNotificationChannel(Context context) { + if(!ch_created) { + ch_created = true; + NotificationChannel channel = new NotificationChannel(MSG_CHANNEL_ID, + "Message notifications", NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription("Informs about new messages."); + NotificationManager notificationManager = context.getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + } + } } diff --git a/src/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java index 077efe658..ffdca5fa4 100644 --- a/src/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java +++ b/src/org/thoughtcrime/securesms/notifications/MultipleRecipientNotificationBuilder.java @@ -4,6 +4,7 @@ import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; @@ -41,6 +42,10 @@ public class MultipleRecipientNotificationBuilder extends AbstractNotificationBu setColor(context.getResources().getColor(R.color.delta_primary)); setSmallIcon(R.drawable.icon_notification); + if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) { + createMsgNotificationChannel(context); + setChannelId(MSG_CHANNEL_ID); + } setContentTitle(context.getString(R.string.app_name)); setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0)); setCategory(NotificationCompat.CATEGORY_MESSAGE); diff --git a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java index 55fcf32f0..0fedd1c30 100644 --- a/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java +++ b/src/org/thoughtcrime/securesms/notifications/SingleRecipientNotificationBuilder.java @@ -51,6 +51,10 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil super(context, privacy); setSmallIcon(R.drawable.icon_notification); + if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) { + createMsgNotificationChannel(context); + setChannelId(MSG_CHANNEL_ID); + } setColor(context.getResources().getColor(R.color.delta_primary)); setPriority(Prefs.getNotificationPriority(context)); setCategory(NotificationCompat.CATEGORY_MESSAGE);