mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-20 13:23:59 +00:00
[traffic][android] Move Java source files to android/sdk
Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright © 2017–2020 traffxml.org.
|
||||
*
|
||||
* Relicensed to CoMaps by the original author.
|
||||
*/
|
||||
|
||||
package app.organicmaps.sdk.traffxml;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import app.organicmaps.sdk.traffxml.Version;
|
||||
import app.organicmaps.sdk.traffxml.AndroidTransport;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentFilter.MalformedMimeTypeException;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
public class AndroidConsumer {
|
||||
/**
|
||||
* Creates an Intent filter which matches the Intents a TraFF consumer needs to receive.
|
||||
*
|
||||
* <p>Different filters are available for consumers implementing different versions of the TraFF
|
||||
* specification.
|
||||
*
|
||||
* @param version The version of the TraFF specification (one of the constants in {@link org.traffxml.traff.Version})
|
||||
*
|
||||
* @return An intent filter matching the necessary Intents
|
||||
*/
|
||||
public static IntentFilter createIntentFilter(int version) {
|
||||
IntentFilter res = new IntentFilter();
|
||||
switch (version) {
|
||||
case Version.V0_7:
|
||||
res.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
|
||||
break;
|
||||
case Version.V0_8:
|
||||
res.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
|
||||
res.addDataScheme(AndroidTransport.CONTENT_SCHEMA);
|
||||
try {
|
||||
res.addDataType(AndroidTransport.MIME_TYPE_TRAFF);
|
||||
} catch (MalformedMimeTypeException e) {
|
||||
// as long as the constant is a well-formed MIME type, this exception never gets thrown
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid version code: " + version);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a TraFF intent to a source.
|
||||
*
|
||||
* <p>This encapsulates most of the low-level Android handling.
|
||||
*
|
||||
* <p>If the recipient specified in {@code packageName} declares multiple receivers for the intent in its
|
||||
* manifest, a separate intent will be delivered to each of them. The intent will not be delivered to
|
||||
* receivers registered at runtime.
|
||||
*
|
||||
* <p>All intents are sent as explicit ordered broadcasts. This means two things:
|
||||
*
|
||||
* <p>Any app which declares a matching receiver in its manifest will be woken up to process the intent.
|
||||
* This works even with certain Android 7 builds which restrict intent delivery to apps which are not
|
||||
* currently running.
|
||||
*
|
||||
* <p>It is safe for the recipient to unconditionally set result data. If the recipient does not set
|
||||
* result data, the result will have a result code of
|
||||
* {@link org.traffxml.transport.android.AndroidTransport#RESULT_INTERNAL_ERROR}, no data and no extras.
|
||||
*
|
||||
* @param context The context
|
||||
* @param action The intent action.
|
||||
* @param data The intent data (for TraFF, this is the content provider URI), or null
|
||||
* @param extras The extras for the intent
|
||||
* @param packageName The package name for the intent recipient, or null to deliver the intent to all matching receivers
|
||||
* @param receiverPermission A permission which the recipient must hold, or null if not required
|
||||
* @param resultReceiver A BroadcastReceiver which will receive the result for the intent
|
||||
*/
|
||||
public static void sendTraffIntent(Context context, String action, Uri data, Bundle extras, String packageName,
|
||||
String receiverPermission, BroadcastReceiver resultReceiver) {
|
||||
Intent outIntent = new Intent(action);
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> receivers = pm.queryBroadcastReceivers(outIntent, 0);
|
||||
if (receivers != null)
|
||||
for (ResolveInfo receiver : receivers) {
|
||||
if ((packageName != null) && !packageName.equals(receiver.activityInfo.applicationInfo.packageName))
|
||||
continue;
|
||||
ComponentName cn = new ComponentName(receiver.activityInfo.applicationInfo.packageName,
|
||||
receiver.activityInfo.name);
|
||||
outIntent = new Intent(action);
|
||||
if (data != null)
|
||||
outIntent.setData(data);
|
||||
if (extras != null)
|
||||
outIntent.putExtras(extras);
|
||||
outIntent.setComponent(cn);
|
||||
context.sendOrderedBroadcast (outIntent,
|
||||
receiverPermission,
|
||||
resultReceiver,
|
||||
null, // scheduler,
|
||||
AndroidTransport.RESULT_INTERNAL_ERROR, // initialCode,
|
||||
null, // initialData,
|
||||
null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright © 2019–2020 traffxml.org.
|
||||
*
|
||||
* Relicensed to CoMaps by the original author.
|
||||
*/
|
||||
|
||||
package app.organicmaps.sdk.traffxml;
|
||||
|
||||
public class AndroidTransport {
|
||||
/**
|
||||
* Intent to poll a peer for its capabilities.
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*/
|
||||
public static final String ACTION_TRAFF_GET_CAPABILITIES = "org.traffxml.traff.GET_CAPABILITIES";
|
||||
|
||||
/**
|
||||
* Intent to send a heartbeat to a peer.
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*/
|
||||
public static final String ACTION_TRAFF_HEARTBEAT = "org.traffxml.traff.GET_HEARTBEAT";
|
||||
|
||||
/**
|
||||
* Intent to poll a source for information.
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>Polling is a legacy feature on Android and deprecated in TraFF 0.8 (rather than polling, TraFF 0.8
|
||||
* applications query the content provider). Therefore, poll operations are subscriptionless, and the
|
||||
* source should either reply with all messages it currently holds, or ignore the request.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String ACTION_TRAFF_POLL = "org.traffxml.traff.POLL";
|
||||
|
||||
/**
|
||||
* Intent for a push feed.
|
||||
*
|
||||
* <p>This is a broadcast intent. It can be used in different forms:
|
||||
*
|
||||
* <p>As of TraFF 0.8, it must be sent as an explicit broadcast and include the
|
||||
* {@link #EXTRA_SUBSCRIPTION_ID} extra. The intent data must be a URI to the content provider from which
|
||||
* the messages can be retrieved. The {@link #EXTRA_FEED} extra is not supported. The feed is part of a
|
||||
* subscription and will contain only changes over feeds sent previously as part of the same
|
||||
* subscription.
|
||||
*
|
||||
* <p>Legacy applications omit the {@link #EXTRA_SUBSCRIPTION_ID} extra and may send it as an implicit
|
||||
* broadcast. If an application supports both legacy transport and TraFF 0.8 or later, it must include
|
||||
* the {@link #EXTRA_PACKAGE} extra. The feed is sent in the {@link #EXTRA_FEED} extra, as legacy
|
||||
* applications may not support content providers. If sent as a response to a subscriptionless poll, the
|
||||
* source should include all messages it holds, else the set of messages included is at the discretion of
|
||||
* the source.
|
||||
*
|
||||
* <p>Future applications may reintroduce unsolicited push operations for certain scenarios.
|
||||
*/
|
||||
public static final String ACTION_TRAFF_PUSH = "org.traffxml.traff.FEED";
|
||||
|
||||
/**
|
||||
* Intent for a subscription request.
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>The filter list must be specified in the {@link #EXTRA_FILTER_LIST} extra.
|
||||
*
|
||||
* <p>The sender must indicate its package name in the {@link #EXTRA_PACKAGE} extra.
|
||||
*/
|
||||
public static final String ACTION_TRAFF_SUBSCRIBE = "org.traffxml.traff.SUBSCRIBE";
|
||||
|
||||
/**
|
||||
* Intent for a subscription change request,
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>This intent must have {@link #EXTRA_SUBSCRIPTION_ID} set to the ID of an existing subscription between
|
||||
* the calling consumer and the source which receives the broadcast.
|
||||
*
|
||||
* <p>The new filter list must be specified in the {@link #EXTRA_FILTER_LIST} extra.
|
||||
*/
|
||||
public static final String ACTION_TRAFF_SUBSCRIPTION_CHANGE = "org.traffxml.traff.SUBSCRIPTION_CHANGE";
|
||||
|
||||
/**
|
||||
* Intent for an unsubscribe request,
|
||||
*
|
||||
* <p>This is a broadcast intent and must be sent as an explicit broadcast.
|
||||
*
|
||||
* <p>This intent must have {@link #EXTRA_SUBSCRIPTION_ID} set to the ID of an existing subscription between
|
||||
* the calling consumer and the source which receives the broadcast. It signals that the consumer is no
|
||||
* longer interested in receiving messages related to that subscription, and that the source should stop
|
||||
* sending updates. Unsubscribing from a nonexistent subscription is a no-op.
|
||||
*/
|
||||
public static final String ACTION_TRAFF_UNSUBSCRIBE = "org.traffxml.traff.UNSUBSCRIBE";
|
||||
|
||||
/**
|
||||
* Name for the column which holds the message data.
|
||||
*/
|
||||
public static final String COLUMN_DATA = "data";
|
||||
|
||||
/**
|
||||
* Schema for TraFF content URIs.
|
||||
*/
|
||||
public static final String CONTENT_SCHEMA = "content";
|
||||
|
||||
/**
|
||||
* String representations of TraFF result codes
|
||||
*/
|
||||
public static final String[] ERROR_STRINGS = {
|
||||
"unknown (0)",
|
||||
"invalid request (1)",
|
||||
"subscription rejected by the source (2)",
|
||||
"requested area not covered (3)",
|
||||
"requested area partially covered (4)",
|
||||
"subscription ID not recognized by the source (5)",
|
||||
"unknown (6)",
|
||||
"source reported an internal error (7)"
|
||||
};
|
||||
|
||||
/**
|
||||
* Extra which contains the capabilities of the peer.
|
||||
*
|
||||
* <p>This is a String extra. It contains a {@code capabilities} XML element.
|
||||
*/
|
||||
public static final String EXTRA_CAPABILITIES = "capabilities";
|
||||
|
||||
/**
|
||||
* Extra which contains a TraFF feed.
|
||||
*
|
||||
* <p>This is a String extra. It contains a {@code feed} XML element.
|
||||
*
|
||||
* <p>The sender should be careful to keep the size of this extra low, as Android has a 1 MByte limit on all
|
||||
* pending Binder transactions. However, there is no feedback to the sender about the capacity still
|
||||
* available, or whether a request exceeds that limit. Therefore, senders should keep the size if each
|
||||
* feed significantly below that limit. If necessary, they should split up a feed into multiple smaller
|
||||
* ones and send them with a delay in between.
|
||||
*
|
||||
* <p>This mechanism is deprecated since TraFF 0.8 and peers are no longer required to support it. Peers
|
||||
* which support TraFF 0.8 must rely on content providers for message transport.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String EXTRA_FEED = "feed";
|
||||
|
||||
/**
|
||||
* Extra which contains a filter list.
|
||||
*
|
||||
* <p>This is a String extra. It contains a {@code filter_list} XML element.
|
||||
*/
|
||||
public static final String EXTRA_FILTER_LIST = "filter_list";
|
||||
|
||||
/**
|
||||
* Extra which contains the package name of the app sending it.
|
||||
*
|
||||
* <p>This is a String extra.
|
||||
*/
|
||||
public static final String EXTRA_PACKAGE = "package";
|
||||
|
||||
/**
|
||||
* Extra which contains a subscription ID.
|
||||
*
|
||||
* <p>This is a String extra.
|
||||
*/
|
||||
public static final String EXTRA_SUBSCRIPTION_ID = "subscription_id";
|
||||
|
||||
/**
|
||||
* Extra which contains the timeout duration for a subscription.
|
||||
*
|
||||
* <p>This is an integer extra.
|
||||
*/
|
||||
public static final String EXTRA_TIMEOUT = "timeout";
|
||||
|
||||
/**
|
||||
* The MIME type for TraFF content providers.
|
||||
*/
|
||||
public static final String MIME_TYPE_TRAFF = "vnd.android.cursor.dir/org.traffxml.message";
|
||||
|
||||
/**
|
||||
* The operation completed successfully.
|
||||
*/
|
||||
public static final int RESULT_OK = -1;
|
||||
|
||||
/**
|
||||
* An internal error prevented the recipient from fulfilling the request.
|
||||
*/
|
||||
public static final int RESULT_INTERNAL_ERROR = 7;
|
||||
|
||||
/**
|
||||
* A nonexistent operation was attempted, or an operation was attempted with incomplete or otherwise
|
||||
* invalid data.
|
||||
*/
|
||||
public static final int RESULT_INVALID = 1;
|
||||
|
||||
/**
|
||||
* The subscription was rejected, and no messages will be sent.
|
||||
*/
|
||||
public static final int RESULT_SUBSCRIPTION_REJECTED = 2;
|
||||
|
||||
/**
|
||||
* The subscription was rejected because the source will never provide messages matching the selection.
|
||||
*/
|
||||
public static final int RESULT_NOT_COVERED = 3;
|
||||
|
||||
/**
|
||||
* The subscription was accepted but the source can only provide messages for parts of the selection.
|
||||
*/
|
||||
public static final int RESULT_PARTIALLY_COVERED = 4;
|
||||
|
||||
/**
|
||||
* The request failed because it refers to a subscription which does not exist between the source and
|
||||
* consumer involved.
|
||||
*/
|
||||
public static final int RESULT_SUBSCRIPTION_UNKNOWN = 5;
|
||||
|
||||
/**
|
||||
* The request failed because the aggregator does not accept unsolicited push requests from the sensor.
|
||||
*/
|
||||
public static final int RESULT_PUSH_REJECTED = 6;
|
||||
|
||||
public static String formatTraffError(int code) {
|
||||
if ((code < 0) || (code >= ERROR_STRINGS.length))
|
||||
return String.format("unknown (%d)", code);
|
||||
else
|
||||
return ERROR_STRINGS[code];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package app.organicmaps.sdk.traffxml;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Abstract superclass for TraFF source implementations.
|
||||
*/
|
||||
public abstract class SourceImpl extends BroadcastReceiver
|
||||
{
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param context The application context
|
||||
*/
|
||||
public SourceImpl(Context context, long nativeManager)
|
||||
{
|
||||
super();
|
||||
this.context = context;
|
||||
this.nativeManager = nativeManager;
|
||||
}
|
||||
|
||||
protected Context context;
|
||||
|
||||
/**
|
||||
* The native `TraffSourceManager` instance.
|
||||
*/
|
||||
protected long nativeManager;
|
||||
|
||||
/**
|
||||
* Subscribes to a traffic source.
|
||||
*
|
||||
* @param filterList The filter list in XML format
|
||||
*/
|
||||
public abstract void subscribe(String filterList);
|
||||
|
||||
/**
|
||||
* Changes an existing traffic subscription.
|
||||
*
|
||||
* @param filterList The filter list in XML format
|
||||
*/
|
||||
public abstract void changeSubscription(String filterList);
|
||||
|
||||
/**
|
||||
* Unsubscribes from a traffic source we are subscribed to.
|
||||
*/
|
||||
public abstract void unsubscribe();
|
||||
|
||||
/**
|
||||
* Forwards a newly received TraFF feed to the traffic module for processing.
|
||||
*
|
||||
* Called when a TraFF feed is received. This is a wrapper around {@link #onFeedReceivedImpl(long, String)}.
|
||||
*
|
||||
* @param feed The TraFF feed
|
||||
*/
|
||||
protected void onFeedReceived(String feed)
|
||||
{
|
||||
onFeedReceivedImpl(nativeManager, feed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards a newly received TraFF feed to the traffic module for processing.
|
||||
*
|
||||
* Called when a TraFF feed is received.
|
||||
*
|
||||
* @param nativeManager The native `TraffSourceManager` instance
|
||||
* @param feed The TraFF feed
|
||||
*/
|
||||
protected static native void onFeedReceivedImpl(long nativeManager, String feed);
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package app.organicmaps.sdk.traffxml;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import app.organicmaps.sdk.util.log.Logger;
|
||||
|
||||
/**
|
||||
* Implementation for a TraFF 0.7 source.
|
||||
*/
|
||||
public class SourceImplV0_7 extends SourceImpl
|
||||
{
|
||||
private PackageManager pm;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param context The application context
|
||||
*/
|
||||
public SourceImplV0_7(Context context, long nativeManager)
|
||||
{
|
||||
super(context, nativeManager);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to a traffic source.
|
||||
*
|
||||
* @param filterList The filter list in XML format
|
||||
*/
|
||||
@Override
|
||||
public void subscribe(String filterList)
|
||||
{
|
||||
IntentFilter traffFilter07 = new IntentFilter();
|
||||
traffFilter07.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
|
||||
|
||||
this.context.registerReceiver(this, traffFilter07);
|
||||
|
||||
// Broadcast a poll intent to all TraFF 0.7-only receivers
|
||||
Intent outIntent = new Intent(AndroidTransport.ACTION_TRAFF_POLL);
|
||||
pm = this.context.getPackageManager();
|
||||
List<ResolveInfo> receivers07 = pm.queryBroadcastReceivers(outIntent, 0);
|
||||
List<ResolveInfo> receivers08 = pm.queryBroadcastReceivers(new Intent(AndroidTransport.ACTION_TRAFF_GET_CAPABILITIES), 0);
|
||||
if (receivers07 != null)
|
||||
{
|
||||
/*
|
||||
* Get receivers which support only TraFF 0.7 and poll them.
|
||||
* If there are no TraFF 0.7 sources at the moment, we register the receiver nonetheless.
|
||||
* That way, if any new sources are added during the session, we get any messages they send.
|
||||
*/
|
||||
if (receivers08 != null)
|
||||
receivers07.removeAll(receivers08);
|
||||
for (ResolveInfo receiver : receivers07)
|
||||
{
|
||||
ComponentName cn = new ComponentName(receiver.activityInfo.applicationInfo.packageName,
|
||||
receiver.activityInfo.name);
|
||||
outIntent = new Intent(AndroidTransport.ACTION_TRAFF_POLL);
|
||||
outIntent.setComponent(cn);
|
||||
this.context.sendBroadcast(outIntent, Manifest.permission.ACCESS_COARSE_LOCATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes an existing traffic subscription.
|
||||
*
|
||||
* This implementation does nothing, as TraFF 0.7 does not support subscriptions.
|
||||
*
|
||||
* @param filterList The filter list in XML format
|
||||
*/
|
||||
@Override
|
||||
public void changeSubscription(String filterList)
|
||||
{
|
||||
// NOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from a traffic source we are subscribed to.
|
||||
*/
|
||||
@Override
|
||||
public void unsubscribe()
|
||||
{
|
||||
this.context.unregisterReceiver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
if (intent == null)
|
||||
return;
|
||||
|
||||
if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_PUSH))
|
||||
{
|
||||
/* 0.7 feed */
|
||||
String packageName = intent.getStringExtra(AndroidTransport.EXTRA_PACKAGE);
|
||||
/*
|
||||
* If the feed comes from a TraFF 0.8+ source, skip it (this may happen with “bilingual”
|
||||
* TraFF 0.7/0.8 sources). That ensures the only way to get information from such sources is
|
||||
* through a TraFF 0.8 subscription. Fetching the list from scratch each time ensures that
|
||||
* apps installed during runtime get considered.)
|
||||
*/
|
||||
if (packageName != null)
|
||||
{
|
||||
for (ResolveInfo info : pm.queryBroadcastReceivers(new Intent(AndroidTransport.ACTION_TRAFF_GET_CAPABILITIES), 0))
|
||||
if (packageName.equals(info.resolvePackageName))
|
||||
return;
|
||||
}
|
||||
String feed = intent.getStringExtra(AndroidTransport.EXTRA_FEED);
|
||||
if (feed == null)
|
||||
{
|
||||
Logger.w(this.getClass().getSimpleName(), "empty feed, ignoring");
|
||||
}
|
||||
else
|
||||
{
|
||||
onFeedReceived(feed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
package app.organicmaps.sdk.traffxml;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.IntentFilter.MalformedMimeTypeException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import app.organicmaps.sdk.util.log.Logger;
|
||||
|
||||
/**
|
||||
* Implementation for a TraFF 0.8 source.
|
||||
*/
|
||||
public class SourceImplV0_8 extends SourceImpl
|
||||
{
|
||||
|
||||
private String packageName;
|
||||
private String subscriptionId = null;
|
||||
|
||||
/**
|
||||
* Creates a new instance.
|
||||
*
|
||||
* @param context The application context
|
||||
* @param packageName The package name for the source
|
||||
*/
|
||||
public SourceImplV0_8(Context context, long nativeManager, String packageName)
|
||||
{
|
||||
super(context, nativeManager);
|
||||
this.packageName = packageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to a traffic source.
|
||||
*
|
||||
* @param filterList The filter list in XML format
|
||||
*/
|
||||
@Override
|
||||
public void subscribe(String filterList)
|
||||
{
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(AndroidTransport.ACTION_TRAFF_PUSH);
|
||||
filter.addDataScheme(AndroidTransport.CONTENT_SCHEMA);
|
||||
try
|
||||
{
|
||||
filter.addDataType(AndroidTransport.MIME_TYPE_TRAFF);
|
||||
}
|
||||
catch (MalformedMimeTypeException e)
|
||||
{
|
||||
// as long as the constant is a well-formed MIME type, this exception never gets thrown
|
||||
// TODO revisit logging
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
context.registerReceiver(this, filter);
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(AndroidTransport.EXTRA_PACKAGE, context.getPackageName());
|
||||
extras.putString(AndroidTransport.EXTRA_FILTER_LIST, filterList);
|
||||
AndroidConsumer.sendTraffIntent(context, AndroidTransport.ACTION_TRAFF_SUBSCRIBE, null,
|
||||
extras, packageName, Manifest.permission.ACCESS_COARSE_LOCATION, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes an existing traffic subscription.
|
||||
*
|
||||
* @param filterList The filter list in XML format
|
||||
*/
|
||||
@Override
|
||||
public void changeSubscription(String filterList)
|
||||
{
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(AndroidTransport.EXTRA_SUBSCRIPTION_ID, subscriptionId);
|
||||
extras.putString(AndroidTransport.EXTRA_FILTER_LIST, filterList);
|
||||
AndroidConsumer.sendTraffIntent(context, AndroidTransport.ACTION_TRAFF_SUBSCRIPTION_CHANGE, null,
|
||||
extras, packageName, Manifest.permission.ACCESS_COARSE_LOCATION, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribes from a traffic source we are subscribed to.
|
||||
*/
|
||||
@Override
|
||||
public void unsubscribe()
|
||||
{
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString(AndroidTransport.EXTRA_SUBSCRIPTION_ID, subscriptionId);
|
||||
AndroidConsumer.sendTraffIntent(this.context, AndroidTransport.ACTION_TRAFF_UNSUBSCRIBE, null,
|
||||
extras, packageName, Manifest.permission.ACCESS_COARSE_LOCATION, this);
|
||||
|
||||
this.context.unregisterReceiver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
{
|
||||
if (intent == null)
|
||||
return;
|
||||
|
||||
if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_PUSH))
|
||||
{
|
||||
Uri uri = intent.getData();
|
||||
if (uri != null)
|
||||
{
|
||||
/* 0.8 feed */
|
||||
String subscriptionId = intent.getStringExtra(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
|
||||
if (subscriptionId.equals(this.subscriptionId))
|
||||
fetchMessages(context, uri);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.w(this.getClass().getSimpleName(), "no URI in feed, ignoring");
|
||||
} // uri != null
|
||||
} else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_SUBSCRIBE)) {
|
||||
if (this.getResultCode() != AndroidTransport.RESULT_OK) {
|
||||
Bundle extras = this.getResultExtras(true);
|
||||
if (extras != null)
|
||||
Logger.e(this.getClass().getSimpleName(), String.format("subscription to %s failed, %s",
|
||||
extras.getString(AndroidTransport.EXTRA_PACKAGE), AndroidTransport.formatTraffError(this.getResultCode())));
|
||||
else
|
||||
Logger.e(this.getClass().getSimpleName(), String.format("subscription failed, %s",
|
||||
AndroidTransport.formatTraffError(this.getResultCode())));
|
||||
if (this.getResultCode() == AndroidTransport.RESULT_INTERNAL_ERROR)
|
||||
Logger.e(this.getClass().getSimpleName(), "Make sure the TraFF source app has at least coarse location permission, even when running in background");
|
||||
return;
|
||||
}
|
||||
Bundle extras = this.getResultExtras(true);
|
||||
String data = this.getResultData();
|
||||
String packageName = extras.getString(AndroidTransport.EXTRA_PACKAGE);
|
||||
if (!this.packageName.equals(packageName))
|
||||
return;
|
||||
String subscriptionId = extras.getString(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
|
||||
if (subscriptionId == null) {
|
||||
Logger.e(this.getClass().getSimpleName(),
|
||||
String.format("subscription to %s failed: no subscription ID returned", packageName));
|
||||
return;
|
||||
} else if (packageName == null) {
|
||||
Logger.e(this.getClass().getSimpleName(), "subscription failed: no package name");
|
||||
return;
|
||||
} else if (data == null) {
|
||||
Logger.w(this.getClass().getSimpleName(),
|
||||
String.format("subscription to %s successful (ID: %s) but no content URI was supplied. "
|
||||
+ "This is an issue with the source and may result in delayed message retrieval.",
|
||||
packageName, subscriptionId));
|
||||
this.subscriptionId = subscriptionId;
|
||||
return;
|
||||
}
|
||||
Logger.d(this.getClass().getSimpleName(),
|
||||
"subscription to " + packageName + " successful, ID: " + subscriptionId);
|
||||
this.subscriptionId = subscriptionId;
|
||||
fetchMessages(context, Uri.parse(data));
|
||||
} else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_SUBSCRIPTION_CHANGE)) {
|
||||
if (this.getResultCode() != AndroidTransport.RESULT_OK) {
|
||||
Bundle extras = this.getResultExtras(true);
|
||||
if (extras != null)
|
||||
Logger.e(this.getClass().getSimpleName(),
|
||||
String.format("subscription change for %s failed: %s",
|
||||
extras.getString(AndroidTransport.EXTRA_SUBSCRIPTION_ID),
|
||||
AndroidTransport.formatTraffError(this.getResultCode())));
|
||||
else
|
||||
Logger.e(this.getClass().getSimpleName(),
|
||||
String.format("subscription change failed: %s",
|
||||
AndroidTransport.formatTraffError(this.getResultCode())));
|
||||
return;
|
||||
}
|
||||
Bundle extras = intent.getExtras();
|
||||
String data = this.getResultData();
|
||||
String subscriptionId = extras.getString(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
|
||||
if (subscriptionId == null) {
|
||||
Logger.w(this.getClass().getSimpleName(),
|
||||
"subscription change successful but the source did not specify the subscription ID. "
|
||||
+ "This is an issue with the source and may result in delayed message retrieval. "
|
||||
+ "URI: " + data);
|
||||
return;
|
||||
} else if (!subscriptionId.equals(this.subscriptionId)) {
|
||||
return;
|
||||
} else if (data == null) {
|
||||
Logger.w(this.getClass().getSimpleName(),
|
||||
String.format("subscription change for %s successful but no content URI was supplied. "
|
||||
+ "This is an issue with the source and may result in delayed message retrieval.",
|
||||
subscriptionId));
|
||||
return;
|
||||
}
|
||||
Logger.d(this.getClass().getSimpleName(),
|
||||
"subscription change for " + subscriptionId + " successful");
|
||||
fetchMessages(context, Uri.parse(data));
|
||||
} else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_UNSUBSCRIBE)) {
|
||||
String subscriptionId = intent.getStringExtra(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
|
||||
if (subscriptionId.equals(this.subscriptionId))
|
||||
this.subscriptionId = null;
|
||||
// TODO is there anything to do here? (Comment below is from Navit)
|
||||
/*
|
||||
* If we ever unsubscribe for reasons other than that we are shutting down or got a feed for
|
||||
* a subscription we don’t recognize, or if we start keeping a persistent list of
|
||||
* subscriptions, we need to delete the subscription from our list. Until then, there is
|
||||
* nothing to do here: either the subscription isn’t in the list, or we are about to shut
|
||||
* down and the whole list is about to get discarded.
|
||||
*/
|
||||
} else if (intent.getAction().equals(AndroidTransport.ACTION_TRAFF_HEARTBEAT)) {
|
||||
String subscriptionId = intent.getStringExtra(AndroidTransport.EXTRA_SUBSCRIPTION_ID);
|
||||
if (subscriptionId.equals(this.subscriptionId)) {
|
||||
Logger.d(this.getClass().getSimpleName(),
|
||||
String.format("got a heartbeat from %s for subscription %s; sending result",
|
||||
intent.getStringExtra(AndroidTransport.EXTRA_PACKAGE), subscriptionId));
|
||||
this.setResult(AndroidTransport.RESULT_OK, null, null);
|
||||
}
|
||||
} // intent.getAction()
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches TraFF messages from a content provider.
|
||||
*
|
||||
* @param context The context to use for the content resolver
|
||||
* @param uri The content provider URI
|
||||
*/
|
||||
private void fetchMessages(Context context, Uri uri) {
|
||||
try {
|
||||
Cursor cursor = context.getContentResolver().query(uri, new String[] {AndroidTransport.COLUMN_DATA}, null, null, null);
|
||||
if (cursor == null)
|
||||
return;
|
||||
if (cursor.getCount() < 1) {
|
||||
cursor.close();
|
||||
return;
|
||||
}
|
||||
StringBuilder builder = new StringBuilder("<feed>\n");
|
||||
while (cursor.moveToNext())
|
||||
builder.append(cursor.getString(cursor.getColumnIndex(AndroidTransport.COLUMN_DATA))).append("\n");
|
||||
builder.append("</feed>");
|
||||
cursor.close();
|
||||
onFeedReceived(builder.toString());
|
||||
} catch (Exception e) {
|
||||
Logger.w(this.getClass().getSimpleName(),
|
||||
String.format("Unable to fetch messages from %s", uri.toString()), e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2019–2020 traffxml.org.
|
||||
*
|
||||
* Relicensed to CoMaps by the original author.
|
||||
*/
|
||||
|
||||
package app.organicmaps.sdk.traffxml;
|
||||
|
||||
/**
|
||||
* Constants for versions.
|
||||
*/
|
||||
public class Version {
|
||||
/** Version 0.7: introduced transport on Android. */
|
||||
public static final int V0_7 = 7;
|
||||
|
||||
/** Version 0.8: introduced subscriptions and HTTP transport. */
|
||||
public static final int V0_8 = 8;
|
||||
}
|
||||
Reference in New Issue
Block a user