diff options
29 files changed, 1033 insertions, 1 deletions
diff --git a/samples/viewhost/.gitignore b/samples/viewhost/.gitignore new file mode 100644 index 00000000..9c4de582 --- /dev/null +++ b/samples/viewhost/.gitignore @@ -0,0 +1,7 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/samples/viewhost/Android.mk b/samples/viewhost/Android.mk new file mode 100644 index 00000000..67e82a37 --- /dev/null +++ b/samples/viewhost/Android.mk @@ -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) diff --git a/samples/viewhost/AndroidManifest.xml b/samples/viewhost/AndroidManifest.xml new file mode 100644 index 00000000..1e30cf05 --- /dev/null +++ b/samples/viewhost/AndroidManifest.xml @@ -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> diff --git a/samples/viewhost/res/layout/activity_main.xml b/samples/viewhost/res/layout/activity_main.xml new file mode 100644 index 00000000..a21a338d --- /dev/null +++ b/samples/viewhost/res/layout/activity_main.xml @@ -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>
\ No newline at end of file diff --git a/samples/viewhost/res/mipmap-hdpi/ic_launcher.png b/samples/viewhost/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..cde69bcc --- /dev/null +++ b/samples/viewhost/res/mipmap-hdpi/ic_launcher.png diff --git a/samples/viewhost/res/mipmap-mdpi/ic_launcher.png b/samples/viewhost/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..c133a0cb --- /dev/null +++ b/samples/viewhost/res/mipmap-mdpi/ic_launcher.png diff --git a/samples/viewhost/res/mipmap-xhdpi/ic_launcher.png b/samples/viewhost/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..bfa42f0e --- /dev/null +++ b/samples/viewhost/res/mipmap-xhdpi/ic_launcher.png diff --git a/samples/viewhost/res/mipmap-xxhdpi/ic_launcher.png b/samples/viewhost/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..324e72cd --- /dev/null +++ b/samples/viewhost/res/mipmap-xxhdpi/ic_launcher.png diff --git a/samples/viewhost/res/values-v21/styles.xml b/samples/viewhost/res/values-v21/styles.xml new file mode 100644 index 00000000..9712f4b5 --- /dev/null +++ b/samples/viewhost/res/values-v21/styles.xml @@ -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> diff --git a/samples/viewhost/res/values-w820dp/dimens.xml b/samples/viewhost/res/values-w820dp/dimens.xml new file mode 100644 index 00000000..4784b6fd --- /dev/null +++ b/samples/viewhost/res/values-w820dp/dimens.xml @@ -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> diff --git a/samples/viewhost/res/values/dimens.xml b/samples/viewhost/res/values/dimens.xml new file mode 100644 index 00000000..d9dcc26b --- /dev/null +++ b/samples/viewhost/res/values/dimens.xml @@ -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> diff --git a/samples/viewhost/res/values/strings.xml b/samples/viewhost/res/values/strings.xml new file mode 100644 index 00000000..4762b189 --- /dev/null +++ b/samples/viewhost/res/values/strings.xml @@ -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> diff --git a/samples/viewhost/res/values/styles.xml b/samples/viewhost/res/values/styles.xml new file mode 100644 index 00000000..9531e170 --- /dev/null +++ b/samples/viewhost/res/values/styles.xml @@ -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> diff --git a/samples/viewhost/src/org/cyanogenmod/samples/extviewhost/MainActivity.java b/samples/viewhost/src/org/cyanogenmod/samples/extviewhost/MainActivity.java new file mode 100644 index 00000000..0ce662e1 --- /dev/null +++ b/samples/viewhost/src/org/cyanogenmod/samples/extviewhost/MainActivity.java @@ -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); + } + +} diff --git a/samples/viewprovider/Android.mk b/samples/viewprovider/Android.mk new file mode 100644 index 00000000..dda3a6f6 --- /dev/null +++ b/samples/viewprovider/Android.mk @@ -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)
\ No newline at end of file diff --git a/samples/viewprovider/AndroidManifest.xml b/samples/viewprovider/AndroidManifest.xml new file mode 100644 index 00000000..0556a03d --- /dev/null +++ b/samples/viewprovider/AndroidManifest.xml @@ -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> diff --git a/samples/viewprovider/res/drawable-hdpi/ic_launcher.png b/samples/viewprovider/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..96a442e5 --- /dev/null +++ b/samples/viewprovider/res/drawable-hdpi/ic_launcher.png diff --git a/samples/viewprovider/res/drawable-ldpi/ic_launcher.png b/samples/viewprovider/res/drawable-ldpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..99238729 --- /dev/null +++ b/samples/viewprovider/res/drawable-ldpi/ic_launcher.png diff --git a/samples/viewprovider/res/drawable-mdpi/ic_launcher.png b/samples/viewprovider/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..359047df --- /dev/null +++ b/samples/viewprovider/res/drawable-mdpi/ic_launcher.png diff --git a/samples/viewprovider/res/drawable-xhdpi/ic_launcher.png b/samples/viewprovider/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 00000000..71c6d760 --- /dev/null +++ b/samples/viewprovider/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/viewprovider/res/layout/main.xml b/samples/viewprovider/res/layout/main.xml new file mode 100644 index 00000000..c2742aaf --- /dev/null +++ b/samples/viewprovider/res/layout/main.xml @@ -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> + diff --git a/samples/viewprovider/res/values/strings.xml b/samples/viewprovider/res/values/strings.xml new file mode 100644 index 00000000..6d6780c6 --- /dev/null +++ b/samples/viewprovider/res/values/strings.xml @@ -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> diff --git a/samples/viewprovider/src/org/cyanogenmod/samples/extview/SampleProviderService.java b/samples/viewprovider/src/org/cyanogenmod/samples/extview/SampleProviderService.java new file mode 100644 index 00000000..21ef2741 --- /dev/null +++ b/samples/viewprovider/src/org/cyanogenmod/samples/extview/SampleProviderService.java @@ -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); + } + } +} diff --git a/src/java/cyanogenmod/externalviews/ExternalView.java b/src/java/cyanogenmod/externalviews/ExternalView.java new file mode 100644 index 00000000..a593af3f --- /dev/null +++ b/src/java/cyanogenmod/externalviews/ExternalView.java @@ -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); + } + } +} diff --git a/src/java/cyanogenmod/externalviews/ExternalViewProperties.java b/src/java/cyanogenmod/externalviews/ExternalViewProperties.java new file mode 100644 index 00000000..2b4404d9 --- /dev/null +++ b/src/java/cyanogenmod/externalviews/ExternalViewProperties.java @@ -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; + } +} diff --git a/src/java/cyanogenmod/externalviews/ExternalViewProviderService.java b/src/java/cyanogenmod/externalviews/ExternalViewProviderService.java new file mode 100644 index 00000000..45230022 --- /dev/null +++ b/src/java/cyanogenmod/externalviews/ExternalViewProviderService.java @@ -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() {} + } +} diff --git a/src/java/cyanogenmod/externalviews/IExternalViewProvider.aidl b/src/java/cyanogenmod/externalviews/IExternalViewProvider.aidl new file mode 100644 index 00000000..e05215f0 --- /dev/null +++ b/src/java/cyanogenmod/externalviews/IExternalViewProvider.aidl @@ -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); +} diff --git a/src/java/cyanogenmod/externalviews/IExternalViewProviderFactory.aidl b/src/java/cyanogenmod/externalviews/IExternalViewProviderFactory.aidl new file mode 100644 index 00000000..75a8b353 --- /dev/null +++ b/src/java/cyanogenmod/externalviews/IExternalViewProviderFactory.aidl @@ -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); +} diff --git a/tests/Android.mk b/tests/Android.mk index d60a11df..e8a73835 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -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 |