Prepare notification permissions

pull/208/head
M66B 3 years ago
parent 5b668cb5d0
commit 45b5afca61

@ -6,6 +6,7 @@
* Added searching for text in draft
* Added OAuth for Gmail POP3 accounts
* Android 13 compatibility (notification permissions)
* Small improvements and minor bug fixes
* Updated translations

@ -353,6 +353,7 @@ The following Android permissions are **required**:
* *prevent device from sleeping* (WAKE_LOCK): to keep the device awake while performing actions, like synchronization of messages
* *use fingerprint hardware* (USE_FINGERPRINT) and *use biometric hardware* (USE_BIOMETRIC): to use biometric authentication (fingerprint, face unlock, etc)
* *ask to ingore battery optimizations* (REQUEST_IGNORE_BATTERY_OPTIMIZATIONS): to disable battery optimizations, please see [this FAQ](#user-content-faq175) for more information
* *allow the app to show notifications* (POST_NOTIFICATIONS): to show new message notifications and (account) warnings and errors (Android 13 and later only)
* *Google Play (in-app) billing service* (BILLING): for in-app purchases
<br />

@ -23,7 +23,7 @@ android {
defaultConfig {
applicationId "eu.faircode.email"
minSdkVersion 21
targetSdkVersion 32
targetSdkVersion 33
versionCode getVersionCode()
versionName "1." + getVersionCode()
archivesBaseName = "FairEmail-v$versionName" + getRevision()

@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="com.android.vending.BILLING" />

@ -6,6 +6,7 @@
* Added searching for text in draft
* Added OAuth for Gmail POP3 accounts
* Android 13 compatibility (notification permissions)
* Small improvements and minor bug fixes
* Updated translations

@ -1638,9 +1638,10 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
try {
NotificationManager nm =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NotificationHelper.NOTIFICATION_UPDATE,
builder.build());
Helper.getSystemService(ActivityView.this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_UPDATE,
builder.build());
} catch (Throwable ex) {
Log.w(ex);
}

@ -720,9 +720,10 @@ class Core {
if (title != null) {
NotificationCompat.Builder builder =
getNotificationError(context, "warning", account, message.id, new Throwable(title, ex));
nm.notify(op.name + ":" + op.message,
NotificationHelper.NOTIFICATION_TAGGED,
builder.build());
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(op.name + ":" + op.message,
NotificationHelper.NOTIFICATION_TAGGED,
builder.build());
}
}
@ -5134,7 +5135,8 @@ class Core {
: " channel=" + notification.getChannelId()) +
" sort=" + notification.getSortKey());
try {
nm.notify(tag, NotificationHelper.NOTIFICATION_TAGGED, notification);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(tag, NotificationHelper.NOTIFICATION_TAGGED, notification);
// https://github.com/leolin310148/ShortcutBadger/wiki/Xiaomi-Device-Support
if (id == 0 && badge && Helper.isXiaomi())
ShortcutBadger.applyNotification(context, notification, current);

@ -1245,7 +1245,8 @@ public class FragmentFolders extends FragmentBase {
builder.setProgress(ids.size(), i, false);
Notification notification = builder.build();
notification.flags |= Notification.FLAG_NO_CLEAR;
nm.notify("export", NotificationHelper.NOTIFICATION_TAGGED, notification);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify("export", NotificationHelper.NOTIFICATION_TAGGED, notification);
}
long id = ids.get(i);

@ -622,7 +622,7 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
swCheckWeekly.setEnabled(checked);
if (!checked) {
NotificationManager nm =
(NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
Helper.getSystemService(getContext(), NotificationManager.class);
nm.cancel(NotificationHelper.NOTIFICATION_UPDATE);
}
}
@ -1550,7 +1550,8 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
if (!Helper.isPlayStoreInstall() &&
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
NotificationManager nm =
Helper.getSystemService(getContext(), NotificationManager.class);
NotificationChannel notification = nm.getNotificationChannel("update");
if (notification != null) {

@ -62,6 +62,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.widget.Group;
import androidx.core.os.BuildCompat;
import androidx.core.view.MenuCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Lifecycle;
@ -69,6 +70,7 @@ import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
import java.util.ArrayList;
import java.util.List;
public class FragmentSetup extends FragmentBase {
@ -470,8 +472,11 @@ public class FragmentSetup extends FragmentBase {
public void onClick(View view) {
try {
btnPermissions.setEnabled(false);
String permission = Manifest.permission.READ_CONTACTS;
requestPermissions(new String[]{permission}, REQUEST_PERMISSIONS);
List<String> requesting = new ArrayList<>();
for (String permission : getDesiredPermissions())
if (!hasPermission(permission))
requesting.add((permission));
requestPermissions(requesting.toArray(new String[0]), REQUEST_PERMISSIONS);
} catch (Throwable ex) {
Log.unexpectedError(getParentFragmentManager(), ex);
/*
@ -695,7 +700,7 @@ public class FragmentSetup extends FragmentBase {
grpDataSaver.setVisibility(View.GONE);
tvStamina.setVisibility(View.GONE);
setContactsPermission(hasPermission(Manifest.permission.READ_CONTACTS));
setGrantedPermissions();
return view;
}
@ -959,20 +964,38 @@ public class FragmentSetup extends FragmentBase {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
for (int i = 0; i < permissions.length; i++)
if (Manifest.permission.READ_CONTACTS.equals(permissions[i]))
setContactsPermission(grantResults[i] == PackageManager.PERMISSION_GRANTED);
setGrantedPermissions();
}
private void setContactsPermission(boolean granted) {
if (granted)
private List<String> getDesiredPermissions() {
List<String> permissions = new ArrayList<>();
permissions.add(Manifest.permission.READ_CONTACTS);
if (BuildCompat.isAtLeastT())
permissions.add(Manifest.permission.POST_NOTIFICATIONS);
return permissions;
}
private void setGrantedPermissions() {
List<String> granted = new ArrayList<>();
for (String permission : getDesiredPermissions())
if (hasPermission(permission))
granted.add(permission);
if (granted.contains(Manifest.permission.READ_CONTACTS))
ContactInfo.init(getContext().getApplicationContext());
tvPermissionsDone.setText(granted ? R.string.title_setup_done : R.string.title_setup_to_do);
tvPermissionsDone.setTextColor(granted ? textColorPrimary : colorWarning);
tvPermissionsDone.setTypeface(null, granted ? Typeface.NORMAL : Typeface.BOLD);
tvPermissionsDone.setCompoundDrawablesWithIntrinsicBounds(granted ? check : null, null, null, null);
btnPermissions.setEnabled(!granted);
boolean all = true;
for (String permission : getDesiredPermissions())
if (!granted.contains(permission)) {
all = false;
break;
}
tvPermissionsDone.setText(all ? R.string.title_setup_done : R.string.title_setup_to_do);
tvPermissionsDone.setTextColor(all ? textColorPrimary : colorWarning);
tvPermissionsDone.setTypeface(null, all ? Typeface.NORMAL : Typeface.BOLD);
tvPermissionsDone.setCompoundDrawablesWithIntrinsicBounds(all ? check : null, null, null, null);
btnPermissions.setEnabled(!all);
}
private void onDeleteAccount(Bundle args) {

@ -149,6 +149,13 @@ class NotificationHelper {
nm.createNotificationChannelGroup(group);
}
static boolean areNotificationsEnabled(NotificationManager nm) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
return true;
else
return nm.areNotificationsEnabled();
}
@RequiresApi(api = Build.VERSION_CODES.O)
static void clear(Context context) {
NotificationManager nm = Helper.getSystemService(context, NotificationManager.class);

@ -107,8 +107,10 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
EntityLog.log(ServiceSend.this, "Unsent=" + (unsent == null ? null : unsent.count));
try {
NotificationManager nm = Helper.getSystemService(ServiceSend.this, NotificationManager.class);
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
NotificationManager nm =
Helper.getSystemService(ServiceSend.this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
} catch (Throwable ex) {
Log.w(ex);
}
@ -331,8 +333,10 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
EntityLog.log(ServiceSend.this, "Service send suitable=" + suitable);
try {
NotificationManager nm = Helper.getSystemService(ServiceSend.this, NotificationManager.class);
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
NotificationManager nm =
Helper.getSystemService(ServiceSend.this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
} catch (Throwable ex) {
Log.w(ex);
}
@ -430,10 +434,11 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
try {
int tries_left = (unrecoverable ? 0 : RETRY_MAX - op.tries);
NotificationManager nm = Helper.getSystemService(this, NotificationManager.class);
nm.notify("send:" + message.id,
NotificationHelper.NOTIFICATION_TAGGED,
getNotificationError(
MessageHelper.formatAddressesShort(message.to), ex, tries_left).build());
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify("send:" + message.id,
NotificationHelper.NOTIFICATION_TAGGED,
getNotificationError(
MessageHelper.formatAddressesShort(message.to), ex, tries_left).build());
} catch (Throwable ex1) {
Log.w(ex1);
}
@ -517,7 +522,8 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
}
NotificationManager nm = Helper.getSystemService(this, NotificationManager.class);
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(true));
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(true));
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean reply_move = prefs.getBoolean("reply_move", false);
@ -731,7 +737,8 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
if (now > last + PROGRESS_UPDATE_INTERVAL) {
last = now;
lastProgress = progress;
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
}
}
}
@ -772,7 +779,8 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
iservice.close();
if (lastProgress >= 0) {
lastProgress = -1;
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_SEND, getNotificationService(false));
}
db.identity().setIdentityState(ident.id, null);
}

@ -419,9 +419,11 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
if (!isBackgroundService(ServiceSynchronize.this))
try {
NotificationManager nm = Helper.getSystemService(ServiceSynchronize.this, NotificationManager.class);
nm.notify(NotificationHelper.NOTIFICATION_SYNCHRONIZE,
getNotificationService(lastAccounts, lastOperations));
NotificationManager nm =
Helper.getSystemService(ServiceSynchronize.this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_SYNCHRONIZE,
getNotificationService(lastAccounts, lastOperations));
} catch (Throwable ex) {
/*
java.lang.NullPointerException: Attempt to invoke interface method 'java.util.Iterator java.lang.Iterable.iterator()' on a null object reference
@ -1478,10 +1480,12 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
if (!ConnectionHelper.isMaxConnections(message))
try {
NotificationManager nm = Helper.getSystemService(ServiceSynchronize.this, NotificationManager.class);
nm.notify("alert:" + account.id,
NotificationHelper.NOTIFICATION_TAGGED,
getNotificationAlert(account, message).build());
NotificationManager nm =
Helper.getSystemService(ServiceSynchronize.this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify("alert:" + account.id,
NotificationHelper.NOTIFICATION_TAGGED,
getNotificationAlert(account, message).build());
} catch (Throwable ex) {
Log.w(ex);
}
@ -1541,11 +1545,13 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
try {
state.setBackoff(2 * CONNECT_BACKOFF_ALARM_MAX * 60);
NotificationManager nm = Helper.getSystemService(this, NotificationManager.class);
nm.notify("receive:" + account.id,
NotificationHelper.NOTIFICATION_TAGGED,
Core.getNotificationError(this, "error", account, 0, ex)
.build());
NotificationManager nm =
Helper.getSystemService(this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify("receive:" + account.id,
NotificationHelper.NOTIFICATION_TAGGED,
Core.getNotificationError(this, "error", account, 0, ex)
.build());
} catch (Throwable ex1) {
Log.w(ex1);
}
@ -2339,11 +2345,13 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
Helper.getDateTimeInstance(this, DateFormat.SHORT, DateFormat.SHORT)
.format(account.last_connected)), ex);
try {
NotificationManager nm = Helper.getSystemService(this, NotificationManager.class);
nm.notify("receive:" + account.id,
NotificationHelper.NOTIFICATION_TAGGED,
Core.getNotificationError(this, "warning", account, 0, warning)
.build());
NotificationManager nm =
Helper.getSystemService(this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify("receive:" + account.id,
NotificationHelper.NOTIFICATION_TAGGED,
Core.getNotificationError(this, "warning", account, 0, warning)
.build());
} catch (Throwable ex1) {
Log.w(ex1);
}
@ -2787,9 +2795,11 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
if (!isBackgroundService(ServiceSynchronize.this))
try {
NotificationManager nm = Helper.getSystemService(ServiceSynchronize.this, NotificationManager.class);
nm.notify(NotificationHelper.NOTIFICATION_SYNCHRONIZE,
getNotificationService(lastAccounts, lastOperations));
NotificationManager nm =
Helper.getSystemService(ServiceSynchronize.this, NotificationManager.class);
if (NotificationHelper.areNotificationsEnabled(nm))
nm.notify(NotificationHelper.NOTIFICATION_SYNCHRONIZE,
getNotificationService(lastAccounts, lastOperations));
} catch (Throwable ex) {
Log.w(ex);
}

@ -6,6 +6,7 @@
* Added searching for text in draft
* Added OAuth for Gmail POP3 accounts
* Android 13 compatibility (notification permissions)
* Small improvements and minor bug fixes
* Updated translations

Loading…
Cancel
Save