ExtView: Add keyguard specific interface for KeyguardExternalView

KeyguardExternalView requires extra state callbacks that can be passed
on to the KeyguardExternalViewProvider.  For this reason the
KeyguardExternalViewProvider no longer extends ExternalViewProvider
and instead provides the full implementation needed for ExternalViews
as well as the specific keyguard components.

Change-Id: Icf3589a201bf0ab29ba6e00e2bd6c0149e955e1d
This commit is contained in:
d34d 2015-12-21 10:34:15 -08:00 committed by Gerrit Code Review
parent d1f0a6e4ef
commit f2741cb04c
4 changed files with 533 additions and 14 deletions

View File

@ -42,4 +42,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/org.cy
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/org.cyanogenmod.platform.sdk_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/org.cyanogenmod.platform.internal_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/docs/cm-api-stubs-timestamp)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/cmsdk_stubs_current_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/cmsdk_stubs_current_intermediates)
# KeyguardExternalView uses a new interface which requires cleaning to avoid a runtime exception
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/org.cyanogenmod.platform_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/org.cyanogenmod.platform.sdk_intermediates)

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2015, The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cyanogenmod.externalviews;
import android.graphics.Rect;
/** @hide */
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
oneway void onKeyguardShowing(boolean screenOn);
oneway void onKeyguardDismissed();
oneway void onBouncerShowing(boolean showing);
oneway void onScreenTurnedOn();
oneway void onScreenTurnedOff();
void alterWindow(in int x, in int y, in int width, in int height, in boolean visible,
in Rect clipRect);
}

View File

@ -16,24 +16,40 @@
package cyanogenmod.externalviews;
import android.content.Context;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import java.util.LinkedList;
/**
* TODO: unhide once documented and finalized
* @hide
*/
public final class KeyguardExternalView extends ExternalView {
public class KeyguardExternalView extends View implements Application.ActivityLifecycleCallbacks,
ViewTreeObserver.OnPreDrawListener {
public static final String EXTRA_PERMISSION_LIST = "permissions_list";
public static final String CATEGORY_KEYGUARD_GRANT_PERMISSION
= "org.cyanogenmod.intent.category.KEYGUARD_GRANT_PERMISSION";
private LinkedList<Runnable> mQueue = new LinkedList<Runnable>();
private Context mContext;
private final ExternalViewProperties mExternalViewProperties;
private volatile IKeyguardExternalViewProvider mExternalViewProvider;
private final Point mDisplaySize;
public KeyguardExternalView(Context context, AttributeSet attrs) {
@ -41,22 +57,65 @@ public final class KeyguardExternalView extends ExternalView {
}
public KeyguardExternalView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context,attrs);
this(context, attrs);
}
public KeyguardExternalView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
this(context,attrs);
public KeyguardExternalView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context, attrs);
}
public KeyguardExternalView(Context context, AttributeSet attributeSet,
ComponentName componentName) {
super(context,attributeSet,componentName);
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);
}
mDisplaySize = new Point();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getRealSize(mDisplaySize);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
mExternalViewProvider = IKeyguardExternalViewProvider.Stub.asInterface(
IExternalViewProviderFactory.Stub.asInterface(service).
createExternalView(null));
executeQueue();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mExternalViewProvider = null;
}
};
private void executeQueue() {
while (!mQueue.isEmpty()) {
Runnable r = mQueue.pop();
r.run();
}
}
protected void performAction(Runnable r) {
if (mExternalViewProvider != null) {
r.run();
} else {
mQueue.add(r);
}
}
// view overrides, for positioning
@Override
public boolean onPreDraw() {
if (!mExternalViewProperties.hasChanged()) {
@ -81,4 +140,179 @@ public final class KeyguardExternalView extends ExternalView {
});
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
public void onDetachedFromWindow() {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onDetach();
} catch (RemoteException e) {
}
}
});
}
@Override
public void onAttachedToWindow() {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onAttach(null);
} catch (RemoteException e) {
}
}
});
}
/**
* 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
*/
public void setProviderComponent(ComponentName componentName) {
// unbind any existing external view provider
if (mExternalViewProvider != null) {
mContext.unbindService(mServiceConnection);
}
if (componentName != null) {
mContext.bindService(new Intent().setComponent(componentName),
mServiceConnection, Context.BIND_AUTO_CREATE);
}
}
public void onKeyguardShowing(final boolean screenOn) {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onKeyguardShowing(screenOn);
} catch (RemoteException e) {
}
}
});
}
public void onKeyguardDismissed() {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onKeyguardDismissed();
} catch (RemoteException e) {
}
}
});
}
public void onBouncerShowing(final boolean showing) {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onBouncerShowing(showing);
} catch (RemoteException e) {
}
}
});
}
public void onScreenTurnedOn() {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onScreenTurnedOn();
} catch (RemoteException e) {
}
}
});
}
public void onScreenTurnedOff() {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onScreenTurnedOff();
} catch (RemoteException e) {
}
}
});
}
}

View File

