Refactored export/import notification channels

pull/156/head
M66B 5 years ago
parent de37f8aabf
commit 183bb1b7a9

@ -19,6 +19,10 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@ -31,6 +35,8 @@ import android.content.res.AssetFileDescriptor;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -42,6 +48,7 @@ import android.view.View;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.documentfile.provider.DocumentFile;
@ -602,6 +609,7 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
Log.i("Collecting data");
DB db = DB.getInstance(context);
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// Accounts
JSONArray jaccounts = new JSONArray();
@ -609,6 +617,18 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
// Account
JSONObject jaccount = account.toJSON();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (account.notify) {
NotificationChannel channel = nm.getNotificationChannel(
EntityAccount.getNotificationChannelId(account.id));
if (channel != null && channel.getImportance() != NotificationManager.IMPORTANCE_NONE) {
JSONObject jchannel = channelToJSON(channel);
jaccount.put("channel", jchannel);
Log.i("Exported account channel=" + jchannel);
}
}
}
// Identities
JSONArray jidentities = new JSONArray();
for (EntityIdentity identity : db.identity().getIdentities(account.id))
@ -619,10 +639,22 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
JSONArray jfolders = new JSONArray();
for (EntityFolder folder : db.folder().getFolders(account.id)) {
JSONObject jfolder = folder.toJSON();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = nm.getNotificationChannel(
EntityFolder.getNotificationChannelId(folder.id));
if (channel != null && channel.getImportance() != NotificationManager.IMPORTANCE_NONE) {
JSONObject jchannel = channelToJSON(channel);
jfolder.put("channel", jchannel);
Log.i("Exported folder channel=" + jchannel);
}
}
JSONArray jrules = new JSONArray();
for (EntityRule rule : db.rule().getRules(folder.id))
jrules.put(rule.toJSON());
jfolder.put("rules", jrules);
jfolders.put(jfolder);
}
jaccount.put("folders", jfolders);
@ -656,8 +688,20 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
jexport.put("accounts", jaccounts);
jexport.put("answers", janswers);
jexport.put("settings", jsettings);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
jexport.put("channels", ApplicationEx.channelsToJSON(context));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
JSONArray jchannels = new JSONArray();
for (NotificationChannel channel : nm.getNotificationChannels()) {
String id = channel.getId();
if (id.startsWith("notification.") && id.contains("@") &&
channel.getImportance() != NotificationManager.IMPORTANCE_NONE) {
JSONObject jchannel = channelToJSON(channel);
jchannels.put(jchannel);
Log.i("Exported contact channel=" + jchannel);
}
}
jexport.put("channels", jchannels);
}
ContentResolver resolver = context.getContentResolver();
DocumentFile file = DocumentFile.fromSingleUri(context, uri);
@ -761,6 +805,7 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
JSONObject jimport = new JSONObject(data.toString());
DB db = DB.getInstance(context);
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
try {
db.beginTransaction();
@ -795,9 +840,21 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
account.id = db.account().insertAccount(account);
Log.i("Imported account=" + account.name);
account.deleteNotificationChannel(context);
if (account.notify)
account.createNotificationChannel(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
account.deleteNotificationChannel(context);
if (account.notify)
if (jaccount.has("channel")) {
NotificationChannelGroup group = new NotificationChannelGroup(account.name, account.name);
nm.createNotificationChannelGroup(group);
JSONObject jchannel = (JSONObject) jaccount.get("channel");
jchannel.put("id", EntityAccount.getNotificationChannelId(account.id));
nm.createNotificationChannel(channelFromJSON(context, jchannel));
Log.i("Imported account channel=" + jchannel);
} else
account.createNotificationChannel(context);
}
Map<Long, Long> xIdentity = new HashMap<>();
JSONArray jidentities = (JSONArray) jaccount.get("identities");
@ -833,6 +890,19 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
if (Objects.equals(swipe_right, id))
account.swipe_right = folder.id;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (jfolder.has("channel")) {
NotificationChannelGroup group = new NotificationChannelGroup(account.name, account.name);
nm.createNotificationChannelGroup(group);
JSONObject jchannel = (JSONObject) jfolder.get("channel");
jchannel.put("id", EntityFolder.getNotificationChannelId(folder.id));
nm.createNotificationChannel(channelFromJSON(context, jchannel));
Log.i("Imported folder channel=" + jchannel);
}
}
if (jfolder.has("rules")) {
JSONArray jrules = jfolder.getJSONArray("rules");
for (int r = 0; r < jrules.length(); r++) {
@ -916,11 +986,17 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
editor.apply();
ApplicationEx.upgrade(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (jimport.has("channels")) {
JSONArray jchannels = jimport.getJSONArray("channels");
ApplicationEx.channelsFromJSON(context, jchannels);
for (int i = 0; i < jchannels.length(); i++) {
JSONObject jchannel = (JSONObject) jchannels.get(i);
nm.createNotificationChannel(channelFromJSON(context, jchannel));
Log.i("Imported contact channel=" + jchannel);
}
}
}
db.setTransactionSuccessful();
} finally {
@ -950,6 +1026,63 @@ public class ActivitySetup extends ActivityBilling implements FragmentManager.On
}.execute(this, args, "setup:import");
}
@RequiresApi(api = Build.VERSION_CODES.O)
private JSONObject channelToJSON(NotificationChannel channel) throws JSONException {
JSONObject jchannel = new JSONObject();
jchannel.put("id", channel.getId());
jchannel.put("group", channel.getGroup());
jchannel.put("name", channel.getName());
jchannel.put("description", channel.getDescription());
jchannel.put("importance", channel.getImportance());
jchannel.put("dnd", channel.canBypassDnd());
jchannel.put("visibility", channel.getLockscreenVisibility());
jchannel.put("badge", channel.canShowBadge());
Uri sound = channel.getSound();
if (sound != null)
jchannel.put("sound", sound.toString());
// audio attributes
jchannel.put("light", channel.shouldShowLights());
// color
jchannel.put("vibrate", channel.shouldVibrate());
// pattern
return jchannel;
}
@RequiresApi(api = Build.VERSION_CODES.O)
static NotificationChannel channelFromJSON(Context context, JSONObject jchannel) throws JSONException {
NotificationChannel channel = new NotificationChannel(
jchannel.getString("id"),
jchannel.getString("name"),
jchannel.getInt("importance"));
channel.setGroup(jchannel.getString("group"));
if (jchannel.has("description") && !jchannel.isNull("description"))
channel.setDescription(jchannel.getString("description"));
channel.setBypassDnd(jchannel.getBoolean("dnd"));
channel.setLockscreenVisibility(jchannel.getInt("visibility"));
channel.setShowBadge(jchannel.getBoolean("badge"));
if (jchannel.has("sound") && !jchannel.isNull("sound")) {
Uri uri = Uri.parse(jchannel.getString("sound"));
Ringtone ringtone = RingtoneManager.getRingtone(context, uri);
if (ringtone != null)
channel.setSound(uri, Notification.AUDIO_ATTRIBUTES_DEFAULT);
}
channel.enableLights(jchannel.getBoolean("light"));
channel.enableVibration(jchannel.getBoolean("vibrate"));
return channel;
}
private void onEditAccount(Intent intent) {
FragmentAccount fragment = new FragmentAccount();
fragment.setArguments(intent.getExtras());

@ -19,7 +19,6 @@ package eu.faircode.email;
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
@ -43,6 +42,7 @@ import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.PopupMenu;
import androidx.lifecycle.LifecycleOwner;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@ -410,15 +410,18 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
return true;
case R.string.title_create_channel:
onActionCreateChannel();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
onActionCreateChannel();
return true;
case R.string.title_edit_channel:
onActionEditChannel();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
onActionEditChannel();
return true;
case R.string.title_delete_channel:
onActionDeleteChannel();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
onActionDeleteChannel();
return true;
default:
@ -622,6 +625,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
.putExtra("id", folder.id));
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void onActionCreateChannel() {
if (!Helper.isPro(context)) {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
@ -633,7 +637,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
onActionEditChannel();
}
@TargetApi(Build.VERSION_CODES.O)
@RequiresApi(api = Build.VERSION_CODES.O)
private void onActionEditChannel() {
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName())
@ -641,6 +645,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
context.startActivity(intent);
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void onActionDeleteChannel() {
folder.deleteNotificationChannel(context);
}

@ -28,19 +28,14 @@ import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.DeadSystemException;
import android.os.Handler;
import android.os.RemoteException;
import android.text.TextUtils;
import android.view.OrientationEventListener;
import android.webkit.CookieManager;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.preference.PreferenceManager;
import com.bugsnag.android.BeforeNotify;
@ -51,18 +46,12 @@ import com.bugsnag.android.Error;
import com.bugsnag.android.Report;
import com.sun.mail.iap.ProtocolException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@ -74,10 +63,6 @@ import javax.mail.MessagingException;
public class ApplicationEx extends Application {
private Thread.UncaughtExceptionHandler prev = null;
private static final List<String> DEFAULT_CHANNEL_NAMES = Collections.unmodifiableList(Arrays.asList(
"service", "notification", "warning", "error"
));
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(getLocalizedContext(base));
@ -334,89 +319,6 @@ public class ApplicationEx extends Application {
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
static JSONArray channelsToJSON(Context context) throws JSONException {
JSONArray jchannels = new JSONArray();
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
for (NotificationChannel channel : nm.getNotificationChannels())
if (!DEFAULT_CHANNEL_NAMES.contains(channel.getId())) {
JSONObject jchannel = new JSONObject();
jchannel.put("id", channel.getId());
jchannel.put("group", channel.getGroup());
jchannel.put("name", channel.getName());
jchannel.put("description", channel.getDescription());
jchannel.put("importance", channel.getImportance());
jchannel.put("dnd", channel.canBypassDnd());
jchannel.put("visibility", channel.getLockscreenVisibility());
jchannel.put("badge", channel.canShowBadge());
Uri sound = channel.getSound();
if (sound != null)
jchannel.put("sound", sound.toString());
// audio attributes
jchannel.put("light", channel.shouldShowLights());
// color
jchannel.put("vibrate", channel.shouldVibrate());
// pattern
jchannels.put(jchannel);
}
return jchannels;
}
@RequiresApi(api = Build.VERSION_CODES.O)
static void channelsFromJSON(Context context, JSONArray jchannels) throws JSONException {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
for (int c = 0; c < jchannels.length(); c++) {
JSONObject jchannel = (JSONObject) jchannels.get(c);
// Legacy
if (!jchannel.has("group") ||
jchannel.isNull("group") ||
TextUtils.isEmpty(jchannel.getString("group")))
continue;
String id = jchannel.getString("id");
if (nm.getNotificationChannel(id) == null) {
NotificationChannel channel = new NotificationChannel(
id,
jchannel.getString("name"),
jchannel.getInt("importance"));
String groupName = jchannel.getString("group");
NotificationChannelGroup group = new NotificationChannelGroup(groupName, groupName);
nm.createNotificationChannelGroup(group);
channel.setGroup(groupName);
if (jchannel.has("description") && !jchannel.isNull("description"))
channel.setDescription(jchannel.getString("description"));
channel.setBypassDnd(jchannel.getBoolean("dnd"));
channel.setLockscreenVisibility(jchannel.getInt("visibility"));
channel.setShowBadge(jchannel.getBoolean("badge"));
if (jchannel.has("sound") && !jchannel.isNull("sound")) {
Uri uri = Uri.parse(jchannel.getString("sound"));
Ringtone ringtone = RingtoneManager.getRingtone(context, uri);
if (ringtone != null)
channel.setSound(uri, Notification.AUDIO_ATTRIBUTES_DEFAULT);
}
channel.enableLights(jchannel.getBoolean("light"));
channel.enableVibration(jchannel.getBoolean("vibrate"));
Log.i("Creating channel=" + channel);
nm.createNotificationChannel(channel);
}
}
}
public boolean ownFault(Throwable ex) {
if (ex instanceof OutOfMemoryError)
return false;

@ -24,8 +24,10 @@ import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@ -104,28 +106,26 @@ public class EntityAccount extends EntityOrder implements Serializable {
return "notification" + (id == 0 ? "" : "." + id);
}
@RequiresApi(api = Build.VERSION_CODES.O)
void createNotificationChannel(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannelGroup group = new NotificationChannelGroup(name, name);
nm.createNotificationChannelGroup(group);
NotificationChannel channel = new NotificationChannel(
getNotificationChannelId(id), name,
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup(name);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
channel.enableLights(true);
nm.createNotificationChannel(channel);
}
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannelGroup group = new NotificationChannelGroup(name, name);
nm.createNotificationChannelGroup(group);
NotificationChannel channel = new NotificationChannel(
getNotificationChannelId(id), name,
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup(name);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
channel.enableLights(true);
nm.createNotificationChannel(channel);
}
@RequiresApi(api = Build.VERSION_CODES.O)
void deleteNotificationChannel(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.deleteNotificationChannel(getNotificationChannelId(id));
}
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.deleteNotificationChannel(getNotificationChannelId(id));
}
@Override

@ -943,12 +943,14 @@ public class FragmentAccount extends FragmentBase {
EntityLog.log(context, (update ? "Updated" : "Added") + " account=" + account.name);
// Make sure the channel exists on commit
if (account.notify) {
// Add or update notification channel
account.deleteNotificationChannel(context);
account.createNotificationChannel(context);
} else if (!account.synchronize)
account.deleteNotificationChannel(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (account.notify) {
// Add or update notification channel
account.deleteNotificationChannel(context);
account.createNotificationChannel(context);
} else if (!account.synchronize)
account.deleteNotificationChannel(context);
}
List<EntityFolder> folders = new ArrayList<>();

@ -464,10 +464,12 @@ public class ServiceSynchronize extends LifecycleService {
// Start monitoring accounts
List<EntityAccount> accounts = db.account().getSynchronizingAccounts();
for (final EntityAccount account : accounts) {
if (account.notify)
account.createNotificationChannel(ServiceSynchronize.this);
else
account.deleteNotificationChannel(ServiceSynchronize.this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (account.notify)
account.createNotificationChannel(ServiceSynchronize.this);
else
account.deleteNotificationChannel(ServiceSynchronize.this);
}
Log.i(account.host + "/" + account.user + " run");
final Core.State astate = new Core.State(state);

@ -24,7 +24,9 @@ import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import androidx.annotation.RequiresApi;
import androidx.room.Ignore;
import java.io.Serializable;
@ -72,28 +74,26 @@ public class TupleFolderEx extends EntityFolder implements Serializable {
return false;
}
@RequiresApi(api = Build.VERSION_CODES.O)
void createNotificationChannel(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannelGroup group = new NotificationChannelGroup(accountName, accountName);
nm.createNotificationChannelGroup(group);
NotificationChannel channel = new NotificationChannel(
getNotificationChannelId(id), getDisplayName(context),
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup(accountName);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
channel.enableLights(true);
nm.createNotificationChannel(channel);
}
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannelGroup group = new NotificationChannelGroup(accountName, accountName);
nm.createNotificationChannelGroup(group);
NotificationChannel channel = new NotificationChannel(
getNotificationChannelId(id), getDisplayName(context),
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup(accountName);
channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
channel.enableLights(true);
nm.createNotificationChannel(channel);
}
@RequiresApi(api = Build.VERSION_CODES.O)
void deleteNotificationChannel(Context context) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.deleteNotificationChannel(getNotificationChannelId(id));
}
NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
nm.deleteNotificationChannel(getNotificationChannelId(id));
}
@Override

Loading…
Cancel
Save