Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,43 @@ See [Wiki](https://github.com/tony19/logback-android/wiki) for documentation.
7. Open logcat for your device (via the _Android Monitor_ tab in Android Studio).
8. Click the app menu, and select the menu-option. You should see "hello world" in logcat.

## Providing Android context externally

In order to support various [special properties](https://github.com/tony19/logback-android/wiki#special-properties-for-xml-config) the code requires
access to an Android [Context](https://developer.android.com/reference/android/content/Context) instance. By default, the framework uses a workaround based
on reflection that seems to work for the time being. However, in view of Google's [restrictions on non-SDK interfaces](https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces)
this code might not work anymore. Therefore, the framework provides a special API that enables the application to provide an Android context instance that
will be used instead of the workaround. **Note:** the context instance must be provided *before* it is needed by the framework, so the best place for it
would be in the application's *onCreate* callback:

```java
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();

// Assuming no logging occurs before this
AndroidContextUtils.setApplicationContext(this);
}
}
```

If an earlier initialization is required, then one might consider overriding `attachBaseContext`, although at this stage the context instance might not be
fully initialized. This might be good enough though if by the time the context is used by the framework it becomes fully initialized.

```java
public class MyApplication extends Application {

@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
AndroidContextUtils.setApplicationContext(base);
}
}
```

*Note:* despite the fact that the method is called `setApplicationContext` - the user may use *any* `ContextWrapper` component (*Application, Activity, Service*) - the
framework will actually use the ["pure" application context](https://developer.android.com/reference/android/content/Context#getApplicationContext()).

## Download

Expand Down
4 changes: 2 additions & 2 deletions logback-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ android {
buildToolsVersion '30.0.3'

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_6
targetCompatibility JavaVersion.VERSION_1_6
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}

defaultConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package ch.qos.logback.core.android;

import android.annotation.TargetApi;
import android.content.ContextWrapper;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
Expand All @@ -25,6 +24,7 @@
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.atomic.AtomicReference;

import ch.qos.logback.core.Context;
import ch.qos.logback.core.CoreConstants;
Expand All @@ -37,14 +37,16 @@
* @since 1.0.8-1
*/
public class AndroidContextUtil {
private ContextWrapper context;
private static final AtomicReference<android.content.Context> CONTEXT_HOLDER = new AtomicReference<>();

private final android.content.Context context;

public AndroidContextUtil() {
this(getContext());
}

public AndroidContextUtil(ContextWrapper contextWrapper) {
this.context = contextWrapper;
public AndroidContextUtil(android.content.Context context) {
this.context = (context == null) ? null : context.getApplicationContext();
}

public static boolean containsProperties(String value) {
Expand All @@ -71,11 +73,21 @@ public void setupProperties(Context context) {
context.putProperty(CoreConstants.VERSION_NAME_KEY, getVersionName());
}

protected static ContextWrapper getContext() {
public static void setApplicationContext(android.content.Context context) {
CONTEXT_HOLDER.set((context == null) ? null : context.getApplicationContext());
}

protected static android.content.Context getContext() {
android.content.Context context = CONTEXT_HOLDER.get();
if (context != null) {
return context.getApplicationContext();
}

try {
Class<?> c = Class.forName("android.app.AppGlobals");
Method method = c.getDeclaredMethod("getInitialApplication");
return (ContextWrapper)method.invoke(c);
context = (android.content.Context) method.invoke(c);
return (context == null) ? null : context.getApplicationContext();
} catch (ClassNotFoundException e) {
//e.printStackTrace();
} catch (NoSuchMethodException e) {
Expand Down Expand Up @@ -116,10 +128,10 @@ public String getMountedExternalStorageDirectoryPath() {
*
* @return the absolute path to the external storage directory
*/
@TargetApi(8)
@TargetApi(Build.VERSION_CODES.FROYO)
@SuppressWarnings("deprecation")
public String getExternalStorageDirectoryPath() {
if (Build.VERSION.SDK_INT >= 29) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return getExternalFilesDirectoryPath();
} else {
return absPath(Environment.getExternalStorageDirectory());
Expand All @@ -134,7 +146,7 @@ public String getExternalStorageDirectoryPath() {
*
* @return the absolute path to the external storage directory
*/
@TargetApi(8)
@TargetApi(Build.VERSION_CODES.FROYO)
public String getExternalFilesDirectoryPath() {
return this.context != null
? absPath(this.context.getExternalFilesDir(null))
Expand Down Expand Up @@ -185,9 +197,9 @@ public String getFilesDirectoryPath() {
* @return the absolute path to the files directory
* (example: "/data/data/com.example/nobackup/files")
*/
@TargetApi(21)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public String getNoBackupFilesDirectoryPath() {
return Build.VERSION.SDK_INT >= 21 &&
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
this.context != null
? absPath(this.context.getNoBackupFilesDir())
: "";
Expand All @@ -201,10 +213,8 @@ public String getNoBackupFilesDirectoryPath() {
* (example: "/data/data/com.example/databases")
*/
public String getDatabaseDirectoryPath() {
return this.context != null
&& this.context.getDatabasePath("x") != null
? this.context.getDatabasePath("x").getParent()
: "";
File dbPath = (this.context == null) ? null : this.context.getDatabasePath("x");
return (dbPath != null) ? dbPath.getParent() : "";
}

public String getDatabasePath(String databaseName) {
Expand All @@ -217,8 +227,12 @@ public String getVersionCode() {
String versionCode = "";
if (this.context != null) {
try {
PackageInfo pkgInfo = this.context.getPackageManager().getPackageInfo(getPackageName(), 0);
versionCode = "" + pkgInfo.versionCode;
PackageManager pm = this.context.getPackageManager();
PackageInfo pkgInfo = pm.getPackageInfo(getPackageName(), 0);
versionCode = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
? Long.toString(pkgInfo.getLongVersionCode())
: Integer.toString(pkgInfo.versionCode)
;
} catch (PackageManager.NameNotFoundException e) {
}
}
Expand All @@ -229,15 +243,16 @@ public String getVersionName() {
String versionName = "";
if (this.context != null) {
try {
PackageInfo pkgInfo = this.context.getPackageManager().getPackageInfo(getPackageName(), 0);
PackageManager pm = this.context.getPackageManager();
PackageInfo pkgInfo = pm.getPackageInfo(getPackageName(), 0);
versionName = pkgInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
}
}
return versionName != null ? versionName : "";
}

private String absPath(File file) {
private static String absPath(File file) {
return file != null ? file.getAbsolutePath() : "";
}
}