[traffic][android] Move Java source files to android/sdk

Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
mvglasow
2025-11-06 21:09:26 +02:00
parent 781d973faa
commit d82b545e30
6 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,111 @@
/*
* Copyright © 20172020 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);
}
}
}

View File

@@ -0,0 +1,222 @@
/*
* Copyright © 20192020 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];
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}
}

View File

@@ -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 dont 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 isnt 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();
}
}
}

View File

@@ -0,0 +1,18 @@
/*
* Copyright © 20192020 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;
}