Enable to add ComponentCallbacks to Activity
This CL is mainly from the requirements to receive WindowMetrics updates in Jetpack Library. It also benefits the developers if they want to listen to Activity's configuration changes outside Activity Test: atest RegisterComponentCallbacksTest Bug: 199442549 Change-Id: Iff5c0a4d9c61d30b84ff9aee465f1b6b06f1a48d
This commit is contained in:
parent
9950dab656
commit
ba29cdb63e
@ -47,7 +47,9 @@ import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentCallbacks;
|
||||
import android.content.ComponentCallbacks2;
|
||||
import android.content.ComponentCallbacksController;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
@ -982,6 +984,8 @@ public class Activity extends ContextThemeWrapper
|
||||
@Nullable
|
||||
private DumpableContainerImpl mDumpableContainer;
|
||||
|
||||
private ComponentCallbacksController mCallbacksController;
|
||||
|
||||
private final WindowControllerCallback mWindowControllerCallback =
|
||||
new WindowControllerCallback() {
|
||||
/**
|
||||
@ -1323,6 +1327,28 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerComponentCallbacks(ComponentCallbacks callback) {
|
||||
if (CompatChanges.isChangeEnabled(OVERRIDABLE_COMPONENT_CALLBACKS)
|
||||
&& mCallbacksController == null) {
|
||||
mCallbacksController = new ComponentCallbacksController();
|
||||
}
|
||||
if (mCallbacksController != null) {
|
||||
mCallbacksController.registerCallbacks(callback);
|
||||
} else {
|
||||
super.registerComponentCallbacks(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterComponentCallbacks(ComponentCallbacks callback) {
|
||||
if (mCallbacksController != null) {
|
||||
mCallbacksController.unregisterCallbacks(callback);
|
||||
} else {
|
||||
super.unregisterComponentCallbacks(callback);
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchActivityPreCreated(@Nullable Bundle savedInstanceState) {
|
||||
getApplication().dispatchActivityPreCreated(this, savedInstanceState);
|
||||
Object[] callbacks = collectActivityLifecycleCallbacks();
|
||||
@ -2668,10 +2694,12 @@ public class Activity extends ContextThemeWrapper
|
||||
if (mUiTranslationController != null) {
|
||||
mUiTranslationController.onActivityDestroyed();
|
||||
}
|
||||
|
||||
if (mDefaultBackCallback != null) {
|
||||
getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback);
|
||||
}
|
||||
if (mCallbacksController != null) {
|
||||
mCallbacksController.clearCallbacks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2991,6 +3019,9 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
|
||||
dispatchActivityConfigurationChanged();
|
||||
if (mCallbacksController != null) {
|
||||
mCallbacksController.dispatchConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3162,12 +3193,18 @@ public class Activity extends ContextThemeWrapper
|
||||
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onLowMemory " + this);
|
||||
mCalled = true;
|
||||
mFragments.dispatchLowMemory();
|
||||
if (mCallbacksController != null) {
|
||||
mCallbacksController.dispatchLowMemory();
|
||||
}
|
||||
}
|
||||
|
||||
public void onTrimMemory(int level) {
|
||||
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onTrimMemory " + this + ": " + level);
|
||||
mCalled = true;
|
||||
mFragments.dispatchTrimMemory(level);
|
||||
if (mCallbacksController != null) {
|
||||
mCallbacksController.dispatchTrimMemory(level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,6 +36,7 @@ import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.annotation.UiContext;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.app.GameManager;
|
||||
@ -45,6 +46,8 @@ import android.app.VrManager;
|
||||
import android.app.ambientcontext.AmbientContextManager;
|
||||
import android.app.people.PeopleManager;
|
||||
import android.app.time.TimeManager;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@ -85,6 +88,7 @@ import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
|
||||
import android.view.textclassifier.TextClassificationManager;
|
||||
import android.window.WindowContext;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.compat.IPlatformCompat;
|
||||
import com.android.internal.compat.IPlatformCompatNative;
|
||||
|
||||
@ -111,6 +115,19 @@ import java.util.function.Consumer;
|
||||
* broadcasting and receiving intents, etc.
|
||||
*/
|
||||
public abstract class Context {
|
||||
/**
|
||||
* After {@link Build.VERSION_CODES#TIRAMISU},
|
||||
* {@link #registerComponentCallbacks(ComponentCallbacks)} will add a {@link ComponentCallbacks}
|
||||
* to {@link Activity} or {@link ContextWrapper#getBaseContext()} instead of always adding to
|
||||
* {@link #getApplicationContext()}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@ChangeId
|
||||
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
|
||||
@VisibleForTesting
|
||||
public static final long OVERRIDABLE_COMPONENT_CALLBACKS = 193247900L;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = true, prefix = { "MODE_" }, value = {
|
||||
MODE_PRIVATE,
|
||||
|
@ -25,8 +25,6 @@ import android.annotation.UiContext;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.IServiceConnection;
|
||||
import android.app.compat.CompatChanges;
|
||||
import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@ -73,16 +71,6 @@ public class ContextWrapper extends Context {
|
||||
@UnsupportedAppUsage
|
||||
Context mBase;
|
||||
|
||||
/**
|
||||
* After {@link Build.VERSION_CODES#TIRAMISU},
|
||||
* {@link #registerComponentCallbacks(ComponentCallbacks)} will delegate to
|
||||
* {@link #getBaseContext()} instead of {@link #getApplicationContext()}.
|
||||
*/
|
||||
@ChangeId
|
||||
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
|
||||
@VisibleForTesting
|
||||
static final long COMPONENT_CALLBACK_ON_WRAPPER = 193247900L;
|
||||
|
||||
/**
|
||||
* A list to store {@link ComponentCallbacks} which
|
||||
* passes to {@link #registerComponentCallbacks(ComponentCallbacks)} before
|
||||
@ -1355,7 +1343,7 @@ public class ContextWrapper extends Context {
|
||||
public void registerComponentCallbacks(ComponentCallbacks callback) {
|
||||
if (mBase != null) {
|
||||
mBase.registerComponentCallbacks(callback);
|
||||
} else if (!CompatChanges.isChangeEnabled(COMPONENT_CALLBACK_ON_WRAPPER)) {
|
||||
} else if (!CompatChanges.isChangeEnabled(OVERRIDABLE_COMPONENT_CALLBACKS)) {
|
||||
super.registerComponentCallbacks(callback);
|
||||
synchronized (mLock) {
|
||||
// Also register ComponentCallbacks to ContextWrapper, so we can find the correct
|
||||
@ -1397,7 +1385,7 @@ public class ContextWrapper extends Context {
|
||||
mCallbacksRegisteredToSuper.remove(callback);
|
||||
} else if (mBase != null) {
|
||||
mBase.unregisterComponentCallbacks(callback);
|
||||
} else if (CompatChanges.isChangeEnabled(COMPONENT_CALLBACK_ON_WRAPPER)) {
|
||||
} else if (CompatChanges.isChangeEnabled(OVERRIDABLE_COMPONENT_CALLBACKS)) {
|
||||
// Throw exception for Application that is targeting S-v2+
|
||||
throw new IllegalStateException("ComponentCallbacks must be unregistered after "
|
||||
+ "this ContextWrapper is attached to a base Context.");
|
||||
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.app.activity;
|
||||
|
||||
import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
|
||||
import static android.content.Context.OVERRIDABLE_COMPONENT_CALLBACKS;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.app.activity.ActivityThreadTest.TestActivity;
|
||||
import android.compat.testing.PlatformCompatChangeRule;
|
||||
import android.content.ComponentCallbacks;
|
||||
import android.content.TestComponentCallbacks2;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.core.app.ActivityScenario;
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Test for verifying {@link Activity#registerComponentCallbacks(ComponentCallbacks)} behavior.
|
||||
* Build/Install/Run:
|
||||
* atest FrameworksCoreTests:android.app.activity.RegisterComponentCallbacksTest
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
public class RegisterComponentCallbacksTest {
|
||||
@Rule
|
||||
public ActivityScenarioRule rule = new ActivityScenarioRule<>(TestActivity.class);
|
||||
@Rule
|
||||
public TestRule compatChangeRule = new PlatformCompatChangeRule();
|
||||
|
||||
@Test
|
||||
public void testRegisterComponentCallbacks() {
|
||||
final ActivityScenario scenario = rule.getScenario();
|
||||
final TestComponentCallbacks2 callbacks = new TestComponentCallbacks2();
|
||||
final Configuration config = new Configuration();
|
||||
config.fontScale = 1.2f;
|
||||
config.windowConfiguration.setWindowingMode(
|
||||
WindowConfiguration.WINDOWING_MODE_FREEFORM);
|
||||
config.windowConfiguration.setBounds(new Rect(0, 0, 100, 100));
|
||||
final int trimMemoryLevel = TRIM_MEMORY_RUNNING_LOW;
|
||||
|
||||
scenario.onActivity(activity -> {
|
||||
// It should be no-op to unregister a ComponentCallbacks without registration.
|
||||
activity.unregisterComponentCallbacks(callbacks);
|
||||
|
||||
activity.registerComponentCallbacks(callbacks);
|
||||
// Verify #onConfigurationChanged
|
||||
activity.onConfigurationChanged(config);
|
||||
assertThat(callbacks.mConfiguration).isEqualTo(config);
|
||||
// Verify #onTrimMemory
|
||||
activity.onTrimMemory(trimMemoryLevel);
|
||||
assertThat(callbacks.mLevel).isEqualTo(trimMemoryLevel);
|
||||
// verify #onLowMemory
|
||||
activity.onLowMemory();
|
||||
assertThat(callbacks.mLowMemoryCalled).isTrue();
|
||||
|
||||
activity.unregisterComponentCallbacks(callbacks);
|
||||
});
|
||||
}
|
||||
|
||||
@DisableCompatChanges(OVERRIDABLE_COMPONENT_CALLBACKS)
|
||||
@Test
|
||||
public void testRegisterComponentCallbacksBeforeT() {
|
||||
final ActivityScenario scenario = rule.getScenario();
|
||||
final TestComponentCallbacks2 callbacks = new TestComponentCallbacks2();
|
||||
final Configuration config = new Configuration();
|
||||
config.fontScale = 1.2f;
|
||||
config.windowConfiguration.setWindowingMode(
|
||||
WindowConfiguration.WINDOWING_MODE_FREEFORM);
|
||||
config.windowConfiguration.setBounds(new Rect(0, 0, 100, 100));
|
||||
final int trimMemoryLevel = TRIM_MEMORY_RUNNING_LOW;
|
||||
|
||||
scenario.onActivity(activity -> {
|
||||
// It should be no-op to unregister a ComponentCallbacks without registration.
|
||||
activity.unregisterComponentCallbacks(callbacks);
|
||||
|
||||
activity.registerComponentCallbacks(callbacks);
|
||||
// Verify #onConfigurationChanged
|
||||
activity.onConfigurationChanged(config);
|
||||
assertWithMessage("The ComponentCallbacks must be added to #getApplicationContext "
|
||||
+ "before T.").that(callbacks.mConfiguration).isNull();
|
||||
// Verify #onTrimMemory
|
||||
activity.onTrimMemory(trimMemoryLevel);
|
||||
assertWithMessage("The ComponentCallbacks must be added to #getApplicationContext "
|
||||
+ "before T.").that(callbacks.mLevel).isEqualTo(0);
|
||||
// verify #onLowMemory
|
||||
activity.onLowMemory();
|
||||
assertWithMessage("The ComponentCallbacks must be added to #getApplicationContext "
|
||||
+ "before T.").that(callbacks.mLowMemoryCalled).isFalse();
|
||||
|
||||
activity.unregisterComponentCallbacks(callbacks);
|
||||
});
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
|
||||
package android.content;
|
||||
|
||||
import static android.content.ContextWrapper.COMPONENT_CALLBACK_ON_WRAPPER;
|
||||
import static android.content.Context.OVERRIDABLE_COMPONENT_CALLBACKS;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
|
||||
@ -61,7 +61,7 @@ public class ContextWrapperTest {
|
||||
* register {@link ComponentCallbacks} to {@link ContextWrapper#getApplicationContext} before
|
||||
* {@link ContextWrapper#attachBaseContext(Context)}.
|
||||
*/
|
||||
@DisableCompatChanges(COMPONENT_CALLBACK_ON_WRAPPER)
|
||||
@DisableCompatChanges(OVERRIDABLE_COMPONENT_CALLBACKS)
|
||||
@Test
|
||||
public void testRegisterComponentCallbacksWithoutBaseContextBeforeT() {
|
||||
final ContextWrapper wrapper = new TestContextWrapper(null /* base */);
|
||||
|
@ -20,10 +20,10 @@ import android.content.res.Configuration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
class TestComponentCallbacks2 implements ComponentCallbacks2 {
|
||||
android.content.res.Configuration mConfiguration;
|
||||
boolean mLowMemoryCalled;
|
||||
int mLevel;
|
||||
public class TestComponentCallbacks2 implements ComponentCallbacks2 {
|
||||
public Configuration mConfiguration = null;
|
||||
public boolean mLowMemoryCalled = false;
|
||||
public int mLevel = 0;
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
|
@ -62,6 +62,7 @@ public final class FrameworksTestsFilter extends SelectTest {
|
||||
"android.view.PendingInsetsControllerTest",
|
||||
"android.window.", // all tests under the package.
|
||||
"android.app.activity.ActivityThreadTest",
|
||||
"android.app.activity.RegisterComponentCallbacksTest"
|
||||
};
|
||||
|
||||
public FrameworksTestsFilter(Bundle testArgs) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user