/*
* Copyright (C) 2015 The Android Open Source 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 android.service.chooser;
import android.annotation.SdkConstant;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import java.util.List;
A service that receives calls from the system when the user is asked to choose a target for an intent explicitly by another app. The calling app must have invoked ACTION_CHOOSER
as handled by the system; applications do not have the ability to query a ChooserTargetService directly. Which ChooserTargetServices are queried depends on a system-level policy decision
made at the moment the chooser is invoked, including but not limited to user time
spent with the app package or associated components in the foreground, recency of usage
or frequency of usage. These will generally correlate with the order that app targets
are shown in the list of intent handlers shown in the system chooser or resolver.
To extend this class, you must declare the service in your manifest file with the BIND_CHOOSER_TARGET_SERVICE.BIND_CHOOSER_TARGET_SERVICE
permission and include an intent filter with the SERVICE_INTERFACE
action. For example:
<service android:name=".MyChooserTargetService"
android:label="@string/service_name"
android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
<intent-filter>
<action android:name="android.service.chooser.ChooserTargetService" />
</intent-filter>
</service>
For the system to query your service, you must add a <meta-data> element to the
Activity in your manifest that can handle Intents that you would also like to provide
optional deep links for. For example, a chat app might offer deep links to recent active
conversations instead of invoking a generic picker after the app itself is chosen as a target.
The meta-data element should have the name
android.service.chooser.chooser_target_service
and a value corresponding to
the component name of your service. Example:
<activity android:name=".MyShareActivity"
android:label="@string/share_activity_label">
<intent-filter>
<action android:name="android.intent.action.SEND" />
</intent-filter>
<meta-data android:name="android.service.chooser.chooser_target_service"
android:value=".MyChooserTargetService" />
</activity>
/**
* A service that receives calls from the system when the user is asked to choose
* a target for an intent explicitly by another app. The calling app must have invoked
* {@link android.content.Intent#ACTION_CHOOSER ACTION_CHOOSER} as handled by the system;
* applications do not have the ability to query a ChooserTargetService directly.
*
* <p>Which ChooserTargetServices are queried depends on a system-level policy decision
* made at the moment the chooser is invoked, including but not limited to user time
* spent with the app package or associated components in the foreground, recency of usage
* or frequency of usage. These will generally correlate with the order that app targets
* are shown in the list of intent handlers shown in the system chooser or resolver.</p>
*
* <p>To extend this class, you must declare the service in your manifest file with
* the {@link android.Manifest.permission#BIND_CHOOSER_TARGET_SERVICE} permission
* and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
* <pre>
* <service android:name=".MyChooserTargetService"
* android:label="@string/service_name"
* android:permission="android.permission.BIND_CHOOSER_TARGET_SERVICE">
* <intent-filter>
* <action android:name="android.service.chooser.ChooserTargetService" />
* </intent-filter>
* </service>
* </pre>
*
* <p>For the system to query your service, you must add a <meta-data> element to the
* Activity in your manifest that can handle Intents that you would also like to provide
* optional deep links for. For example, a chat app might offer deep links to recent active
* conversations instead of invoking a generic picker after the app itself is chosen as a target.
* </p>
*
* <p>The meta-data element should have the name
* <code>android.service.chooser.chooser_target_service</code> and a value corresponding to
* the component name of your service. Example:</p>
* <pre>
* <activity android:name=".MyShareActivity"
* android:label="@string/share_activity_label">
* <intent-filter>
* <action android:name="android.intent.action.SEND" />
* </intent-filter>
* <meta-data android:name="android.service.chooser.chooser_target_service"
* android:value=".MyChooserTargetService" />
* </activity>
* </pre>
*/
public abstract class ChooserTargetService extends Service {
// TAG = "ChooserTargetService[MySubclass]";
private final String TAG = ChooserTargetService.class.getSimpleName()
+ '[' + getClass().getSimpleName() + ']';
private static final boolean DEBUG = false;
The Intent action that a ChooserTargetService must respond to
/**
* The Intent action that a ChooserTargetService must respond to
*/
@SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
public static final String SERVICE_INTERFACE = "android.service.chooser.ChooserTargetService";
The name of the meta-data
element that must be present on an
activity
element in a manifest to link it to a ChooserTargetService
/**
* The name of the <code>meta-data</code> element that must be present on an
* <code>activity</code> element in a manifest to link it to a ChooserTargetService
*/
public static final String META_DATA_NAME = "android.service.chooser.chooser_target_service";
The permission that a ChooserTargetService must require in order to bind to it.
If this permission is not enforced the system will skip that ChooserTargetService.
/**
* The permission that a ChooserTargetService must require in order to bind to it.
* If this permission is not enforced the system will skip that ChooserTargetService.
*/
public static final String BIND_PERMISSION = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
private IChooserTargetServiceWrapper mWrapper = null;
Called by the system to retrieve a set of deep-link targets
that can handle an intent. The returned list should be sorted such that the most relevant targets appear first.
The score for each ChooserTarget will be combined with the system's score for the original
target Activity to sort and filter targets presented to the user.
Important: Calls to this method from other applications will occur on
a binder thread, not on your app's main thread. Make sure that access to relevant data
within your app is thread-safe.
Params: - targetActivityName – the ComponentName of the matched activity that referred the system
to this ChooserTargetService
- matchedFilter – the specific IntentFilter on the component that was matched
Returns: a list of deep-link targets to fulfill the intent match, sorted by relevance
/**
* Called by the system to retrieve a set of deep-link {@link ChooserTarget targets} that
* can handle an intent.
*
* <p>The returned list should be sorted such that the most relevant targets appear first.
* The score for each ChooserTarget will be combined with the system's score for the original
* target Activity to sort and filter targets presented to the user.</p>
*
* <p><em>Important:</em> Calls to this method from other applications will occur on
* a binder thread, not on your app's main thread. Make sure that access to relevant data
* within your app is thread-safe.</p>
*
* @param targetActivityName the ComponentName of the matched activity that referred the system
* to this ChooserTargetService
* @param matchedFilter the specific IntentFilter on the component that was matched
* @return a list of deep-link targets to fulfill the intent match, sorted by relevance
*/
public abstract List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName,
IntentFilter matchedFilter);
@Override
public IBinder onBind(Intent intent) {
if (DEBUG) Log.d(TAG, "onBind " + intent);
if (!SERVICE_INTERFACE.equals(intent.getAction())) {
if (DEBUG) Log.d(TAG, "bad intent action " + intent.getAction() + "; returning null");
return null;
}
if (mWrapper == null) {
mWrapper = new IChooserTargetServiceWrapper();
}
return mWrapper;
}
private class IChooserTargetServiceWrapper extends IChooserTargetService.Stub {
@Override
public void getChooserTargets(ComponentName targetComponentName,
IntentFilter matchedFilter, IChooserTargetResult result) throws RemoteException {
List<ChooserTarget> targets = null;
final long id = clearCallingIdentity();
try {
if (DEBUG) {
Log.d(TAG, "getChooserTargets calling onGetChooserTargets; "
+ targetComponentName + " filter: " + matchedFilter);
}
targets = onGetChooserTargets(targetComponentName, matchedFilter);
} finally {
restoreCallingIdentity(id);
result.sendResult(targets);
if (DEBUG) Log.d(TAG, "Sent results");
}
}
}
}