External views for cm13

Initial ExternalViewProvider

Change-Id: Ib35d79d3ae01d2e1a98020c5e65e4fbe50d2d322

Fill out methods and run everything on main thread

Change-Id: I05dd1bce5c24057101cd85656d28b01e6681434a

Fix aidl

Change-Id: Ie4b9a4463fdea37a0c5d657ffbc8796d51b57303

Add sample provider

Change-Id: I51b73302f85927aba2e9ea30ce47d6958af8e451

Create window and view earlier and only once

Change-Id: I60264b3c2de302f6f628320f436ac2aeb39de42c

visibility

Change-Id: I6ed2d6431c5aec9d4959a51828c42a700aabbd22

Add logging

Change-Id: I224587bcdfd1ff08673a618c5e45fef384041b8b

Add external view

Change-Id: Ic7f1ba96857239ac0fa43777f7d637716b7aead7

Resource fixes

Change-Id: I75324900d36a465575338c8b0ec9cc568ea50b9a

Add view host

Change-Id: Ief57371563feca707f420a509827c78c48bedf9c

Such hacks :\

Change-Id: I11ff460e7e7fed9cb14531ec3fb4699a37021ee0

Use a real window

Change-Id: Ie79f8188beaa871c6b8c96303c3221cdd7352fc6

Make api able to handle multiple instances of provided views

Change-Id: I185a8950aa577f920eaf7582c53f3316b88765c8

Ensure attribute set is passed to constructor

Change-Id: I9ebb4e8b0f7538ea5d0e7da179b1f1df812532b1

Add clip rect support

Change-Id: I23ac08b9e9e1e07e72bbf761ede1de87f4e4b084

Use final clipRect, instead of member

Change-Id: I2fa5f6ff30adf30fc0fe10f9fac1869ab4c03904
This commit is contained in:
Chris Sarbora 2015-08-06 14:14:47 -07:00 committed by Clark Scheff
parent 9691d4224c
commit b9d7733a67
29 changed files with 1033 additions and 1 deletions

7
samples/viewhost/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures

View File

@ -0,0 +1,24 @@
# 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.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_STATIC_JAVA_LIBRARIES := \
org.cyanogenmod.platform.sdk
LOCAL_SRC_FILES := $(call all-java-files-under, src/)
LOCAL_PACKAGE_NAME := CMExternalViewHost
include $(BUILD_PACKAGE)

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.cyanogenmod.samples.extviewhost" >
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin">
<LinearLayout
android:id="@+id/root"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<cyanogenmod.externalviews.ExternalView
android:id="@+id/external_view"
android:layout_width="match_parent"
android:layout_height="200dp"/>
<View
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#FF0000"/>
<View
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#00FF00"/>
<View
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#0000FF"/>
</LinearLayout>
</ScrollView>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<style name="AppTheme" parent="android:Theme.Material.Light">
</style>
</resources>

View File

@ -0,0 +1,21 @@
<!--
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.
-->
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@ -0,0 +1,20 @@
<!--
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.
-->
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@ -0,0 +1,21 @@
<!--
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.
-->
<resources>
<string name="app_name">ExternalViewTest</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
</resources>

View File

