Added Application Suggestions.
Added in custom Resolver to handle providing suggestions.
Added in Service to handle providing suggestions to custom resolver.
Added in ability to provider suggestions through a Proxy to another
application which must be installed during compile time if one is
to be used. This is a similar implementation to how the Location
Services work.
Change-Id: Id960260596b7bb6485caa1e1d07744e387a4c6e9
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestManagerService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestManagerService.java
new file mode 100644
index 0000000..d7a6ad4
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestManagerService.java
@@ -0,0 +1,76 @@
+/**
+ * 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.platform.internal;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.SystemService;
+
+import cyanogenmod.app.CMContextConstants;
+import cyanogenmod.app.suggest.ApplicationSuggestion;
+import cyanogenmod.app.suggest.IAppSuggestManager;
+import cyanogenmod.platform.Manifest;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppSuggestManagerService extends SystemService {
+ private static final String TAG = "AppSgstMgrService";
+ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final String NAME = "appsuggest";
+
+ public static final String ACTION = "org.cyanogenmod.app.suggest";
+
+ private AppSuggestProviderInterface mImpl;
+
+ private final IBinder mService = new IAppSuggestManager.Stub() {
+ public boolean handles(Intent intent) {
+ if (mImpl == null) return false;
+
+ return mImpl.handles(intent);
+ }
+
+ public List<ApplicationSuggestion> getSuggestions(Intent intent) {
+ if (mImpl == null) return new ArrayList<>(0);
+
+ return mImpl.getSuggestions(intent);
+ }
+ };
+
+ public AppSuggestManagerService(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ mImpl = AppSuggestProviderProxy.createAndBind(mContext, TAG, ACTION,
+ R.bool.config_enableAppSuggestOverlay,
+ R.string.config_appSuggestProviderPackageName,
+ R.array.config_appSuggestProviderPackageNames);
+ if (mImpl == null) {
+ Slog.e(TAG, "no app suggest provider found");
+ } else {
+ Slog.i(TAG, "Bound to to suggest provider");
+ }
+
+ publishBinderService(CMContextConstants.CM_APP_SUGGEST_SERVICE, mService);
+ }
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderInterface.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderInterface.java
new file mode 100644
index 0000000..da815ce
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderInterface.java
@@ -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 org.cyanogenmod.platform.internal;
+
+import android.content.Intent;
+import cyanogenmod.app.suggest.ApplicationSuggestion;
+
+import java.util.List;
+
+/**
+ * App Suggestion Manager's interface for Applicaiton Suggestion Providers.
+ *
+ * @hide
+ */
+public interface AppSuggestProviderInterface {
+ boolean handles(Intent intent);
+ List<ApplicationSuggestion> getSuggestions(Intent intent);
+}
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderProxy.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderProxy.java
new file mode 100644
index 0000000..0357f73
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/AppSuggestProviderProxy.java
@@ -0,0 +1,102 @@
+/**
+ * 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.platform.internal;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+import com.android.server.ServiceWatcher;
+
+import cyanogenmod.app.suggest.ApplicationSuggestion;
+import cyanogenmod.app.suggest.IAppSuggestProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class AppSuggestProviderProxy implements AppSuggestProviderInterface {
+ private static final String TAG = AppSuggestProviderProxy.class.getSimpleName();
+ private static final boolean DEBUG = AppSuggestManagerService.DEBUG;
+
+ public static AppSuggestProviderProxy createAndBind(
+ Context context, String name, String action,
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId) {
+ AppSuggestProviderProxy proxy = new AppSuggestProviderProxy(context, name, action,
+ overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId);
+ if (proxy.bind()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private Context mContext;
+ private ServiceWatcher mServiceWatcher;
+
+ private AppSuggestProviderProxy(Context context, String name, String action,
+ int overlaySwitchResId, int defaultServicePackageNameResId,
+ int initialPackageNamesResId) {
+ mContext = context;
+ mServiceWatcher = new ServiceWatcher(mContext, TAG + "-" + name, action, overlaySwitchResId,
+ defaultServicePackageNameResId, initialPackageNamesResId, null, null);
+ }
+
+ private boolean bind() {
+ return mServiceWatcher.start();
+ }
+
+ private IAppSuggestProvider getService() {
+ return IAppSuggestProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
+
+ @Override
+ public boolean handles(Intent intent) {
+ IAppSuggestProvider service = getService();
+ if (service == null) return false;
+
+ try {
+ return service.handles(intent);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ return false;
+ }
+
+ @Override
+ public List<ApplicationSuggestion> getSuggestions(Intent intent) {
+ IAppSuggestProvider service = getService();
+ if (service == null) return new ArrayList<>(0);
+
+ try {
+ return service.getSuggestions(intent);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ return new ArrayList<>(0);
+ }
+}
diff --git a/cm/res/AndroidManifest.xml b/cm/res/AndroidManifest.xml
index 8c40827..ec801cd 100644
--- a/cm/res/AndroidManifest.xml
+++ b/cm/res/AndroidManifest.xml
@@ -120,6 +120,13 @@
android:description="@string/permdesc_managePersistentStorage"
android:protectionLevel="system|signature" />
+ <!-- Permission for accessing a provider of app suggestions
+ @hide -->
+ <permission android:name="cyanogenmod.permission.ACCESS_APP_SUGGESTIONS"
+ android:label="@string/permlab_accessAppSuggestions"
+ android:description="@string/permdesc_accessAppSuggestions"
+ android:protectionLevel="signature|system|development" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/cm/res/res/values/config.xml b/cm/res/res/values/config.xml
new file mode 100644
index 0000000..eb8982d
--- /dev/null
+++ b/cm/res/res/values/config.xml
@@ -0,0 +1,41 @@
+<?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>
+ <!-- Whether to enable app suggest overlay which allows app suggest
+ provider to be replaced by an app at run-time. When disabled, only
+ the config_appSuggestProviderPackageName will be searched for app
+ suggest provider, otherwise packages whos signature matches the
+ signature of config_appSuggestProviderPackageNames will be searched,
+ and the service with the highest version number will be picked.
+ Anyone who wants to disable the overlay mechanism can set it to false.
+
+ Note: There appears to be an issue with false if we reinstall the provider which causes
+ it to not get the update and fail to reconnect on package update. It's safer to just
+ use the list version with config_appSuggestProviderPackageNames.
+ -->
+ <bool name="config_enableAppSuggestOverlay" translatable="false">true</bool>
+
+ <!-- Package name providing app suggest support. Used only when
+ config_enableAppSuggestOverlay is false. -->
+ <string name="config_appSuggestProviderPackageName" translatable="false">com.cyanogen.app.suggest</string>
+
+ <!-- List of packages providing app suggest support. Used only when
+ config_enableAppSuggestOverlay is true. -->
+ <string-array name="config_appSuggestProviderPackageNames" translatable="false">
+ <item>com.cyanogen.app.suggest</item>
+ </string-array>
+</resources>
\ No newline at end of file
diff --git a/cm/res/res/values/strings.xml b/cm/res/res/values/strings.xml
index e727080..bcfed5a 100644
--- a/cm/res/res/values/strings.xml
+++ b/cm/res/res/values/strings.xml
@@ -70,6 +70,10 @@
<string name="permlab_managePersistentStorage">manage persistent storage</string>
<string name="permdesc_managePersistentStorage">Allows an app to read or write properties which may persist thrοugh a factory reset.</string>
+ <!-- Labels for the ACCESS_APP_SUGGESTIONS permission -->
+ <string name="permlab_accessAppSuggestions">access application suggestions</string>
+ <string name="permdesc_accessAppSuggestions">Allows an app to access application suggestions.</string>
+
<!-- Label to show for a service that is running because it is observing the user's custom tiles. -->
<string name="custom_tile_listener_binding_label">Custom tile listener</string>
diff --git a/cm/res/res/values/symbols.xml b/cm/res/res/values/symbols.xml
index 3dcf497..7977939 100644
--- a/cm/res/res/values/symbols.xml
+++ b/cm/res/res/values/symbols.xml
@@ -19,6 +19,10 @@
SDK. Instead, put them here. -->
<private-symbols package="org.cyanogenmod.platform.internal" />
+ <java-symbol type="bool" name="config_enableAppSuggestOverlay"/>
+ <java-symbol type="string" name="config_appSuggestProviderPackageName"/>
+ <java-symbol type="array" name="config_appSuggestProviderPackageNames"/>
+
<java-symbol type="string" name="custom_tile_listener_binding_label" />
<!-- Profiles -->