Request the user to ignore the battery optimizations which avoid running Delta Chat properly.

This commit is contained in:
B. Petersen
2017-04-07 19:36:05 +02:00
parent b838e0d69e
commit b941b9e21a
9 changed files with 146 additions and 30 deletions
@@ -203,20 +203,7 @@ public class ApplicationLoader extends Application {
UserConfig.loadConfig();
// create a timer that wakes up the CPU from time to time
try
{
Intent intent = new Intent(applicationContext, TimerReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(applicationContext, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager)applicationContext.getSystemService(Activity.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime()+60*1000,
60*1000,
alarmIntent);
}
catch( Exception e) {
Log.e("DeltaChat", "Cannot create alarm.");
}
TimerReceiver.scheduleNextAlarm();
// make sure, the notifications for the "deaddrop" dialog are muted by default
SharedPreferences notificationPreferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE);
@@ -273,6 +260,6 @@ public class ApplicationLoader extends Application {
public static void stayAwakeForAMoment()
{
stayAwakeWakeLock.acquire(10*60*1000); // 10 Minutes to wait for "after chat" messages, after that, we sleep most time, see wakeupWakeLock
stayAwakeWakeLock.acquire(1*60*1000); // 1 Minute to wait for "after chat" messages, after that, we sleep most time, see wakeupWakeLock
}
}
@@ -22,18 +22,49 @@
package com.b44t.messenger;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
public class TimerReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
// acquire for 3 seconds, this should wake up the threads,
// _if_ there is more to do, the backend acquires an additional wakelock using MR_EVENT_WAKE_LOCK
ApplicationLoader.wakeupWakeLock.acquire(3*1000); // do this first!
Log.i("DeltaChat", "*** TimerReceiver.onReceive()");
// acquire for 5 seconds, this should wake un the threads,
// _if_ there is more to do, the backend acquires an additional wakelock using MR_EVENT_WAKE_LOCK
ApplicationLoader.wakeupWakeLock.acquire(5*1000);
// we assume, the IMAP thread is alive. I cannot imagine, the thread was killed with the App being running.
// (if the whole App was killed, the IMAP thread is already started by MrMailbox.connect() if we're here)
// (the thread itself will reconnect to the IMAP server as needed)
// create the next alarm in about a minute
scheduleNextAlarm();
}
public static void scheduleNextAlarm()
{
try {
Intent intent = new Intent(ApplicationLoader.applicationContext, TimerReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(ApplicationLoader.applicationContext, 0, intent, 0);
long triggerAtMillis = System.currentTimeMillis() + 60 * 1000;
AlarmManager alarmManager = (AlarmManager) ApplicationLoader.applicationContext.getSystemService(Activity.ALARM_SERVICE);
if( Build.VERSION.SDK_INT >= 23 ) {
// a simple AlarmManager.set() is no longer send in the new DOZE mode
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, alarmIntent);
}
else {
alarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, alarmIntent);
}
}
catch(Exception e) { Log.e("DeltaChat", "Cannot create alarm.", e); }
}
}
@@ -60,7 +60,8 @@ public class BaseFragment {
RC13_AVATAR_IMAGE_CAPTURE = 13,
RC14_AVATAR_GET_CONTENT = 14,
RC21_CHAT_PICK_WO_COMPR = 21,
RC500_PHOTO_VIEW = 500;
RC500_PHOTO_VIEW = 500,
RC600_BATTERY_REQUEST_DONE = 600;
public BaseFragment() {
classGuid = ApplicationLoader.generateClassGuid();
@@ -32,11 +32,16 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Outline;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -53,6 +58,7 @@ import android.widget.TextView;
import com.b44t.messenger.AndroidUtilities;
import com.b44t.messenger.ApplicationLoader;
import com.b44t.messenger.FileLog;
import com.b44t.messenger.ImageLoader;
import com.b44t.messenger.LocaleController;
import com.b44t.messenger.MrChat;
@@ -516,30 +522,107 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter.
dialogsSearchAdapter.notifyDataSetChanged();
}
if (checkPermission && !onlySelect && Build.VERSION.SDK_INT >= 23) {
Activity activity = getParentActivity();
if (activity != null) {
checkPermission = false;
if (activity.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
askForPermissons();
checkPermission = false;
askForIgnoreBatteryOptimization(); // after that, requestForOtherPermissions() is called
}
}
@TargetApi(Build.VERSION_CODES.M)
private void askForIgnoreBatteryOptimization() {
boolean requestIgnoreActivityStarted = false;
try {
String packageName = ApplicationLoader.applicationContext.getPackageName();
PowerManager pm = (PowerManager) ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
// As FCM (was: GCM) does not support IMAP servers and there are other server involved,
// the only possibility for us to get notified about new messages, is to keep the connection to the server alive.
// This is done by ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS.
//
// This is an "Acceptable Use Cases for Whitelisting", see
// https://developer.android.com/training/monitoring-device-state/doze-standby.html#whitelisting-cases
// "Instant messaging, chat, or calling app; enterprise VOIP apps |
// No, can't use FCM because of technical dependency on another messaging service or Doze and App Standby break the core function of the app |
// Acceptable"
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
getParentActivity().startActivityForResult(intent, BaseFragment.RC600_BATTERY_REQUEST_DONE);
requestIgnoreActivityStarted = true;
}
} catch (Exception e) {
Log.e("DeltaChat", "cannot ignore battery optimizations.", e);
}
if( !requestIgnoreActivityStarted ) {
askForOtherPermissons(); // otherwise, it gets started on RC600_BATTERY_REQUEST_DONE
}
}
@Override
public void onActivityResultFragment(int requestCode, int resultCode, Intent data) {
if (requestCode == BaseFragment.RC600_BATTERY_REQUEST_DONE) {
boolean requestIgnoreActivityMaybeRestarted = false;
if( Build.VERSION.SDK_INT >= 23 ) {
PowerManager pm = (PowerManager) ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(ApplicationLoader.applicationContext.getPackageName())) {
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
builder.setMessage(AndroidUtilities.replaceTags(ApplicationLoader.applicationContext.getString(R.string.PermissionBattery)));
builder.setPositiveButton(ApplicationLoader.applicationContext.getString(R.string.OK), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
askForIgnoreBatteryOptimization();
}
});
builder.setCancelable(false);
builder.setNegativeButton(ApplicationLoader.applicationContext.getString(R.string.Cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
askForOtherPermissons();
}
});
builder.show();
requestIgnoreActivityMaybeRestarted = true;
/*-- this is an alternative implementation to the alert above
IF we use this, we should handle the situation, ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS cannot be started due to a missing REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission
checkPermission = true;
getParentActivity().finish();
return;
*/
}
}
if( !requestIgnoreActivityMaybeRestarted ) {
askForOtherPermissons();
}
}
}
@TargetApi(Build.VERSION_CODES.M)
private void askForPermissons() {
private void askForOtherPermissons() {
Activity activity = getParentActivity();
if (activity == null) {
return;
}
if (activity.checkSelfPermission(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED
&& activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
return; // everything is fine
}
ArrayList<String> permissons = new ArrayList<>();
if (activity.checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
permissons.add(Manifest.permission.READ_CONTACTS);
}
if (activity.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissons.add(Manifest.permission.READ_EXTERNAL_STORAGE);
permissons.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
String[] items = permissons.toArray(new String[permissons.size()]);
activity.requestPermissions(items, LaunchActivity.REQ_CONTACT_N_STORAGE_PERMISON_ID);
}
@@ -31,6 +31,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.PowerManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
@@ -177,7 +178,7 @@ public class SettingsActivity extends BaseFragment {
private String getAndroidInfo()
{
String abi = "ErrAbi";
int versionCode = 0;
int versionCode = 0, ignoreBatteryOptimizations = -1;
try {
PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0);
versionCode = pInfo.versionCode;
@@ -195,6 +196,10 @@ public class SettingsActivity extends BaseFragment {
abi = "universal";
break;
}
if( Build.VERSION.SDK_INT >= 23 ) {
PowerManager pm = (PowerManager) ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE);
ignoreBatteryOptimizations = pm.isIgnoringBatteryOptimizations(ApplicationLoader.applicationContext.getPackageName())? 1 : 0;
}
} catch (Exception e) {}
return "SDK_INT=" + Build.VERSION.SDK_INT
@@ -202,6 +207,7 @@ public class SettingsActivity extends BaseFragment {
+ "\nMODEL=" + Build.MODEL
+ "\nDEBUG_VERSION=" + BuildVars.DEBUG_VERSION
+ "\nABI=" + abi // ABI = Application Binary Interface
+ "\nignoreBatteryOptimizations=" + ignoreBatteryOptimizations
+ "\nversionCode=" + versionCode;
}