@ -16,23 +16,265 @@
package cyanogenmod.externalviews;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import com.android.internal.policy.PhoneWindow;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* TODO: unhide once documented and finalized
* @hide
*/
public abstract class KeyguardExternalViewProviderService extends ExternalViewProviderService {
public abstract class KeyguardExternalViewProviderService extends Service {
private static final String TAG = KeyguardExternalViewProviderService.class.getSimpleName();
private static final boolean DEBUG = false;
protected abstract class Provider extends ExternalViewProviderService.Provider {
protected Provider(Bundle options) {
super(options);
private WindowManager mWindowManager;
private final Handler mHandler = new Handler();
@Override
public void onCreate() {
super.onCreate();
mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
}
@Override
public final IBinder onBind(Intent intent) {
return new IExternalViewProviderFactory.Stub() {
@Override public IBinder createExternalView(final Bundle options) {
FutureTask<IBinder> c = new FutureTask<IBinder>(new Callable<IBinder>() {
@Override
public IBinder call() throws Exception {
return KeyguardExternalViewProviderService.this
.createExternalView(options).mImpl;
}
});
mHandler.post(c);
try {
return c.get();
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "error: ", e);
return null;
}
}
};
}
protected abstract Provider createExternalView(Bundle options);
protected abstract class Provider {
private final class ProviderImpl extends IKeyguardExternalViewProvider.Stub {
private final Window mWindow;
private final WindowManager.LayoutParams mParams;
private boolean mShouldShow = true;
private boolean mAskedShow = false;
public ProviderImpl(Provider provider) {
mWindow = new PhoneWindow(KeyguardExternalViewProviderService.this);
((ViewGroup) mWindow.getDecorView()).addView(onCreateView());
mParams = new WindowManager.LayoutParams();
mParams.type = provider.getWindowType();
mParams.flags = provider.getWindowFlags();
mParams.gravity = Gravity.LEFT | Gravity.TOP;
mParams.format = PixelFormat.TRANSPARENT;
}
@Override
public void onAttach(IBinder windowToken) throws RemoteException {
mHandler.post(new Runnable() {
@Override
public void run() {
mWindowManager.addView(mWindow.getDecorView(), mParams);
Provider.this.onAttach();
}
});
}
@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() {
@Override
public void run() {
mWindowManager.removeView(mWindow.getDecorView());
Provider.this.onDetach();
}
});
}
@Override
public void onKeyguardShowing(final boolean screenOn) throws RemoteException {
mHandler.post(new Runnable() {
@Override
public void run() {
Provider.this.onKeyguardShowing(screenOn);
}
});
}
@Override
public void onKeyguardDismissed() throws RemoteException {
mHandler.post(new Runnable() {
@Override
public void run() {
Provider.this.onKeyguardDismissed();
}
});
}
@Override
public void onBouncerShowing(final boolean showing) throws RemoteException {
mHandler.post(new Runnable() {
@Override
public void run() {
Provider.this.onBouncerShowing(showing);
}
});
}
@Override
public void onScreenTurnedOn() throws RemoteException {
mHandler.post(new Runnable() {
@Override
public void run() {
Provider.this.onScreenTurnedOn();
}
});
}
@Override
public void onScreenTurnedOff() throws RemoteException {
mHandler.post(new Runnable() {
@Override
public void run() {
Provider.this.onScreenTurnedOff();
}
});
}
@Override
public void alterWindow(final int x, final int y, final int width, final int height,
final boolean visible, final Rect clipRect) {
mHandler.post(new Runnable() {
@Override
public void run() {
mParams.x = x;
mParams.y = y;
mParams.width = width;
mParams.height = height;
if (DEBUG) Log.d(TAG, mParams.toString());
mAskedShow = visible;
updateVisibility();
View decorView = mWindow.getDecorView();
if (decorView.getVisibility() == View.VISIBLE) {
decorView.setClipBounds(clipRect);
}
if (mWindow.getDecorView().getVisibility() != View.GONE)
mWindowManager.updateViewLayout(mWindow.getDecorView(), mParams);
}
});
}
private void updateVisibility() {
if (DEBUG) Log.d(TAG, "shouldShow = " + mShouldShow + " askedShow = " + mAskedShow);
mWindow.getDecorView().setVisibility(mShouldShow && mAskedShow ?
View.VISIBLE : View.GONE);
}
}
private final ProviderImpl mImpl = new ProviderImpl(this);
private final Bundle mOptions;
protected Provider(Bundle options) {
mOptions = options;
}
protected Bundle getOptions() {
return mOptions;
}
protected void onAttach() {}
protected abstract View onCreateView();
protected void onStart() {}
protected void onResume() {}
protected void onPause() {}
protected void onStop() {}
protected void onDetach() {}
protected abstract void onKeyguardShowing(boolean screenOn);
protected abstract void onKeyguardDismissed();
protected abstract void onBouncerShowing(boolean showing);
protected abstract void onScreenTurnedOn();
protected abstract void onScreenTurnedOff();
/*package*/ final int getWindowType() {
return WindowManager.LayoutParams.TYPE_KEYGUARD_PANEL;
}