From f2741cb04cd6e1735e65cdf0fefb6f50fb737746 Mon Sep 17 00:00:00 2001 From: d34d Date: Mon, 21 Dec 2015 10:34:15 -0800 Subject: [PATCH] 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 --- CleanSpec.mk | 5 +- .../IKeyguardExternalViewProvider.aidl | 40 +++ .../externalviews/KeyguardExternalView.java | 252 +++++++++++++++++- .../KeyguardExternalViewProviderService.java | 250 ++++++++++++++++- 4 files changed, 533 insertions(+), 14 deletions(-) create mode 100644 src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl diff --git a/CleanSpec.mk b/CleanSpec.mk index 0501ef0..3f23969 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -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) \ No newline at end of file +$(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) diff --git a/src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl b/src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl new file mode 100644 index 0000000..10f069e --- /dev/null +++ b/src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl @@ -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); +} diff --git a/src/java/cyanogenmod/externalviews/KeyguardExternalView.java b/src/java/cyanogenmod/externalviews/KeyguardExternalView.java index 516422f..b340a77 100644 --- a/src/java/cyanogenmod/externalviews/KeyguardExternalView.java +++ b/src/java/cyanogenmod/externalviews/KeyguardExternalView.java @@ -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 mQueue = new LinkedList(); + + 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) { + } + } + }); + } } diff --git a/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java b/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java index 208b667..6efcb66 100644 --- a/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java +++ b/src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java @@ -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 c = new FutureTask(new Callable() { + @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; }