From 17529b7dc1f1ae5205167fcbd8644e776872b454 Mon Sep 17 00:00:00 2001 From: Daniel Nishi Date: Thu, 16 Mar 2017 16:02:02 -0700 Subject: [PATCH] Add lifecycle methods for managing the options menu. Test: Settings robotests Change-Id: I087cd0ec097041a4c4e0dd561dd2ec62f45f5390 --- .../settings/core/lifecycle/Lifecycle.java | 34 ++++++++ .../core/lifecycle/ObservableActivity.java | 29 +++++++ .../lifecycle/ObservableDialogFragment.java | 23 ++++++ .../core/lifecycle/ObservableFragment.java | 27 +++++++ .../ObservablePreferenceFragment.java | 26 ++++++ .../lifecycle/events/OnCreateOptionsMenu.java | 24 ++++++ .../events/OnOptionsItemSelected.java | 23 ++++++ .../events/OnPrepareOptionsMenu.java | 24 ++++++ .../core/lifecycle/LifecycleTest.java | 79 ++++++++++++++++++- 9 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 src/com/android/settings/core/lifecycle/events/OnCreateOptionsMenu.java create mode 100644 src/com/android/settings/core/lifecycle/events/OnOptionsItemSelected.java create mode 100644 src/com/android/settings/core/lifecycle/events/OnPrepareOptionsMenu.java diff --git a/src/com/android/settings/core/lifecycle/Lifecycle.java b/src/com/android/settings/core/lifecycle/Lifecycle.java index 9a42cd9530e..ef236884a1f 100644 --- a/src/com/android/settings/core/lifecycle/Lifecycle.java +++ b/src/com/android/settings/core/lifecycle/Lifecycle.java @@ -18,12 +18,19 @@ package com.android.settings.core.lifecycle; import android.annotation.UiThread; import android.content.Context; import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.v7.preference.PreferenceScreen; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import com.android.settings.core.lifecycle.events.OnAttach; import com.android.settings.core.lifecycle.events.OnCreate; +import com.android.settings.core.lifecycle.events.OnCreateOptionsMenu; import com.android.settings.core.lifecycle.events.OnDestroy; +import com.android.settings.core.lifecycle.events.OnOptionsItemSelected; import com.android.settings.core.lifecycle.events.OnPause; +import com.android.settings.core.lifecycle.events.OnPrepareOptionsMenu; import com.android.settings.core.lifecycle.events.OnResume; import com.android.settings.core.lifecycle.events.OnSaveInstanceState; import com.android.settings.core.lifecycle.events.OnStart; @@ -122,4 +129,31 @@ public class Lifecycle { } } } + + public void onCreateOptionsMenu(final Menu menu, final @Nullable MenuInflater inflater) { + for (LifecycleObserver observer : mObservers) { + if (observer instanceof OnCreateOptionsMenu) { + ((OnCreateOptionsMenu) observer).onCreateOptionsMenu(menu, inflater); + } + } + } + + public void onPrepareOptionsMenu(final Menu menu) { + for (LifecycleObserver observer : mObservers) { + if (observer instanceof OnPrepareOptionsMenu) { + ((OnPrepareOptionsMenu) observer).onPrepareOptionsMenu(menu); + } + } + } + + public boolean onOptionsItemSelected(final MenuItem menuItem) { + for (LifecycleObserver observer : mObservers) { + if (observer instanceof OnOptionsItemSelected) { + if (((OnOptionsItemSelected) observer).onOptionsItemSelected(menuItem)) { + return true; + } + } + } + return false; + } } diff --git a/src/com/android/settings/core/lifecycle/ObservableActivity.java b/src/com/android/settings/core/lifecycle/ObservableActivity.java index bf64c859deb..006c9ae9a72 100644 --- a/src/com/android/settings/core/lifecycle/ObservableActivity.java +++ b/src/com/android/settings/core/lifecycle/ObservableActivity.java @@ -19,6 +19,8 @@ import android.annotation.Nullable; import android.app.Activity; import android.os.Bundle; import android.os.PersistableBundle; +import android.view.Menu; +import android.view.MenuItem; /** * {@link Activity} that has hooks to observe activity lifecycle events. @@ -73,4 +75,31 @@ public class ObservableActivity extends Activity { mLifecycle.onDestroy(); super.onDestroy(); } + + @Override + public boolean onCreateOptionsMenu(final Menu menu) { + if (super.onCreateOptionsMenu(menu)) { + mLifecycle.onCreateOptionsMenu(menu, null); + return true; + } + return false; + } + + @Override + public boolean onPrepareOptionsMenu(final Menu menu) { + if (super.onPrepareOptionsMenu(menu)) { + mLifecycle.onPrepareOptionsMenu(menu); + return true; + } + return false; + } + + @Override + public boolean onOptionsItemSelected(final MenuItem menuItem) { + boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem); + if (!lifecycleHandled) { + return super.onOptionsItemSelected(menuItem); + } + return lifecycleHandled; + } } diff --git a/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java b/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java index 7b35ce575b0..c3265dde0da 100644 --- a/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java +++ b/src/com/android/settings/core/lifecycle/ObservableDialogFragment.java @@ -17,6 +17,9 @@ package com.android.settings.core.lifecycle; import android.app.DialogFragment; import android.content.Context; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; /** * {@link DialogFragment} that has hooks to observe fragment lifecycle events. @@ -61,4 +64,24 @@ public class ObservableDialogFragment extends DialogFragment { super.onDestroy(); } + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + mLifecycle.onCreateOptionsMenu(menu, inflater); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public void onPrepareOptionsMenu(final Menu menu) { + mLifecycle.onPrepareOptionsMenu(menu); + super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(final MenuItem menuItem) { + boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem); + if (!lifecycleHandled) { + return super.onOptionsItemSelected(menuItem); + } + return lifecycleHandled; + } } diff --git a/src/com/android/settings/core/lifecycle/ObservableFragment.java b/src/com/android/settings/core/lifecycle/ObservableFragment.java index b1463250cbc..8dae7ead3cb 100644 --- a/src/com/android/settings/core/lifecycle/ObservableFragment.java +++ b/src/com/android/settings/core/lifecycle/ObservableFragment.java @@ -20,6 +20,9 @@ import android.annotation.CallSuper; import android.app.Fragment; import android.content.Context; import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; public class ObservableFragment extends Fragment { @@ -84,4 +87,28 @@ public class ObservableFragment extends Fragment { mLifecycle.onDestroy(); super.onDestroy(); } + + @CallSuper + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + mLifecycle.onCreateOptionsMenu(menu, inflater); + super.onCreateOptionsMenu(menu, inflater); + } + + @CallSuper + @Override + public void onPrepareOptionsMenu(final Menu menu) { + mLifecycle.onPrepareOptionsMenu(menu); + super.onPrepareOptionsMenu(menu); + } + + @CallSuper + @Override + public boolean onOptionsItemSelected(final MenuItem menuItem) { + boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem); + if (!lifecycleHandled) { + return super.onOptionsItemSelected(menuItem); + } + return lifecycleHandled; + } } \ No newline at end of file diff --git a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java index abe14273bb0..94a0be1b68c 100644 --- a/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java +++ b/src/com/android/settings/core/lifecycle/ObservablePreferenceFragment.java @@ -21,6 +21,9 @@ import android.content.Context; import android.os.Bundle; import android.support.v14.preference.PreferenceFragment; import android.support.v7.preference.PreferenceScreen; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; /** * {@link PreferenceFragment} that has hooks to observe fragment lifecycle events. @@ -95,4 +98,27 @@ public abstract class ObservablePreferenceFragment extends PreferenceFragment { super.onDestroy(); } + @CallSuper + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + mLifecycle.onCreateOptionsMenu(menu, inflater); + super.onCreateOptionsMenu(menu, inflater); + } + + @CallSuper + @Override + public void onPrepareOptionsMenu(final Menu menu) { + mLifecycle.onPrepareOptionsMenu(menu); + super.onPrepareOptionsMenu(menu); + } + + @CallSuper + @Override + public boolean onOptionsItemSelected(final MenuItem menuItem) { + boolean lifecycleHandled = mLifecycle.onOptionsItemSelected(menuItem); + if (!lifecycleHandled) { + return super.onOptionsItemSelected(menuItem); + } + return lifecycleHandled; + } } diff --git a/src/com/android/settings/core/lifecycle/events/OnCreateOptionsMenu.java b/src/com/android/settings/core/lifecycle/events/OnCreateOptionsMenu.java new file mode 100644 index 00000000000..4c794ba8eab --- /dev/null +++ b/src/com/android/settings/core/lifecycle/events/OnCreateOptionsMenu.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 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.settings.core.lifecycle.events; + +import android.view.Menu; +import android.view.MenuInflater; + +public interface OnCreateOptionsMenu { + void onCreateOptionsMenu(Menu menu, MenuInflater inflater); +} diff --git a/src/com/android/settings/core/lifecycle/events/OnOptionsItemSelected.java b/src/com/android/settings/core/lifecycle/events/OnOptionsItemSelected.java new file mode 100644 index 00000000000..b34b407f415 --- /dev/null +++ b/src/com/android/settings/core/lifecycle/events/OnOptionsItemSelected.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 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.settings.core.lifecycle.events; + +import android.view.MenuItem; + +public interface OnOptionsItemSelected { + boolean onOptionsItemSelected(MenuItem menuItem); +} diff --git a/src/com/android/settings/core/lifecycle/events/OnPrepareOptionsMenu.java b/src/com/android/settings/core/lifecycle/events/OnPrepareOptionsMenu.java new file mode 100644 index 00000000000..d64280764c0 --- /dev/null +++ b/src/com/android/settings/core/lifecycle/events/OnPrepareOptionsMenu.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 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.settings.core.lifecycle.events; + +import android.view.Menu; +import android.view.MenuInflater; + +public interface OnPrepareOptionsMenu { + void onPrepareOptionsMenu(Menu menu); +} diff --git a/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java b/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java index db83bbb459b..d863143b952 100644 --- a/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java +++ b/tests/robotests/src/com/android/settings/core/lifecycle/LifecycleTest.java @@ -16,12 +16,18 @@ package com.android.settings.core.lifecycle; import android.content.Context; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.core.lifecycle.events.OnAttach; +import com.android.settings.core.lifecycle.events.OnCreateOptionsMenu; import com.android.settings.core.lifecycle.events.OnDestroy; +import com.android.settings.core.lifecycle.events.OnOptionsItemSelected; import com.android.settings.core.lifecycle.events.OnPause; +import com.android.settings.core.lifecycle.events.OnPrepareOptionsMenu; import com.android.settings.core.lifecycle.events.OnResume; import com.android.settings.core.lifecycle.events.OnStart; import com.android.settings.core.lifecycle.events.OnStop; @@ -71,7 +77,8 @@ public class LifecycleTest { } public static class TestObserver implements LifecycleObserver, OnAttach, OnStart, OnResume, - OnPause, OnStop, OnDestroy { + OnPause, OnStop, OnDestroy, OnCreateOptionsMenu, OnPrepareOptionsMenu, + OnOptionsItemSelected { boolean mOnAttachObserved; boolean mOnAttachHasContext; @@ -80,6 +87,9 @@ public class LifecycleTest { boolean mOnPauseObserved; boolean mOnStopObserved; boolean mOnDestroyObserved; + boolean mOnCreateOptionsMenuObserved; + boolean mOnPrepareOptionsMenuObserved; + boolean mOnOptionsItemSelectedObserved; @Override public void onAttach(Context context) { @@ -111,6 +121,22 @@ public class LifecycleTest { public void onDestroy() { mOnDestroyObserved = true; } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + mOnCreateOptionsMenuObserved = true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + mOnOptionsItemSelectedObserved = true; + return true; + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + mOnPrepareOptionsMenuObserved = true; + } } @Test @@ -122,6 +148,12 @@ public class LifecycleTest { assertThat(activity.mActObserver.mOnStartObserved).isTrue(); ac.resume(); assertThat(activity.mActObserver.mOnResumeObserved).isTrue(); + activity.onCreateOptionsMenu(null); + assertThat(activity.mActObserver.mOnCreateOptionsMenuObserved).isTrue(); + activity.onPrepareOptionsMenu(null); + assertThat(activity.mActObserver.mOnPrepareOptionsMenuObserved).isTrue(); + activity.onOptionsItemSelected(null); + assertThat(activity.mActObserver.mOnOptionsItemSelectedObserved).isTrue(); ac.pause(); assertThat(activity.mActObserver.mOnPauseObserved).isTrue(); ac.stop(); @@ -136,7 +168,11 @@ public class LifecycleTest { Robolectric.buildFragment(TestDialogFragment.class); TestDialogFragment fragment = fragmentController.get(); - fragmentController.attach().create().start().resume().pause().stop().destroy(); + fragmentController.attach().create().start().resume(); + fragment.onCreateOptionsMenu(null, null); + fragment.onPrepareOptionsMenu(null); + fragment.onOptionsItemSelected(null); + fragmentController.pause().stop().destroy(); assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue(); assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue(); @@ -145,6 +181,9 @@ public class LifecycleTest { assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue(); assertThat(fragment.mFragObserver.mOnStopObserved).isTrue(); assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue(); + assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue(); + assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue(); + assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue(); } @Test @@ -153,7 +192,11 @@ public class LifecycleTest { Robolectric.buildFragment(TestFragment.class); TestFragment fragment = fragmentController.get(); - fragmentController.attach().create().start().resume().pause().stop().destroy(); + fragmentController.attach().create().start().resume(); + fragment.onCreateOptionsMenu(null, null); + fragment.onPrepareOptionsMenu(null); + fragment.onOptionsItemSelected(null); + fragmentController.pause().stop().destroy(); assertThat(fragment.mFragObserver.mOnAttachObserved).isTrue(); assertThat(fragment.mFragObserver.mOnAttachHasContext).isTrue(); @@ -162,5 +205,35 @@ public class LifecycleTest { assertThat(fragment.mFragObserver.mOnPauseObserved).isTrue(); assertThat(fragment.mFragObserver.mOnStopObserved).isTrue(); assertThat(fragment.mFragObserver.mOnDestroyObserved).isTrue(); + assertThat(fragment.mFragObserver.mOnCreateOptionsMenuObserved).isTrue(); + assertThat(fragment.mFragObserver.mOnPrepareOptionsMenuObserved).isTrue(); + assertThat(fragment.mFragObserver.mOnOptionsItemSelectedObserved).isTrue(); + } + + private static class OptionItemAccepter implements LifecycleObserver, OnOptionsItemSelected { + public boolean wasCalled = false; + + @Override + public boolean onOptionsItemSelected(MenuItem menuItem) { + wasCalled = true; + return false; + } + } + + @Test + public void onOptionItemSelectedShortCircuitsIfAnObserverHandlesTheMenuItem() { + FragmentController fragmentController = + Robolectric.buildFragment(TestFragment.class); + TestFragment fragment = fragmentController.get(); + OptionItemAccepter accepter = new OptionItemAccepter(); + fragment.getLifecycle().addObserver(accepter); + + fragmentController.attach().create().start().resume(); + fragment.onCreateOptionsMenu(null, null); + fragment.onPrepareOptionsMenu(null); + fragment.onOptionsItemSelected(null); + fragmentController.pause().stop().destroy(); + + assertThat(accepter.wasCalled).isFalse(); } }