<commit_before>package com.tapad.tapestry;

import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;

import com.tapad.tapestry.deviceidentification.TypedIdentifier;
import com.tapad.tapestry.http.HttpStack;
import com.tapad.tapestry.http.HttpStackFactory;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static android.preference.PreferenceManager.getDefaultSharedPreferences;
import static com.tapad.tapestry.TapestryError.CLIENT_REQUEST_ERROR;
import static com.tapad.tapestry.TapestryError.OPTED_OUT;

/**
 * A client for sending requests to the Tapestry Web API and returning
 * responses. The client should be instantiated once your activity has entered
 * {@code onCreate()}. If you use the client across many Activities, you may
 * wish to use the {@link TapestryService} wrapper instead.
 * <p/>
 * The client sends a {@link TapestryRequest} as an HTTP request to the Tapestry
 * Web API and parses the JSON response as {@link TapestryResponse}. When
 * calling the client from the Android UI thread, it is recommended to send
 * requests asynchronously by passing in a {@link TapestryCallback} so as not to
 * block the UI thread.
 * <p/>
 * An example of sending a request and receiving an asynchronous response:
 * <blockquote>
 * 
 * <pre>
 * TapestryClient client = new TapestryClient(context);
 * TapestryRequest request = new TapestryRequest();
 * // TODO build request
 * client.send(request, new TapestryCallback() {
 *     {@literal @}Override
 *     public void receive(TapestryResponse response) {
 *         // TODO handle response
 *     }
 * });
 * </pre>
 * 
 * </blockquote> Note if the callback updates the UI then you should use the
 * {@link TapestryUICallback} convenience class which runs the callback on the
 * UI thread. The client throws no exceptions unless
 * {@link Logging#setThrowExceptions(boolean)} has been set to true.
 */
public class TapestryClient {
	public static final String DEFAULT_URL = "http://tapestry.tapad.com/tapestry/1";
	public static final String PREF_TAPAD_DEVICE_ID = "_tapad_device_id";
	public static final String OPTED_OUT_DEVICE_ID = "OptedOut";
	public static final String TAPESTRY_HEADER = "X-Tapestry-Id";
	private static final ExecutorService executor = Executors.newFixedThreadPool(2);
	private final ConnectivityManager connectivityManager;
	private final Queue<Runnable> requestQueue = new LinkedList<Runnable>();
	private final TapestryTracking tracking;
	private final String url;
	private final String partnerId;
	private final HttpStack stack;

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called. Partner id will be read from
	 * {@code tapad.PARTNER_ID} in the manifest or default if none exists. The
	 * url of the API will be read from {@code tapad.API_URL} in the manifest or
	 * default if none exists.
	 * 
	 * @param context
	 *            The context of the app
	 */
	public TapestryClient(Context context) {
		this(context, getMetaData(context, "tapad.PARTNER_ID", null));
	}

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called. The url of the API will be read from
	 * {@code tapad.API_URL} in the manifest or default if none exists.
	 * 
	 * @param context
	 *            The context of the app
	 * @param partnerId
	 *            The Tapestry partner id that has been assigned to you
	 */
	public TapestryClient(Context context, String partnerId) {
		this(context, partnerId, getMetaData(context, "tapad.API_URL", DEFAULT_URL));
	}

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called.
	 * 
	 * @param context
	 *            The context of the app
	 * @param partnerId
	 *            The Tapestry partner id that has been assigned to you
	 * @param url
	 *            The url of the tapestry host
	 */
	public TapestryClient(Context context, String partnerId, String url) {
		this(context, partnerId, url, HttpStackFactory.getDefaultStack(context));
	}

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called.
	 * 
	 * @param context
	 *            The context of the app
	 * @param partnerId
	 *            The Tapestry partner id that has been assigned to you
	 * @param url
	 *            The url of the tapestry host
	 * @param stack
	 *            The stack for sending and receiving http requests 
	 */
	public TapestryClient(Context context, String partnerId, String url, HttpStack stack) {
		this.tracking = new TapestryTracking(context);
		this.partnerId = partnerId;
		this.url = url;
		this.stack = stack;
		connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		if (this.partnerId == null)
			throw new RuntimeException("Partner id must be specified in the manifest or during instantiation");
	}

