diff --git a/src/java/cyanogenmod/externalviews/IKeyguardExternalViewCallbacks.aidl b/src/java/cyanogenmod/externalviews/IKeyguardExternalViewCallbacks.aidl index 175314e..e6331dd 100644 --- a/src/java/cyanogenmod/externalviews/IKeyguardExternalViewCallbacks.aidl +++ b/src/java/cyanogenmod/externalviews/IKeyguardExternalViewCallbacks.aidl @@ -20,8 +20,8 @@ import android.content.Intent; /** @hide */ interface IKeyguardExternalViewCallbacks { - oneway void dismiss(); - oneway void dismissAndStartActivity(in Intent intent); + boolean requestDismiss(); + boolean requestDismissAndStartActivity(in Intent intent); oneway void collapseNotificationPanel(); oneway void setInteractivity(boolean isInteractive); } diff --git a/src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl b/src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl index 352827c..7405763 100644 --- a/src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl +++ b/src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl @@ -24,10 +24,6 @@ import cyanogenmod.externalviews.IKeyguardExternalViewCallbacks; interface IKeyguardExternalViewProvider { oneway void onAttach(in IBinder windowToken); - oneway void onStart(); - oneway void onResume(); - oneway void onPause(); - oneway void onStop(); oneway void onDetach(); // Keyguard specific interface diff --git a/src/java/cyanogenmod/externalviews/KeyguardExternalView.java b/src/java/cyanogenmod/externalviews/KeyguardExternalView.java index 1ce797b..f6ddfde 100644 --- a/src/java/cyanogenmod/externalviews/KeyguardExternalView.java +++ b/src/java/cyanogenmod/externalviews/KeyguardExternalView.java @@ -35,13 +35,28 @@ import android.view.WindowManager; import java.util.LinkedList; /** - * TODO: unhide once documented and finalized + * This class provides a placeholder view for hosting an external view, provided by a + * {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService}, within the lock screen. + * + *

This class is intended to only be used within the SystemUi process.

* @hide */ -public class KeyguardExternalView extends View implements Application.ActivityLifecycleCallbacks, - ViewTreeObserver.OnPreDrawListener, IBinder.DeathRecipient { +public class KeyguardExternalView extends View implements ViewTreeObserver.OnPreDrawListener, + IBinder.DeathRecipient { + /** + * An extra passed via an intent that provides a list of permissions that should be requested + * from the user. + */ public static final String EXTRA_PERMISSION_LIST = "permissions_list"; + + /** + * Category defining an activity to call to request permissions that a + * {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} will need. Apps that + * provide a {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} should + * check that they have the required permission before making any method calls that would + * require a dangerous permission to be granted. + */ public static final String CATEGORY_KEYGUARD_GRANT_PERMISSION = "org.cyanogenmod.intent.category.KEYGUARD_GRANT_PERMISSION"; @@ -68,13 +83,17 @@ public class KeyguardExternalView extends View implements Application.ActivityLi this(context, attrs); } + /** + * @param context + * @param attributeSet + * @param componentName The component name for the + * {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} + * that will be bound to create the external view. + */ public KeyguardExternalView(Context context, AttributeSet attributeSet, ComponentName componentName) { super(context, attributeSet); mContext = getContext(); mExternalViewProperties = new ExternalViewProperties(this, mContext); - Application app = (mContext instanceof Activity) ? ((Activity) mContext).getApplication() - : (Application) mContext; - app.registerActivityLifecycleCallbacks(this); if (componentName != null) { mContext.bindService(new Intent().setComponent(componentName), mServiceConnection, Context.BIND_AUTO_CREATE); @@ -117,17 +136,21 @@ public class KeyguardExternalView extends View implements Application.ActivityLi private final IKeyguardExternalViewCallbacks mKeyguardExternalViewCallbacks = new IKeyguardExternalViewCallbacks.Stub() { @Override - public void dismiss() throws RemoteException { + public boolean requestDismiss() throws RemoteException { if (mCallback != null) { - mCallback.dismiss(); + return mCallback.requestDismiss(); } + + return false; } @Override - public void dismissAndStartActivity(Intent intent) throws RemoteException { + public boolean requestDismissAndStartActivity(Intent intent) throws RemoteException { if (mCallback != null) { - mCallback.dismissAndStartActivity(intent); + return mCallback.requestDismissAndStartActivity(intent); } + + return false; } @Override @@ -185,76 +208,6 @@ public class KeyguardExternalView extends View implements Application.ActivityLi return true; } - // Activity lifecycle callbacks - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - } - - @Override - public void onActivityStarted(Activity activity) { - performAction(new Runnable() { - @Override - public void run() { - try { - mExternalViewProvider.onStart(); - } catch (RemoteException e) { - } - } - }); - } - - @Override - public void onActivityResumed(Activity activity) { - performAction(new Runnable() { - @Override - public void run() { - try { - mExternalViewProvider.onResume(); - } catch (RemoteException e) { - } - getViewTreeObserver().addOnPreDrawListener(KeyguardExternalView.this); - } - }); - } - - @Override - public void onActivityPaused(Activity activity) { - performAction(new Runnable() { - @Override - public void run() { - try { - mExternalViewProvider.onPause(); - } catch (RemoteException e) { - } - getViewTreeObserver().removeOnPreDrawListener(KeyguardExternalView.this); - } - }); - } - - @Override - public void onActivityStopped(Activity activity) { - performAction(new Runnable() { - @Override - public void run() { - try { - mExternalViewProvider.onStop(); - } catch (RemoteException e) { - } - } - }); - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - } - - @Override - public void onActivityDestroyed(Activity activity) { - mExternalViewProvider = null; - mContext.unbindService(mServiceConnection); - } - // Placeholder callbacks @Override @@ -291,10 +244,11 @@ public class KeyguardExternalView extends View implements Application.ActivityLi } /** - * Sets the component of the ExternalViewProviderService to be used for this ExternalView. - * If a provider is already connected to this view, it is first unbound before binding to the - * new provider. - * @param componentName + * Sets the component of the {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} + * to be used for this ExternalView. If a provider is already connected to this view, it is + * first unbound before binding to the new provider. + * @param componentName The {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} + * to bind to. */ public void setProviderComponent(ComponentName componentName) { // unbind any existing external view provider @@ -307,6 +261,10 @@ public class KeyguardExternalView extends View implements Application.ActivityLi } } + /** + * Called from the host when the keyguard is being shown to the user. + * @param screenOn True if the screen is currently on. + */ public void onKeyguardShowing(final boolean screenOn) { performAction(new Runnable() { @Override @@ -319,6 +277,10 @@ public class KeyguardExternalView extends View implements Application.ActivityLi }); } + /** + * Called from the host when the user has unlocked the device. Once this is called the lock + * lock screen should no longer displayed. + */ public void onKeyguardDismissed() { performAction(new Runnable() { @Override @@ -331,6 +293,12 @@ public class KeyguardExternalView extends View implements Application.ActivityLi }); } + /** + * Called from the host when the keyguard is displaying the security screen for the user to + * enter their pin, password, or pattern. + * @param showing True if the bouncer is being show or false when it is dismissed without the + * device being unlocked. + */ public void onBouncerShowing(final boolean showing) { performAction(new Runnable() { @Override @@ -343,6 +311,9 @@ public class KeyguardExternalView extends View implements Application.ActivityLi }); } + /** + * Called from the host when the screen is turned on. + */ public void onScreenTurnedOn() { performAction(new Runnable() { @Override @@ -355,6 +326,9 @@ public class KeyguardExternalView extends View implements Application.ActivityLi }); } + /** + * Called from the host when the screen is turned off. + */ public void onScreenTurnedOff() { performAction(new Runnable() { @Override @@ -367,14 +341,38 @@ public class KeyguardExternalView extends View implements Application.ActivityLi }); } + /** + * External views provided by a + * {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} can be either + * interactive or non-interactive. + * + *

A non-interactive component does not receive any input events and functions similar to a + * live wallpaper.

+ * + *

An interactive component can receive input events and allows the user to interact with it + * when the notification panel is not being displayed on top of the external view.

+ * + * @return True if the current external view is interactive. + */ public boolean isInteractive() { return mIsInteractive; } + /** + * Registers a {@link cyanogenmod.externalviews.KeyguardExternalView.KeyguardExternalViewCallbacks} + * for receiving events from the + * {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} + * @param callback The callback to register + */ public void registerKeyguardExternalViewCallback(KeyguardExternalViewCallbacks callback) { mCallback = callback; } + /** + * Unregister a previously registered + * {@link cyanogenmod.externalviews.KeyguardExternalView.KeyguardExternalViewCallbacks} + * @param callback The callback to unregister + */ public void unregisterKeyguardExternalViewCallback(KeyguardExternalViewCallbacks callback) { if (mCallback != callback) { throw new IllegalArgumentException("Callback not registered"); @@ -382,10 +380,15 @@ public class KeyguardExternalView extends View implements Application.ActivityLi mCallback = null; } + /** + * Callback interface for a {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService} + * to send events to the host's registered + * {@link cyanogenmod.externalviews.KeyguardExternalView.KeyguardExternalViewCallbacks} + */ public interface KeyguardExternalViewCallbacks { - public void dismiss(); - public void dismissAndStartActivity(Intent intent); - public void collapseNotificationPanel(); - public void providerDied(); + boolean requestDismiss(); + boolean requestDismissAndStartActivity(Intent intent); + void collapseNotificationPanel(); + void providerDied(); } } diff --git a/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java b/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java index 3b627b4..0a40d29 100644 --- a/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java +++ b/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java @@ -16,6 +16,7 @@ package cyanogenmod.externalviews; +import android.annotation.NonNull; import android.app.Service; import android.content.Context; import android.content.Intent; @@ -39,8 +40,17 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** - * TODO: unhide once documented and finalized - * @hide + * A class for providing a view that can be displayed within the lock screen. Applications that + * wish to provide a view to be displayed within the lock screen should extend this service. + * + *

Applications extending this class should include the + * {@link cyanogenmod.platform.Manifest.permission#THIRD_PARTY_KEYGUARD} permission in their + * manifest

+ + *

Applications extending this class should also extend + * {@link KeyguardExternalViewProviderService.Provider} and return a new instance of + * {@link KeyguardExternalViewProviderService.Provider} in + * {@link KeyguardExternalViewProviderService#createExternalView(Bundle)}.

*/ public abstract class KeyguardExternalViewProviderService extends Service { @@ -85,8 +95,16 @@ public abstract class KeyguardExternalViewProviderService extends Service { }; } + /** + * Called when the host has bound to this service. + * @param options Optional bundle. This param is currently not used. + * @return The newly created provider. + */ protected abstract Provider createExternalView(Bundle options); + /** + * This class provides an interface for the host and service to communicate to each other. + */ protected abstract class Provider { private final class ProviderImpl extends IKeyguardExternalViewProvider.Stub { private final Window mWindow; @@ -120,50 +138,6 @@ public abstract class KeyguardExternalViewProviderService extends Service { }); } - @Override - public void onStart() throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - Provider.this.onStart(); - } - }); - } - - @Override - public void onResume() throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - mShouldShow = true; - updateVisibility(); - Provider.this.onResume(); - } - }); - } - - @Override - public void onPause() throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - mShouldShow = false; - updateVisibility(); - Provider.this.onPause(); - } - }); - } - - @Override - public void onStop() throws RemoteException { - mHandler.post(new Runnable() { - @Override - public void run() { - Provider.this.onStop(); - } - }); - } - @Override public void onDetach() throws RemoteException { mHandler.post(new Runnable() { @@ -270,28 +244,32 @@ public abstract class KeyguardExternalViewProviderService extends Service { } // callbacks from provider to host - protected final void dismiss() { + protected final boolean requestDismiss() { + boolean ret = true; int N = mCallbacks.beginBroadcast(); for(int i=0; i < N; i++) { IKeyguardExternalViewCallbacks callback = mCallbacks.getBroadcastItem(0); try { - callback.dismiss(); + ret &= callback.requestDismiss(); } catch(RemoteException e) { } } mCallbacks.finishBroadcast(); + return ret; } - protected final void dismissAndStartActivity(final Intent intent) { + protected final boolean requestDismissAndStartActivity(final Intent intent) { + boolean ret = true; int N = mCallbacks.beginBroadcast(); for(int i=0; i < N; i++) { IKeyguardExternalViewCallbacks callback = mCallbacks.getBroadcastItem(0); try { - callback.dismissAndStartActivity(intent); + ret &= callback.requestDismissAndStartActivity(intent); } catch(RemoteException e) { } } mCallbacks.finishBroadcast(); + return ret; } protected final void collapseNotificationPanel() { @@ -331,34 +309,127 @@ public abstract class KeyguardExternalViewProviderService extends Service { return mOptions; } + /** + * Called when the host view is attached to a window. + */ protected void onAttach() {} - protected abstract View onCreateView(); - protected void onStart() {} - protected void onResume() {} - protected void onPause() {} - protected void onStop() {} + + /** + * Called when the host view is detached from a window. + */ protected void onDetach() {} + /** + * Callback used for getting the view to be displayed within the host's content. + * @return The view to be displayed within the host's content. If null is returned no + * content will be displayed. + */ + protected abstract View onCreateView(); + // keyguard events + + /** + * Called from the host when the keyguard is being shown to the user. + * @param screenOn True if the screen is currently on. + */ protected abstract void onKeyguardShowing(boolean screenOn); + + /** + * Called from the host when the user has unlocked the device. Once this is called the lock + * lock screen is no longer being displayed. + * + *

The view component should enter a paused state when this is called, and save any state + * information that may be needed once the lock screen is displayed again. For example, a + * non-interactive component that provides animated visuals should pause playback of those + * animations and save the state, if necessary, of that animation.

+ */ protected abstract void onKeyguardDismissed(); + + /** + * Called from the host when the keyguard is displaying the security screen for the user to + * enter their pin, password, or pattern. + * + *

Interactive components will no longer have focus when the bouncer is displayed and + * should enter a paused or idle state while the bouncer is being shown.

+ * @param showing True if the bouncer is being show or false when it is dismissed without the + * device being unlocked. + */ protected abstract void onBouncerShowing(boolean showing); + + /** + * Called from the host when the screen is turned on. + * + *

The provided view should return to a running state when this is called. For example, + * a non-interactive component that provides animated visuals should resume playback of + * those animations.

+ */ protected abstract void onScreenTurnedOn(); + + /** + * Called from the host when the screen is turned off. + * + *

The provided view should provided view should pause its activity, if not currently + * in a paused state, and do any work necessary to be ready when the screen is turned + * back on. This will allow for a seamless user experience once the screen is turned on. + *

+ */ protected abstract void onScreenTurnedOff(); // callbacks from provider to host - protected final void dismiss() { - mImpl.dismiss(); + + /** + * Request that the keyguard be dismissed. Calling this method will dismiss the lock + * screen, if it is a not secure, or present the user with the security screen for the user + * to enter their security code to finish dismissing the lock screen. + * + *

If the user has a secure lock screen and dismisses the bouncer without entering their + * secure code, the lock screen will not be dismissed and + * {@link KeyguardExternalViewProviderService.Provider#onBouncerShowing(boolean)} will be + * called with {@code onShowing} being set to false, indicating that the lock screen was not + * dismissed as requested.

+ * @return True if the call succeeded. + */ + protected final boolean requestDismiss() { + return mImpl.requestDismiss(); } - protected final void dismissAndStartActivity(final Intent intent) { - mImpl.dismissAndStartActivity(intent); + /** + * Request that the keyguard be dismissed and the activity provided by the given intent be + * started once the keyguard is dismissed. If a secure lock screen is being used the user + * will need to enter their correct security code to finish dismissing the lock screen. + * + *

If the user has a secure lock screen and dismisses the bouncer without entering their + * secure code, the lock screen will not be dismissed and + * {@link KeyguardExternalViewProviderService.Provider#onBouncerShowing(boolean)} will be + * called with onShowing being set to false, indicating that the lock screen was not + * dismissed as requested.

+ * @param intent An intent specifying an activity to launch. + * @return True if the call succeeded. + */ + protected final boolean dismissAndStartActivity(final Intent intent) { + return mImpl.requestDismissAndStartActivity(intent); } + /** + * Call this method when you would like to take focus and hide the notification panel. + * + *

You should call this method if your component requires focus and the users's + * attention. The user will still be able to bring the notifications back into view by + * sliding down from the status bar. + * Calling this method has no effect for non-interactive components.

+ */ protected final void collapseNotificationPanel() { mImpl.collapseNotificationPanel(); } + /** + * This method should be called when the provided view needs to change from interactive to + * non-interactive and vice versa. + * + *

Interactive components can receive input focus and receive user interaction while + * non-interactive components never receive focus and are purely visual.

+ * @param isInteractive + */ protected final void setInteractivity(final boolean isInteractive) { mImpl.setInteractivity(isInteractive); }