diff options
| author | 2018-12-06 19:41:28 +0000 | |
|---|---|---|
| committer | 2018-12-06 19:41:28 +0000 | |
| commit | 7132ca19ff129f28d753679056406fa04d95852c (patch) | |
| tree | 901ec0a981cd072e6e554bc8b74985d7a1dc2c6c | |
| parent | 7ae9663d6c08f127a19ca4e94e8fead08ad88f1e (diff) | |
| parent | 8e4e4cb9a660312e9f07a15e4dcf681746a1a0e9 (diff) | |
Merge "Bring lifecycle to sysui"
8 files changed, 497 insertions, 8 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 04c427f87125..38dadd4b961d 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -19,6 +19,8 @@ import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; import static android.app.StatusBarManager.DISABLE_NONE; import static android.provider.Settings.System.SHOW_BATTERY_PERCENT; +import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.animation.ArgbEvaluator; @@ -166,6 +168,7 @@ public class BatteryMeterView extends LinearLayout implements setClipChildren(false); setClipToPadding(false); + Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this); } public void setForceShowPercent(boolean show) { @@ -289,7 +292,6 @@ public class BatteryMeterView extends LinearLayout implements Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser); updateShowPercent(); subscribeForTunerUpdates(); - Dependency.get(ConfigurationController.class).addCallback(this); mUserTracker.startTracking(); } @@ -300,7 +302,6 @@ public class BatteryMeterView extends LinearLayout implements mBatteryController.removeCallback(this); getContext().getContentResolver().unregisterContentObserver(mSettingObserver); unsubscribeFromTunerUpdates(); - Dependency.get(ConfigurationController.class).removeCallback(this); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java index d2bfd123f113..ae0a1452905d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java @@ -32,7 +32,6 @@ import android.annotation.IdRes; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.Fragment; import android.app.IActivityTaskManager; import android.app.StatusBarManager; import android.content.BroadcastReceiver; @@ -90,6 +89,7 @@ import com.android.systemui.statusbar.phone.ContextualButton.ContextButtonListen import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyButtonView; +import com.android.systemui.util.LifecycleFragment; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -101,7 +101,7 @@ import java.util.function.Consumer; * Fragment containing the NavigationBarFragment. Contains logic for what happens * on clicks and view states of the nav bar. */ -public class NavigationBarFragment extends Fragment implements Callbacks { +public class NavigationBarFragment extends LifecycleFragment implements Callbacks { public static final String TAG = "NavigationBar"; private static final boolean DEBUG = false; @@ -199,7 +199,7 @@ public class NavigationBarFragment extends Fragment implements Callbacks { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCommandQueue = SysUiServiceProvider.getComponent(getContext(), CommandQueue.class); - mCommandQueue.addCallback(this); + mCommandQueue.observe(getLifecycle(), this); mStatusBar = SysUiServiceProvider.getComponent(getContext(), StatusBar.class); mRecents = SysUiServiceProvider.getComponent(getContext(), Recents.class); mDivider = SysUiServiceProvider.getComponent(getContext(), Divider.class); @@ -225,7 +225,6 @@ public class NavigationBarFragment extends Fragment implements Callbacks { @Override public void onDestroy() { super.onDestroy(); - mCommandQueue.removeCallback(this); Dependency.get(AccessibilityManagerWrapper.class).removeCallback( mAccessibilityListener); mContentResolver.unregisterContentObserver(mMagnificationObserver); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java index 9042ca6d15ca..626eef5867f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java @@ -1,8 +1,9 @@ /* * Copyright (C) 2016 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 + * 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 * @@ -14,7 +15,35 @@ package com.android.systemui.statusbar.policy; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.Lifecycle.Event; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; + public interface CallbackController<T> { void addCallback(T listener); void removeCallback(T listener); + + /** + * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state + * and {@link #removeCallback(Object)} when not resumed automatically. + */ + default T observe(LifecycleOwner owner, T listener) { + return observe(owner.getLifecycle(), listener); + } + + /** + * Wrapper to {@link #addCallback(Object)} when a lifecycle is in the resumed state + * and {@link #removeCallback(Object)} when not resumed automatically. + */ + default T observe(Lifecycle lifecycle, T listener) { + lifecycle.addObserver((LifecycleEventObserver) (lifecycleOwner, event) -> { + if (event == Event.ON_RESUME) { + addCallback(listener); + } else if (event == Event.ON_PAUSE) { + removeCallback(listener); + } + }); + return listener; + } } diff --git a/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java b/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java new file mode 100644 index 000000000000..c7f385dcf88c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/LifecycleFragment.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 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 com.android.systemui.util; + +import static androidx.lifecycle.Lifecycle.Event.ON_CREATE; +import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + +import android.annotation.CallSuper; +import android.app.Fragment; +import android.os.Bundle; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +/** + * Version of {@link #Fragment} that is a {@link LifecycleOwner}. + */ +public class LifecycleFragment extends Fragment implements LifecycleOwner { + + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); + + public Lifecycle getLifecycle() { + return mLifecycle; + } + + @CallSuper + @Override + public void onCreate(Bundle savedInstanceState) { + mLifecycle.handleLifecycleEvent(ON_CREATE); + super.onCreate(savedInstanceState); + } + + @CallSuper + @Override + public void onStart() { + mLifecycle.handleLifecycleEvent(ON_START); + super.onStart(); + } + + @CallSuper + @Override + public void onResume() { + mLifecycle.handleLifecycleEvent(ON_RESUME); + super.onResume(); + } + + @CallSuper + @Override + public void onPause() { + mLifecycle.handleLifecycleEvent(ON_PAUSE); + super.onPause(); + } + + @CallSuper + @Override + public void onStop() { + mLifecycle.handleLifecycleEvent(ON_STOP); + super.onStop(); + } + + @CallSuper + @Override + public void onDestroy() { + mLifecycle.handleLifecycleEvent(ON_DESTROY); + super.onDestroy(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java new file mode 100644 index 000000000000..711a0dfe1931 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/SysuiLifecycle.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 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 com.android.systemui.util; + +import static androidx.lifecycle.Lifecycle.State.DESTROYED; +import static androidx.lifecycle.Lifecycle.State.RESUMED; + +import android.view.View; +import android.view.View.OnAttachStateChangeListener; + +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; + +/** + * Tools for generating lifecycle from sysui objects. + */ +public class SysuiLifecycle { + + private SysuiLifecycle() { + } + + /** + * Get a lifecycle that will be put into the resumed state when the view is attached + * and goes to the destroyed state when the view is detached. + */ + public static LifecycleOwner viewAttachLifecycle(View v) { + return new ViewLifecycle(v); + } + + private static class ViewLifecycle implements LifecycleOwner, OnAttachStateChangeListener { + private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); + + ViewLifecycle(View v) { + v.addOnAttachStateChangeListener(this); + } + + @NonNull + @Override + public Lifecycle getLifecycle() { + return mLifecycle; + } + + @Override + public void onViewAttachedToWindow(View v) { + mLifecycle.markState(RESUMED); + } + + @Override + public void onViewDetachedFromWindow(View v) { + mLifecycle.markState(DESTROYED); + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java new file mode 100644 index 000000000000..93c97ec14a42 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackControllerTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 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 com.android.systemui.statusbar.policy; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.Lifecycle.Event; +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +@RunWithLooper +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class CallbackControllerTest extends SysuiTestCase { + + @Test + public void testAddCallback() { + Lifecycle lifecycle = mock(Lifecycle.class); + LifecycleOwner owner = () -> lifecycle; + Object callback = new Object(); + Controller controller = mock(Controller.class); + + // observe and get the lifecycle observer that gets registered. + ArgumentCaptor<LifecycleEventObserver> observer = + ArgumentCaptor.forClass(LifecycleEventObserver.class); + controller.observe(owner, callback); + verify(lifecycle).addObserver(observer.capture()); + + // move to resume state and make sure the callback gets registered. + observer.getValue().onStateChanged(owner, Event.ON_RESUME); + verify(controller).addCallback(eq(callback)); + } + + @Test + public void testRemoveCallback() { + Lifecycle lifecycle = mock(Lifecycle.class); + LifecycleOwner owner = () -> lifecycle; + Object callback = new Object(); + Controller controller = mock(Controller.class); + + // observe and get the lifecycle observer that gets registered. + ArgumentCaptor<LifecycleEventObserver> observer = + ArgumentCaptor.forClass(LifecycleEventObserver.class); + controller.observe(owner, callback); + verify(lifecycle).addObserver(observer.capture()); + + // move to pause state and make sure the callback gets unregistered. + observer.getValue().onStateChanged(owner, Event.ON_PAUSE); + verify(controller).removeCallback(eq(callback)); + } + + private static class Controller implements CallbackController<Object> { + @Override + public void addCallback(Object listener) { + } + + @Override + public void removeCallback(Object listener) { + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java new file mode 100644 index 000000000000..d4e38d8ea2f3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/LifecycleFragmentTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2018 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 com.android.systemui.util; + +import static androidx.lifecycle.Lifecycle.Event.ON_CREATE; +import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; + +import androidx.lifecycle.LifecycleEventObserver; + +import com.android.systemui.SysuiBaseFragmentTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWithLooper +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class LifecycleFragmentTest extends SysuiBaseFragmentTest { + + public LifecycleFragmentTest() { + super(LifecycleFragment.class); + } + + @Test + public void testCreateLifecycle() { + LifecycleFragment fragment = (LifecycleFragment) mFragment; + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + fragment.getLifecycle().addObserver(observer); + + mFragments.dispatchCreate(); + TestableLooper.get(this).processAllMessages(); + + verify(observer).onStateChanged(eq(fragment), eq(ON_CREATE)); + } + + @Test + public void testResumeLifecycle() { + LifecycleFragment fragment = (LifecycleFragment) mFragment; + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + fragment.getLifecycle().addObserver(observer); + + mFragments.dispatchResume(); + TestableLooper.get(this).processAllMessages(); + + verify(observer).onStateChanged(eq(fragment), eq(ON_RESUME)); + } + + @Test + public void testStartLifecycle() { + LifecycleFragment fragment = (LifecycleFragment) mFragment; + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + fragment.getLifecycle().addObserver(observer); + + mFragments.dispatchStart(); + TestableLooper.get(this).processAllMessages(); + + verify(observer).onStateChanged(eq(fragment), eq(ON_START)); + } + + @Test + public void testStopLifecycle() { + LifecycleFragment fragment = (LifecycleFragment) mFragment; + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + fragment.getLifecycle().addObserver(observer); + + mFragments.dispatchStart(); + TestableLooper.get(this).processAllMessages(); + mFragments.dispatchStop(); + TestableLooper.get(this).processAllMessages(); + + verify(observer).onStateChanged(eq(fragment), eq(ON_STOP)); + } + + @Test + public void testPauseLifecycle() { + LifecycleFragment fragment = (LifecycleFragment) mFragment; + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + fragment.getLifecycle().addObserver(observer); + + mFragments.dispatchResume(); + TestableLooper.get(this).processAllMessages(); + mFragments.dispatchPause(); + TestableLooper.get(this).processAllMessages(); + + verify(observer).onStateChanged(eq(fragment), eq(ON_PAUSE)); + } + + @Test + public void testDestroyLifecycle() { + LifecycleFragment fragment = (LifecycleFragment) mFragment; + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + fragment.getLifecycle().addObserver(observer); + + mFragments.dispatchCreate(); + TestableLooper.get(this).processAllMessages(); + mFragments.dispatchDestroy(); + TestableLooper.get(this).processAllMessages(); + mFragments = null; + + verify(observer).onStateChanged(eq(fragment), eq(ON_DESTROY)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java new file mode 100644 index 000000000000..d63bbe6326f3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/SysuiLifecycleTest.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 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 com.android.systemui.util; + +import static androidx.lifecycle.Lifecycle.Event.ON_CREATE; +import static androidx.lifecycle.Lifecycle.Event.ON_DESTROY; +import static androidx.lifecycle.Lifecycle.Event.ON_PAUSE; +import static androidx.lifecycle.Lifecycle.Event.ON_RESUME; +import static androidx.lifecycle.Lifecycle.Event.ON_START; +import static androidx.lifecycle.Lifecycle.Event.ON_STOP; + +import static com.android.systemui.util.SysuiLifecycle.viewAttachLifecycle; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; +import android.testing.ViewUtils; +import android.view.View; + +import androidx.lifecycle.LifecycleEventObserver; +import androidx.lifecycle.LifecycleOwner; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWithLooper +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class SysuiLifecycleTest extends SysuiTestCase { + + @Test + public void testAttach() { + View v = new View(mContext); + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + LifecycleOwner lifecycle = viewAttachLifecycle(v); + lifecycle.getLifecycle().addObserver(observer); + + ViewUtils.attachView(v); + TestableLooper.get(this).processAllMessages(); + + verify(observer).onStateChanged(eq(lifecycle), eq(ON_CREATE)); + verify(observer).onStateChanged(eq(lifecycle), eq(ON_START)); + verify(observer).onStateChanged(eq(lifecycle), eq(ON_RESUME)); + + ViewUtils.detachView(v); + TestableLooper.get(this).processAllMessages(); + } + + @Test + public void testDetach() { + View v = new View(mContext); + LifecycleEventObserver observer = mock(LifecycleEventObserver.class); + LifecycleOwner lifecycle = viewAttachLifecycle(v); + lifecycle.getLifecycle().addObserver(observer); + + ViewUtils.attachView(v); + TestableLooper.get(this).processAllMessages(); + + ViewUtils.detachView(v); + TestableLooper.get(this).processAllMessages(); + + verify(observer).onStateChanged(eq(lifecycle), eq(ON_PAUSE)); + verify(observer).onStateChanged(eq(lifecycle), eq(ON_STOP)); + verify(observer).onStateChanged(eq(lifecycle), eq(ON_DESTROY)); + } +} |