ExtView: Document the keyguard external view APIs

Change-Id: Ia714c8a4afc40abda2448ba4a757ea34af05a65a
This commit is contained in:
d34d 2015-12-29 08:37:56 -08:00 committed by Gerrit Code Review
parent 7107e06911
commit dbb87e233d
4 changed files with 223 additions and 153 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -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.
*
* <p>This class is intended to only be used within the SystemUi process.</p>
* @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.
*
* <p>A non-interactive component does not receive any input events and functions similar to a
* live wallpaper.</p>
*
* <p>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.</p>
*
* @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();
}
}

View File

@ -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.
*
* <p>Applications extending this class should include the
* {@link cyanogenmod.platform.Manifest.permission#THIRD_PARTY_KEYGUARD} permission in their
* manifest</p>
* <p>Applications extending this class should also extend
* {@link KeyguardExternalViewProviderService.Provider} and return a new instance of
* {@link KeyguardExternalViewProviderService.Provider} in
* {@link KeyguardExternalViewProviderService#createExternalView(Bundle)}.</p>
*/
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.
*
* <p>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.</p>
*/
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.
*
* <p>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.</p>
* @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.
*
* <p>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.</p>
*/
protected abstract void onScreenTurnedOn();
/**
* Called from the host when the screen is turned off.
*
* <p>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.
* </p>
*/
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.
*
* <p>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.</p>
* @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.
*
* <p>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.</p>
* @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.
*
* <p>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.</p>
*/
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.
*
* <p>Interactive components can receive input focus and receive user interaction while
* non-interactive components never receive focus and are purely visual.</p>
* @param isInteractive
*/
protected final void setInteractivity(final boolean isInteractive) {
mImpl.setInteractivity(isInteractive);
}