@ -0,0 +1,23 @@
<!--
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.
-->
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@ -0,0 +1,37 @@
/*
* 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 org.cyanogenmod.samples.extviewhost;
import android.app.Activity;
import android.content.ComponentName;
import android.os.Bundle;
import cyanogenmod.externalviews.ExternalView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ExternalView ev = (ExternalView) findViewById(R.id.external_view);
ComponentName cn = new ComponentName("org.cyanogenmod.samples.extview",
"org.cyanogenmod.samples.extview.SampleProviderService");
ev.setProviderComponent(cn);
}
}

View File

@ -0,0 +1,24 @@
# 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.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_STATIC_JAVA_LIBRARIES := \
org.cyanogenmod.platform.sdk
LOCAL_SRC_FILES := $(call all-java-files-under, src/)
LOCAL_PACKAGE_NAME := CMExternalViewProvider
include $(BUILD_PACKAGE)

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.cyanogenmod.samples.extview"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="23" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
<service android:name=".SampleProviderService"
android:exported="true">
</service>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal">
<Button
android:id="@+id/create_bluetooth_on_wifi_trigger_connect"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bluetooth_on_wifi_trigger"/>
<TextView
android:id="@+id/create_bt_on_wifi_status"
android:layout_width="match_parent"
android:layout_height="300dp" />
</LinearLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<resources>
<string name="app_name">Profiles Sample</string>
<string name="bluetooth_on_wifi_trigger">Create profile that triggers on top AP from WifiManager</string>
</resources>

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 org.cyanogenmod.samples.extview;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import cyanogenmod.externalviews.ExternalViewProviderService;
public class SampleProviderService extends ExternalViewProviderService {
@Override
protected ExternalViewProviderService.Provider createExternalView(Bundle options) {
return new ProviderImpl(options);
}
private class ProviderImpl extends Provider {
protected ProviderImpl(Bundle options) {
super(options);
}
@Override
protected View onCreateView() {
return LayoutInflater.from(SampleProviderService.this).inflate(R.layout.main, null);
}
}
}

View File

@ -0,0 +1,246 @@
/*
* 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.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.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 java.util.LinkedList;
/**
* TODO: unhide once documented and finalized
* @hide
*/
public class ExternalView extends View implements Application.ActivityLifecycleCallbacks,
ViewTreeObserver.OnPreDrawListener {
private Context mContext;
private LinkedList<Runnable> mQueue = new LinkedList<Runnable>();
private volatile IExternalViewProvider mExternalViewProvider;
private final ExternalViewProperties mExternalViewProperties;
public ExternalView(Context context, AttributeSet attrs) {
this(context, attrs, null);
}
public ExternalView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs);
}
public ExternalView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
this(context, attrs);
}
public ExternalView(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);
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
try {
mExternalViewProvider = IExternalViewProvider.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();
}
}
private void performAction(Runnable r) {
if (mExternalViewProvider != null) {
r.run();
} else {
mQueue.add(r);
}
}
// view overrides, for positioning
@Override
public boolean onPreDraw() {
long cur = System.currentTimeMillis();
if (!mExternalViewProperties.hasChanged()) {
return true;
}
final int x = mExternalViewProperties.getX();
final int y = mExternalViewProperties.getY();
final int width = mExternalViewProperties.getWidth();
final int height = mExternalViewProperties.getHeight();
final boolean visible = mExternalViewProperties.isVisible();
final Rect clipRect = mExternalViewProperties.getHitRect();
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.alterWindow(x, y, width, height, visible,
clipRect);
} catch (RemoteException e) {
}
}
});
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(ExternalView.this);
}
});
}
@Override
public void onActivityPaused(Activity activity) {
performAction(new Runnable() {
@Override
public void run() {
try {
mExternalViewProvider.onPause();
} catch (RemoteException e) {
}
getViewTreeObserver().removeOnPreDrawListener(ExternalView.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);
}
}
}

View File

@ -0,0 +1,95 @@
/*
* 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.app.Activity;
import android.app.Application;
import android.content.Context;
import android.graphics.Rect;
import android.view.View;
/**
* TODO: unhide once documented and finalized
* @hide
*/
public class ExternalViewProperties {
private final int[] mScreenCoords = new int[2];
private final View mView;
private final View mDecorView;
private int mWidth, mHeight;
private boolean mVisible;
private Rect mHitRect = new Rect();
ExternalViewProperties(View view, Context context) {
mView = view;
if (context instanceof Activity) {
mDecorView = ((Activity) context).getWindow().getDecorView();
} else {
mDecorView = null;
}
}
public Rect getHitRect() {
return mHitRect;
}
public int getX() {
return mScreenCoords[0];
}
public int getY() {
return mScreenCoords[1];
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public boolean isVisible() {
return mVisible;
}
public boolean hasChanged() {
int previousWidth = mWidth;
int previousHeight = mHeight;
mWidth = mView.getWidth();
mHeight = mView.getHeight();
int previousX = mScreenCoords[0];
int previousY = mScreenCoords[1];
mView.getLocationOnScreen(mScreenCoords);
int newX = mScreenCoords[0];
int newY = mScreenCoords[1];
mHitRect.setEmpty();
if (mDecorView != null) {
mDecorView.getHitRect(mHitRect);
}
boolean visible = mView.getLocalVisibleRect(mHitRect);
mVisible = visible;
// Check if anything actually changed
return previousX != newX || previousY != newY
|| previousWidth != mWidth || previousHeight != mHeight
|| mVisible != visible;
}
}

View File

@ -0,0 +1,224 @@
/*
* 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.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 ExternalViewProviderService extends Service {
private static final String TAG = "ExternalViewProvider";
private static final boolean DEBUG = false;
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 ExternalViewProviderService.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 IExternalViewProvider.Stub {
private final Window mWindow;
private final WindowManager.LayoutParams mParams;
private boolean mShouldShow = true;
private boolean mAskedShow = false;
public ProviderImpl() {
mWindow = new PhoneWindow(ExternalViewProviderService.this);
((ViewGroup) mWindow.getDecorView()).addView(onCreateView());
mParams = new WindowManager.LayoutParams();
mParams.type = WindowManager.LayoutParams.TYPE_PHONE;
mParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
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 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();
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() {}
}
}

View File

@ -0,0 +1,32 @@
/**
* 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 IExternalViewProvider
{
oneway void onAttach(in IBinder windowToken);
oneway void onStart();
oneway void onResume();
oneway void onPause();
oneway void onStop();
oneway void onDetach();
void alterWindow(in int x, in int y, in int width, in int height, in boolean visible, in Rect clipRect);
}

View File

@ -0,0 +1,25 @@
/**
* 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.os.Bundle;
/** @hide */
interface IExternalViewProviderFactory
{
IBinder createExternalView(in Bundle options);
}

View File

@ -21,7 +21,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := \
org.cyanogenmod.platform.sdk
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_SRC_FILES := $(call all-subdir-java-files, src/)
LOCAL_PACKAGE_NAME := CMPlatformTests
LOCAL_CERTIFICATE := platform