aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CleanSpec.mk5
-rw-r--r--src/java/cyanogenmod/externalviews/IKeyguardExternalViewProvider.aidl40
-rw-r--r--src/java/cyanogenmod/externalviews/KeyguardExternalView.java252
-rw-r--r--src/java/cyanogenmod/externalviews/KeyguardExternalViewProviderService.java248
4 files changed, 532 insertions, 13 deletions
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<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) {
+ }
+ }
+ });
+ }
}
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 {
+ 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) {
- super(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;
}