	private static String getMetaData(Context context, String key, String defaultValue) {
		try {
			String value = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA).metaData.get(key).toString();
			return value == null ? defaultValue : value;
		} catch (Exception e) {
			return defaultValue;
		}
	}

	/**
	 * Sends a request asynchronously using a worker thread pool.
	 * 
	 * @param callback
	 *            A callback that will be called when the Tapestry server
	 *            responds
	 */
	public void send(final TapestryCallback callback) {
		send(new TapestryRequest(), callback);
	}

	/**
	 * Sends a request asynchronously using a worker thread pool, without
	 * returning a response.
	 * 
	 * @param request
	 *            The request
	 */
	public void send(final TapestryRequest request) {
		send(request, TapestryCallback.DO_NOTHING);
	}

	/**
	 * Sends a request asynchronously using a worker thread pool.
	 * 
	 * @param request
	 *            The request
	 * @param callback
	 *            A callback that will be called when the Tapestry server
	 *            responds
	 */
	public void send(final TapestryRequest request, final TapestryCallback callback) {
		requestQueue.add(new Runnable() {
			@Override
			public void run() {
				callback.receive(sendSynchronously(request));
			}
		});
		executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					while (!isOnline() && requestQueue.size() > 0)
							Thread.sleep(1000);
					while (!requestQueue.isEmpty())
						executor.execute(requestQueue.remove());
				} catch (Exception e) {
					Logging.e("Exception in queuing thread", e);
				}
			}
		});
	}

	public boolean isOnline() {
		try {
		    NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
		    return netInfo != null && netInfo.isConnectedOrConnecting();
		} catch (Exception e) {
			Logging.e("Error getting network status", e);
			return true;
		}
	}

	/**
	 * Sends a request synchronously, blocking until response is received.
	 * 
	 * @param request<commit_msg>make sure client defaults to connected<commit_after>package com.tapad.tapestry;

import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.text.TextUtils;

import com.tapad.tapestry.deviceidentification.TypedIdentifier;
import com.tapad.tapestry.http.HttpStack;
import com.tapad.tapestry.http.HttpStackFactory;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static android.preference.PreferenceManager.getDefaultSharedPreferences;
import static com.tapad.tapestry.TapestryError.CLIENT_REQUEST_ERROR;
import static com.tapad.tapestry.TapestryError.OPTED_OUT;

/**
 * A client for sending requests to the Tapestry Web API and returning
 * responses. The client should be instantiated once your activity has entered
 * {@code onCreate()}. If you use the client across many Activities, you may
 * wish to use the {@link TapestryService} wrapper instead.
 * <p/>
 * The client sends a {@link TapestryRequest} as an HTTP request to the Tapestry
 * Web API and parses the JSON response as {@link TapestryResponse}. When
 * calling the client from the Android UI thread, it is recommended to send
 * requests asynchronously by passing in a {@link TapestryCallback} so as not to
 * block the UI thread.
 * <p/>
 * An example of sending a request and receiving an asynchronous response:
 * <blockquote>
 * 
 * <pre>
 * TapestryClient client = new TapestryClient(context);
 * TapestryRequest request = new TapestryRequest();
 * // TODO build request
 * client.send(request, new TapestryCallback() {
 *     {@literal @}Override
 *     public void receive(TapestryResponse response) {
 *         // TODO handle response
 *     }
 * });
 * </pre>
 * 
 * </blockquote> Note if the callback updates the UI then you should use the
 * {@link TapestryUICallback} convenience class which runs the callback on the
 * UI thread. The client throws no exceptions unless
 * {@link Logging#setThrowExceptions(boolean)} has been set to true.
 */
