<commit_before>package io.jxcore.node;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.thaliproject.p2p.btconnectorlib.ConnectionManager;
import org.thaliproject.p2p.btconnectorlib.ConnectionManager.ConnectionManagerState;
import org.thaliproject.p2p.btconnectorlib.PeerDeviceProperties;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 *
 */
public class ConnectorHelper implements ConnectionManager.ConnectionManagerListener {
    /**
     * A listener interface for relaying connection status change events to Node layer.
     * This interface only applies to outgoing connection attempts (i.e. when you call
     * ConnectorHelper.connect()
     */
    public interface JxCoreExtensionListener {
        void onConnectionStatusChanged(String info, int port);
    }

    private static final String TAG = ConnectorHelper.class.getName();
    private static final String SERVICE_TYPE = "Cordovap2p._tcp";
    private static final String BLUETOOTH_UUID_AS_STRING = "fa87c0d0-afac-11de-8a39-0800200c9a66";
    private static final String BLUETOOTH_NAME = "Thaili_Bluetooth";
    private static final UUID BLUETOOTH_UUID = UUID.fromString(BLUETOOTH_UUID_AS_STRING);
    private static final int MAXIMUM_NUMBER_OF_CONNECTIONS = 100; // TODO: Determine a way to figure out a proper value here

    private final Context mContext;
    private final Thread.UncaughtExceptionHandler mThreadUncaughtExceptionHandler;
    private final CopyOnWriteArrayList<PeerDeviceProperties> mLastPeerDeviceList = new CopyOnWriteArrayList<PeerDeviceProperties>();
    private final CopyOnWriteArrayList<IncomingSocketThread> mIncomingSocketThreads = new CopyOnWriteArrayList<IncomingSocketThread>();
    private final CopyOnWriteArrayList<OutgoingSocketThread> mOutgoingSocketThreads = new CopyOnWriteArrayList<OutgoingSocketThread>();
    private final HashMap<String, JxCoreExtensionListener> mOutgoingConnectionListeners = new HashMap<String, JxCoreExtensionListener>();
    private ConnectionManager mConnectionManager = null;
    private int mServerPort = 0;

    /**
     * Constructor.
     */
    public ConnectorHelper() {
        mContext = jxcore.activity.getBaseContext();

        mThreadUncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, final Throwable ex) {
                final Throwable tempException = ex;

                new Handler(jxcore.activity.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        Log.e(TAG, "Unhandled exception: " + ex.getMessage(), ex);
                        throw new RuntimeException(tempException);
                    }
                });
            }
        };
    }

    /**
     *
     * @param peerName
     * @param port
     * @return
     */
    public synchronized boolean start(String peerName, int port) {
        stop();
        mServerPort = port;

        mConnectionManager = new ConnectionManager(
                mContext, this, BLUETOOTH_UUID, BLUETOOTH_NAME, SERVICE_TYPE);

        if (mConnectionManager.initialize(getBluetoothAddress(), peerName)) {
            if (mLastPeerDeviceList.size() > 0) {
                JSONArray jsonArray = new JSONArray();

                for (PeerDeviceProperties peerDeviceProperties : mLastPeerDeviceList) {
                    jsonArray.put(getAvailabilityStatus(peerDeviceProperties, true));
                }

                jxcore.CallJSMethod(JXcoreExtension.EVENTSTRING_PEERAVAILABILITY, jsonArray.toString());
            }

            Log.i(TAG, "start: OK");
        } else {
            mConnectionManager = null;
        }

        return (mConnectionManager != null);
    }

    /**
     *
     */
    public synchronized void stop() {
        if (mConnectionManager != null) {
            Log.i(TAG, "stop");
            mConnectionManager.stop();
            mConnectionManager = null;
        }

        disconnectAllOutgoingConnections();
        disconnectAllIncomingConnections();
    }

    public boolean isRunning() {
        return (mConnectionManager != null);
    }

    /**
     * Disconnects the outgoing connection with the given peer ID.
     * @param peerId The ID of the peer to disconnect.
     * @return True, if the peer was found and disconnected.
     */
    public synchronized boolean disconnectOutgoingConnection(final String peerId) {
        boolean wasFoundAndDisconnected = false;

        for (OutgoingSocketThread outgoingSocketThread : mOutgoingSocketThreads) {
            if (outgoingSocketThread != null && outgoingSocketThread.getPeerId().equalsIgnoreCase(peerId)) {
                Log.i(TAG, "closeAndRemoveOutgoingConnectionThread: Disconnecting connection, peer ID: " + peerId);
                mOutgoingConnectionListeners.remove(peerId);
                mOutgoingSocketThreads.remove(outgoingSocketThread);
                outgoingSocketThread.close();
                wasFoundAndDisconnected = true;
                break;
            }
        }

        return wasFoundAndDisconnected;
    }

    /**
     * @return Our Bluetooth address or empty string, if not resolved.
     */
    public String getBluetoothAddress() {
        BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
        return bluetooth == null ? "" : bluetooth.getAddress();
    }

    /**
     *
     * @return
     */
    @TargetApi(18)
    @SuppressLint("NewApi")
    public String isBleAdvertisingSupported() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return "Build version is " + Build.VERSION.SDK_INT;
        }

        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
            return "Feature FEATURE_BLUETOOTH_LE not found";
        }
<commit_msg>Commit to #345: Renamed classes, cleaned up the code and improved logging.<commit_after>package io.jxcore.node;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.thaliproject.p2p.btconnectorlib.ConnectionManager;
import org.thaliproject.p2p.btconnectorlib.ConnectionManager.ConnectionManagerState;
import org.thaliproject.p2p.btconnectorlib.PeerDeviceProperties;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 *
 */
