diff options
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)); +    } +}  |