Replace android.util.Log with logback

This commit is contained in:
José Rebelo
2026-02-11 22:11:26 +00:00
parent 68008f0205
commit 03805df13c
37 changed files with 368 additions and 423 deletions
@@ -48,7 +48,6 @@ import android.os.Bundle;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.provider.ContactsContract.PhoneLookup;
import android.util.Log;
import android.util.TypedValue;
import android.view.Window;
import android.view.WindowManager;
@@ -60,17 +59,16 @@ import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.LoggerFactory;
import androidx.lifecycle.ProcessLifecycleOwner;
import java.io.File;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import ch.qos.logback.core.spi.LifeCycle;
import nodomain.freeyourgadget.gadgetbridge.activities.ControlCenterv2;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
@@ -87,7 +85,6 @@ import nodomain.freeyourgadget.gadgetbridge.prefs.GBPrefsMigrator;
import nodomain.freeyourgadget.gadgetbridge.service.NotificationCollectorMonitorService;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
import nodomain.freeyourgadget.gadgetbridge.util.BondingUtil;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.InternetHelperSingleton;
@@ -103,9 +100,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.preferences.DevicePrefs;
* logging and DB access.
*/
public class GBApplication extends Application {
// Since this class must not log to slf4j, we use plain android.util.Log
private static final String TAG = "GBApplication";
private static volatile ShutdownHook SHUTDOWN_HOOK;
private static final Logger LOG = LoggerFactory.getLogger(GBApplication.class);
private static GBApplication context;
private static DeviceService deviceService;
@@ -128,17 +123,6 @@ public class GBApplication extends Application {
private static GBApplication app;
private static final Logging logging = new Logging() {
@Override
protected String createLogDirectory() throws IOException {
if (GBEnvironment.env().isLocalTest()) {
return System.getProperty(Logging.PROP_LOGFILES_DIR);
} else {
File dir = FileUtils.getExternalFilesDir();
return dir.getAbsolutePath();
}
}
};
private static Locale language;
private DeviceManager deviceManager;
@@ -146,25 +130,8 @@ public class GBApplication extends Application {
private OpenTracksContentObserver openTracksObserver;
/// flush the log buffer and stop file logging
private static final class ShutdownHook implements Runnable {
@Override
public void run() {
try {
logging.stopFileLogger();
} catch (Throwable ignored) {
}
try {
LifeCycle lifeCycle = (LifeCycle) LoggerFactory.getILoggerFactory();
lifeCycle.stop();
} catch (Throwable ignored) {
}
}
}
public static void quit() {
GB.log("Quitting Gadgetbridge...", GB.INFO, null);
LOG.info("Quitting Gadgetbridge...");
BondingUtil.StopObservingAll(getContext());
Intent quitIntent = new Intent(GBApplication.ACTION_QUIT);
LocalBroadcastManager.getInstance(context).sendBroadcast(quitIntent);
@@ -174,7 +141,7 @@ public class GBApplication extends Application {
}
public static void restart() {
GB.log("Restarting Gadgetbridge...", GB.INFO, null);
LOG.info("Restarting Gadgetbridge...");
BondingUtil.StopObservingAll(getContext());
final Intent quitIntent = new Intent(GBApplication.ACTION_QUIT);
LocalBroadcastManager.getInstance(context).sendBroadcast(quitIntent);
@@ -244,10 +211,6 @@ public class GBApplication extends Application {
}
}
public static Logging getLogging() {
return logging;
}
protected DeviceService createDeviceService() {
return new GBDeviceService(this);
}
@@ -274,9 +237,7 @@ public class GBApplication extends Application {
GBDatabaseManager.setupDatabase(this);
}
// don't do anything here before we set up logging, otherwise
// slf4j may be implicitly initialized before we properly configured it.
setupLogging(isFileLoggingEnabled());
Logging.getInstance().initialize(prefs.getBoolean("log_to_file", false));
migratePrefsIfNeeded();
@@ -324,7 +285,7 @@ public class GBApplication extends Application {
if (!prefs.getBoolean("prefs_key_enable_deprecated_notificationcollectormonitor", false)) {
return;
}
Log.i(TAG, "Starting the deprecated NotificationCollectorMonitorService foreground service.");
LOG.info("Starting the deprecated NotificationCollectorMonitorService foreground service.");
try {
//the following will ensure the notification manager is kept alive
Intent serviceIntent = new Intent(context, NotificationCollectorMonitorService.class);
@@ -379,40 +340,11 @@ public class GBApplication extends Application {
return false;
}
public static void setupLogging(boolean enabled) {
logging.setupLogging(enabled);
// prepare for log shutdown
if(SHUTDOWN_HOOK == null) {
//noinspection NonThreadSafeLazyInitialization
SHUTDOWN_HOOK = new ShutdownHook();
Thread thread = new Thread(SHUTDOWN_HOOK, "shutdownHook");
Runtime runtime = Runtime.getRuntime();
runtime.addShutdownHook(thread);
}
}
public static String getLogPath() {
String path = logging.getLogPath();
if (path == null) {
// file logging is currently disabled but there still might be an old logfile
try {
path = logging.createLogDirectory() + File.separator + "gadgetbridge.log";
} catch (Exception ignored) {
}
}
return path;
}
private void setupExceptionHandler(final boolean notifyOnCrash) {
final GBExceptionHandler handler = new GBExceptionHandler(Thread.getDefaultUncaughtExceptionHandler(), notifyOnCrash);
Thread.setDefaultUncaughtExceptionHandler(handler);
}
public static boolean isFileLoggingEnabled() {
return prefs.getBoolean("log_to_file", false);
}
public static boolean minimizeNotification() {
return prefs.getBoolean("minimize_priority", false);
}
@@ -536,7 +468,7 @@ public class GBApplication extends Application {
public static boolean appIsNotifBlacklisted(String packageName) {
if (apps_notification_blacklist == null) {
GB.log("appIsNotifBlacklisted: apps_notification_blacklist is null!", GB.INFO, null);
LOG.info("appIsNotifBlacklisted: apps_notification_blacklist is null!");
}
return apps_notification_blacklist != null && apps_notification_blacklist.contains(packageName);
}
@@ -547,22 +479,22 @@ public class GBApplication extends Application {
public static void setAppsNotifBlackList(Set<String> packageNames, SharedPreferences.Editor editor) {
if (packageNames == null) {
GB.log("Set null apps_notification_blacklist", GB.INFO, null);
LOG.info("Set null apps_notification_blacklist");
apps_notification_blacklist = new HashSet<>();
} else {
apps_notification_blacklist = new HashSet<>(packageNames);
}
GB.log("New apps_notification_blacklist has " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
LOG.info("New apps_notification_blacklist has {} entries", apps_notification_blacklist.size());
saveAppsNotifBlackList(editor);
}
private static void loadAppsNotifBlackList() {
GB.log("Loading apps_notification_blacklist", GB.INFO, null);
LOG.info("Loading apps_notification_blacklist");
apps_notification_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_BLACKLIST, null); // lgtm [java/abstract-to-concrete-cast]
if (apps_notification_blacklist == null) {
apps_notification_blacklist = new HashSet<>();
}
GB.log("Loaded apps_notification_blacklist has " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
LOG.info("Loaded apps_notification_blacklist has {} entries", apps_notification_blacklist.size());
}
private static void saveAppsNotifBlackList() {
@@ -570,7 +502,7 @@ public class GBApplication extends Application {
}
private static void saveAppsNotifBlackList(SharedPreferences.Editor editor) {
GB.log("Saving apps_notification_blacklist with " + apps_notification_blacklist.size() + " entries", GB.INFO, null);
LOG.info("Saving apps_notification_blacklist with {} entries", apps_notification_blacklist.size());
if (apps_notification_blacklist.isEmpty()) {
editor.putStringSet(GBPrefs.PACKAGE_BLACKLIST, null);
} else {
@@ -586,7 +518,7 @@ public class GBApplication extends Application {
}
public static synchronized void removeFromAppsNotifBlacklist(String packageName) {
GB.log("Removing from apps_notification_blacklist: " + packageName, GB.INFO, null);
LOG.info("Removing from apps_notification_blacklist: {}", packageName);
apps_notification_blacklist.remove(packageName);
saveAppsNotifBlackList();
}
@@ -595,7 +527,7 @@ public class GBApplication extends Application {
public static boolean appIsPebbleBlacklisted(String sender) {
if (apps_pebblemsg_blacklist == null) {
GB.log("appIsPebbleBlacklisted: apps_pebblemsg_blacklist is null!", GB.INFO, null);
LOG.info("appIsPebbleBlacklisted: apps_pebblemsg_blacklist is null!");
}
return apps_pebblemsg_blacklist != null && apps_pebblemsg_blacklist.contains(sender);
}
@@ -606,22 +538,22 @@ public class GBApplication extends Application {
public static void setAppsPebbleBlackList(Set<String> packageNames, SharedPreferences.Editor editor) {
if (packageNames == null) {
GB.log("Set null apps_pebblemsg_blacklist", GB.INFO, null);
LOG.info("Set null apps_pebblemsg_blacklist");
apps_pebblemsg_blacklist = new HashSet<>();
} else {
apps_pebblemsg_blacklist = new HashSet<>(packageNames);
}
GB.log("New apps_pebblemsg_blacklist has " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
LOG.info("New apps_pebblemsg_blacklist has {} entries", apps_pebblemsg_blacklist.size());
saveAppsPebbleBlackList(editor);
}
private static void loadAppsPebbleBlackList() {
GB.log("Loading apps_pebblemsg_blacklist", GB.INFO, null);
LOG.info("Loading apps_pebblemsg_blacklist");
apps_pebblemsg_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, null); // lgtm [java/abstract-to-concrete-cast]
if (apps_pebblemsg_blacklist == null) {
apps_pebblemsg_blacklist = new HashSet<>();
}
GB.log("Loaded apps_pebblemsg_blacklist has " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
LOG.info("Loaded apps_pebblemsg_blacklist has {} entries", apps_pebblemsg_blacklist.size());
}
private static void saveAppsPebbleBlackList() {
@@ -629,7 +561,7 @@ public class GBApplication extends Application {
}
private static void saveAppsPebbleBlackList(SharedPreferences.Editor editor) {
GB.log("Saving apps_pebblemsg_blacklist with " + apps_pebblemsg_blacklist.size() + " entries", GB.INFO, null);
LOG.info("Saving apps_pebblemsg_blacklist with {} entries", apps_pebblemsg_blacklist.size());
if (apps_pebblemsg_blacklist.isEmpty()) {
editor.putStringSet(GBPrefs.PACKAGE_PEBBLEMSG_BLACKLIST, null);
} else {
@@ -645,7 +577,7 @@ public class GBApplication extends Application {
}
public static synchronized void removeFromAppsPebbleBlacklist(String packageName) {
GB.log("Removing from apps_pebblemsg_blacklist: " + packageName, GB.INFO, null);
LOG.info("Removing from apps_pebblemsg_blacklist: {}", packageName);
apps_pebblemsg_blacklist.remove(packageNameToPebbleMsgSender(packageName));
saveAppsPebbleBlackList();
}
@@ -802,7 +734,7 @@ public class GBApplication extends Application {
try {
return getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA).versionName;
} catch (PackageManager.NameNotFoundException e) {
GB.log("Unable to determine Gadgetbridge's version", GB.WARN, e);
LOG.warn("Unable to determine Gadgetbridge's version", e);
return "0.0.0";
}
}
@@ -813,7 +745,7 @@ public class GBApplication extends Application {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_META_DATA);
return String.format("%s %s", appInfo.name, packageInfo.versionName);
} catch (PackageManager.NameNotFoundException e) {
GB.log("Unable to determine Gadgetbridge's name/version", GB.WARN, e);
LOG.warn("Unable to determine Gadgetbridge's name/version", e);
return "Gadgetbridge";
}
}
@@ -831,7 +763,7 @@ public class GBApplication extends Application {
public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {
boolean preventScreenshots = getPrefs().getBoolean(GBPrefs.BLOCK_SCREENSHOTS, false);
if (preventScreenshots) {
GB.log("set FLAG_SECURE for " + activity.getLocalClassName(), GB.DEBUG, null);
LOG.debug("set FLAG_SECURE for {}", activity.getLocalClassName());
Window window = activity.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
@@ -3,7 +3,9 @@ package nodomain.freeyourgadget.gadgetbridge;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
@@ -19,7 +21,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
* This class is NOT thread-safe, all calls should be guarded by the upstream write lock.
*/
public class GBDatabase {
private static final String TAG = "GBDatabase";
private static final Logger LOG = LoggerFactory.getLogger(GBDatabase.class);
public static final String DATABASE_NAME = "Gadgetbridge";
private DaoMaster daoMaster = null;
@@ -40,25 +43,25 @@ public class GBDatabase {
void closeDatabase() {
if (session == null) {
Log.w(TAG, "Database was already closed");
LOG.warn("Database was already closed");
return;
}
Log.d(TAG, "Trying to close database from " + Thread.currentThread().getName());
LOG.debug("Trying to close database");
session.clear();
session.getDatabase().close();
session = null;
daoMaster = null;
helper = null;
Log.d(TAG, "Database closed");
LOG.info("Database closed");
}
void setupDatabase(final Context context) {
if (session != null) {
Log.w(TAG, "Database already setup");
LOG.warn("Database already setup");
return;
}
Log.i(TAG, "Setting up database from " + Thread.currentThread().getName());
LOG.debug("Setting up database");
if (GBEnvironment.env().isTest()) {
helper = new DaoMaster.DevOpenHelper(context, null, null);
@@ -71,7 +74,7 @@ public class GBDatabase {
if (session == null) {
throw new RuntimeException("Unable to create database session");
}
Log.d(TAG, "Database setup finished");
LOG.info("Database setup finished");
}
void importDB(final InputStream inputStream) throws IllegalStateException, IOException {
@@ -1,7 +1,9 @@
package nodomain.freeyourgadget.gadgetbridge;
import android.content.Context;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
@@ -15,7 +17,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
public class GBDatabaseManager {
private static final String TAG = "GBDatabaseManager";
private static final Logger LOG = LoggerFactory.getLogger(GBDatabaseManager.class);
private static final ReentrantReadWriteLock DB_LOCK = new ReentrantReadWriteLock(true);
private static final GBDatabase GB_DATABASE = new GBDatabase();
@@ -24,7 +26,7 @@ public class GBDatabaseManager {
}
public static void closeDatabase() {
Log.v(TAG, "Trying to close database from " + Thread.currentThread().getName());
LOG.trace("Trying to close database");
DB_LOCK.writeLock().lock();
try {
GB_DATABASE.closeDatabase();
@@ -34,7 +36,7 @@ public class GBDatabaseManager {
}
public static void setupDatabase(final Context context) {
Log.v(TAG, "Setting up database from " + Thread.currentThread().getName());
LOG.trace("Setting up database");
DB_LOCK.writeLock().lock();
try {
GB_DATABASE.setupDatabase(context);
@@ -56,26 +58,26 @@ public class GBDatabaseManager {
*/
public static DBHandler acquireWrite() throws GBException {
try {
Log.v(TAG, "Trying to acquire write lock from " + Thread.currentThread().getName());
LOG.trace("Trying to acquire write lock");
if (DB_LOCK.writeLock().tryLock(30, TimeUnit.SECONDS)) {
Log.v(TAG, "Acquired write lock from " + Thread.currentThread().getName());
LOG.trace("Acquired write lock");
return new LockHandler(DB_LOCK.writeLock(), GB_DATABASE.getDaoMaster(), GB_DATABASE.getSession());
}
} catch (final InterruptedException e) {
Log.e(TAG, "Interrupted while waiting for DB lock");
LOG.error("Interrupted while waiting for write DB lock", e);
}
throw new GBException("Failed to acquire database write lock");
}
public static DBHandler acquireReadOnly() throws GBException {
try {
Log.v(TAG, "Trying to acquire read lock from " + Thread.currentThread().getName());
LOG.trace("Trying to acquire read lock");
if (DB_LOCK.readLock().tryLock(30, TimeUnit.SECONDS)) {
Log.v(TAG, "Acquired read lock from " + Thread.currentThread().getName());
LOG.trace("Acquired read lock");
return new LockHandler(DB_LOCK.readLock(), GB_DATABASE.getDaoMaster(), GB_DATABASE.getSession());
}
} catch (final InterruptedException e) {
Log.e(TAG, "Interrupted while waiting for DB lock");
LOG.error("Interrupted while waiting for read DB lock", e);
}
throw new GBException("Failed to acquire database read lock");
}
@@ -86,7 +88,7 @@ public class GBDatabaseManager {
* @return true on successful deletion
*/
public static boolean deleteActivityDatabase(final Context context) {
Log.v(TAG, "Deleting activity database from " + Thread.currentThread().getName());
LOG.trace("Deleting activity database");
DB_LOCK.writeLock().lock();
try {
@@ -101,7 +103,7 @@ public class GBDatabaseManager {
}
public static void exportDB(final File destFile) throws IOException {
Log.v(TAG, "Exporting database to file from " + Thread.currentThread().getName());
LOG.trace("Exporting database to file");
DB_LOCK.writeLock().lock();
try {
@@ -112,7 +114,7 @@ public class GBDatabaseManager {
}
public static void exportDB(final OutputStream dest) throws IOException {
Log.v(TAG, "Exporting database to OutputStream from " + Thread.currentThread().getName());
LOG.trace("Exporting database to OutputStream");
DB_LOCK.writeLock().lock();
try {
@@ -128,7 +130,7 @@ public class GBDatabaseManager {
* @return true on successful deletion
*/
public static boolean deleteOldActivityDatabase(final Context context) {
Log.v(TAG, "Deleting old activity database from " + Thread.currentThread().getName());
LOG.trace("Deleting old activity database");
final DBHelper dbHelper = new DBHelper(context);
boolean result = true;
@@ -143,7 +145,7 @@ public class GBDatabaseManager {
}
public static void importDB(final InputStream inputStream) throws IllegalStateException, IOException {
Log.v(TAG, "Importing database from " + Thread.currentThread().getName());
LOG.trace("Importing database");
DB_LOCK.writeLock().lock();
try {
@@ -39,6 +39,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB;
/**
* Catches otherwise uncaught exceptions, logs them and terminates the app.
*/
@SuppressWarnings("ClassCanBeRecord")
public class GBExceptionHandler implements Thread.UncaughtExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(GBExceptionHandler.class);
private final Thread.UncaughtExceptionHandler mDelegate;
@@ -50,7 +51,7 @@ public class GBExceptionHandler implements Thread.UncaughtExceptionHandler {
}
/// Log and notify the unhandled exception
/// Flushing and closing the log is handled by {@link GBApplication.ShutdownHook}
/// Flushing and closing the log is handled by {@link Logging} shutdown hook
@Override
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable ex) {
// This method is only called if something is seriously wrong so be very generous
@@ -17,7 +17,9 @@
package nodomain.freeyourgadget.gadgetbridge;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.locks.Lock;
@@ -29,7 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
* Provides low-level access to the database.
*/
public class LockHandler implements DBHandler {
private static final String TAG = "LockHandler";
private static final Logger LOG = LoggerFactory.getLogger(LockHandler.class);
private final Lock lock;
@@ -55,11 +57,11 @@ public class LockHandler implements DBHandler {
@Override
public void close() {
if (closed) {
Log.w(TAG, lock.getClass().getSimpleName() + " was already closed (" + Thread.currentThread().getName() + ")");
LOG.warn("{} was already closed", lock.getClass().getSimpleName());
return;
}
closed = true;
Log.d(TAG, "Releasing " + lock.getClass().getSimpleName() + " from " + Thread.currentThread().getName());
LOG.trace("Releasing {}", lock.getClass().getSimpleName());
lock.unlock();
}
@@ -18,10 +18,10 @@
package nodomain.freeyourgadget.gadgetbridge;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,19 +36,39 @@ import ch.qos.logback.core.Appender;
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.spi.LifeCycle;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.StatusPrinter;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
public abstract class Logging {
// Only used for tests
public static final String PROP_LOGFILES_DIR = "GB_LOGFILES_DIR";
public class Logging {
private static final Logger LOG = LoggerFactory.getLogger(Logging.class);
private static final Logging INSTANCE = new Logging();
private Logging() {
}
private String logDirectory;
private FileAppender<ILoggingEvent> fileLogger;
private boolean initialized = false;
public void setupLogging(boolean enable) {
public static Logging getInstance() {
return INSTANCE;
}
public void initialize(final boolean enable) {
setFileLoggingEnabled(enable);
// prepare for log shutdown
if (!initialized) {
final Thread thread = new Thread(this::shutdown, "shutdownHook");
final Runtime runtime = Runtime.getRuntime();
runtime.addShutdownHook(thread);
initialized = true;
}
}
public void setFileLoggingEnabled(final boolean enable) {
try {
if (!isFileLoggerInitialized()) {
init();
@@ -58,29 +78,43 @@ public abstract class Logging {
} else {
stopFileLogger();
}
getLogger().info("Gadgetbridge version: {}-{} {} {}", BuildConfig.VERSION_NAME,
LOG.info("Gadgetbridge version: {}-{} {} {}", BuildConfig.VERSION_NAME,
BuildConfig.GIT_HASH_SHORT, BuildConfig.FLAVOR, BuildConfig.BUILD_TYPE);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.BAKLAVA) {
getLogger().info(
LOG.info(
"Android: SDK_INT={} SECURITY_PATCH={}",
Build.VERSION.SDK_INT,
Build.VERSION.SECURITY_PATCH
);
} else {
getLogger().info(
LOG.info(
"Android: SDK_INT={} SDK_INT_FULL={} SECURITY_PATCH={}",
Build.VERSION.SDK_INT,
Build.VERSION.SDK_INT_FULL,
Build.VERSION.SECURITY_PATCH
);
}
} catch (Exception ex) {
Log.e("GBApplication", "External files dir not available, cannot log to file", ex);
} catch (final Exception ex) {
LOG.error("External files dir not available, cannot log to file", ex);
stopFileLogger();
}
}
/// flush the log buffer and stop file logging
public void shutdown() {
try {
stopFileLogger();
} catch (Throwable ignored) {
}
try {
LifeCycle lifeCycle = (LifeCycle) LoggerFactory.getILoggerFactory();
lifeCycle.stop();
} catch (Throwable ignored) {
}
}
@Nullable
public String getLogPath() {
if (fileLogger != null)
@@ -89,9 +123,12 @@ public abstract class Logging {
return null;
}
public void setImmediateFlush(final boolean immediateFlush) {
if (fileLogger != null) {
public void flush() {
if (fileLogger != null && !fileLogger.isImmediateFlush()) {
fileLogger.setImmediateFlush(true);
// Write something so it's actually flushed
LOG.debug("Flushing logs");
fileLogger.setImmediateFlush(false);
}
}
@@ -107,38 +144,36 @@ public abstract class Logging {
// Logger logger = LoggerFactory.getLogger(Logging.class);
}
protected abstract String createLogDirectory() throws IOException;
protected String createLogDirectory() throws IOException {
return FileUtils.getExternalFilesDir().getAbsolutePath();
}
protected void init() throws IOException {
Log.i("GBApplication", "Initializing logging");
private void init() throws IOException {
LOG.debug("Initializing logging");
logDirectory = createLogDirectory();
if (logDirectory == null) {
throw new IllegalArgumentException("log directory must not be null");
}
}
private Logger getLogger() {
return LoggerFactory.getLogger(Logging.class);
}
private void startFileLogger() {
if (fileLogger != null) {
Log.w("GBApplication", "Logger already started");
LOG.warn("Logger already started");
return;
}
if (logDirectory == null) {
Log.e("GBApplication", "Can't start file logging without a log directory");
LOG.error("Can't start file logging without a log directory");
return;
}
final FileAppender fileAppender = createFileAppender(logDirectory);
final FileAppender<ILoggingEvent> fileAppender = createFileAppender(logDirectory);
fileAppender.start();
attachLogger(fileAppender);
fileLogger = fileAppender;
}
void stopFileLogger() {
public void stopFileLogger() {
if (fileLogger == null) {
return;
}
@@ -152,34 +187,35 @@ public abstract class Logging {
fileLogger = null;
}
private void attachLogger(Appender<ILoggingEvent> logger) {
private static void attachLogger(final Appender<ILoggingEvent> logger) {
try {
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
if (!root.isAttached(logger)) {
root.addAppender(logger);
}
} catch (Throwable ex) {
Log.e("GBApplication", "Error attaching logger appender", ex);
} catch (final Throwable e) {
LOG.error("Error attaching logger appender", e);
}
}
private void detachLogger(Appender<ILoggingEvent> logger) {
private static void detachLogger(final Appender<ILoggingEvent> logger) {
try {
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
if (logger != null && root.isAttached(logger)) {
root.detachAppender(logger);
}
} catch (Throwable ex) {
Log.e("GBApplication", "Error detaching logger appender", ex);
} catch (final Throwable e) {
LOG.error("Error detaching logger appender", e);
}
}
@VisibleForTesting
public FileAppender<ILoggingEvent> getFileLogger() {
return fileLogger;
}
@NonNull
public static String formatBytes(@Nullable byte[] bytes) {
public static String formatBytes(@Nullable final byte[] bytes) {
if (bytes == null) {
return "(null)";
} else if (bytes.length == 0) {
@@ -194,13 +230,7 @@ public abstract class Logging {
return builder.toString();
}
public static void logBytes(Logger logger, byte[] value) {
if (value != null) {
logger.warn("DATA: " + GB.hexdump(value, 0, value.length));
}
}
public static FileAppender createFileAppender(final String logDirectory) {
private static FileAppender<ILoggingEvent> createFileAppender(final String logDirectory) {
final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
final PatternLayoutEncoder ple = new PatternLayoutEncoder();
@@ -209,8 +239,8 @@ public abstract class Logging {
ple.setContext(lc);
ple.start();
final SizeAndTimeBasedRollingPolicy rollingPolicy = new SizeAndTimeBasedRollingPolicy();
final RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<ILoggingEvent>();
final SizeAndTimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new SizeAndTimeBasedRollingPolicy<>();
final RollingFileAppender<ILoggingEvent> fileAppender = new RollingFileAppender<>();
rollingPolicy.setContext(lc);
rollingPolicy.setFileNamePattern(logDirectory + "/gadgetbridge-%d{yyyy-MM-dd}.%i.log.zip");
@@ -39,11 +39,14 @@ import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreference;
import androidx.preference.SwitchPreferenceCompat;
import com.bytehamster.lib.preferencesearch.SearchPreferenceResult;
import com.google.android.material.color.DynamicColors;
@@ -60,6 +63,7 @@ import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.automations.AutomationsSettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.charts.ChartsPreferencesActivity;
@@ -160,15 +164,15 @@ public class SettingsActivity extends AbstractSettingsActivityV2 {
});
}
pref = findPreference("log_to_file");
if (pref != null) {
pref.setOnPreferenceChangeListener((preference, newVal) -> {
final SwitchPreferenceCompat logToFilePreference = findPreference("log_to_file");
if (logToFilePreference != null) {
logToFilePreference.setOnPreferenceChangeListener((preference, newVal) -> {
boolean doEnable = Boolean.TRUE.equals(newVal);
try {
if (doEnable) {
FileUtils.getExternalFilesDir(); // ensures that it is created
}
GBApplication.setupLogging(doEnable);
Logging.getInstance().setFileLoggingEnabled(doEnable);
} catch (IOException ex) {
GB.toast(requireContext().getApplicationContext(),
getString(R.string.error_creating_directory_for_logfiles, ex.getLocalizedMessage()),
@@ -179,10 +183,24 @@ public class SettingsActivity extends AbstractSettingsActivityV2 {
return true;
});
// If we didn't manage to initialize file logging, disable the preference
if (!GBApplication.getLogging().isFileLoggerInitialized()) {
pref.setEnabled(false);
pref.setSummary(R.string.pref_write_logfiles_not_available);
// If we didn't manage to initialize file logging, disable the preference and show the button to initialize again
if (!Logging.getInstance().isFileLoggerInitialized()) {
logToFilePreference.setEnabled(false);
logToFilePreference.setSummary(R.string.pref_write_logfiles_not_available);
final Preference logRestart = findPreference("log_restart");
if (logRestart != null) {
logRestart.setVisible(true);
logRestart.setOnPreferenceClickListener(preference -> {
Logging.getInstance().setFileLoggingEnabled(logToFilePreference.isChecked());
if (Logging.getInstance().isFileLoggerInitialized()) {
logToFilePreference.setEnabled(true);
logToFilePreference.setSummary(null);
logRestart.setVisible(false);
}
return true;
});
}
}
}
@@ -10,6 +10,8 @@ import nodomain.freeyourgadget.gadgetbridge.GBDatabaseManager
import nodomain.freeyourgadget.gadgetbridge.R
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper
import nodomain.freeyourgadget.gadgetbridge.util.GB
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class DatabaseDebugFragment : AbstractDebugFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -59,7 +61,7 @@ class DatabaseDebugFragment : AbstractDebugFragment() {
tableNames.sortWith(Comparator { o1, o2 -> o1.compareTo(o2, true) })
}
} catch (e: Exception) {
GB.log("Error accessing database", GB.ERROR, e)
LOG.error("Error accessing database", e)
}
for (tableName in tableNames) {
@@ -142,11 +144,13 @@ class DatabaseDebugFragment : AbstractDebugFragment() {
db.database.execSQL("DROP TABLE IF EXISTS TABLE_NAME_TO_DROP_HERE")
}
} catch (e: Exception) {
GB.log("Error accessing database", GB.ERROR, e)
LOG.error("Error accessing database", e)
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger(DatabaseDebugFragment::class.java)
private const val PREF_DEBUG_DATABASE_VERSION = "pref_debug_database_version"
private const val DANGEROUS_ACTIONS = "dangerous_actions"
private const val PREF_DEBUG_DELETE_OLD_DATABASE = "pref_debug_delete_old_database"
@@ -111,7 +111,7 @@ class DatabaseTableDebugFragment : AbstractDebugFragment() {
}
}
} catch (e: Exception) {
GB.log("Error accessing database", GB.ERROR, e)
LOG.error("Error accessing database", e)
}
}
@@ -8,15 +8,15 @@ import androidx.core.content.FileProvider
import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import nodomain.freeyourgadget.gadgetbridge.GBApplication
import nodomain.freeyourgadget.gadgetbridge.Logging
import nodomain.freeyourgadget.gadgetbridge.R
import nodomain.freeyourgadget.gadgetbridge.activities.CameraActivity
import nodomain.freeyourgadget.gadgetbridge.activities.PermissionsActivity
import nodomain.freeyourgadget.gadgetbridge.activities.welcome.WelcomeActivity
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCameraRemote
import nodomain.freeyourgadget.gadgetbridge.util.DeviceTypeDialog
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils
import nodomain.freeyourgadget.gadgetbridge.util.GB
import nodomain.freeyourgadget.gadgetbridge.util.DeviceTypeDialog
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
@@ -24,8 +24,11 @@ import java.io.IOException
import java.lang.Boolean
import kotlin.Any
import kotlin.String
import kotlin.Suppress
import kotlin.apply
import kotlin.let
@Suppress("unused")
class MainDebugFragment : AbstractDebugFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.debug_preferences_main, rootKey)
@@ -37,14 +40,15 @@ class MainDebugFragment : AbstractDebugFragment() {
}
onClick(PREF_DEBUG_SHARE_LOGS) { showLogSharingWarning() }
findPreference<SwitchPreferenceCompat>(LOG_TO_FILE)?.let {
val logToFilePreference = findPreference<SwitchPreferenceCompat>(LOG_TO_FILE)
logToFilePreference?.let {
it.setOnPreferenceChangeListener { _: Preference?, newVal: Any? ->
val doEnable = Boolean.TRUE == newVal
try {
if (doEnable) {
FileUtils.getExternalFilesDir() // ensures that it is created
}
GBApplication.setupLogging(doEnable)
Logging.getInstance().setFileLoggingEnabled(doEnable)
} catch (ex: IOException) {
GB.toast(
requireContext().applicationContext,
@@ -58,10 +62,29 @@ class MainDebugFragment : AbstractDebugFragment() {
}
// If we didn't manage to initialize file logging, disable the preference
if (!GBApplication.getLogging().isFileLoggerInitialized) {
if (!Logging.getInstance().isFileLoggerInitialized) {
it.isEnabled = false
it.setSummary(R.string.pref_write_logfiles_not_available)
}
// If we didn't manage to initialize file logging, disable the preference and show the button to initialize again
if (!Logging.getInstance().isFileLoggerInitialized()) {
logToFilePreference.isEnabled = false
logToFilePreference.setSummary(R.string.pref_write_logfiles_not_available)
val logRestart = findPreference<Preference?>("log_restart")
if (logRestart != null) {
logRestart.isVisible = true
logRestart.setOnPreferenceClickListener { preference: Preference? ->
Logging.getInstance().setFileLoggingEnabled(logToFilePreference.isChecked)
if (Logging.getInstance().isFileLoggerInitialized) {
logToFilePreference.isEnabled = true
logToFilePreference.setSummary(null)
logRestart.isVisible = false
}
true
}
}
}
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
@@ -99,15 +122,13 @@ class MainDebugFragment : AbstractDebugFragment() {
}
private fun shareLog() {
val fileName = GBApplication.getLogPath()
val fileName = Logging.getInstance().logPath
if (fileName == null || fileName.isEmpty()) {
return
}
// Flush the logs, so that we ensure latest lines are also there
GBApplication.getLogging().setImmediateFlush(true)
LOG.debug("Flushing logs before sharing")
GBApplication.getLogging().setImmediateFlush(false)
Logging.getInstance().flush()
val logFile = File(fileName)
if (!logFile.exists()) {
@@ -135,6 +156,7 @@ class MainDebugFragment : AbstractDebugFragment() {
private const val PREF_DEBUG_ADD_TEST_DEVICE = "pref_debug_add_test_device"
private const val PREF_HEADER_LOGS = "pref_header_logs"
private const val LOG_TO_FILE = "log_to_file"
private const val LOG_RESTART = "log_restart"
private const val PREF_DEBUG_SHARE_LOGS = "pref_debug_share_logs"
private const val PREF_HEADER_DEBUG = "pref_header_debug"
private const val PREF_DEBUG_NOTIFICATIONS = "pref_debug_notifications"
@@ -21,10 +21,15 @@ import android.content.Intent;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class GBDeviceEventDisplayMessage extends GBDeviceEvent {
private static final Logger LOG = LoggerFactory.getLogger(GBDeviceEventDisplayMessage.class);
public String message;
public int duration;
public int severity;
@@ -33,10 +38,6 @@ public class GBDeviceEventDisplayMessage extends GBDeviceEvent {
* An event for displaying a message to the user. How the message is displayed
* is a detail of the current activity, which needs to listen to the Intent
* GB.ACTION_DISPLAY_MESSAGE.
*
* @param message
* @param duration
* @param severity
*/
public GBDeviceEventDisplayMessage(String message, int duration, int severity) {
this.message = message;
@@ -46,7 +47,7 @@ public class GBDeviceEventDisplayMessage extends GBDeviceEvent {
@Override
public void evaluate(final Context context, final GBDevice device) {
GB.log(this.message, this.severity, null);
GB.log(LOG, this.message, this.severity, null);
Intent messageIntent = new Intent(GB.ACTION_DISPLAY_MESSAGE);
messageIntent.putExtra(GB.DISPLAY_MESSAGE_MESSAGE, this.message);
@@ -28,13 +28,15 @@ import android.graphics.PorterDuff;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import nodomain.freeyourgadget.gadgetbridge.R;
@@ -44,6 +46,8 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class ImageEditActivity extends AbstractGBActivity implements View.OnTouchListener {
private static final Logger LOG = LoggerFactory.getLogger(ImageEditActivity.class);
static final public int RESULT_CODE_EDIT_SUCCESS = 0;
ImageView overlay;
@@ -85,7 +89,7 @@ public class ImageEditActivity extends AbstractGBActivity implements View.OnTouc
try {
fitImageToLayout(mainLayout);
} catch (IOException | RuntimeException e) {
GB.log("Error formatting image", GB.ERROR, e);
LOG.error("Error formatting image", e);
GB.toast("Error formatting image", Toast.LENGTH_LONG, GB.ERROR);
finish();
}
@@ -16,8 +16,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
import android.util.Log;
import java.io.Serializable;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.PlayNotificationRequest;
@@ -29,15 +27,6 @@ public class NotificationConfiguration implements Serializable {
private boolean respectSilentMode;
private long id = -1;
NotificationConfiguration(short min, short hour, String packageName, String appName, boolean respectSilentMode, PlayNotificationRequest.VibrationType vibration) {
this.min = min;
this.hour = hour;
this.packageName = packageName;
this.appName = appName;
this.respectSilentMode = respectSilentMode;
this.vibration = vibration;
}
public NotificationConfiguration(short min, short hour, short subEye, PlayNotificationRequest.VibrationType vibration) {
this.min = min;
this.hour = hour;
@@ -81,7 +70,6 @@ public class NotificationConfiguration implements Serializable {
}
public boolean getRespectSilentMode() {
Log.d("Config", "respect: " + respectSilentMode);
return respectSilentMode;
}
@@ -20,16 +20,19 @@ import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.PlayNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class PackageConfigHelper {
private static final Logger LOG = LoggerFactory.getLogger(PackageConfigHelper.class);
public static final String DB_NAME = "qhybridNotifications.db";
public static final String DB_ID = "id";
public static final String DB_TABLE = "notifications";
@@ -45,7 +48,7 @@ public class PackageConfigHelper {
try {
initDB();
} catch (Exception e) {
GB.log("error getting database", GB.ERROR, e);
LOG.error("error getting database", e);
}
}
@@ -96,7 +99,7 @@ public class PackageConfigHelper {
PlayNotificationRequest.VibrationType.fromValue((byte) cursor.getInt(vibrationPos)),
cursor.getInt(idPos)
));
Log.d("Settings", "setting #" + cursor.getPosition() + ": " + cursor.getInt(silentPos));
LOG.debug("setting #{}: {}", cursor.getPosition(), cursor.getInt(silentPos));
} while (cursor.moveToNext());
}
return list;
@@ -115,16 +118,15 @@ public class PackageConfigHelper {
return null;
}
c.moveToFirst();
NotificationConfiguration settings = new NotificationConfiguration(
(short) c.getInt(c.getColumnIndex(DB_MINUTE)),
(short) c.getInt(c.getColumnIndex(DB_HOUR)),
c.getString(c.getColumnIndex(DB_PACKAGE)),
c.getString(c.getColumnIndex(DB_APPNAME)),
c.getInt(c.getColumnIndex(DB_RESPECT_SILENT)) == 1,
PlayNotificationRequest.VibrationType.fromValue((byte) c.getInt(c.getColumnIndex(DB_VIBRATION))),
c.getInt(c.getColumnIndex(DB_ID))
return new NotificationConfiguration(
(short) c.getInt(c.getColumnIndexOrThrow(DB_MINUTE)),
(short) c.getInt(c.getColumnIndexOrThrow(DB_HOUR)),
c.getString(c.getColumnIndexOrThrow(DB_PACKAGE)),
c.getString(c.getColumnIndexOrThrow(DB_APPNAME)),
c.getInt(c.getColumnIndexOrThrow(DB_RESPECT_SILENT)) == 1,
PlayNotificationRequest.VibrationType.fromValue((byte) c.getInt(c.getColumnIndexOrThrow(DB_VIBRATION))),
c.getInt(c.getColumnIndexOrThrow(DB_ID))
);
return settings;
}
}
}
@@ -145,7 +147,7 @@ public class PackageConfigHelper {
}
public void deleteNotificationConfiguration(NotificationConfiguration packageSettings) throws Exception {
Log.d("DB", "deleting id " + packageSettings.getId());
LOG.debug("deleting id {}", packageSettings.getId());
if(packageSettings.getId() == -1) return;
try (DBHandler db = GBApplication.acquireDB()) {
@@ -24,11 +24,10 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.Gravity;
import android.view.KeyEvent;
@@ -64,6 +63,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.ArrayList;
@@ -80,8 +81,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSuppo
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig.ConfigPayload;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
public class QHybridConfigActivity extends AbstractGBActivity {
private static final Logger LOG = LoggerFactory.getLogger(QHybridConfigActivity.class);
PackageAdapter adapter;
ArrayList<NotificationConfiguration> list;
PackageConfigHelper helper;
@@ -469,7 +473,7 @@ public class QHybridConfigActivity extends AbstractGBActivity {
buttonIntent.putExtra(FossilWatchAdapter.ITEM_BUTTONS, buttonConfig.toString());
LocalBroadcastManager.getInstance(QHybridConfigActivity.this).sendBroadcast(buttonIntent);
} catch (JSONException e) {
GB.log("error", GB.ERROR, e);
LOG.error("Failed to update button config", e);
}
}
})
@@ -572,11 +576,8 @@ public class QHybridConfigActivity extends AbstractGBActivity {
}
class PackageAdapter extends ArrayAdapter<NotificationConfiguration> {
PackageManager manager;
PackageAdapter(@NonNull Context context, int resource, @NonNull List<NotificationConfiguration> objects) {
super(context, resource, objects);
manager = context.getPackageManager();
}
@NonNull
@@ -599,10 +600,9 @@ public class QHybridConfigActivity extends AbstractGBActivity {
return addButton;
}
try {
((ImageView) view.findViewById(R.id.packageIcon)).setImageDrawable(manager.getApplicationIcon(settings.getPackageName()));
} catch (PackageManager.NameNotFoundException e) {
GB.log("error", GB.ERROR, e);
final Drawable appIcon = NotificationUtils.getAppIcon(getContext(), settings.getPackageName());
if (appIcon != null) {
((ImageView) view.findViewById(R.id.packageIcon)).setImageDrawable(appIcon);
}
final int square_side = 100;
((TextView) view.findViewById(R.id.packageName)).setText(settings.getAppName());
@@ -1,12 +1,14 @@
package nodomain.freeyourgadget.gadgetbridge.prefs;
import android.content.SharedPreferences;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.prefs.migrators.PreferenceMigrator55;
public class GBPrefsMigrator {
private static final String TAG = "GBPrefsMigrator";
private static final Logger LOG = LoggerFactory.getLogger(GBPrefsMigrator.class);
public static final String PREFS_VERSION = "shared_preferences_version";
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below
@@ -16,7 +18,7 @@ public class GBPrefsMigrator {
public static void migratePrefsIfNeeded(final SharedPreferences sharedPrefs) {
final int oldVersion = getPrefsFileVersion(sharedPrefs);
if (oldVersion != CURRENT_PREFS_VERSION) {
Log.i(TAG, "Migrating preferences from " + oldVersion + " to " + CURRENT_PREFS_VERSION);
LOG.info("Migrating preferences from {} to {}", oldVersion, CURRENT_PREFS_VERSION);
final SharedPreferences.Editor editor = sharedPrefs.edit();
@@ -16,10 +16,11 @@ import static nodomain.freeyourgadget.gadgetbridge.model.DeviceType.WATCHXPLUS;
import android.content.SharedPreferences;
import android.os.Build;
import android.util.Log;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.util.ArrayList;
@@ -52,6 +53,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
*/
@SuppressWarnings("deprecation")
public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
private static final Logger LOG = LoggerFactory.getLogger(PreferenceMigrator55.class);
private final SharedPreferences sharedPrefs;
private final GBPrefs prefs;
@@ -97,7 +100,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
try {
legacyGender_1 = sharedPrefs.getInt(ActivityUser.PREF_USER_GENDER, 2);
} catch (Exception e) {
Log.e(TAG, "Could not access legacy activity gender", e);
LOG.error("Could not access legacy activity gender", e);
}
editor.putString(ActivityUser.PREF_USER_GENDER, Integer.toString(legacyGender_1));
}
@@ -213,7 +216,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
editor.remove("miband3_language");
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 3", e);
LOG.error("Failed to migrate prefs to version 3", e);
}
}
if (oldVersion < 4) {
@@ -233,7 +236,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 4", e);
LOG.error("Failed to migrate prefs to version 4", e);
}
}
if (oldVersion < 5) {
@@ -297,7 +300,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
editor.remove("zetime_wrist");
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 5", e);
LOG.error("Failed to migrate prefs to version 5", e);
}
}
if (oldVersion < 6) {
@@ -331,9 +334,9 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
DaoSession daoSession = db.getDaoSession();
List<Device> activeDevices = DBHelper.getActiveDevices(daoSession);
migrateBooleanPrefToPerDevicePref("transliteration", false, "pref_transliteration_enabled", (ArrayList) activeDevices);
Log.w(TAG, "migrating transliteration settings");
LOG.warn("migrating transliteration settings");
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 9", e);
LOG.error("Failed to migrate prefs to version 9", e);
}
}
if (oldVersion < 10) {
@@ -347,14 +350,14 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
DeviceType deviceType = DeviceType.fromName(dbDevice.getTypeName());
if (deviceType == GALAXY_BUDS) {
GB.log("migrating Galaxy Buds volume", GB.INFO, null);
LOG.info("migrating Galaxy Buds volume");
String volume = deviceSharedPrefs.getString(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOLUME, "1");
deviceSharedPrefsEdit.putInt(DeviceSettingsPreferenceConst.PREF_GALAXY_BUDS_AMBIENT_VOLUME, Integer.parseInt(volume));
}
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 10", e);
LOG.error("Failed to migrate prefs to version 10", e);
}
}
if (oldVersion < 11) {
@@ -445,7 +448,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
editor.remove("mi2_inactivity_warnings_end");
editor.remove("mi_fitness_goal");
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 11", e);
LOG.error("Failed to migrate prefs to version 11", e);
}
}
if (oldVersion < 12) {
@@ -485,7 +488,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 13", e);
LOG.error("Failed to migrate prefs to version 13", e);
}
}
@@ -509,7 +512,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 14", e);
LOG.error("Failed to migrate prefs to version 14", e);
}
}
@@ -528,7 +531,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 15", e);
LOG.error("Failed to migrate prefs to version 15", e);
}
}
@@ -552,7 +555,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 16", e);
LOG.error("Failed to migrate prefs to version 16", e);
}
}
@@ -576,7 +579,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 17", e);
LOG.error("Failed to migrate prefs to version 17", e);
}
editor.remove(GBPrefs.CALENDAR_BLACKLIST);
@@ -602,7 +605,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 18", e);
LOG.error("Failed to migrate prefs to version 18", e);
}
}
@@ -637,7 +640,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 20", e);
LOG.error("Failed to migrate prefs to version 20", e);
}
}
@@ -667,7 +670,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 21", e);
LOG.error("Failed to migrate prefs to version 21", e);
}
}
@@ -687,7 +690,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 22", e);
LOG.error("Failed to migrate prefs to version 22", e);
}
}
@@ -716,7 +719,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 26", e);
LOG.error("Failed to migrate prefs to version 26", e);
}
}
@@ -747,7 +750,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 27", e);
LOG.error("Failed to migrate prefs to version 27", e);
}
}
@@ -788,7 +791,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 28", e);
LOG.error("Failed to migrate prefs to version 28", e);
}
}
@@ -810,7 +813,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 29", e);
LOG.error("Failed to migrate prefs to version 29", e);
}
}
@@ -832,7 +835,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 30", e);
LOG.error("Failed to migrate prefs to version 30", e);
}
}
@@ -862,7 +865,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 31", e);
LOG.error("Failed to migrate prefs to version 31", e);
}
}
@@ -892,7 +895,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 32", e);
LOG.error("Failed to migrate prefs to version 32", e);
}
}
@@ -922,7 +925,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 33", e);
LOG.error("Failed to migrate prefs to version 33", e);
}
}
@@ -963,7 +966,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 34", e);
LOG.error("Failed to migrate prefs to version 34", e);
}
}
@@ -1029,7 +1032,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 35", e);
LOG.error("Failed to migrate prefs to version 35", e);
}
}
if (oldVersion < 36) {
@@ -1065,7 +1068,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 36", e);
LOG.error("Failed to migrate prefs to version 36", e);
}
}
@@ -1086,11 +1089,11 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
if (yearOfBirthValue > 1800 && yearOfBirthValue < 3000) {
editor.putString("activity_user_date_of_birth", String.format(Locale.ROOT, "%s-01-01", yearOfBirth.trim()));
} else {
Log.e(TAG, "Year of birth out of range, not migrating - " + yearOfBirth);
LOG.error("Year of birth out of range, not migrating - " + yearOfBirth);
}
}
} catch (final Exception e) {
Log.e(TAG, "Failed to migrate year of birth to date of birth in version 38", e);
LOG.error("Failed to migrate year of birth to date of birth in version 38", e);
}
}
@@ -1122,7 +1125,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 39", e);
LOG.error("Failed to migrate prefs to version 39", e);
}
}
@@ -1152,7 +1155,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 40", e);
LOG.error("Failed to migrate prefs to version 40", e);
}
}
@@ -1201,7 +1204,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 43", e);
LOG.error("Failed to migrate prefs to version 43", e);
}
}
@@ -1215,7 +1218,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
editor.putBoolean("first_run", false);
}
} catch (final Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 44", e);
LOG.error("Failed to migrate prefs to version 44", e);
}
}
@@ -1249,7 +1252,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 45", e);
LOG.error("Failed to migrate prefs to version 45", e);
}
}
@@ -1268,7 +1271,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 46", e);
LOG.error("Failed to migrate prefs to version 46", e);
}
}
@@ -1315,7 +1318,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 49", e);
LOG.error("Failed to migrate prefs to version 49", e);
}
}
@@ -1349,7 +1352,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 50", e);
LOG.error("Failed to migrate prefs to version 50", e);
}
}
@@ -1384,7 +1387,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate prefs to version 54", e);
LOG.error("Failed to migrate prefs to version 54", e);
}
}
}
@@ -1419,7 +1422,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to migrate device types", e);
LOG.error("Failed to migrate device types", e);
}
}
@@ -1436,7 +1439,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
DeviceType deviceType = DeviceType.fromName(dbDevice.getTypeName());
if (deviceTypes.contains(deviceType)) {
Log.i(TAG, "migrating global string preference " + globalPref + " for " + deviceType.name() + " " + dbDevice.getIdentifier());
LOG.info("migrating global string preference {} for {} {}", globalPref, deviceType.name(), dbDevice.getIdentifier());
deviceSharedPrefsEdit.putString(perDevicePref, globalPrefValue);
}
deviceSharedPrefsEdit.apply();
@@ -1445,7 +1448,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
editor.remove(globalPref);
editor.apply();
} catch (Exception e) {
Log.w(TAG, "error acquiring DB lock");
LOG.warn("error acquiring DB lock");
}
}
@@ -1463,7 +1466,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
DeviceType deviceType = DeviceType.fromName(dbDevice.getTypeName());
if (deviceTypes.contains(deviceType)) {
Log.i(TAG, "migrating global boolean preference " + globalPref + " for " + deviceType.name() + " " + dbDevice.getIdentifier());
LOG.info("migrating global boolean preference {} for {} {}", globalPref, deviceType.name(), dbDevice.getIdentifier());
deviceSharedPrefsEdit.putBoolean(perDevicePref, globalPrefValue);
}
deviceSharedPrefsEdit.apply();
@@ -1472,7 +1475,7 @@ public class PreferenceMigrator55 extends AbstractPreferenceMigrator {
editor.remove(globalPref);
editor.apply();
} catch (Exception e) {
Log.e(TAG, "Failed to migrate " + globalPref, e);
LOG.error("Failed to migrate {}", globalPref, e);
}
}
}
@@ -24,7 +24,6 @@ import org.slf4j.Logger;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.AbstractDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BleNamesResolver;
@@ -173,14 +172,6 @@ public abstract class AbstractBTBRDeviceSupport extends AbstractDeviceSupport im
return 0;
}
/**
* Utility method that may be used to log incoming messages when we don't know how to deal with them yet.
*/
public void logMessageContent(byte[] value) {
logger.info("RECEIVED DATA WITH LENGTH: {}", (value != null) ? value.length : "(null)");
Logging.logBytes(logger, value);
}
@Override
public void onConnectionEstablished() {
try {
@@ -44,7 +44,6 @@ import java.util.Set;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
@@ -81,7 +80,9 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
public AbstractBTLEMultiDeviceSupport(Logger logger, int deviceCount) {
this.logger = logger;
this.deviceCount = deviceCount;
//noinspection unchecked
mSupportedServices = new Set[deviceCount];
//noinspection unchecked
mSupportedServerServices = new Set[deviceCount];
mMTUs = new int[deviceCount];
for (int i = 0; i < deviceCount; i++) {
@@ -92,16 +93,13 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
mQueues = new BtLEQueue[deviceCount];
bleApis = new BleIntentApi[deviceCount];
devices = new GBDevice[deviceCount];
//noinspection unchecked
mAvailableCharacteristics = new Map[deviceCount];
if (logger == null) {
throw new IllegalArgumentException("logger must not be null");
}
}
public AbstractBTLEMultiDeviceSupport(Logger logger) {
this(logger, 1);
}
private void validateDeviceIndex(int deviceIdx) {
if (deviceIdx < 0 || deviceIdx > deviceCount) {
throw new IllegalArgumentException(
@@ -429,14 +427,6 @@ public abstract class AbstractBTLEMultiDeviceSupport extends AbstractBTLEDeviceS
return mSupportedServices[deviceIdx];
}
/**
* Utility method that may be used to log incoming messages when we don't know how to deal with them yet.
*/
public void logMessageContent(byte[] value) {
logger.info("RECEIVED DATA WITH LENGTH: {}", (value != null) ? value.length : "(null)");
Logging.logBytes(logger, value);
}
// default implementations of event handler methods (gatt callbacks)
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
@@ -44,10 +44,10 @@ import java.util.Set;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.CheckInitializedAction;
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.AbstractBleProfile;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
/**
@@ -111,10 +111,6 @@ public abstract class AbstractBTLESingleDeviceSupport extends AbstractBTLEDevice
}
}
public BleIntentApi getBleApi() {
return bleApi;
}
@Override
public void onSendConfiguration(String config) {
super.onSendConfiguration(config);
@@ -339,8 +335,11 @@ public abstract class AbstractBTLESingleDeviceSupport extends AbstractBTLEDevice
* Utility method that may be used to log incoming messages when we don't know how to deal with them yet.
*/
public void logMessageContent(byte[] value) {
logger.info("RECEIVED DATA WITH LENGTH: {}", (value != null) ? value.length : "(null)");
Logging.logBytes(logger, value);
if (value != null) {
logger.info("RECEIVED DATA WITH LENGTH: {}: {}", value.length, GB.hexdump(value));
} else {
logger.warn("RECEIVED DATA: (null)");
}
}
// default implementations of event handler methods (gatt callbacks)
@@ -16,21 +16,16 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.parser;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.entities.BaseActivitySummary;
import nodomain.freeyourgadget.gadgetbridge.entities.HybridHRSpo2Sample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.StringUtils;
public class ActivityFileParser {
@@ -17,7 +17,6 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests;
import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -28,7 +27,7 @@ import java.util.UUID;
public abstract class Request {
protected byte[] data;
private Logger logger = (Logger) LoggerFactory.getLogger(getName());
private final Logger logger = LoggerFactory.getLogger(getName());
//protected ByteBuffer buffer;
public Request(){
@@ -16,16 +16,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.device_info;
import android.util.Log;
import androidx.annotation.NonNull;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle;
public class DeviceSecurityVersionInfo implements DeviceInfo {
private int versionMajor, versionMinor;
@@ -16,8 +16,6 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public enum ResultCode {
SUCCESS(0, true),
INVALID_OPERATION_DATA(1, false),
@@ -46,24 +44,21 @@ public enum ResultCode {
// no clue what there one mean
UNKNOWN_1(-125, false);
boolean success;
int code;
final boolean success;
final int code;
private ResultCode(int code, boolean success){
ResultCode(int code, boolean success) {
this.code = code;
this.success = success;
}
public boolean inidicatesSuccess(){
public boolean inidicatesSuccess() {
return this.success;
}
public static ResultCode fromCode(int code){
for (ResultCode resultCode : ResultCode.values()){
if(resultCode.code == code) {
if(resultCode == UNKNOWN_1){
GB.log("dunno what code this is: " + code, GB.INFO, null);
}
public static ResultCode fromCode(int code) {
for (ResultCode resultCode : ResultCode.values()) {
if (resultCode.code == code) {
return resultCode;
}
}
@@ -17,7 +17,6 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit;
import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -17,7 +17,6 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit;
import android.bluetooth.BluetoothGattCharacteristic;
import android.util.Log;
import java.nio.ByteBuffer;
@@ -20,6 +20,9 @@ package nodomain.freeyourgadget.gadgetbridge.util;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -43,6 +46,8 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
* Some utility methods for dealing with Alarms.
*/
public class AlarmUtils {
private static final Logger LOG = LoggerFactory.getLogger(AlarmUtils.class);
/**
* Creates an auto-generated (not user-configurable), non-recurring alarm. This alarm
* may not be stored in the database. Some features are not available (e.g. device id, user id).
@@ -58,8 +63,6 @@ public class AlarmUtils {
/**
* Creates a default Alarm
* @param device
* @param position
*/
@Nullable
public static Alarm createDefaultAlarm(GBDevice gbDevice, int position) {
@@ -67,7 +70,7 @@ public class AlarmUtils {
DaoSession daoSession = db.getDaoSession();
return createDefaultAlarm(daoSession, gbDevice, position);
} catch (Exception e) {
GB.log("Error accessing database", GB.ERROR, e);
LOG.error("Error accessing database", e);
return null;
}
}
@@ -146,7 +149,7 @@ public class AlarmUtils {
Collections.sort(alarms, AlarmUtils.createComparator());
return alarms;
} catch (Exception e) {
GB.log("Error accessing database", GB.ERROR, e);
LOG.error("Error accessing database", e);
return Collections.emptyList();
}
}
@@ -188,7 +191,7 @@ public class AlarmUtils {
daoSession.insertOrReplace(oneshot);
all_alarms = DBHelper.getAlarms(gbDevice);
} catch (Exception e) {
GB.log("error storing one shot quick alarm", GB.ERROR, e);
LOG.error("error storing one shot quick alarm", e);
}
return all_alarms;
}
@@ -26,6 +26,9 @@ import android.os.Environment;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
@@ -52,8 +55,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBEnvironment;
public class FileUtils {
// Don't use slf4j here -- would be a bootstrapping problem
private static final String TAG = "FileUtils";
private static final Logger LOG = LoggerFactory.getLogger(FileUtils.class);
private static final List<String> KNOWN_PACKAGES = Arrays.asList(
"nodomain.freeyourgadget.gadgetbridge",
@@ -65,10 +67,6 @@ public class FileUtils {
/**
* Copies the the given sourceFile to destFile, overwriting it, in case it exists.
*
* @param sourceFile
* @param destFile
* @throws IOException
*/
public static void copyFile(File sourceFile, File destFile) throws IOException {
if (!sourceFile.exists()) {
@@ -89,7 +87,6 @@ public class FileUtils {
* Copies the contents of the given input stream to the destination file.
* @param inputStream the contents to write. Note: the caller has to close the input stream!
* @param destFile the file to write to
* @throws IOException
*/
public static void copyStreamToFile(InputStream inputStream, File destFile) throws IOException {
try (FileOutputStream fout = new FileOutputStream(destFile)) {
@@ -105,7 +102,6 @@ public class FileUtils {
* Copies the contents of the given string to the destination file.
* @param string the contents to write.
* @param dst the file to write to
* @throws IOException
*/
public static void copyStringToFile(String string, File dst, String mode) throws IOException {
boolean append = true;
@@ -120,7 +116,6 @@ public class FileUtils {
* Copies the contents of the given file to the destination output stream.
* @param src the file from which to read.
* @param dst the output stream that is written to. Note: the caller has to close the output stream!
* @throws IOException
*/
public static void copyFileToStream(File src, OutputStream dst) throws IOException {
try (FileInputStream in = new FileInputStream(src)) {
@@ -153,7 +148,6 @@ public class FileUtils {
* @param context the application context.
* @param src the file from which the content should be copied.
* @param dst the destination uri.
* @throws IOException
*/
public static void copyFileToURI(Context context, File src, Uri dst) throws IOException {
OutputStream out = context.getContentResolver().openOutputStream(dst);
@@ -170,7 +164,6 @@ public class FileUtils {
* in UTF-8 encoding.
* @param file the file to read
* @return the file contents as a newline-delimited string
* @throws IOException
* @see #getStringFromFile(File, String)
*/
public static String getStringFromFile(File file) throws IOException {
@@ -182,7 +175,6 @@ public class FileUtils {
* given encoding.
* @param file the file to read
* @return the file contents as a newline-delimited string
* @throws IOException
* @see #getStringFromFile(File)
*/
public static String getStringFromFile(File file, String encoding) throws IOException {
@@ -220,7 +212,6 @@ public class FileUtils {
* It doesn't matter whether child shall represent a file or a directory.
* The parent directory will automatically be created, if necessary.
* @param child the path to become relative to the external files directory
* @throws IOException
* @see #getExternalFilesDir()
*/
public static File getExternalFile(String child) throws IOException {
@@ -245,7 +236,7 @@ public class FileUtils {
file.delete();
return true;
} catch (FileNotFoundException e) {
GB.log("Cannot write to directory: " + dir.getAbsolutePath(), GB.INFO, e);
LOG.debug("Cannot write to directory: {}", dir.getAbsolutePath(), e);
return false;
}
}
@@ -259,7 +250,6 @@ public class FileUtils {
* can actually write to them. But when created, they *should* be writable.
*
* @return the list of writable directories
* @throws IOException
*/
@NonNull
private static List<File> getWritableExternalFilesDirs() throws IOException {
@@ -284,20 +274,19 @@ public class FileUtils {
if (dirs.length == 0) {
throw new IOException("Unable to access external files dirs: 0");
}
for (int i = 0; i < dirs.length; i++) {
File dir = dirs[i];
for (File dir : dirs) {
if (dir == null) {
continue;
}
if (!dir.exists() && !dir.mkdirs()) {
GB.log("Unable to create directories: " + dir.getAbsolutePath(), GB.INFO, null);
LOG.info("Unable to create directories: {}", dir.getAbsolutePath());
continue;
}
if (!GBEnvironment.env().isLocalTest()) { // don't do this with robolectric
final String storageState = Environment.getExternalStorageState(dir);
if (!Environment.MEDIA_MOUNTED.equals(storageState)) {
GB.log("ignoring '" + storageState + "' external storage dir: " + dir, GB.INFO, null);
LOG.info("ignoring '{}' external storage dir: {}", storageState, dir);
continue;
}
}
@@ -538,18 +538,22 @@ public class GB {
}
public static void log(String message, int severity, Throwable ex) {
log(LOG, message, severity, ex);
}
public static void log(Logger logger, String message, int severity, Throwable ex) {
switch (severity) {
case INFO:
LOG.info(message, ex);
logger.info(message, ex);
break;
case WARN:
LOG.warn(message, ex);
logger.warn(message, ex);
break;
case ERROR:
LOG.error(message, ex);
logger.error(message, ex);
break;
case DEBUG:
LOG.debug(message, ex);
logger.debug(message, ex);
}
}
@@ -25,11 +25,13 @@ import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.time.LocalTime;
import java.util.Date;
@@ -39,8 +41,7 @@ import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
public class GBPrefs extends Prefs {
// Since this class must not log to slf4j, we use plain android.util.Log
private static final String TAG = "GBPrefs";
private static final Logger LOG = LoggerFactory.getLogger(GBPrefs.class);
public static final String PACKAGE_BLACKLIST = "package_blacklist";
public static final String PACKAGE_PEBBLEMSG_BLACKLIST = "package_pebblemsg_blacklist";
@@ -146,7 +147,7 @@ public class GBPrefs extends Prefs {
try {
return DateTimeUtils.dayFromString(date);
} catch (ParseException ex) {
GB.log("Error parsing date: " + date, GB.ERROR, ex);
LOG.error("Error parsing date: {}", date, ex);
return null;
}
}
@@ -158,7 +159,7 @@ public class GBPrefs extends Prefs {
public float[] getLongLat(Context context) {
float latitude = getFloat("location_latitude", 0);
float longitude = getFloat("location_longitude", 0);
Log.i(TAG, "got longitude/latitude from preferences: " + latitude + "/" + longitude);
LOG.info("got longitude/latitude from preferences: {}/{}", latitude, longitude);
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED &&
getBoolean("use_updated_location_if_available", false)) {
@@ -170,7 +171,7 @@ public class GBPrefs extends Prefs {
if (lastKnownLocation != null) {
latitude = (float) lastKnownLocation.getLatitude();
longitude = (float) lastKnownLocation.getLongitude();
Log.i(TAG, "got longitude/latitude from last known location: " + latitude + "/" + longitude);
LOG.info("got longitude/latitude from last known location: {}/{}", latitude, longitude);
}
}
}
+2
View File
@@ -751,6 +751,8 @@
<string name="watch9_pairing_tap_hint">When your watch vibrates, shake the device or press its button.</string>
<string name="title_activity_sleepmonitor">Sleep monitor</string>
<string name="pref_write_logfiles">Write log files</string>
<string name="pref_restart_logging_title">Initialize logging</string>
<string name="pref_restart_logging_summary">Click here to try to initialize the logging file</string>
<string name="pref_cache_weather">Cache weather information</string>
<string name="pref_experimental_settings_title">Experimental settings</string>
<string name="pref_experimental_settings_summary">Experimental settings might be unstable or not work as expected. Proceed at your own risk.</string>
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:icon="@drawable/ic_add"
@@ -19,6 +20,13 @@
android:layout="@layout/preference_checkbox"
android:title="@string/pref_write_logfiles" />
<Preference
android:icon="@drawable/ic_refresh"
android:key="log_restart"
android:summary="@string/pref_restart_logging_summary"
android:title="@string/pref_restart_logging_title"
app:isPreferenceVisible="false" />
<Preference
android:dependency="log_to_file"
android:icon="@drawable/ic_share"
+6
View File
@@ -368,6 +368,12 @@
android:layout="@layout/preference_checkbox"
android:title="@string/pref_write_logfiles"
app:iconSpaceReserved="false" />
<Preference
android:key="log_restart"
android:summary="@string/pref_restart_logging_summary"
android:title="@string/pref_restart_logging_title"
app:iconSpaceReserved="false"
app:isPreferenceVisible="false" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:key="crash_notification"
@@ -1,16 +1,19 @@
package nodomain.freeyourgadget.gadgetbridge.test;
import android.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
public class GBTestApplication extends GBApplication {
private static final Logger LOG = LoggerFactory.getLogger(GBTestApplication.class);
@Override
protected void migratePrefsIfNeeded() {
// In tests, do not migrate preferences
// FIXME: This is not ideal. In tests, do not migrate preferences. We should be able to initialize
// the database before the application is created so that this works and is actually testable., but
// right now all it does is spam the logs with dozens of stack traces for failed preference migrations
Log.i("GBTestApplication", "Skipping preference migration during tests");
LOG.info("Skipping preference migration during tests");
}
}
@@ -1,20 +1,13 @@
package nodomain.freeyourgadget.gadgetbridge.test;
import org.junit.After;
import org.junit.Test;
import java.io.File;
import androidx.annotation.NonNull;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Tests dynamic enablement and disablement of file appenders.
@@ -24,44 +17,19 @@ public class LoggingTest extends TestBase {
public LoggingTest() {
}
private Logging logging = GBApplication.getLogging();
@Override
@After
public void tearDown() throws Exception {
super.tearDown();
assertTrue(FileUtils.deleteRecursively(getLogFilesDir()));
}
@NonNull
private File getLogFilesDir() {
String dirName = System.getProperty(Logging.PROP_LOGFILES_DIR);
if (dirName != null && dirName.length() > 5) {
return new File(dirName);
}
fail("Property " + Logging.PROP_LOGFILES_DIR + " has invalid value: " + dirName);
return null; // not reached
}
@Test
public void testToggleLogging() {
try {
getLogFilesDir();
} catch (AssertionError ignored) {
// expected, as not yet set up
}
final Logging logging = Logging.getInstance();
try {
logging.setupLogging(true);
File dir = getLogFilesDir();
assertEquals(1, dir.list().length);
logging.setFileLoggingEnabled(true);
assertNotNull(logging.getFileLogger());
assertTrue(logging.getFileLogger().isStarted());
logging.setupLogging(false);
logging.setFileLoggingEnabled(false);
assertNull(logging.getFileLogger());
logging.setupLogging(true);
logging.setFileLoggingEnabled(true);
assertNotNull(logging.getFileLogger());
assertTrue(logging.getFileLogger().isStarted());
} catch (AssertionError ex) {
@@ -11,6 +11,7 @@ import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import java.io.File;
import java.util.Objects;
import ch.qos.logback.classic.util.ContextInitializer;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
@@ -20,10 +21,8 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import static org.junit.Assert.assertNotNull;
import static nodomain.freeyourgadget.gadgetbridge.Logging.PROP_LOGFILES_DIR;
/**
* Base class for all testcases in Gadgetbridge that are supposed to run locally
@@ -35,31 +34,20 @@ import static nodomain.freeyourgadget.gadgetbridge.Logging.PROP_LOGFILES_DIR;
@RunWith(RobolectricTestRunner.class)
@Config(sdk = 23, application = GBTestApplication.class)
public abstract class TestBase {
protected static File logFilesDir;
protected GBApplication app = (GBApplication) RuntimeEnvironment.application;
protected DaoSession daoSession;
protected DBHandler dbHandler;
// Make sure logging is set up for all testcases, so that we can debug problems
@BeforeClass
public static void setupSuite() throws Exception {
public static void setupSuite() {
GBEnvironment.setupEnvironment(GBEnvironment.createLocalTestEnvironment());
// print everything going to android.util.Log to System.out
System.setProperty("robolectric.logging", "stdout");
// properties might be preconfigured in build.gradle because of test ordering problems
String logDir = System.getProperty(PROP_LOGFILES_DIR);
if (logDir != null) {
logFilesDir = new File(logDir);
} else {
logFilesDir = FileUtils.createTempDir("logfiles");
System.setProperty(PROP_LOGFILES_DIR, logFilesDir.getAbsolutePath());
}
if (System.getProperty(ContextInitializer.CONFIG_FILE_PROPERTY) == null) {
File workingDir = new File(System.getProperty("user.dir"));
File workingDir = new File(Objects.requireNonNull(System.getProperty("user.dir")));
File configFile = new File(workingDir, "src/main/assets/logback.xml");
System.out.println(configFile.getAbsolutePath());
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, configFile.getAbsolutePath());
@@ -6,10 +6,10 @@ import org.slf4j.LoggerFactory;
import java.util.GregorianCalendar;
import nodomain.freeyourgadget.gadgetbridge.Logging;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandDateConverter;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
/**
* A simple class for trying out things, not actually testing something.
@@ -22,7 +22,7 @@ public class Tryout extends TestBase {
int v = 1 << 7 | 1 << 2;
byte b = (byte) v;
LOG.info("v: " + v);
Logging.logBytes(LOG, new byte[] { b });
LOG.warn(GB.hexdump(new byte[] { b }));
byte[] bs = new byte[] {(byte) 0xf0,0x28,0x00,0x00 };
LOG.warn("uint32: " + BLETypeConversions.toUint32(bs));
@@ -35,7 +35,7 @@ public class Tryout extends TestBase {
GregorianCalendar calendar = MiBandDateConverter.createCalendar();
byte[] bytes = MiBandDateConverter.calendarToRawBytes(calendar,"fake");
LOG.info("Calender: " + DateTimeUtils.formatDateTime(calendar.getTime()));
Logging.logBytes(LOG, bytes);
LOG.warn(GB.hexdump(bytes));
}