diff options
author | dtrainor@chromium.org <dtrainor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-21 23:04:08 +0000 |
---|---|---|
committer | dtrainor@chromium.org <dtrainor@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-21 23:04:08 +0000 |
commit | e5c934a20f125922bbe2916402648fbd532c73d7 (patch) | |
tree | 79e2ea2042f10f7bb4716f5efb59b23f0faa0937 /base/android | |
parent | 675a5a9f1a77c17ee0f26bc72be4a926796dfe95 (diff) | |
download | chromium_src-e5c934a20f125922bbe2916402648fbd532c73d7.zip chromium_src-e5c934a20f125922bbe2916402648fbd532c73d7.tar.gz chromium_src-e5c934a20f125922bbe2916402648fbd532c73d7.tar.bz2 |
Refactor ActivityStatus to not store current
- Refactor ActivityStatus to expose application level visibility over activity visbility.
- Add a listener for the visibility of the Application (are any Activities visible?)
BUG=341231
NOTRY=true
Review URL: https://codereview.chromium.org/159173002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@252679 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/android')
16 files changed, 896 insertions, 488 deletions
diff --git a/base/android/activity_state_list.h b/base/android/activity_state_list.h deleted file mode 100644 index 43c0f80..0000000 --- a/base/android/activity_state_list.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file intentionally does not have header guards, it's included -// inside a macro to generate enum values. - -#ifndef DEFINE_ACTIVITY_STATE -#error "DEFINE_ACTIVITY_STATE should be defined before including this file" -#endif -DEFINE_ACTIVITY_STATE(CREATED, 1) -DEFINE_ACTIVITY_STATE(STARTED, 2) -DEFINE_ACTIVITY_STATE(RESUMED, 3) -DEFINE_ACTIVITY_STATE(PAUSED, 4) -DEFINE_ACTIVITY_STATE(STOPPED, 5) -DEFINE_ACTIVITY_STATE(DESTROYED, 6) diff --git a/base/android/activity_status.cc b/base/android/activity_status.cc deleted file mode 100644 index 4d0be32..0000000 --- a/base/android/activity_status.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/android/activity_status.h" - -#include <jni.h> - -#include "base/memory/singleton.h" -#include "jni/ActivityStatus_jni.h" - -namespace base { -namespace android { - -ActivityStatus::Listener::Listener( - const ActivityStatus::StateChangeCallback& callback) - : callback_(callback) { - ActivityStatus::GetInstance()->RegisterListener(this); -} - -ActivityStatus::Listener::~Listener() { - ActivityStatus::GetInstance()->UnregisterListener(this); -} - -void ActivityStatus::Listener::Notify(ActivityState state) { - callback_.Run(state); -} - -// static -ActivityStatus* ActivityStatus::GetInstance() { - return Singleton<ActivityStatus, - LeakySingletonTraits<ActivityStatus> >::get(); -} - -static void OnActivityStateChange(JNIEnv* env, jclass clazz, int new_state) { - ActivityStatus* activity_status = ActivityStatus::GetInstance(); - ActivityState activity_state = static_cast<ActivityState>(new_state); - activity_status->OnActivityStateChange(activity_state); -} - -bool ActivityStatus::RegisterBindings(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -ActivityStatus::ActivityStatus() - : observers_(new ObserverListThreadSafe<Listener>()) { - Java_ActivityStatus_registerThreadSafeNativeStateListener( - base::android::AttachCurrentThread()); -} - -ActivityStatus::~ActivityStatus() {} - -void ActivityStatus::RegisterListener(Listener* listener) { - observers_->AddObserver(listener); -} - -void ActivityStatus::UnregisterListener(Listener* listener) { - observers_->RemoveObserver(listener); -} - -void ActivityStatus::OnActivityStateChange(ActivityState new_state) { - observers_->Notify(&ActivityStatus::Listener::Notify, new_state); -} - -} // namespace android -} // namespace base diff --git a/base/android/activity_status.h b/base/android/activity_status.h deleted file mode 100644 index 7975a78..0000000 --- a/base/android/activity_status.h +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_ANDROID_ACTIVITY_STATUS_H_ -#define BASE_ANDROID_ACTIVITY_STATUS_H_ - -#include <jni.h> - -#include "base/android/jni_android.h" -#include "base/base_export.h" -#include "base/basictypes.h" -#include "base/memory/ref_counted.h" -#include "base/memory/singleton.h" -#include "base/observer_list_threadsafe.h" - -namespace base { -namespace android { - -// Define activity state values like ACTIVITY_STATE_CREATED in a -// way that ensures they're always the same than their Java counterpart. -enum ActivityState { -#define DEFINE_ACTIVITY_STATE(x, y) ACTIVITY_STATE_##x = y, -#include "base/android/activity_state_list.h" -#undef DEFINE_ACTIVITY_STATE -}; - -// A native helper class to listen to state changes of the current -// Android Activity. This mirrors org.chromium.base.ActivityStatus. -// any thread. -// -// To start listening, create a new instance, passing a callback to a -// function that takes an ActivityState parameter. To stop listening, -// simply delete the listener object. The implementation guarantees -// that the callback will always be called on the thread that created -// the listener. -// -// Example: -// -// void OnActivityStateChange(ActivityState state) { -// ... -// } -// -// // Start listening. -// ActivityStatus::Listener* my_listener = -// new ActivityStatus::Listener(base::Bind(&OnActivityStateChange)); -// -// ... -// -// // Stop listening. -// delete my_listener -// -class BASE_EXPORT ActivityStatus { - public: - typedef base::Callback<void(ActivityState)> StateChangeCallback; - - class Listener { - public: - explicit Listener(const StateChangeCallback& callback); - ~Listener(); - - private: - friend class ActivityStatus; - - void Notify(ActivityState state); - - StateChangeCallback callback_; - - DISALLOW_COPY_AND_ASSIGN(Listener); - }; - - // NOTE: The Java ActivityStatus is a singleton too. - static ActivityStatus* GetInstance(); - - // Internal use: must be public to be called from base_jni_registrar.cc - static bool RegisterBindings(JNIEnv* env); - - // Internal use only: must be public to be called from JNI and unit tests. - void OnActivityStateChange(ActivityState new_state); - - private: - friend struct DefaultSingletonTraits<ActivityStatus>; - - ActivityStatus(); - ~ActivityStatus(); - - void RegisterListener(Listener* listener); - void UnregisterListener(Listener* listener); - - scoped_refptr<ObserverListThreadSafe<Listener> > observers_; - - DISALLOW_COPY_AND_ASSIGN(ActivityStatus); -}; - -} // namespace android -} // namespace base - -#endif // BASE_ANDROID_ACTIVITY_STATUS_H_ diff --git a/base/android/application_state_list.h b/base/android/application_state_list.h new file mode 100644 index 0000000..cbc833e --- /dev/null +++ b/base/android/application_state_list.h @@ -0,0 +1,17 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file intentionally does not have header guards, it's included +// inside a macro to generate enum values. + +// Note that these states represent the most visible Activity state. +// If there are activities with states paused and stopped, only +// HAS_PAUSED_ACTIVITIES should be returned. +#ifndef DEFINE_APPLICATION_STATE +#error "DEFINE_APPLICATION_STATE should be defined before including this file" +#endif +DEFINE_APPLICATION_STATE(HAS_RUNNING_ACTIVITIES, 1) +DEFINE_APPLICATION_STATE(HAS_PAUSED_ACTIVITIES, 2) +DEFINE_APPLICATION_STATE(HAS_STOPPED_ACTIVITIES, 3) +DEFINE_APPLICATION_STATE(HAS_DESTROYED_ACTIVITIES, 4) diff --git a/base/android/application_status_listener.cc b/base/android/application_status_listener.cc new file mode 100644 index 0000000..26365d3 --- /dev/null +++ b/base/android/application_status_listener.cc @@ -0,0 +1,74 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/application_status_listener.h" + +#include <jni.h> + +#include "base/lazy_instance.h" +#include "base/observer_list_threadsafe.h" +#include "jni/ApplicationStatus_jni.h" + +namespace { +struct LeakyLazyObserverListTraits : + base::internal::LeakyLazyInstanceTraits< + ObserverListThreadSafe<base::android::ApplicationStatusListener> > { + static ObserverListThreadSafe<base::android::ApplicationStatusListener>* + New(void* instance) { + ObserverListThreadSafe<base::android::ApplicationStatusListener>* ret = + base::internal::LeakyLazyInstanceTraits<ObserverListThreadSafe< + base::android::ApplicationStatusListener> >::New(instance); + // Leaky. + ret->AddRef(); + return ret; + } +}; + +base::LazyInstance<ObserverListThreadSafe< + base::android::ApplicationStatusListener>, + LeakyLazyObserverListTraits> g_observers = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +namespace base { +namespace android { + +ApplicationStatusListener::ApplicationStatusListener( + const ApplicationStatusListener::ApplicationStateChangeCallback& callback) + : callback_(callback) { + DCHECK(!callback_.is_null()); + g_observers.Get().AddObserver(this); +} + +ApplicationStatusListener::~ApplicationStatusListener() { + g_observers.Get().RemoveObserver(this); +} + +void ApplicationStatusListener::Notify(ApplicationState state) { + callback_.Run(state); +} + +// static +bool ApplicationStatusListener::RegisterBindings(JNIEnv* env) { + bool result = RegisterNativesImpl(env); + if (result) + Java_ApplicationStatus_registerThreadSafeNativeApplicationStateListener(env); + return result; +} + +// static +void ApplicationStatusListener::NotifyApplicationStateChange( + ApplicationState state) { + g_observers.Get().Notify(&ApplicationStatusListener::Notify, state); +} + +static void OnApplicationStateChange(JNIEnv* env, + jclass clazz, + jint new_state) { + ApplicationState application_state = static_cast<ApplicationState>(new_state); + ApplicationStatusListener::NotifyApplicationStateChange(application_state); +} + +} // namespace android +} // namespace base diff --git a/base/android/application_status_listener.h b/base/android/application_status_listener.h new file mode 100644 index 0000000..ef98985 --- /dev/null +++ b/base/android/application_status_listener.h @@ -0,0 +1,79 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_ +#define BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_ + +#include <jni.h> + +#include "base/android/jni_android.h" +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/singleton.h" +#include "base/observer_list_threadsafe.h" + +namespace base { +namespace android { + +// Define application state values like APPLICATION_STATE_VISIBLE in a +// way that ensures they're always the same than their Java counterpart. +enum ApplicationState { +#define DEFINE_APPLICATION_STATE(x, y) APPLICATION_STATE_##x = y, +#include "base/android/application_state_list.h" +#undef DEFINE_APPLICATION_STATE +}; + +// A native helper class to listen to state changes of the Android +// Application. This mirrors org.chromium.base.ApplicationStatus. +// any thread. +// +// To start listening, create a new instance, passing a callback to a +// function that takes an ApplicationState parameter. To stop listening, +// simply delete the listener object. The implementation guarantees +// that the callback will always be called on the thread that created +// the listener. +// +// Example: +// +// void OnApplicationStateChange(ApplicationState state) { +// ... +// } +// +// // Start listening. +// ApplicationStatusListener* my_listener = +// new ApplicationStatusListener( +// base::Bind(&OnApplicationStateChange)); +// +// ... +// +// // Stop listening. +// delete my_listener +// +class BASE_EXPORT ApplicationStatusListener { + public: + typedef base::Callback<void(ApplicationState)> ApplicationStateChangeCallback; + + explicit ApplicationStatusListener( + const ApplicationStateChangeCallback& callback); + ~ApplicationStatusListener(); + + // Internal use: must be public to be called from base_jni_registrar.cc + static bool RegisterBindings(JNIEnv* env); + + // Internal use only: must be public to be called from JNI and unit tests. + static void NotifyApplicationStateChange(ApplicationState state); + + private: + void Notify(ApplicationState state); + + ApplicationStateChangeCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationStatusListener); +}; + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_ diff --git a/base/android/activity_status_unittest.cc b/base/android/application_status_listener_unittest.cc index 3eb0d10..1049628 100644 --- a/base/android/activity_status_unittest.cc +++ b/base/android/application_status_listener_unittest.cc @@ -1,8 +1,8 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/android/activity_status.h" +#include "base/android/application_status_listener.h" #include "base/bind.h" #include "base/callback_forward.h" #include "base/logging.h" @@ -20,11 +20,12 @@ namespace { using base::android::ScopedJavaLocalRef; -// An invalid ActivityState value. -const ActivityState kInvalidActivityState = static_cast<ActivityState>(100); +// An invalid ApplicationState value. +const ApplicationState kInvalidApplicationState = + static_cast<ApplicationState>(100); // Used to generate a callback that stores the new state at a given location. -void StoreStateTo(ActivityState* target, ActivityState state) { +void StoreStateTo(ApplicationState* target, ApplicationState state) { *target = state; } @@ -39,10 +40,9 @@ void RunTasksUntilIdle() { class MultiThreadedTest { public: MultiThreadedTest() - : activity_status_(ActivityStatus::GetInstance()), - state_(kInvalidActivityState), + : state_(kInvalidApplicationState), event_(false, false), - thread_("ActivityStatusTest thread"), + thread_("ApplicationStatusTest thread"), main_() { } @@ -58,14 +58,16 @@ class MultiThreadedTest { event_.Wait(); // Change state, then wait for the thread to modify state. - activity_status_->OnActivityStateChange(ACTIVITY_STATE_CREATED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); event_.Wait(); - EXPECT_EQ(ACTIVITY_STATE_CREATED, state_); + EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, state_); // Again - activity_status_->OnActivityStateChange(ACTIVITY_STATE_DESTROYED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES); event_.Wait(); - EXPECT_EQ(ACTIVITY_STATE_DESTROYED, state_); + EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, state_); } private: @@ -75,51 +77,51 @@ class MultiThreadedTest { void RegisterThreadForEvents() { ExpectOnThread(); - listener_.reset(new ActivityStatus::Listener(base::Bind( + listener_.reset(new ApplicationStatusListener(base::Bind( &MultiThreadedTest::StoreStateAndSignal, base::Unretained(this)))); EXPECT_TRUE(listener_.get()); event_.Signal(); } - void StoreStateAndSignal(ActivityState state) { + void StoreStateAndSignal(ApplicationState state) { ExpectOnThread(); state_ = state; event_.Signal(); } - ActivityStatus* const activity_status_; - ActivityState state_; + ApplicationState state_; base::WaitableEvent event_; base::Thread thread_; base::MessageLoop main_; - scoped_ptr<ActivityStatus::Listener> listener_; + scoped_ptr<ApplicationStatusListener> listener_; }; } // namespace -TEST(ActivityStatusTest, SingleThread) { +TEST(ApplicationStatusListenerTest, SingleThread) { MessageLoop message_loop; - ActivityState result = kInvalidActivityState; + ApplicationState result = kInvalidApplicationState; // Create a new listener that stores the new state into |result| on every // state change. - ActivityStatus::Listener listener( + ApplicationStatusListener listener( base::Bind(&StoreStateTo, base::Unretained(&result))); - EXPECT_EQ(kInvalidActivityState, result); + EXPECT_EQ(kInvalidApplicationState, result); - ActivityStatus* const activity_status = ActivityStatus::GetInstance(); - activity_status->OnActivityStateChange(ACTIVITY_STATE_CREATED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_RUNNING_ACTIVITIES); RunTasksUntilIdle(); - EXPECT_EQ(ACTIVITY_STATE_CREATED, result); + EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, result); - activity_status->OnActivityStateChange(ACTIVITY_STATE_DESTROYED); + ApplicationStatusListener::NotifyApplicationStateChange( + APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES); RunTasksUntilIdle(); - EXPECT_EQ(ACTIVITY_STATE_DESTROYED, result); + EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, result); } -TEST(ActivityStatusTest, TwoThreads) { +TEST(ApplicationStatusListenerTest, TwoThreads) { MultiThreadedTest test; test.Run(); } diff --git a/base/android/base_jni_registrar.cc b/base/android/base_jni_registrar.cc index c474375..e14fd44 100644 --- a/base/android/base_jni_registrar.cc +++ b/base/android/base_jni_registrar.cc @@ -4,7 +4,7 @@ #include "base/android/base_jni_registrar.h" -#include "base/android/activity_status.h" +#include "base/android/application_status_listener.h" #include "base/android/build_info.h" #include "base/android/command_line_android.h" #include "base/android/content_uri_utils.h" @@ -28,7 +28,8 @@ namespace base { namespace android { static RegistrationMethod kBaseRegisteredMethods[] = { - { "ActivityStatus", base::android::ActivityStatus::RegisterBindings }, + { "ApplicationStatusListener", + base::android::ApplicationStatusListener::RegisterBindings }, { "BuildInfo", base::android::BuildInfo::RegisterBindings }, { "CommandLine", base::android::RegisterCommandLine }, { "ContentUriUtils", base::RegisterContentUriUtils }, diff --git a/base/android/java/src/org/chromium/base/ActivityState.java b/base/android/java/src/org/chromium/base/ActivityState.java new file mode 100644 index 0000000..98aff62 --- /dev/null +++ b/base/android/java/src/org/chromium/base/ActivityState.java @@ -0,0 +1,40 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base; + +/** + * A set of states that represent the last state change of an Activity. + */ +public interface ActivityState { + /** + * Represents Activity#onCreate(). + */ + public final int CREATED = 1; + + /** + * Represents Activity#onStart(). + */ + public final int STARTED = 2; + + /** + * Represents Activity#onResume(). + */ + public final int RESUMED = 3; + + /** + * Represents Activity#onPause(). + */ + public final int PAUSED = 4; + + /** + * Represents Activity#onStop(). + */ + public final int STOPPED = 5; + + /** + * Represents Activity#onDestroy(). This is also used when the state of an Activity is unknown. + */ + public final int DESTROYED = 6; +} diff --git a/base/android/java/src/org/chromium/base/ActivityState.template b/base/android/java/src/org/chromium/base/ActivityState.template deleted file mode 100644 index 5b3a9d0..0000000 --- a/base/android/java/src/org/chromium/base/ActivityState.template +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -// A simple auto-generated interface used to list the various -// states of an activity as used by both org.chromium.base.ActivityStatus -// and base/android/activity_status.h -interface ActivityState { -#define DEFINE_ACTIVITY_STATE(x,y) public final int x = y; -#include "base/android/activity_state_list.h" -#undef DEFINE_ACTIVITY_STATE -} diff --git a/base/android/java/src/org/chromium/base/ActivityStatus.java b/base/android/java/src/org/chromium/base/ActivityStatus.java deleted file mode 100644 index df43dbe..0000000 --- a/base/android/java/src/org/chromium/base/ActivityStatus.java +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.base; - -import android.app.Activity; -import android.app.Application; -import android.app.Application.ActivityLifecycleCallbacks; -import android.os.Bundle; - -import java.util.HashMap; -import java.util.Map; - -/** - * Provides information about the current activity's status, and a way - * to register / unregister listeners for state changes. - */ -@JNINamespace("base::android") -public class ActivityStatus { - - // Constants matching activity states reported to StateListener.onStateChange - // As an implementation detail, these are now defined in the auto-generated - // ActivityState interface, to be shared with C++. - public static final int CREATED = ActivityState.CREATED; - public static final int STARTED = ActivityState.STARTED; - public static final int RESUMED = ActivityState.RESUMED; - public static final int PAUSED = ActivityState.PAUSED; - public static final int STOPPED = ActivityState.STOPPED; - public static final int DESTROYED = ActivityState.DESTROYED; - - // Last activity that was shown (or null if none or it was destroyed). - private static Activity sActivity; - - private static final Map<Activity, Integer> sActivityStates = - new HashMap<Activity, Integer>(); - - private static final ObserverList<StateListener> sStateListeners = - new ObserverList<StateListener>(); - - /** - * Interface to be implemented by listeners. - */ - public interface StateListener { - /** - * Called when the activity's state changes. - * @param newState New activity state. - */ - public void onActivityStateChange(int newState); - } - - private ActivityStatus() {} - - /** - * Initializes the activity status for a specified application. - * - * @param application The application whose status you wish to monitor. - */ - public static void initialize(Application application) { - application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - onStateChange(activity, CREATED); - } - - @Override - public void onActivityDestroyed(Activity activity) { - onStateChange(activity, DESTROYED); - } - - @Override - public void onActivityPaused(Activity activity) { - onStateChange(activity, PAUSED); - } - - @Override - public void onActivityResumed(Activity activity) { - onStateChange(activity, RESUMED); - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} - - @Override - public void onActivityStarted(Activity activity) { - onStateChange(activity, STARTED); - } - - @Override - public void onActivityStopped(Activity activity) { - onStateChange(activity, STOPPED); - } - }); - } - - /** - * Must be called by the main activity when it changes state. - * - * @param activity Current activity. - * @param newState New state value. - */ - private static void onStateChange(Activity activity, int newState) { - if (activity == null) throw new IllegalArgumentException("null activity is not supported"); - - if (sActivity != activity) { - // ActivityStatus is notified with the CREATED event very late during the main activity - // creation to avoid making startup performance worse than it is by notifying observers - // that could do some expensive work. This can lead to non-CREATED events being fired - // before the CREATED event which is problematic. - // TODO(pliard): fix http://crbug.com/176837. - if (sActivity == null - || newState == CREATED || newState == RESUMED || newState == STARTED) { - sActivity = activity; - } - } - - if (newState != DESTROYED) { - sActivityStates.put(activity, newState); - } else { - sActivityStates.remove(activity); - } - - if (sActivity == activity) { - for (StateListener listener : sStateListeners) { - listener.onActivityStateChange(newState); - } - if (newState == DESTROYED) { - sActivity = null; - } - } - } - - /** - * Testing method to update the state of the specified activity. - */ - public static void onStateChangeForTesting(Activity activity, int newState) { - onStateChange(activity, newState); - } - - /** - * @return The current activity. - */ - public static Activity getActivity() { - return sActivity; - } - - /** - * @return The current activity's state (if no activity is registered, then DESTROYED will - * be returned). - */ - public static int getState() { - return getStateForActivity(sActivity); - } - - /** - * Query the state for a given activity. If the activity is not being tracked, this will - * return {@link #DESTROYED}. - * - * <p> - * When relying on this method, be familiar with the expected life cycle state - * transitions: - * <a href="http://developer.android.com/guide/components/activities.html#Lifecycle"> - * Activity Lifecycle - * </a> - * - * <p> - * During activity transitions (activity B launching in front of activity A), A will completely - * paused before the creation of activity B begins. - * - * <p> - * A basic flow for activity A starting, followed by activity B being opened and then closed: - * <ul> - * <li> -- Starting Activity A -- - * <li> Activity A - CREATED - * <li> Activity A - STARTED - * <li> Activity A - RESUMED - * <li> -- Starting Activity B -- - * <li> Activity A - PAUSED - * <li> Activity B - CREATED - * <li> Activity B - STARTED - * <li> Activity B - RESUMED - * <li> Activity A - STOPPED - * <li> -- Closing Activity B, Activity A regaining focus -- - * <li> Activity B - PAUSED - * <li> Activity A - STARTED - * <li> Activity A - RESUMED - * <li> Activity B - STOPPED - * <li> Activity B - DESTROYED - * </ul> - * - * @param activity The activity whose state is to be returned. - * @return The state of the specified activity. - */ - public static int getStateForActivity(Activity activity) { - Integer currentStatus = sActivityStates.get(activity); - return currentStatus != null ? currentStatus.intValue() : DESTROYED; - } - - /** - * Registers the given listener to receive activity state changes. - * @param listener Listener to receive state changes. - */ - public static void registerStateListener(StateListener listener) { - sStateListeners.addObserver(listener); - } - - /** - * Unregisters the given listener from receiving activity state changes. - * @param listener Listener that doesn't want to receive state changes. - */ - public static void unregisterStateListener(StateListener listener) { - sStateListeners.removeObserver(listener); - } - - /** - * Registers the single thread-safe native activity status listener. - * This handles the case where the caller is not on the main thread. - * Note that this is used by a leaky singleton object from the native - * side, hence lifecycle management is greatly simplified. - */ - @CalledByNative - private static void registerThreadSafeNativeStateListener() { - ThreadUtils.runOnUiThread(new Runnable () { - @Override - public void run() { - // Register a new listener that calls nativeOnActivityStateChange. - sStateListeners.addObserver(new StateListener() { - @Override - public void onActivityStateChange(int newState) { - nativeOnActivityStateChange(newState); - } - }); - } - }); - } - - // Called to notify the native side of state changes. - // IMPORTANT: This is always called on the main thread! - private static native void nativeOnActivityStateChange(int newState); - - /** - * Checks whether or not the Application's current Activity is visible to the user. Note that - * this includes the PAUSED state, which can happen when the Activity is temporarily covered - * by another Activity's Fragment (e.g.). - * @return True if the Activity is visible, false otherwise. - */ - public static boolean isApplicationVisible() { - int state = getState(); - return state != STOPPED && state != DESTROYED; - } - - /** - * Checks to see if there are any active Activity instances being watched by ActivityStatus. - * @return True if all Activities have been destroyed. - */ - public static boolean isEveryActivityDestroyed() { - return sActivityStates.isEmpty(); - } -} diff --git a/base/android/java/src/org/chromium/base/ApplicationState.template b/base/android/java/src/org/chromium/base/ApplicationState.template new file mode 100644 index 0000000..892c055 --- /dev/null +++ b/base/android/java/src/org/chromium/base/ApplicationState.template @@ -0,0 +1,14 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base; + +// A simple auto-generated interface used to list the various +// states of an application as used by both org.chromium.base.ApplicationStatus +// and base/android/application_status.h +public interface ApplicationState { +#define DEFINE_APPLICATION_STATE(x,y) public final int x = y; +#include "base/android/application_state_list.h" +#undef DEFINE_APPLICATION_STATE +} diff --git a/base/android/java/src/org/chromium/base/ApplicationStatus.java b/base/android/java/src/org/chromium/base/ApplicationStatus.java new file mode 100644 index 0000000..ce3cafe --- /dev/null +++ b/base/android/java/src/org/chromium/base/ApplicationStatus.java @@ -0,0 +1,421 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base; + +import android.app.Activity; +import android.app.Application; +import android.app.Application.ActivityLifecycleCallbacks; +import android.content.Context; +import android.os.Bundle; + +import java.util.HashMap; +import java.util.Map; + +/** + * Provides information about the current activity's status, and a way + * to register / unregister listeners for state changes. + */ +@JNINamespace("base::android") +public class ApplicationStatus { + private static class ActivityInfo { + private int mStatus = ActivityState.DESTROYED; + private ObserverList<ActivityStateListener> mListeners = + new ObserverList<ActivityStateListener>(); + + /** + * @return The current {@link ActivityState} of the activity. + */ + public int getStatus() { + return mStatus; + } + + /** + * @param status The new {@link ActivityState} of the activity. + */ + public void setStatus(int status) { + mStatus = status; + } + + /** + * @return A list of {@link ActivityStateListener}s listening to this activity. + */ + public ObserverList<ActivityStateListener> getListeners() { + return mListeners; + } + } + + private static Application sApplication; + + private static Integer sCachedApplicationState; + + /** Last activity that was shown (or null if none or it was destroyed). */ + private static Activity sActivity; + + /** + * + */ + private static final Map<Activity, ActivityInfo> sActivityInfo = + new HashMap<Activity, ActivityInfo>(); + + /** + * + */ + private static final ObserverList<ActivityStateListener> sGeneralActivityStateListeners = + new ObserverList<ActivityStateListener>(); + + /** + * A list of observers to be notified when the visibility state of this {@link Application} + * changes. See {@link #getStateForApplication()}. + */ + private static final ObserverList<ApplicationStateListener> sApplicationStateListeners = + new ObserverList<ApplicationStateListener>(); + + /** + * Interface to be implemented by listeners. + */ + public interface ApplicationStateListener { + /** + * Called when the application's state changes. + * @param newState The application state. + */ + public void onApplicationStateChange(int newState); + } + + /** + * Interface to be implemented by listeners. + */ + public interface ActivityStateListener { + /** + * Called when the activity's state changes. + * @param activity The activity that had a state change. + * @param newState New activity state. + */ + public void onActivityStateChange(Activity activity, int newState); + } + + private ApplicationStatus() {} + + /** + * Initializes the activity status for a specified application. + * + * @param application The application whose status you wish to monitor. + */ + public static void initialize(BaseChromiumApplication application) { + sApplication = application; + + application.registerWindowFocusChangedListener( + new BaseChromiumApplication.WindowFocusChangedListener() { + @Override + public void onWindowFocusChanged(Activity activity, boolean hasFocus) { + if (!hasFocus || activity == sActivity) return; + + int state = getStateForActivity(activity); + + if (state != ActivityState.DESTROYED && state != ActivityState.STOPPED) { + sActivity = activity; + } + + // TODO(dtrainor): Notify of active activity change? + } + }); + + application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(final Activity activity, Bundle savedInstanceState) { + onStateChange(activity, ActivityState.CREATED); + } + + @Override + public void onActivityDestroyed(Activity activity) { + onStateChange(activity, ActivityState.DESTROYED); + } + + @Override + public void onActivityPaused(Activity activity) { + onStateChange(activity, ActivityState.PAUSED); + } + + @Override + public void onActivityResumed(Activity activity) { + onStateChange(activity, ActivityState.RESUMED); + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} + + @Override + public void onActivityStarted(Activity activity) { + onStateChange(activity, ActivityState.STARTED); + } + + @Override + public void onActivityStopped(Activity activity) { + onStateChange(activity, ActivityState.STOPPED); + } + }); + } + + /** + * Must be called by the main activity when it changes state. + * + * @param activity Current activity. + * @param newState New state value. + */ + private static void onStateChange(Activity activity, int newState) { + if (activity == null) throw new IllegalArgumentException("null activity is not supported"); + + if (sActivity == null + || newState == ActivityState.CREATED + || newState == ActivityState.RESUMED + || newState == ActivityState.STARTED) { + sActivity = activity; + } + + int oldApplicationState = getStateForApplication(); + + if (newState == ActivityState.CREATED) { + assert !sActivityInfo.containsKey(activity); + sActivityInfo.put(activity, new ActivityInfo()); + } + + // Invalidate the cached application state. + sCachedApplicationState = null; + + ActivityInfo info = sActivityInfo.get(activity); + info.setStatus(newState); + + // Notify all state observers that are specifically listening to this activity. + for (ActivityStateListener listener : info.getListeners()) { + listener.onActivityStateChange(activity, newState); + } + + // Notify all state observers that are listening globally for all activity state + // changes. + for (ActivityStateListener listener : sGeneralActivityStateListeners) { + listener.onActivityStateChange(activity, newState); + } + + int applicationState = getStateForApplication(); + if (applicationState != oldApplicationState) { + for (ApplicationStateListener listener : sApplicationStateListeners) { + listener.onApplicationStateChange(applicationState); + } + } + + if (newState == ActivityState.DESTROYED) { + sActivityInfo.remove(activity); + if (activity == sActivity) sActivity = null; + } + } + + /** + * Testing method to update the state of the specified activity. + */ + public static void onStateChangeForTesting(Activity activity, int newState) { + onStateChange(activity, newState); + } + + /** + * @return The most recent focused {@link Activity} tracked by this class. Being focused means + * out of all the activities tracked here, it has most recently gained window focus. + */ + public static Activity getLastTrackedFocusedActivity() { + return sActivity; + } + + /** + * @return The {@link Context} for the {@link Application}. + */ + public static Context getApplicationContext() { + return sApplication != null ? sApplication.getApplicationContext() : null; + } + + /** + * Query the state for a given activity. If the activity is not being tracked, this will + * return {@link ActivityState#DESTROYED}. + * + * <p> + * Please note that Chrome can have multiple activities running simultaneously. Please also + * look at {@link #getStateForApplication()} for more details. + * + * <p> + * When relying on this method, be familiar with the expected life cycle state + * transitions: + * <a href="http://developer.android.com/guide/components/activities.html#Lifecycle"> + * Activity Lifecycle + * </a> + * + * <p> + * During activity transitions (activity B launching in front of activity A), A will completely + * paused before the creation of activity B begins. + * + * <p> + * A basic flow for activity A starting, followed by activity B being opened and then closed: + * <ul> + * <li> -- Starting Activity A -- + * <li> Activity A - ActivityState.CREATED + * <li> Activity A - ActivityState.STARTED + * <li> Activity A - ActivityState.RESUMED + * <li> -- Starting Activity B -- + * <li> Activity A - ActivityState.PAUSED + * <li> Activity B - ActivityState.CREATED + * <li> Activity B - ActivityState.STARTED + * <li> Activity B - ActivityState.RESUMED + * <li> Activity A - ActivityState.STOPPED + * <li> -- Closing Activity B, Activity A regaining focus -- + * <li> Activity B - ActivityState.PAUSED + * <li> Activity A - ActivityState.STARTED + * <li> Activity A - ActivityState.RESUMED + * <li> Activity B - ActivityState.STOPPED + * <li> Activity B - ActivityState.DESTROYED + * </ul> + * + * @param activity The activity whose state is to be returned. + * @return The state of the specified activity (see {@link ActivityState}). + */ + public static int getStateForActivity(Activity activity) { + ActivityInfo info = sActivityInfo.get(activity); + return info != null ? info.getStatus() : ActivityState.DESTROYED; + } + + /** + * @return The state of the application (see {@link ApplicationState}). + */ + public static int getStateForApplication() { + if (sCachedApplicationState == null) sCachedApplicationState = determineApplicationState(); + + return sCachedApplicationState.intValue(); + } + + /** + * Checks whether or not any Activity in this Application is visible to the user. Note that + * this includes the PAUSED state, which can happen when the Activity is temporarily covered + * by another Activity's Fragment (e.g.). + * @return Whether any Activity under this Application is visible. + */ + public static boolean hasVisibleActivities() { + int state = getStateForApplication(); + return state == ApplicationState.HAS_RUNNING_ACTIVITIES + || state == ApplicationState.HAS_PAUSED_ACTIVITIES; + } + + /** + * Checks to see if there are any active Activity instances being watched by ApplicationStatus. + * @return True if all Activities have been destroyed. + */ + public static boolean isEveryActivityDestroyed() { + return sActivityInfo.isEmpty(); + } + + /** + * Registers the given listener to receive state changes for all activities. + * @param listener Listener to receive state changes. + */ + public static void registerStateListenerForAllActivities(ActivityStateListener listener) { + sGeneralActivityStateListeners.addObserver(listener); + } + + /** + * Registers the given listener to receive state changes for {@code activity}. After a call to + * {@link ActivityStateListener#onActivityStateChange(Activity, int)} with + * {@link ActivityState#DESTROYED} all listeners associated with that particular + * {@link Activity} are removed. + * @param listener Listener to receive state changes. + * @param activity Activity to track or {@code null} to track all activities. + */ + public static void registerStateListenerForActivity(ActivityStateListener listener, + Activity activity) { + assert activity != null; + + ActivityInfo info = sActivityInfo.get(activity); + assert info != null && info.getStatus() != ActivityState.DESTROYED; + info.getListeners().addObserver(listener); + } + + /** + * Unregisters the given listener from receiving activity state changes. + * @param listener Listener that doesn't want to receive state changes. + */ + public static void unregisterActivityStateListener(ActivityStateListener listener) { + sGeneralActivityStateListeners.removeObserver(listener); + + // Loop through all observer lists for all activities and remove the listener. + for (ActivityInfo info : sActivityInfo.values()) { + info.getListeners().removeObserver(listener); + } + } + + /** + * Registers the given listener to receive state changes for the application. + * @param listener Listener to receive state state changes. + */ + public static void registerApplicationStateListener(ApplicationStateListener listener) { + sApplicationStateListeners.addObserver(listener); + } + + /** + * Unregisters the given listener from receiving state changes. + * @param listener Listener that doesn't want to receive state changes. + */ + public static void unregisterApplicationStateListener(ApplicationStateListener listener) { + sApplicationStateListeners.removeObserver(listener); + } + + /** + * Registers the single thread-safe native activity status listener. + * This handles the case where the caller is not on the main thread. + * Note that this is used by a leaky singleton object from the native + * side, hence lifecycle management is greatly simplified. + */ + @CalledByNative + private static void registerThreadSafeNativeApplicationStateListener() { + ThreadUtils.runOnUiThread(new Runnable () { + @Override + public void run() { + registerApplicationStateListener(new ApplicationStateListener() { + @Override + public void onApplicationStateChange(int newState) { + nativeOnApplicationStateChange(newState); + } + }); + } + }); + } + + /** + * Determines the current application state as defined by {@link ApplicationState}. This will + * loop over all the activities and check their state to determine what the general application + * state should be. + * @return HAS_RUNNING_ACTIVITIES if any activity is not paused, stopped, or destroyed. + * HAS_PAUSED_ACTIVITIES if none are running and one is paused. + * HAS_STOPPED_ACTIVITIES if none are running/paused and one is stopped. + * HAS_DESTROYED_ACTIVITIES if none are running/paused/stopped. + */ + private static int determineApplicationState() { + boolean hasPausedActivity = false; + boolean hasStoppedActivity = false; + + for (ActivityInfo info : sActivityInfo.values()) { + int state = info.getStatus(); + if (state != ActivityState.PAUSED + && state != ActivityState.STOPPED + && state != ActivityState.DESTROYED) { + return ApplicationState.HAS_RUNNING_ACTIVITIES; + } else if (state == ActivityState.PAUSED) { + hasPausedActivity = true; + } else if (state == ActivityState.STOPPED) { + hasStoppedActivity = true; + } + } + + if (hasPausedActivity) return ApplicationState.HAS_PAUSED_ACTIVITIES; + if (hasStoppedActivity) return ApplicationState.HAS_STOPPED_ACTIVITIES; + return ApplicationState.HAS_DESTROYED_ACTIVITIES; + } + + // Called to notify the native side of state changes. + // IMPORTANT: This is always called on the main thread! + private static native void nativeOnApplicationStateChange(int newState); +} diff --git a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java index 97b2ab4..c921dd9 100644 --- a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java +++ b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java @@ -4,17 +4,96 @@ package org.chromium.base; +import android.app.Activity; import android.app.Application; +import android.os.Bundle; +import android.view.Window; /** * Basic application functionality that should be shared among all browser applications. */ public class BaseChromiumApplication extends Application { + /** + * Interface to be implemented by listeners for window focus events. + */ + public interface WindowFocusChangedListener { + /** + * Called when the window focus changes for {@code activity}. + * @param activity The {@link Activity} that has a window focus changed event. + * @param hasFocus Whether or not {@code activity} gained or lost focus. + */ + public void onWindowFocusChanged(Activity activity, boolean hasFocus); + } + + private ObserverList<WindowFocusChangedListener> sWindowFocusListeners = + new ObserverList<WindowFocusChangedListener>(); @Override public void onCreate() { super.onCreate(); - ActivityStatus.initialize(this); + ApplicationStatus.initialize(this); + + registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(final Activity activity, Bundle savedInstanceState) { + Window.Callback callback = activity.getWindow().getCallback(); + activity.getWindow().setCallback(new WindowCallbackWrapper(callback) { + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + + for (WindowFocusChangedListener listener : sWindowFocusListeners) { + listener.onWindowFocusChanged(activity, hasFocus); + } + } + }); + } + + @Override + public void onActivityDestroyed(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityPaused(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityResumed(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityStarted(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityStopped(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + }); } + /** + * Registers a listener to receive window focus updates on activities in this application. + * @param listener Listener to receive window focus events. + */ + public void registerWindowFocusChangedListener(WindowFocusChangedListener listener) { + sWindowFocusListeners.addObserver(listener); + } + + /** + * Unregisters a listener from receiving window focus updates on activities in this application. + * @param listener Listener that doesn't want to receive window focus events. + */ + public void unregisterWindowFocusChangedListener(WindowFocusChangedListener listener) { + sWindowFocusListeners.removeObserver(listener); + } } diff --git a/base/android/java/src/org/chromium/base/PowerMonitor.java b/base/android/java/src/org/chromium/base/PowerMonitor.java index 3929398..b6d8cda 100644 --- a/base/android/java/src/org/chromium/base/PowerMonitor.java +++ b/base/android/java/src/org/chromium/base/PowerMonitor.java @@ -16,7 +16,7 @@ import android.os.Looper; * Integrates native PowerMonitor with the java side. */ @JNINamespace("base::android") -public class PowerMonitor implements ActivityStatus.StateListener { +public class PowerMonitor implements ApplicationStatus.ApplicationStateListener { private static final long SUSPEND_DELAY_MS = 1 * 60 * 1000; // 1 minute. private static class LazyHolder { private static final PowerMonitor INSTANCE = new PowerMonitor(); @@ -47,7 +47,7 @@ public class PowerMonitor implements ActivityStatus.StateListener { public static void create(Context context) { if (sInstance == null) { sInstance = LazyHolder.INSTANCE; - ActivityStatus.registerStateListener(sInstance); + ApplicationStatus.registerApplicationStateListener(sInstance); IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); Intent batteryStatusIntent = context.registerReceiver(null, ifilter); onBatteryChargingChanged(batteryStatusIntent); @@ -71,12 +71,12 @@ public class PowerMonitor implements ActivityStatus.StateListener { } @Override - public void onActivityStateChange(int newState) { - if (newState == ActivityStatus.RESUMED) { + public void onApplicationStateChange(int newState) { + if (newState == ApplicationState.HAS_RUNNING_ACTIVITIES) { // Remove the callback from the message loop in case it hasn't been executed yet. mHandler.removeCallbacks(sSuspendTask); nativeOnMainActivityResumed(); - } else if (newState == ActivityStatus.PAUSED) { + } else if (newState == ApplicationState.HAS_PAUSED_ACTIVITIES) { mHandler.postDelayed(sSuspendTask, SUSPEND_DELAY_MS); } } diff --git a/base/android/java/src/org/chromium/base/WindowCallbackWrapper.java b/base/android/java/src/org/chromium/base/WindowCallbackWrapper.java new file mode 100644 index 0000000..20b5c17 --- /dev/null +++ b/base/android/java/src/org/chromium/base/WindowCallbackWrapper.java @@ -0,0 +1,134 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base; + +import android.view.ActionMode; +import android.view.ActionMode.Callback; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager.LayoutParams; +import android.view.accessibility.AccessibilityEvent; + +/** + * A wrapper for a Window.Callback instance, allowing subclasses to listen to or override specific + * window messages. + */ +class WindowCallbackWrapper implements Window.Callback { + private final Window.Callback mCallback; + + public WindowCallbackWrapper(Window.Callback callback) { + mCallback = callback; + } + + @Override + public boolean dispatchGenericMotionEvent(MotionEvent event) { + return mCallback.dispatchGenericMotionEvent(event); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return mCallback.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + return mCallback.dispatchKeyShortcutEvent(event); + } + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + return mCallback.dispatchPopulateAccessibilityEvent(event); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + return mCallback.dispatchTouchEvent(event); + } + + @Override + public boolean dispatchTrackballEvent(MotionEvent event) { + return mCallback.dispatchTrackballEvent(event); + } + + @Override + public void onActionModeFinished(ActionMode mode) { + mCallback.onActionModeFinished(mode); + } + + @Override + public void onActionModeStarted(ActionMode mode) { + mCallback.onActionModeStarted(mode); + } + + @Override + public void onAttachedToWindow() { + mCallback.onAttachedToWindow(); + } + + @Override + public void onContentChanged() { + mCallback.onContentChanged(); + } + + @Override + public boolean onCreatePanelMenu(int featureId, Menu menu) { + return mCallback.onCreatePanelMenu(featureId, menu); + } + + @Override + public View onCreatePanelView(int featureId) { + return mCallback.onCreatePanelView(featureId); + } + + @Override + public void onDetachedFromWindow() { + mCallback.onDetachedFromWindow(); + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + return mCallback.onMenuItemSelected(featureId, item); + } + + @Override + public boolean onMenuOpened(int featureId, Menu menu) { + return mCallback.onMenuOpened(featureId, menu); + } + + @Override + public void onPanelClosed(int featureId, Menu menu) { + mCallback.onPanelClosed(featureId, menu); + } + + @Override + public boolean onPreparePanel(int featureId, View view, Menu menu) { + return mCallback.onPreparePanel(featureId, view, menu); + } + + @Override + public boolean onSearchRequested() { + return mCallback.onSearchRequested(); + } + + @Override + public void onWindowAttributesChanged(LayoutParams attrs) { + mCallback.onWindowAttributesChanged(attrs); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + mCallback.onWindowFocusChanged(hasFocus); + } + + @Override + public ActionMode onWindowStartingActionMode(Callback callback) { + return mCallback.onWindowStartingActionMode(callback); + } + +}
\ No newline at end of file |