diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 17fd4efec9cc..d8757c36374a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1920,7 +1920,9 @@ public String getSystemServiceName(Class serviceClass) { return SystemServiceRegistry.getSystemServiceName(serviceClass); } - private boolean isUiContext() { + /** @hide */ + @Override + public boolean isUiContext() { return mIsSystemOrSystemUiContext || mIsUiContext || isSystemOrSystemUI(); } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2fe935e35735..e21a31e5e185 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -6103,4 +6103,13 @@ public void assertRuntimeOverlayThemable() { + "get a UI context from ActivityThread#getSystemUiContext()"); } } + + /** + * Indicates if this context is a visual context such as {@link android.app.Activity} or + * a context created from {@link #createWindowContext(int, Bundle)}. + * @hide + */ + public boolean isUiContext() { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index d389d2a4ae09..5dc41e483640 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -1145,4 +1145,12 @@ public void setContentCaptureOptions(ContentCaptureOptions options) { mBase.setContentCaptureOptions(options); } } + + /** + * @hide + */ + @Override + public boolean isUiContext() { + return mBase.isUiContext(); + } } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 4bea623716dc..69d37ab91a1c 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -16,8 +16,11 @@ package android.view; +import static android.os.StrictMode.vmIncorrectContextUseEnabled; + import android.annotation.FloatRange; import android.annotation.TestApi; +import android.app.Activity; import android.app.AppGlobals; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -25,9 +28,12 @@ import android.content.res.Resources; import android.graphics.Rect; import android.os.Build; +import android.os.Bundle; import android.os.RemoteException; +import android.os.StrictMode; import android.provider.Settings; import android.util.DisplayMetrics; +import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; @@ -35,6 +41,8 @@ * Contains methods to standard constants used in the UI for timeouts, sizes, and distances. */ public class ViewConfiguration { + private static final String TAG = "ViewConfiguration"; + /** * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in * dips @@ -372,11 +380,13 @@ public ViewConfiguration() { } /** - * Creates a new configuration for the specified context. The configuration depends on - * various parameters of the context, like the dimension of the display or the density - * of the display. + * Creates a new configuration for the specified visual {@link Context}. The configuration + * depends on various parameters of the {@link Context}, like the dimension of the display or + * the density of the display. * - * @param context The application context used to initialize this view configuration. + * @param context A visual {@link Context} used to initialize the view configuration. It must + * be {@link Activity} or other {@link Context} created with + * {@link Context#createWindowContext(int, Bundle)}. * * @see #get(android.content.Context) * @see android.util.DisplayMetrics @@ -480,13 +490,27 @@ private ViewConfiguration(Context context) { } /** - * Returns a configuration for the specified context. The configuration depends on - * various parameters of the context, like the dimension of the display or the + * Returns a configuration for the specified visual {@link Context}. The configuration depends + * on various parameters of the {@link Context}, like the dimension of the display or the * density of the display. * - * @param context The application context used to initialize the view configuration. + * @param context A visual {@link Context} used to initialize the view configuration. It must + * be {@link Activity} or other {@link Context} created with + * {@link Context#createWindowContext(int, Bundle)}. */ public static ViewConfiguration get(Context context) { + if (!context.isUiContext() && vmIncorrectContextUseEnabled()) { + final String errorMessage = "Tried to access UI constants from a non-visual Context."; + final String message = "UI constants, such as display metrics or window metrics, " + + "must be accessed from Activity or other visual Context. " + + "Use an Activity or a Context created with " + + "Context#createWindowContext(int, Bundle), which are adjusted to the " + + "configuration and visual bounds of an area on screen."; + final Exception exception = new IllegalArgumentException(errorMessage); + StrictMode.onIncorrectContextUsed(message, exception); + Log.e(TAG, errorMessage + message, exception); + } + final DisplayMetrics metrics = context.getResources().getDisplayMetrics(); final int density = (int) (100.0f * metrics.density); diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index 784ee85fea34..cf3b03cae72e 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -928,4 +928,10 @@ public IApplicationThread getIApplicationThread() { public Handler getMainThreadHandler() { throw new UnsupportedOperationException(); } + + /** {@hide} */ + @Override + public boolean isUiContext() { + throw new UnsupportedOperationException(); + } }