public class TapestryClient {
	public static final String DEFAULT_URL = "http://tapestry.tapad.com/tapestry/1";
	public static final String PREF_TAPAD_DEVICE_ID = "_tapad_device_id";
	public static final String OPTED_OUT_DEVICE_ID = "OptedOut";
	public static final String TAPESTRY_HEADER = "X-Tapestry-Id";
	private static final ExecutorService executor = Executors.newFixedThreadPool(2);
	private final ConnectivityManager connectivityManager;
	private final Queue<Runnable> requestQueue = new LinkedList<Runnable>();
	private final TapestryTracking tracking;
	private final String url;
	private final String partnerId;
	private final HttpStack stack;

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called. Partner id will be read from
	 * {@code tapad.PARTNER_ID} in the manifest or default if none exists. The
	 * url of the API will be read from {@code tapad.API_URL} in the manifest or
	 * default if none exists.
	 * 
	 * @param context
	 *            The context of the app
	 */
	public TapestryClient(Context context) {
		this(context, getMetaData(context, "tapad.PARTNER_ID", null));
	}

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called. The url of the API will be read from
	 * {@code tapad.API_URL} in the manifest or default if none exists.
	 * 
	 * @param context
	 *            The context of the app
	 * @param partnerId
	 *            The Tapestry partner id that has been assigned to you
	 */
	public TapestryClient(Context context, String partnerId) {
		this(context, partnerId, getMetaData(context, "tapad.API_URL", DEFAULT_URL));
	}

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called.
	 * 
	 * @param context
	 *            The context of the app
	 * @param partnerId
	 *            The Tapestry partner id that has been assigned to you
	 * @param url
	 *            The url of the tapestry host
	 */
	public TapestryClient(Context context, String partnerId, String url) {
		this(context, partnerId, url, HttpStackFactory.getDefaultStack(context));
	}

	/**
	 * Creates client ready to receive requests. Client cannot be instantiated
	 * before {@code onCreate} is called.
	 * 
	 * @param context
	 *            The context of the app
	 * @param partnerId
	 *            The Tapestry partner id that has been assigned to you
	 * @param url
	 *            The url of the tapestry host
	 * @param stack
	 *            The stack for sending and receiving http requests 
	 */
	public TapestryClient(Context context, String partnerId, String url, HttpStack stack) {
		this.tracking = new TapestryTracking(context);
		this.partnerId = partnerId;
		this.url = url;
		this.stack = stack;
		connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		if (this.partnerId == null)
			throw new RuntimeException("Partner id must be specified in the manifest or during instantiation");
	}

	private static String getMetaData(Context context, String key, String defaultValue) {
		try {
			String value = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA).metaData.get(key).toString();
			return value == null ? defaultValue : value;
		} catch (Exception e) {
			return defaultValue;
		}
	}

	/**
	 * Sends a request asynchronously using a worker thread pool.
	 * 
	 * @param callback
	 *            A callback that will be called when the Tapestry server
	 *            responds
	 */
	public void send(final TapestryCallback callback) {
		send(new TapestryRequest(), callback);
	}

	/**
	 * Sends a request asynchronously using a worker thread pool, without
	 * returning a response.
	 * 
	 * @param request
	 *            The request
	 */
	public void send(final TapestryRequest request) {
		send(request, TapestryCallback.DO_NOTHING);
	}

	/**
	 * Sends a request asynchronously using a worker thread pool.
	 * 
	 * @param request
	 *            The request
	 * @param callback
	 *            A callback that will be called when the Tapestry server
	 *            responds
	 */
	public void send(final TapestryRequest request, final TapestryCallback callback) {
		requestQueue.add(new Runnable() {
			@Override
			public void run() {
				callback.receive(sendSynchronously(request));
			}
		});
		executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					while (!isOnline() && requestQueue.size() > 0)
							Thread.sleep(1000);
					while (!requestQueue.isEmpty())
						executor.execute(requestQueue.remove());
				} catch (Exception e) {
					Logging.e("Exception in queuing thread", e);
				}
			}
		});
	}

	public boolean isOnline() {
		try {
		    NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo();
		    return netInfo.isConnectedOrConnecting();
		} catch (Exception e) {
			Logging.e("Error getting network status", e);
			return true;
		}
	}

	/**
	 * Sends a request synchronously, blocking until response is received.
	 * 
	 * @param request
{'fraction_non_alphanumeric': 0.08655310898767483, 'fraction_numerical': 0.0009693948206619581, 'mean_word_length': 3.6084875558391833, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5825907', 'n_tokens_mistral': 4341, 'n_tokens_neox': 3858, 'n_words': 1430}