public class ConnectorHelper implements ConnectionManager.ConnectionManagerListener {
    /**
     * A listener interface for relaying connection status change events to Node layer.
     * This interface only applies to outgoing connection attempts (i.e. when you call
     * ConnectorHelper.connect()
     */
    public interface JxCoreExtensionListener {
        void onConnectionStatusChanged(String info, int port);
    }

    private static final String TAG = ConnectorHelper.class.getName();
    private static final String SERVICE_TYPE = "Cordovap2p._tcp";
    private static final String BLUETOOTH_UUID_AS_STRING = "fa87c0d0-afac-11de-8a39-0800200c9a66";
    private static final String BLUETOOTH_NAME = "Thaili_Bluetooth";
    private static final UUID BLUETOOTH_UUID = UUID.fromString(BLUETOOTH_UUID_AS_STRING);
    private static final int MAXIMUM_NUMBER_OF_CONNECTIONS = 100; // TODO: Determine a way to figure out a proper value here

    private final Context mContext;
    private final Thread.UncaughtExceptionHandler mThreadUncaughtExceptionHandler;
    private final CopyOnWriteArrayList<PeerDeviceProperties> mLastPeerDeviceList = new CopyOnWriteArrayList<PeerDeviceProperties>();
    private final CopyOnWriteArrayList<IncomingSocketThread> mIncomingSocketThreads = new CopyOnWriteArrayList<IncomingSocketThread>();
    private final CopyOnWriteArrayList<OutgoingSocketThread> mOutgoingSocketThreads = new CopyOnWriteArrayList<OutgoingSocketThread>();
    private final HashMap<String, JxCoreExtensionListener> mOutgoingConnectionListeners = new HashMap<String, JxCoreExtensionListener>();
    private ConnectionManager mConnectionManager = null;
    private int mServerPort = 0;

    /**
     * Constructor.
     */
    public ConnectorHelper() {
        mContext = jxcore.activity.getBaseContext();

        mThreadUncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread thread, final Throwable ex) {
                final Throwable tempException = ex;

                new Handler(jxcore.activity.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        Log.e(TAG, "Unhandled exception: " + ex.getMessage(), ex);
                        throw new RuntimeException(tempException);
                    }
                });
            }
        };
    }

    /**
     *
     * @param peerName
     * @param port
     * @return
     */
    public synchronized boolean start(String peerName, int port) {
        stop();
        mServerPort = port;

        mConnectionManager = new ConnectionManager(
                mContext, this, BLUETOOTH_UUID, BLUETOOTH_NAME, SERVICE_TYPE);

        if (mConnectionManager.initialize(getBluetoothAddress(), peerName)) {
            if (mLastPeerDeviceList.size() > 0) {
                JSONArray jsonArray = new JSONArray();

                for (PeerDeviceProperties peerDeviceProperties : mLastPeerDeviceList) {
                    jsonArray.put(getAvailabilityStatus(peerDeviceProperties, true));
                }

                jxcore.CallJSMethod(JXcoreExtension.EVENTSTRING_PEERAVAILABILITY, jsonArray.toString());
            }

            Log.i(TAG, "start: OK");
        } else {
            mConnectionManager = null;
        }

        return (mConnectionManager != null);
    }

    /**
     *
     */
    public synchronized void stop() {
        if (mConnectionManager != null) {
            Log.i(TAG, "stop");
            mConnectionManager.stop();
            mConnectionManager = null;
        }

        disconnectAllOutgoingConnections();
        disconnectAllIncomingConnections();
    }

    public boolean isRunning() {
        return (mConnectionManager != null);
    }

    /**
     * Disconnects the outgoing connection with the given peer ID.
     * @param peerId The ID of the peer to disconnect.
     * @return True, if the peer was found and disconnected.
     */
    public synchronized boolean disconnectOutgoingConnection(final String peerId) {
        boolean wasFoundAndDisconnected = false;

        for (OutgoingSocketThread outgoingSocketThread : mOutgoingSocketThreads) {
            if (outgoingSocketThread != null && outgoingSocketThread.getPeerId().equalsIgnoreCase(peerId)) {
                Log.i(TAG, "closeAndRemoveOutgoingConnectionThread: Disconnecting connection, peer ID: " + peerId);
                mOutgoingConnectionListeners.remove(peerId);
                mOutgoingSocketThreads.remove(outgoingSocketThread);
                outgoingSocketThread.close();
                wasFoundAndDisconnected = true;
                break;
            }
        }

        return wasFoundAndDisconnected;
    }

    /**
     * @return Our Bluetooth address or empty string, if not resolved.
     */
    public String getBluetoothAddress() {
        BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
        return bluetooth == null ? "" : bluetooth.getAddress();
    }

    /**
     *
     * @return Null, if BLE is supported.
     */
    @TargetApi(18)
    @SuppressLint("NewApi")
    public String isBleAdvertisingSupported() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return "Build version is " + Build.VERSION.SDK_INT;
        }

        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
            return "Feature FEATURE_BLUETOOTH_LE not found";
        }

{'fraction_non_alphanumeric': 0.08239700374531835, 'fraction_numerical': 0.0049157303370786515, 'mean_word_length': 2.906430966168851, 'pattern_counts': {'":': 0, '<': 21, '<?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': '7584792', 'n_tokens_mistral': 3462, 'n_tokens_neox': 3320, 'n_words': 768}