public void setFormatName(String formatName) {
        this.formatName = formatName;
    }

package org.springframework.format.number.money;

import java.util.Locale;
import javax.money.MonetaryAmount;
import javax.money.format.MonetaryAmountFormat;
import javax.money.format.MonetaryFormats;
import org.springframework.format.Formatter;
import org.springframework.lang.Nullable;

/**
 * Formatter for JSR-354 {@link javax.money.MonetaryAmount} values,
 * delegating to {@link javax.money.format.MonetaryAmountFormat#format}
 * and {@link javax.money.format.MonetaryAmountFormat#parse}.
 *
 * @author Juergen Hoeller
 * @since 4.2
 * @see #getMonetaryAmountFormat
 */
public class MonetaryAmountFormatter implements Formatter<MonetaryAmount> {

    @Nullable
    private String formatName;

    /**
     * Create a locale-driven MonetaryAmountFormatter.
     */
    public MonetaryAmountFormatter() {
    }

    /**
     * Create a new MonetaryAmountFormatter for the given format name.
     * @param formatName the format name, to be resolved by the JSR-354
     * provider at runtime
     */
    public MonetaryAmountFormatter(String formatName) {
        this.formatName = formatName;
    }

    /**
     * Specify the format name, to be resolved by the JSR-354 provider
     * at runtime.
     * <p>Default is none, obtaining a {@link MonetaryAmountFormat}
     * based on the current locale.
     */

    @Override
    public String print(MonetaryAmount object, Locale locale) {
        return getMonetaryAmountFormat(locale).format(object);
    }

    @Override
    public MonetaryAmount parse(String text, Locale locale) {
        return getMonetaryAmountFormat(locale).parse(text);
    }

    /**
     * Obtain a MonetaryAmountFormat for the given locale.
     * <p>The default implementation simply calls
     * {@link javax.money.format.MonetaryFormats#getAmountFormat}
     * with either the configured format name or the given locale.
     * @param locale the current locale
     * @return the MonetaryAmountFormat (never {@code null})
     * @see #setFormatName
     */
    protected MonetaryAmountFormat getMonetaryAmountFormat(Locale locale) {
        if (this.formatName != null) {
            return MonetaryFormats.getAmountFormat(this.formatName);
        } else {
            return MonetaryFormats.getAmountFormat(locale);
        }
    }
}


default DataPartition<?> get() {
        return EmptyDataPartition.getInstance();
    }

package edu.iu.dsc.tws.api.tset.fn;

import edu.iu.dsc.tws.api.dataset.DataPartition;
import edu.iu.dsc.tws.api.dataset.EmptyDataPartition;

/**
 * Adds data sink at the end of the graph. {@link SinkFunc} would receive data from an edge of
 * the data flow graph.
 *
 * @param <T> data type
 */
public interface SinkFunc<T> extends TFunction<T, T> {

    /**
     * Every time an edge produces a value, this method is called.
     *
     * @param value the value to add
     * @return true if success
     */
    boolean add(T value);

    /**
     * Return the data partition associated with this task instance. Specifically to be used for
     * caching functionality.
     *
     * @return data partition - type remains unknown as the output of sunk data can be a
     * different type than T. Example, caching gatherTlink,{@literal T -> Tuple<int, T>} but the output would
     * be T
     */


public static String getAuthority(Context context) {
        AUTHORITY = context.getPackageName() + ".provider.communication";
        return AUTHORITY;
    }

package com.aware.providers;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;
import com.aware.Aware;
import com.aware.utils.DatabaseHelper;
import java.util.HashMap;

/**
 * AWARE Communication Content Provider Allows you to access all the recorded
 * communication events on the database Database is located at the SDCard :
 * /AWARE/communication.db
 *
 * @author denzil
 */
public class Communication_Provider extends ContentProvider {

    public static final int DATABASE_VERSION = 3;

    /**
     * Authority of Screen content provider
     */
    public static String AUTHORITY = "com.aware.provider.communication";

    // ContentProvider query paths
    private static final int CALLS = 1;

    private static final int CALLS_ID = 2;

    private static final int MESSAGES = 3;

    private static final int MESSAGES_ID = 4;

    /**
     * Calls content representation
     *
     * @author denzil
     */
    public static final class Calls_Data implements BaseColumns {

        private Calls_Data() {
        }

        public static final Uri CONTENT_URI = Uri.parse("content://" + Communication_Provider.AUTHORITY + "/calls");

        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.aware.calls";

        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.aware.calls";

        public static final String _ID = "_id";

        public static final String TIMESTAMP = "timestamp";

        public static final String DEVICE_ID = "device_id";

        public static final String TYPE = "call_type";

        public static final String DURATION = "call_duration";

        public static final String TRACE = "trace";
    }

    /**
     * Messages content representation
     *
     * @author denzil
     */
    public static final class Messages_Data implements BaseColumns {

        private Messages_Data() {
        }

        public static final Uri CONTENT_URI = Uri.parse("content://" + Communication_Provider.AUTHORITY + "/messages");

        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.aware.messages";

        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.aware.messages";

        public static final String _ID = "_id";

        public static final String TIMESTAMP = "timestamp";

        public static final String DEVICE_ID = "device_id";

        public static final String TYPE = "message_type";

        public static final String TRACE = "trace";
    }

    public static String DATABASE_NAME = "communication.db";

    public static final String[] DATABASE_TABLES = { "calls", "messages" };

    public static final String[] TABLES_FIELDS = { // calls
    "_id integer primary key autoincrement," + "timestamp real default 0," + "device_id text default ''," + "call_type integer default 0," + "call_duration integer default 0," + "trace text default ''", // messages
    "_id integer primary key autoincrement," + "timestamp real default 0," + "device_id text default ''," + "message_type integer default 0," + "trace text default ''" };

    private UriMatcher sUriMatcher = null;

    private HashMap<String, String> callsProjectionMap = null;

    private HashMap<String, String> messageProjectionMap = null;

    private DatabaseHelper dbHelper;

    private static SQLiteDatabase database;

    private void initialiseDatabase() {
        if (dbHelper == null)
            dbHelper = new DatabaseHelper(getContext(), DATABASE_NAME, null, DATABASE_VERSION, DATABASE_TABLES, TABLES_FIELDS);
        if (database == null)
            database = dbHelper.getWritableDatabase();
    }

    /**
     * Delete entry from the database
     */
    @Override
    public synchronized int delete(Uri uri, String selection, String[] selectionArgs) {
        initialiseDatabase();
        // lock database for transaction
        database.beginTransaction();
        int count;
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                count = database.delete(DATABASE_TABLES[0], selection, selectionArgs);
                break;
            case MESSAGES:
                count = database.delete(DATABASE_TABLES[1], selection, selectionArgs);
                break;
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        database.setTransactionSuccessful();
        database.endTransaction();
        getContext().getContentResolver().notifyChange(uri, null, false);
        return count;
    }

    @Override
    public String getType(Uri uri) {
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                return Calls_Data.CONTENT_TYPE;
            case CALLS_ID:
                return Calls_Data.CONTENT_ITEM_TYPE;
            case MESSAGES:
                return Messages_Data.CONTENT_TYPE;
            case MESSAGES_ID:
                return Messages_Data.CONTENT_ITEM_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    /**
     * Insert entry to the database
     */
    @Override
    public synchronized Uri insert(Uri uri, ContentValues initialValues) {
        initialiseDatabase();
        ContentValues values = (initialValues != null) ? new ContentValues(initialValues) : new ContentValues();
        database.beginTransaction();
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                long call_id = database.insertWithOnConflict(DATABASE_TABLES[0], Calls_Data.DEVICE_ID, values, SQLiteDatabase.CONFLICT_IGNORE);
                database.setTransactionSuccessful();
                database.endTransaction();
                if (call_id > 0) {
                    Uri callsUri = ContentUris.withAppendedId(Calls_Data.CONTENT_URI, call_id);
                    getContext().getContentResolver().notifyChange(callsUri, null, false);
                    return callsUri;
                }
                database.endTransaction();
                throw new SQLException("Failed to insert row into " + uri);
            case MESSAGES:
                long message_id = database.insertWithOnConflict(DATABASE_TABLES[1], Messages_Data.DEVICE_ID, values, SQLiteDatabase.CONFLICT_IGNORE);
                database.setTransactionSuccessful();
                database.endTransaction();
                if (message_id > 0) {
                    Uri messagesUri = ContentUris.withAppendedId(Messages_Data.CONTENT_URI, message_id);
                    getContext().getContentResolver().notifyChange(messagesUri, null, false);
                    return messagesUri;
                }
                database.endTransaction();
                throw new SQLException("Failed to insert row into " + uri);
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    /**
     * Returns the provider authority that is dynamic
     * @return
     */

    @Override
    public boolean onCreate() {
        AUTHORITY = getContext().getPackageName() + ".provider.communication";
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[0], CALLS);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[0] + "/#", CALLS_ID);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[1], MESSAGES);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[1] + "/#", MESSAGES_ID);
        callsProjectionMap = new HashMap<String, String>();
        callsProjectionMap.put(Calls_Data._ID, Calls_Data._ID);
        callsProjectionMap.put(Calls_Data.TIMESTAMP, Calls_Data.TIMESTAMP);
        callsProjectionMap.put(Calls_Data.DEVICE_ID, Calls_Data.DEVICE_ID);
        callsProjectionMap.put(Calls_Data.TYPE, Calls_Data.TYPE);
        callsProjectionMap.put(Calls_Data.DURATION, Calls_Data.DURATION);
        callsProjectionMap.put(Calls_Data.TRACE, Calls_Data.TRACE);
        messageProjectionMap = new HashMap<String, String>();
        messageProjectionMap.put(Messages_Data._ID, Messages_Data._ID);
        messageProjectionMap.put(Messages_Data.TIMESTAMP, Messages_Data.TIMESTAMP);
        messageProjectionMap.put(Messages_Data.DEVICE_ID, Messages_Data.DEVICE_ID);
        messageProjectionMap.put(Messages_Data.TYPE, Messages_Data.TYPE);
        messageProjectionMap.put(Messages_Data.TRACE, Messages_Data.TRACE);
        return true;
    }

    /**
     * Query entries from the database
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        initialiseDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setStrict(true);
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                qb.setTables(DATABASE_TABLES[0]);
                qb.setProjectionMap(callsProjectionMap);
                break;
            case MESSAGES:
                qb.setTables(DATABASE_TABLES[1]);
                qb.setProjectionMap(messageProjectionMap);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        try {
            Cursor c = qb.query(database, projection, selection, selectionArgs, null, null, sortOrder);
            c.setNotificationUri(getContext().getContentResolver(), uri);
            return c;
        } catch (IllegalStateException e) {
            if (Aware.DEBUG)
                Log.e(Aware.TAG, e.getMessage());
            return null;
        }
    }

    /**
     * Update application on the database
     */
    @Override
    public synchronized int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        initialiseDatabase();
        database.beginTransaction();
        int count;
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                count = database.update(DATABASE_TABLES[0], values, selection, selectionArgs);
                break;
            case MESSAGES:
                count = database.update(DATABASE_TABLES[1], values, selection, selectionArgs);
                break;
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        database.setTransactionSuccessful();
        database.endTransaction();
        getContext().getContentResolver().notifyChange(uri, null, false);
        return count;
    }
}


switch(sUriMatcher.match(uri)) {
            case CALLS:
                count = database.delete(DATABASE_TABLES[0], selection, selectionArgs);
                break;
            case MESSAGES:
                count = database.delete(DATABASE_TABLES[1], selection, selectionArgs);
                break;
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

package com.aware.providers;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;
import com.aware.Aware;
import com.aware.utils.DatabaseHelper;
import java.util.HashMap;

/**
 * AWARE Communication Content Provider Allows you to access all the recorded
 * communication events on the database Database is located at the SDCard :
 * /AWARE/communication.db
 *
 * @author denzil
 */
public class Communication_Provider extends ContentProvider {

    public static final int DATABASE_VERSION = 3;

    /**
     * Authority of Screen content provider
     */
    public static String AUTHORITY = "com.aware.provider.communication";

    // ContentProvider query paths
    private static final int CALLS = 1;

    private static final int CALLS_ID = 2;

    private static final int MESSAGES = 3;

    private static final int MESSAGES_ID = 4;

    /**
     * Calls content representation
     *
     * @author denzil
     */
    public static final class Calls_Data implements BaseColumns {

        private Calls_Data() {
        }

        public static final Uri CONTENT_URI = Uri.parse("content://" + Communication_Provider.AUTHORITY + "/calls");

        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.aware.calls";

        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.aware.calls";

        public static final String _ID = "_id";

        public static final String TIMESTAMP = "timestamp";

        public static final String DEVICE_ID = "device_id";

        public static final String TYPE = "call_type";

        public static final String DURATION = "call_duration";

        public static final String TRACE = "trace";
    }

    /**
     * Messages content representation
     *
     * @author denzil
     */
    public static final class Messages_Data implements BaseColumns {

        private Messages_Data() {
        }

        public static final Uri CONTENT_URI = Uri.parse("content://" + Communication_Provider.AUTHORITY + "/messages");

        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.aware.messages";

        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.aware.messages";

        public static final String _ID = "_id";

        public static final String TIMESTAMP = "timestamp";

        public static final String DEVICE_ID = "device_id";

        public static final String TYPE = "message_type";

        public static final String TRACE = "trace";
    }

    public static String DATABASE_NAME = "communication.db";

    public static final String[] DATABASE_TABLES = { "calls", "messages" };

    public static final String[] TABLES_FIELDS = { // calls
    "_id integer primary key autoincrement," + "timestamp real default 0," + "device_id text default ''," + "call_type integer default 0," + "call_duration integer default 0," + "trace text default ''", // messages
    "_id integer primary key autoincrement," + "timestamp real default 0," + "device_id text default ''," + "message_type integer default 0," + "trace text default ''" };

    private UriMatcher sUriMatcher = null;

    private HashMap<String, String> callsProjectionMap = null;

    private HashMap<String, String> messageProjectionMap = null;

    private DatabaseHelper dbHelper;

    private static SQLiteDatabase database;

    private void initialiseDatabase() {
        if (dbHelper == null)
            dbHelper = new DatabaseHelper(getContext(), DATABASE_NAME, null, DATABASE_VERSION, DATABASE_TABLES, TABLES_FIELDS);
        if (database == null)
            database = dbHelper.getWritableDatabase();
    }

    /**
     * Delete entry from the database
     */
    @Override
    public synchronized int delete(Uri uri, String selection, String[] selectionArgs) {
        initialiseDatabase();
        // lock database for transaction
        database.beginTransaction();
        int count;

        database.setTransactionSuccessful();
        database.endTransaction();
        getContext().getContentResolver().notifyChange(uri, null, false);
        return count;
    }

    @Override
    public String getType(Uri uri) {
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                return Calls_Data.CONTENT_TYPE;
            case CALLS_ID:
                return Calls_Data.CONTENT_ITEM_TYPE;
            case MESSAGES:
                return Messages_Data.CONTENT_TYPE;
            case MESSAGES_ID:
                return Messages_Data.CONTENT_ITEM_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    /**
     * Insert entry to the database
     */
    @Override
    public synchronized Uri insert(Uri uri, ContentValues initialValues) {
        initialiseDatabase();
        ContentValues values = (initialValues != null) ? new ContentValues(initialValues) : new ContentValues();
        database.beginTransaction();
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                long call_id = database.insertWithOnConflict(DATABASE_TABLES[0], Calls_Data.DEVICE_ID, values, SQLiteDatabase.CONFLICT_IGNORE);
                database.setTransactionSuccessful();
                database.endTransaction();
                if (call_id > 0) {
                    Uri callsUri = ContentUris.withAppendedId(Calls_Data.CONTENT_URI, call_id);
                    getContext().getContentResolver().notifyChange(callsUri, null, false);
                    return callsUri;
                }
                database.endTransaction();
                throw new SQLException("Failed to insert row into " + uri);
            case MESSAGES:
                long message_id = database.insertWithOnConflict(DATABASE_TABLES[1], Messages_Data.DEVICE_ID, values, SQLiteDatabase.CONFLICT_IGNORE);
                database.setTransactionSuccessful();
                database.endTransaction();
                if (message_id > 0) {
                    Uri messagesUri = ContentUris.withAppendedId(Messages_Data.CONTENT_URI, message_id);
                    getContext().getContentResolver().notifyChange(messagesUri, null, false);
                    return messagesUri;
                }
                database.endTransaction();
                throw new SQLException("Failed to insert row into " + uri);
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    /**
     * Returns the provider authority that is dynamic
     * @return
     */
    public static String getAuthority(Context context) {
        AUTHORITY = context.getPackageName() + ".provider.communication";
        return AUTHORITY;
    }

    @Override
    public boolean onCreate() {
        AUTHORITY = getContext().getPackageName() + ".provider.communication";
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[0], CALLS);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[0] + "/#", CALLS_ID);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[1], MESSAGES);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[1] + "/#", MESSAGES_ID);
        callsProjectionMap = new HashMap<String, String>();
        callsProjectionMap.put(Calls_Data._ID, Calls_Data._ID);
        callsProjectionMap.put(Calls_Data.TIMESTAMP, Calls_Data.TIMESTAMP);
        callsProjectionMap.put(Calls_Data.DEVICE_ID, Calls_Data.DEVICE_ID);
        callsProjectionMap.put(Calls_Data.TYPE, Calls_Data.TYPE);
        callsProjectionMap.put(Calls_Data.DURATION, Calls_Data.DURATION);
        callsProjectionMap.put(Calls_Data.TRACE, Calls_Data.TRACE);
        messageProjectionMap = new HashMap<String, String>();
        messageProjectionMap.put(Messages_Data._ID, Messages_Data._ID);
        messageProjectionMap.put(Messages_Data.TIMESTAMP, Messages_Data.TIMESTAMP);
        messageProjectionMap.put(Messages_Data.DEVICE_ID, Messages_Data.DEVICE_ID);
        messageProjectionMap.put(Messages_Data.TYPE, Messages_Data.TYPE);
        messageProjectionMap.put(Messages_Data.TRACE, Messages_Data.TRACE);
        return true;
    }

    /**
     * Query entries from the database
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        initialiseDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setStrict(true);
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                qb.setTables(DATABASE_TABLES[0]);
                qb.setProjectionMap(callsProjectionMap);
                break;
            case MESSAGES:
                qb.setTables(DATABASE_TABLES[1]);
                qb.setProjectionMap(messageProjectionMap);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        try {
            Cursor c = qb.query(database, projection, selection, selectionArgs, null, null, sortOrder);
            c.setNotificationUri(getContext().getContentResolver(), uri);
            return c;
        } catch (IllegalStateException e) {
            if (Aware.DEBUG)
                Log.e(Aware.TAG, e.getMessage());
            return null;
        }
    }

    /**
     * Update application on the database
     */
    @Override
    public synchronized int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        initialiseDatabase();
        database.beginTransaction();
        int count;
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                count = database.update(DATABASE_TABLES[0], values, selection, selectionArgs);
                break;
            case MESSAGES:
                count = database.update(DATABASE_TABLES[1], values, selection, selectionArgs);
                break;
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        database.setTransactionSuccessful();
        database.endTransaction();
        getContext().getContentResolver().notifyChange(uri, null, false);
        return count;
    }
}


try {
            Cursor c = qb.query(database, projection, selection, selectionArgs, null, null, sortOrder);
            c.setNotificationUri(getContext().getContentResolver(), uri);
            return c;
        } catch (IllegalStateException e) {
            if (Aware.DEBUG)
                Log.e(Aware.TAG, e.getMessage());
            return null;
        }

package com.aware.providers;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.Log;
import com.aware.Aware;
import com.aware.utils.DatabaseHelper;
import java.util.HashMap;

/**
 * AWARE Communication Content Provider Allows you to access all the recorded
 * communication events on the database Database is located at the SDCard :
 * /AWARE/communication.db
 *
 * @author denzil
 */
public class Communication_Provider extends ContentProvider {

    public static final int DATABASE_VERSION = 3;

    /**
     * Authority of Screen content provider
     */
    public static String AUTHORITY = "com.aware.provider.communication";

    // ContentProvider query paths
    private static final int CALLS = 1;

    private static final int CALLS_ID = 2;

    private static final int MESSAGES = 3;

    private static final int MESSAGES_ID = 4;

    /**
     * Calls content representation
     *
     * @author denzil
     */
    public static final class Calls_Data implements BaseColumns {

        private Calls_Data() {
        }

        public static final Uri CONTENT_URI = Uri.parse("content://" + Communication_Provider.AUTHORITY + "/calls");

        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.aware.calls";

        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.aware.calls";

        public static final String _ID = "_id";

        public static final String TIMESTAMP = "timestamp";

        public static final String DEVICE_ID = "device_id";

        public static final String TYPE = "call_type";

        public static final String DURATION = "call_duration";

        public static final String TRACE = "trace";
    }

    /**
     * Messages content representation
     *
     * @author denzil
     */
    public static final class Messages_Data implements BaseColumns {

        private Messages_Data() {
        }

        public static final Uri CONTENT_URI = Uri.parse("content://" + Communication_Provider.AUTHORITY + "/messages");

        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.aware.messages";

        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.aware.messages";

        public static final String _ID = "_id";

        public static final String TIMESTAMP = "timestamp";

        public static final String DEVICE_ID = "device_id";

        public static final String TYPE = "message_type";

        public static final String TRACE = "trace";
    }

    public static String DATABASE_NAME = "communication.db";

    public static final String[] DATABASE_TABLES = { "calls", "messages" };

    public static final String[] TABLES_FIELDS = { // calls
    "_id integer primary key autoincrement," + "timestamp real default 0," + "device_id text default ''," + "call_type integer default 0," + "call_duration integer default 0," + "trace text default ''", // messages
    "_id integer primary key autoincrement," + "timestamp real default 0," + "device_id text default ''," + "message_type integer default 0," + "trace text default ''" };

    private UriMatcher sUriMatcher = null;

    private HashMap<String, String> callsProjectionMap = null;

    private HashMap<String, String> messageProjectionMap = null;

    private DatabaseHelper dbHelper;

    private static SQLiteDatabase database;

    private void initialiseDatabase() {
        if (dbHelper == null)
            dbHelper = new DatabaseHelper(getContext(), DATABASE_NAME, null, DATABASE_VERSION, DATABASE_TABLES, TABLES_FIELDS);
        if (database == null)
            database = dbHelper.getWritableDatabase();
    }

    /**
     * Delete entry from the database
     */
    @Override
    public synchronized int delete(Uri uri, String selection, String[] selectionArgs) {
        initialiseDatabase();
        // lock database for transaction
        database.beginTransaction();
        int count;
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                count = database.delete(DATABASE_TABLES[0], selection, selectionArgs);
                break;
            case MESSAGES:
                count = database.delete(DATABASE_TABLES[1], selection, selectionArgs);
                break;
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        database.setTransactionSuccessful();
        database.endTransaction();
        getContext().getContentResolver().notifyChange(uri, null, false);
        return count;
    }

    @Override
    public String getType(Uri uri) {
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                return Calls_Data.CONTENT_TYPE;
            case CALLS_ID:
                return Calls_Data.CONTENT_ITEM_TYPE;
            case MESSAGES:
                return Messages_Data.CONTENT_TYPE;
            case MESSAGES_ID:
                return Messages_Data.CONTENT_ITEM_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    /**
     * Insert entry to the database
     */
    @Override
    public synchronized Uri insert(Uri uri, ContentValues initialValues) {
        initialiseDatabase();
        ContentValues values = (initialValues != null) ? new ContentValues(initialValues) : new ContentValues();
        database.beginTransaction();
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                long call_id = database.insertWithOnConflict(DATABASE_TABLES[0], Calls_Data.DEVICE_ID, values, SQLiteDatabase.CONFLICT_IGNORE);
                database.setTransactionSuccessful();
                database.endTransaction();
                if (call_id > 0) {
                    Uri callsUri = ContentUris.withAppendedId(Calls_Data.CONTENT_URI, call_id);
                    getContext().getContentResolver().notifyChange(callsUri, null, false);
                    return callsUri;
                }
                database.endTransaction();
                throw new SQLException("Failed to insert row into " + uri);
            case MESSAGES:
                long message_id = database.insertWithOnConflict(DATABASE_TABLES[1], Messages_Data.DEVICE_ID, values, SQLiteDatabase.CONFLICT_IGNORE);
                database.setTransactionSuccessful();
                database.endTransaction();
                if (message_id > 0) {
                    Uri messagesUri = ContentUris.withAppendedId(Messages_Data.CONTENT_URI, message_id);
                    getContext().getContentResolver().notifyChange(messagesUri, null, false);
                    return messagesUri;
                }
                database.endTransaction();
                throw new SQLException("Failed to insert row into " + uri);
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }

    /**
     * Returns the provider authority that is dynamic
     * @return
     */
    public static String getAuthority(Context context) {
        AUTHORITY = context.getPackageName() + ".provider.communication";
        return AUTHORITY;
    }

    @Override
    public boolean onCreate() {
        AUTHORITY = getContext().getPackageName() + ".provider.communication";
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[0], CALLS);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[0] + "/#", CALLS_ID);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[1], MESSAGES);
        sUriMatcher.addURI(Communication_Provider.AUTHORITY, DATABASE_TABLES[1] + "/#", MESSAGES_ID);
        callsProjectionMap = new HashMap<String, String>();
        callsProjectionMap.put(Calls_Data._ID, Calls_Data._ID);
        callsProjectionMap.put(Calls_Data.TIMESTAMP, Calls_Data.TIMESTAMP);
        callsProjectionMap.put(Calls_Data.DEVICE_ID, Calls_Data.DEVICE_ID);
        callsProjectionMap.put(Calls_Data.TYPE, Calls_Data.TYPE);
        callsProjectionMap.put(Calls_Data.DURATION, Calls_Data.DURATION);
        callsProjectionMap.put(Calls_Data.TRACE, Calls_Data.TRACE);
        messageProjectionMap = new HashMap<String, String>();
        messageProjectionMap.put(Messages_Data._ID, Messages_Data._ID);
        messageProjectionMap.put(Messages_Data.TIMESTAMP, Messages_Data.TIMESTAMP);
        messageProjectionMap.put(Messages_Data.DEVICE_ID, Messages_Data.DEVICE_ID);
        messageProjectionMap.put(Messages_Data.TYPE, Messages_Data.TYPE);
        messageProjectionMap.put(Messages_Data.TRACE, Messages_Data.TRACE);
        return true;
    }

    /**
     * Query entries from the database
     */
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        initialiseDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        qb.setStrict(true);
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                qb.setTables(DATABASE_TABLES[0]);
                qb.setProjectionMap(callsProjectionMap);
                break;
            case MESSAGES:
                qb.setTables(DATABASE_TABLES[1]);
                qb.setProjectionMap(messageProjectionMap);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

    }

    /**
     * Update application on the database
     */
    @Override
    public synchronized int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        initialiseDatabase();
        database.beginTransaction();
        int count;
        switch(sUriMatcher.match(uri)) {
            case CALLS:
                count = database.update(DATABASE_TABLES[0], values, selection, selectionArgs);
                break;
            case MESSAGES:
                count = database.update(DATABASE_TABLES[1], values, selection, selectionArgs);
                break;
            default:
                database.endTransaction();
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        database.setTransactionSuccessful();
        database.endTransaction();
        getContext().getContentResolver().notifyChange(uri, null, false);
        return count;
    }
}


for (int i = 0; i < len; i++) {
            m1 += adapter1.getDouble(data1, i);
            m2 += adapter2.getDouble(data2, i);
        }

/*
 * This file is part of ELKI:
 * Environment for Developing KDD-Applications Supported by Index-Structures
 *
 *
 */
package elki.math.statistics.dependence;

import java.util.List;
import elki.logging.Logging;
import elki.utilities.Priority;
import elki.utilities.datastructures.arraylike.NumberArrayAdapter;
import elki.utilities.optionhandling.Parameterizer;

/**
 * Pearson product-moment correlation coefficient.
 *
 * @author Erich Schubert
 * @since 0.7.0
 */
@Priority(Priority.RECOMMENDED + 1)
public class PearsonCorrelationDependence implements Dependence {

    private static final Logging LOG = Logging.getLogger(PearsonCorrelationDependence.class);

    public static final PearsonCorrelationDependence STATIC = new PearsonCorrelationDependence();

    protected PearsonCorrelationDependence() {
        super();
    }

    @Override
    public <A, B> double dependence(NumberArrayAdapter<?, A> adapter1, A data1, NumberArrayAdapter<?, B> adapter2, B data2) {
        final int len = Utils.size(adapter1, data1, adapter2, data2);
        // Perform two-pass estimation, which is numerically stable and often faster
        // than the Knuth-Welford approach (see PearsonCorrelationDependence class)
        double m1 = 0., m2 = 0;

        m1 /= len;
        m2 /= len;
        // Second pass: variances and covariance
        double v1 = 0., v2 = 0., cov = 0.;
        for (int i = 0; i < len; i++) {
            double d1 = adapter1.getDouble(data1, i) - m1;
            double d2 = adapter2.getDouble(data2, i) - m2;
            v1 += d1 * d1;
            v2 += d2 * d2;
            cov += d1 * d2;
        }
        final double vsq = v1 * v2;
        // Note: we did not normalize by len, as this cancels out.
        return vsq > 0 ? cov / Math.sqrt(vsq) : 0.;
    }

    @Override
    public <A> double[] dependence(NumberArrayAdapter<?, A> adapter, List<? extends A> data) {
        final int dims = data.size(), len = Utils.size(adapter, data);
        double[] means = new double[dims];
        // Two passes - often faster due to the lower numerical cost
        // And accurate, don't use sum-of-squares.
        for (int j = 0; j < dims; j++) {
            double m = 0.;
            A da = data.get(j);
            for (int i = 0; i < len; i++) {
                m += adapter.getDouble(da, i);
            }
            means[j] = m / len;
        }
        // Build the covariance matrix, lower triangular half
        double[] vst = new double[dims];
        double[] cov = new double[(dims * (dims - 1)) >> 1];
        double[] buf = new double[dims];
        for (int i = 0; i < len; i++) {
            for (int j = 0; j < dims; j++) {
                buf[j] = adapter.getDouble(data.get(j), i) - means[j];
            }
            for (int y = 0, c = 0; y < dims; y++) {
                for (int x = 0; x < y; x++) {
                    cov[c++] += buf[x] * buf[y];
                }
                vst[y] += buf[y] * buf[y];
            }
        }
        // Compute standard deviations (times sqrt(len)!):
        for (int y = 0; y < dims; y++) {
            if (vst[y] == 0.) {
                LOG.warning("PearsonCorrelationDependence is not well defined for constant attributes.");
            }
            vst[y] = Math.sqrt(vst[y]);
        }
        for (int y = 1, c = 0; y < dims; y++) {
            for (int x = 0; x < y; x++) {
                // We don't need to divide by sqrt(len), because it will cancel out with
                // the division we skipped just above.
                cov[c++] /= vst[x] * vst[y];
            }
        }
        return cov;
    }

    /**
     * Parameterization class
     *
     * @author Erich Schubert
     */
    public static class Par implements Parameterizer {

        @Override
        public PearsonCorrelationDependence make() {
            return STATIC;
        }
    }
}


@SuppressWarnings("unused")
    public synchronized void requestAllManifestPermissionsIfNecessary(@Nullable final Activity activity, @Nullable final PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        String[] perms = getManifestPermissions(activity);
        requestPermissionsIfNecessaryForResult(activity, perms, action);
    }

package com.wsg.xsytrade.runtimepermissions;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * A class to help you manage your permissions simply.
 */
public class PermissionsManager {

    private static final String TAG = PermissionsManager.class.getSimpleName();

    private final Set<String> mPendingRequests = new HashSet<String>(1);

    private final Set<String> mPermissions = new HashSet<String>(1);

    private final List<WeakReference<PermissionsResultAction>> mPendingActions = new ArrayList<WeakReference<PermissionsResultAction>>(1);

    private static PermissionsManager mInstance = null;

    public static PermissionsManager getInstance() {
        if (mInstance == null) {
            mInstance = new PermissionsManager();
        }
        return mInstance;
    }

    private PermissionsManager() {
        initializePermissionsMap();
    }

    /**
     * This method uses reflection to read all the permissions in the Manifest class.
     * This is necessary because some permissions do not exist on older versions of Android,
     * since they do not exist, they will be denied when you check whether you have permission
     * which is problematic since a new permission is often added where there was no previous
     * permission required. We initialize a Set of available permissions and check the set
     * when checking if we have permission since we want to know when we are denied a permission
     * because it doesn't exist yet.
     */
    private synchronized void initializePermissionsMap() {
        Field[] fields = Manifest.permission.class.getFields();
        for (Field field : fields) {
            String name = null;
            try {
                name = (String) field.get("");
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Could not access field", e);
            }
            mPermissions.add(name);
        }
    }

    /**
     * This method retrieves all the permissions declared in the application's manifest.
     * It returns a non null array of permisions that can be declared.
     *
     * @param activity the Activity necessary to check what permissions we have.
     * @return a non null array of permissions that are declared in the application manifest.
     */
    @NonNull
    private synchronized String[] getManifestPermissions(@NonNull final Activity activity) {
        PackageInfo packageInfo = null;
        List<String> list = new ArrayList<String>(1);
        try {
            Log.d(TAG, activity.getPackageName());
            packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "A problem occurred when retrieving permissions", e);
        }
        if (packageInfo != null) {
            String[] permissions = packageInfo.requestedPermissions;
            if (permissions != null) {
                for (String perm : permissions) {
                    Log.d(TAG, "Manifest contained permission: " + perm);
                    list.add(perm);
                }
            }
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * This method adds the {@link PermissionsResultAction} to the current list
     * of pending actions that will be completed when the permissions are
     * received. The list of permissions passed to this method are registered
     * in the PermissionsResultAction object so that it will be notified of changes
     * made to these permissions.
     *
     * @param permissions the required permissions for the action to be executed.
     * @param action      the action to add to the current list of pending actions.
     */
    private synchronized void addPendingAction(@NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        if (action == null) {
            return;
        }
        action.registerPermissions(permissions);
        mPendingActions.add(new WeakReference<PermissionsResultAction>(action));
    }

    /**
     * This method removes a pending action from the list of pending actions.
     * It is used for cases where the permission has already been granted, so
     * you immediately wish to remove the pending action from the queue and
     * execute the action.
     *
     * @param action the action to remove
     */
    private synchronized void removePendingAction(@Nullable PermissionsResultAction action) {
        for (Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator(); iterator.hasNext(); ) {
            WeakReference<PermissionsResultAction> weakRef = iterator.next();
            if (weakRef.get() == action || weakRef.get() == null) {
                iterator.remove();
            }
        }
    }

    /**
     * This static method can be used to check whether or not you have a specific permission.
     * It is basically a less verbose method of using {@link ActivityCompat#checkSelfPermission(Context, String)}
     * and will simply return a boolean whether or not you have the permission. If you pass
     * in a null Context object, it will return false as otherwise it cannot check the permission.
     * However, the Activity parameter is nullable so that you can pass in a reference that you
     * are not always sure will be valid or not (e.g. getActivity() from Fragment).
     *
     * @param context    the Context necessary to check the permission
     * @param permission the permission to check
     * @return true if you have been granted the permission, false otherwise
     */
    @SuppressWarnings("unused")
    public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {
        return context != null && (ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission));
    }

    /**
     * This static method can be used to check whether or not you have several specific permissions.
     * It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)}
     * for each permission and will simply return a boolean whether or not you have all the permissions.
     * If you pass in a null Context object, it will return false as otherwise it cannot check the
     * permission. However, the Activity parameter is nullable so that you can pass in a reference
     * that you are not always sure will be valid or not (e.g. getActivity() from Fragment).
     *
     * @param context     the Context necessary to check the permission
     * @param permissions the permissions to check
     * @return true if you have been granted all the permissions, false otherwise
     */
    @SuppressWarnings("unused")
    public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) {
        if (context == null) {
            return false;
        }
        boolean hasAllPermissions = true;
        for (String perm : permissions) {
            hasAllPermissions &= hasPermission(context, perm);
        }
        return hasAllPermissions;
    }

    /**
     * This method will request all the permissions declared in your application manifest
     * for the specified {@link PermissionsResultAction}. The purpose of this method is to enable
     * all permissions to be requested at one shot. The PermissionsResultAction is used to notify
     * you of the user allowing or denying each permission. The Activity and PermissionsResultAction
     * parameters are both annotated Nullable, but this method will not work if the Activity
     * is null. It is only annotated Nullable as a courtesy to prevent crashes in the case
     * that you call this from a Fragment where {@link Fragment#getActivity()} could yield
     * null. Additionally, you will not receive any notification of permissions being granted
     * if you provide a null PermissionsResultAction.
     *
     * @param activity the Activity necessary to request and check permissions.
     * @param action   the PermissionsResultAction used to notify you of permissions being accepted.
     */

    /**
     * This method should be used to execute a {@link PermissionsResultAction} for the array
     * of permissions passed to this method. This method will request the permissions if
     * they need to be requested (i.e. we don't have permission yet) and will add the
     * PermissionsResultAction to the queue to be notified of permissions being granted or
     * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
     * The Activity variable is nullable, but if it is null, the method will fail to execute.
     * This is only nullable as a courtesy for Fragments where getActivity() may yeild null
     * if the Fragment is not currently added to its parent Activity.
     *
     * @param activity    the activity necessary to request the permissions.
     * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
     * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.
     */
    @SuppressWarnings("unused")
    public synchronized void requestPermissionsIfNecessaryForResult(@Nullable Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                // if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                ActivityCompat.requestPermissions(activity, permsToRequest, 1);
            }
        }
    }

    /**
     * This method should be used to execute a {@link PermissionsResultAction} for the array
     * of permissions passed to this method. This method will request the permissions if
     * they need to be requested (i.e. we don't have permission yet) and will add the
     * PermissionsResultAction to the queue to be notified of permissions being granted or
     * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
     * The Fragment variable is used, but if {@link Fragment#getActivity()} returns null, this method
     * will fail to work as the activity reference is necessary to check for permissions.
     *
     * @param fragment    the fragment necessary to request the permissions.
     * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
     * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.
     */
    @SuppressWarnings("unused")
    public synchronized void requestPermissionsIfNecessaryForResult(@NonNull Fragment fragment, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                // if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                fragment.requestPermissions(permsToRequest, 1);
            }
        }
    }

    /**
     * This method notifies the PermissionsManager that the permissions have change. If you are making
     * the permissions requests using an Activity, then this method should be called from the
     * Activity callback onRequestPermissionsResult() with the variables passed to that method. If
     * you are passing a Fragment to make the permissions request, then you should call this in
     * the {@link Fragment#onRequestPermissionsResult(int, String[], int[])} method.
     * It will notify all the pending PermissionsResultAction objects currently
     * in the queue, and will remove the permissions request from the list of pending requests.
     *
     * @param permissions the permissions that have changed.
     * @param results     the values for each permission.
     */
    @SuppressWarnings("unused")
    public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {
        int size = permissions.length;
        if (results.length < size) {
            size = results.length;
        }
        Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();
        while (iterator.hasNext()) {
            PermissionsResultAction action = iterator.next().get();
            for (int n = 0; n < size; n++) {
                if (action == null || action.onResult(permissions[n], results[n])) {
                    iterator.remove();
                    break;
                }
            }
        }
        for (int n = 0; n < size; n++) {
            mPendingRequests.remove(permissions[n]);
        }
    }

    /**
     * When request permissions on devices before Android M (Android 6.0, API Level 23)
     * Do the granted or denied work directly according to the permission status
     *
     * @param activity    the activity to check permissions
     * @param permissions the permissions names
     * @param action      the callback work object, containing what we what to do after
     *                    permission check
     */
    private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        for (String perm : permissions) {
            if (action != null) {
                if (!mPermissions.contains(perm)) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
                    action.onResult(perm, Permissions.DENIED);
                } else {
                    action.onResult(perm, Permissions.GRANTED);
                }
            }
        }
    }

    /**
     * Filter the permissions list:
     * If a permission is not granted, add it to the result list
     * if a permission is granted, do the granted work, do not add it to the result list
     *
     * @param activity    the activity to check permissions
     * @param permissions all the permissions names
     * @param action      the callback work object, containing what we what to do after
     *                    permission check
     * @return a list of permissions names that are not granted yet
     */
    @NonNull
    private List<String> getPermissionsListToRequest(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        List<String> permList = new ArrayList<String>(permissions.length);
        for (String perm : permissions) {
            if (!mPermissions.contains(perm)) {
                if (action != null) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                }
            } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
                if (!mPendingRequests.contains(perm)) {
                    permList.add(perm);
                }
            } else {
                if (action != null) {
                    action.onResult(perm, Permissions.GRANTED);
                }
            }
        }
        return permList;
    }
}


for (Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator(); iterator.hasNext(); ) {
            WeakReference<PermissionsResultAction> weakRef = iterator.next();
            if (weakRef.get() == action || weakRef.get() == null) {
                iterator.remove();
            }
        }

package com.wsg.xsytrade.runtimepermissions;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * A class to help you manage your permissions simply.
 */
public class PermissionsManager {

    private static final String TAG = PermissionsManager.class.getSimpleName();

    private final Set<String> mPendingRequests = new HashSet<String>(1);

    private final Set<String> mPermissions = new HashSet<String>(1);

    private final List<WeakReference<PermissionsResultAction>> mPendingActions = new ArrayList<WeakReference<PermissionsResultAction>>(1);

    private static PermissionsManager mInstance = null;

    public static PermissionsManager getInstance() {
        if (mInstance == null) {
            mInstance = new PermissionsManager();
        }
        return mInstance;
    }

    private PermissionsManager() {
        initializePermissionsMap();
    }

    /**
     * This method uses reflection to read all the permissions in the Manifest class.
     * This is necessary because some permissions do not exist on older versions of Android,
     * since they do not exist, they will be denied when you check whether you have permission
     * which is problematic since a new permission is often added where there was no previous
     * permission required. We initialize a Set of available permissions and check the set
     * when checking if we have permission since we want to know when we are denied a permission
     * because it doesn't exist yet.
     */
    private synchronized void initializePermissionsMap() {
        Field[] fields = Manifest.permission.class.getFields();
        for (Field field : fields) {
            String name = null;
            try {
                name = (String) field.get("");
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Could not access field", e);
            }
            mPermissions.add(name);
        }
    }

    /**
     * This method retrieves all the permissions declared in the application's manifest.
     * It returns a non null array of permisions that can be declared.
     *
     * @param activity the Activity necessary to check what permissions we have.
     * @return a non null array of permissions that are declared in the application manifest.
     */
    @NonNull
    private synchronized String[] getManifestPermissions(@NonNull final Activity activity) {
        PackageInfo packageInfo = null;
        List<String> list = new ArrayList<String>(1);
        try {
            Log.d(TAG, activity.getPackageName());
            packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "A problem occurred when retrieving permissions", e);
        }
        if (packageInfo != null) {
            String[] permissions = packageInfo.requestedPermissions;
            if (permissions != null) {
                for (String perm : permissions) {
                    Log.d(TAG, "Manifest contained permission: " + perm);
                    list.add(perm);
                }
            }
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * This method adds the {@link PermissionsResultAction} to the current list
     * of pending actions that will be completed when the permissions are
     * received. The list of permissions passed to this method are registered
     * in the PermissionsResultAction object so that it will be notified of changes
     * made to these permissions.
     *
     * @param permissions the required permissions for the action to be executed.
     * @param action      the action to add to the current list of pending actions.
     */
    private synchronized void addPendingAction(@NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        if (action == null) {
            return;
        }
        action.registerPermissions(permissions);
        mPendingActions.add(new WeakReference<PermissionsResultAction>(action));
    }

    /**
     * This method removes a pending action from the list of pending actions.
     * It is used for cases where the permission has already been granted, so
     * you immediately wish to remove the pending action from the queue and
     * execute the action.
     *
     * @param action the action to remove
     */
    private synchronized void removePendingAction(@Nullable PermissionsResultAction action) {

    }

    /**
     * This static method can be used to check whether or not you have a specific permission.
     * It is basically a less verbose method of using {@link ActivityCompat#checkSelfPermission(Context, String)}
     * and will simply return a boolean whether or not you have the permission. If you pass
     * in a null Context object, it will return false as otherwise it cannot check the permission.
     * However, the Activity parameter is nullable so that you can pass in a reference that you
     * are not always sure will be valid or not (e.g. getActivity() from Fragment).
     *
     * @param context    the Context necessary to check the permission
     * @param permission the permission to check
     * @return true if you have been granted the permission, false otherwise
     */
    @SuppressWarnings("unused")
    public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {
        return context != null && (ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission));
    }

    /**
     * This static method can be used to check whether or not you have several specific permissions.
     * It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)}
     * for each permission and will simply return a boolean whether or not you have all the permissions.
     * If you pass in a null Context object, it will return false as otherwise it cannot check the
     * permission. However, the Activity parameter is nullable so that you can pass in a reference
     * that you are not always sure will be valid or not (e.g. getActivity() from Fragment).
     *
     * @param context     the Context necessary to check the permission
     * @param permissions the permissions to check
     * @return true if you have been granted all the permissions, false otherwise
     */
    @SuppressWarnings("unused")
    public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) {
        if (context == null) {
            return false;
        }
        boolean hasAllPermissions = true;
        for (String perm : permissions) {
            hasAllPermissions &= hasPermission(context, perm);
        }
        return hasAllPermissions;
    }

    /**
     * This method will request all the permissions declared in your application manifest
     * for the specified {@link PermissionsResultAction}. The purpose of this method is to enable
     * all permissions to be requested at one shot. The PermissionsResultAction is used to notify
     * you of the user allowing or denying each permission. The Activity and PermissionsResultAction
     * parameters are both annotated Nullable, but this method will not work if the Activity
     * is null. It is only annotated Nullable as a courtesy to prevent crashes in the case
     * that you call this from a Fragment where {@link Fragment#getActivity()} could yield
     * null. Additionally, you will not receive any notification of permissions being granted
     * if you provide a null PermissionsResultAction.
     *
     * @param activity the Activity necessary to request and check permissions.
     * @param action   the PermissionsResultAction used to notify you of permissions being accepted.
     */
    @SuppressWarnings("unused")
    public synchronized void requestAllManifestPermissionsIfNecessary(@Nullable final Activity activity, @Nullable final PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        String[] perms = getManifestPermissions(activity);
        requestPermissionsIfNecessaryForResult(activity, perms, action);
    }

    /**
     * This method should be used to execute a {@link PermissionsResultAction} for the array
     * of permissions passed to this method. This method will request the permissions if
     * they need to be requested (i.e. we don't have permission yet) and will add the
     * PermissionsResultAction to the queue to be notified of permissions being granted or
     * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
     * The Activity variable is nullable, but if it is null, the method will fail to execute.
     * This is only nullable as a courtesy for Fragments where getActivity() may yeild null
     * if the Fragment is not currently added to its parent Activity.
     *
     * @param activity    the activity necessary to request the permissions.
     * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
     * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.
     */
    @SuppressWarnings("unused")
    public synchronized void requestPermissionsIfNecessaryForResult(@Nullable Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                // if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                ActivityCompat.requestPermissions(activity, permsToRequest, 1);
            }
        }
    }

    /**
     * This method should be used to execute a {@link PermissionsResultAction} for the array
     * of permissions passed to this method. This method will request the permissions if
     * they need to be requested (i.e. we don't have permission yet) and will add the
     * PermissionsResultAction to the queue to be notified of permissions being granted or
     * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
     * The Fragment variable is used, but if {@link Fragment#getActivity()} returns null, this method
     * will fail to work as the activity reference is necessary to check for permissions.
     *
     * @param fragment    the fragment necessary to request the permissions.
     * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
     * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.
     */
    @SuppressWarnings("unused")
    public synchronized void requestPermissionsIfNecessaryForResult(@NonNull Fragment fragment, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                // if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                fragment.requestPermissions(permsToRequest, 1);
            }
        }
    }

    /**
     * This method notifies the PermissionsManager that the permissions have change. If you are making
     * the permissions requests using an Activity, then this method should be called from the
     * Activity callback onRequestPermissionsResult() with the variables passed to that method. If
     * you are passing a Fragment to make the permissions request, then you should call this in
     * the {@link Fragment#onRequestPermissionsResult(int, String[], int[])} method.
     * It will notify all the pending PermissionsResultAction objects currently
     * in the queue, and will remove the permissions request from the list of pending requests.
     *
     * @param permissions the permissions that have changed.
     * @param results     the values for each permission.
     */
    @SuppressWarnings("unused")
    public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {
        int size = permissions.length;
        if (results.length < size) {
            size = results.length;
        }
        Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();
        while (iterator.hasNext()) {
            PermissionsResultAction action = iterator.next().get();
            for (int n = 0; n < size; n++) {
                if (action == null || action.onResult(permissions[n], results[n])) {
                    iterator.remove();
                    break;
                }
            }
        }
        for (int n = 0; n < size; n++) {
            mPendingRequests.remove(permissions[n]);
        }
    }

    /**
     * When request permissions on devices before Android M (Android 6.0, API Level 23)
     * Do the granted or denied work directly according to the permission status
     *
     * @param activity    the activity to check permissions
     * @param permissions the permissions names
     * @param action      the callback work object, containing what we what to do after
     *                    permission check
     */
    private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        for (String perm : permissions) {
            if (action != null) {
                if (!mPermissions.contains(perm)) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
                    action.onResult(perm, Permissions.DENIED);
                } else {
                    action.onResult(perm, Permissions.GRANTED);
                }
            }
        }
    }

    /**
     * Filter the permissions list:
     * If a permission is not granted, add it to the result list
     * if a permission is granted, do the granted work, do not add it to the result list
     *
     * @param activity    the activity to check permissions
     * @param permissions all the permissions names
     * @param action      the callback work object, containing what we what to do after
     *                    permission check
     * @return a list of permissions names that are not granted yet
     */
    @NonNull
    private List<String> getPermissionsListToRequest(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        List<String> permList = new ArrayList<String>(permissions.length);
        for (String perm : permissions) {
            if (!mPermissions.contains(perm)) {
                if (action != null) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                }
            } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
                if (!mPendingRequests.contains(perm)) {
                    permList.add(perm);
                }
            } else {
                if (action != null) {
                    action.onResult(perm, Permissions.GRANTED);
                }
            }
        }
        return permList;
    }
}


while (iterator.hasNext()) {
            PermissionsResultAction action = iterator.next().get();
            for (int n = 0; n < size; n++) {
                if (action == null || action.onResult(permissions[n], results[n])) {
                    iterator.remove();
                    break;
                }
            }
        }

package com.wsg.xsytrade.runtimepermissions;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * A class to help you manage your permissions simply.
 */
public class PermissionsManager {

    private static final String TAG = PermissionsManager.class.getSimpleName();

    private final Set<String> mPendingRequests = new HashSet<String>(1);

    private final Set<String> mPermissions = new HashSet<String>(1);

    private final List<WeakReference<PermissionsResultAction>> mPendingActions = new ArrayList<WeakReference<PermissionsResultAction>>(1);

    private static PermissionsManager mInstance = null;

    public static PermissionsManager getInstance() {
        if (mInstance == null) {
            mInstance = new PermissionsManager();
        }
        return mInstance;
    }

    private PermissionsManager() {
        initializePermissionsMap();
    }

    /**
     * This method uses reflection to read all the permissions in the Manifest class.
     * This is necessary because some permissions do not exist on older versions of Android,
     * since they do not exist, they will be denied when you check whether you have permission
     * which is problematic since a new permission is often added where there was no previous
     * permission required. We initialize a Set of available permissions and check the set
     * when checking if we have permission since we want to know when we are denied a permission
     * because it doesn't exist yet.
     */
    private synchronized void initializePermissionsMap() {
        Field[] fields = Manifest.permission.class.getFields();
        for (Field field : fields) {
            String name = null;
            try {
                name = (String) field.get("");
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Could not access field", e);
            }
            mPermissions.add(name);
        }
    }

    /**
     * This method retrieves all the permissions declared in the application's manifest.
     * It returns a non null array of permisions that can be declared.
     *
     * @param activity the Activity necessary to check what permissions we have.
     * @return a non null array of permissions that are declared in the application manifest.
     */
    @NonNull
    private synchronized String[] getManifestPermissions(@NonNull final Activity activity) {
        PackageInfo packageInfo = null;
        List<String> list = new ArrayList<String>(1);
        try {
            Log.d(TAG, activity.getPackageName());
            packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "A problem occurred when retrieving permissions", e);
        }
        if (packageInfo != null) {
            String[] permissions = packageInfo.requestedPermissions;
            if (permissions != null) {
                for (String perm : permissions) {
                    Log.d(TAG, "Manifest contained permission: " + perm);
                    list.add(perm);
                }
            }
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * This method adds the {@link PermissionsResultAction} to the current list
     * of pending actions that will be completed when the permissions are
     * received. The list of permissions passed to this method are registered
     * in the PermissionsResultAction object so that it will be notified of changes
     * made to these permissions.
     *
     * @param permissions the required permissions for the action to be executed.
     * @param action      the action to add to the current list of pending actions.
     */
    private synchronized void addPendingAction(@NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        if (action == null) {
            return;
        }
        action.registerPermissions(permissions);
        mPendingActions.add(new WeakReference<PermissionsResultAction>(action));
    }

    /**
     * This method removes a pending action from the list of pending actions.
     * It is used for cases where the permission has already been granted, so
     * you immediately wish to remove the pending action from the queue and
     * execute the action.
     *
     * @param action the action to remove
     */
    private synchronized void removePendingAction(@Nullable PermissionsResultAction action) {
        for (Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator(); iterator.hasNext(); ) {
            WeakReference<PermissionsResultAction> weakRef = iterator.next();
            if (weakRef.get() == action || weakRef.get() == null) {
                iterator.remove();
            }
        }
    }

    /**
     * This static method can be used to check whether or not you have a specific permission.
     * It is basically a less verbose method of using {@link ActivityCompat#checkSelfPermission(Context, String)}
     * and will simply return a boolean whether or not you have the permission. If you pass
     * in a null Context object, it will return false as otherwise it cannot check the permission.
     * However, the Activity parameter is nullable so that you can pass in a reference that you
     * are not always sure will be valid or not (e.g. getActivity() from Fragment).
     *
     * @param context    the Context necessary to check the permission
     * @param permission the permission to check
     * @return true if you have been granted the permission, false otherwise
     */
    @SuppressWarnings("unused")
    public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {
        return context != null && (ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission));
    }

    /**
     * This static method can be used to check whether or not you have several specific permissions.
     * It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)}
     * for each permission and will simply return a boolean whether or not you have all the permissions.
     * If you pass in a null Context object, it will return false as otherwise it cannot check the
     * permission. However, the Activity parameter is nullable so that you can pass in a reference
     * that you are not always sure will be valid or not (e.g. getActivity() from Fragment).
     *
     * @param context     the Context necessary to check the permission
     * @param permissions the permissions to check
     * @return true if you have been granted all the permissions, false otherwise
     */
    @SuppressWarnings("unused")
    public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) {
        if (context == null) {
            return false;
        }
        boolean hasAllPermissions = true;
        for (String perm : permissions) {
            hasAllPermissions &= hasPermission(context, perm);
        }
        return hasAllPermissions;
    }

    /**
     * This method will request all the permissions declared in your application manifest
     * for the specified {@link PermissionsResultAction}. The purpose of this method is to enable
     * all permissions to be requested at one shot. The PermissionsResultAction is used to notify
     * you of the user allowing or denying each permission. The Activity and PermissionsResultAction
     * parameters are both annotated Nullable, but this method will not work if the Activity
     * is null. It is only annotated Nullable as a courtesy to prevent crashes in the case
     * that you call this from a Fragment where {@link Fragment#getActivity()} could yield
     * null. Additionally, you will not receive any notification of permissions being granted
     * if you provide a null PermissionsResultAction.
     *
     * @param activity the Activity necessary to request and check permissions.
     * @param action   the PermissionsResultAction used to notify you of permissions being accepted.
     */
    @SuppressWarnings("unused")
    public synchronized void requestAllManifestPermissionsIfNecessary(@Nullable final Activity activity, @Nullable final PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        String[] perms = getManifestPermissions(activity);
        requestPermissionsIfNecessaryForResult(activity, perms, action);
    }

    /**
     * This method should be used to execute a {@link PermissionsResultAction} for the array
     * of permissions passed to this method. This method will request the permissions if
     * they need to be requested (i.e. we don't have permission yet) and will add the
     * PermissionsResultAction to the queue to be notified of permissions being granted or
     * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
     * The Activity variable is nullable, but if it is null, the method will fail to execute.
     * This is only nullable as a courtesy for Fragments where getActivity() may yeild null
     * if the Fragment is not currently added to its parent Activity.
     *
     * @param activity    the activity necessary to request the permissions.
     * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
     * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.
     */
    @SuppressWarnings("unused")
    public synchronized void requestPermissionsIfNecessaryForResult(@Nullable Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                // if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                ActivityCompat.requestPermissions(activity, permsToRequest, 1);
            }
        }
    }

    /**
     * This method should be used to execute a {@link PermissionsResultAction} for the array
     * of permissions passed to this method. This method will request the permissions if
     * they need to be requested (i.e. we don't have permission yet) and will add the
     * PermissionsResultAction to the queue to be notified of permissions being granted or
     * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately.
     * The Fragment variable is used, but if {@link Fragment#getActivity()} returns null, this method
     * will fail to work as the activity reference is necessary to check for permissions.
     *
     * @param fragment    the fragment necessary to request the permissions.
     * @param permissions the list of permissions to request for the {@link PermissionsResultAction}.
     * @param action      the PermissionsResultAction to notify when the permissions are granted or denied.
     */
    @SuppressWarnings("unused")
    public synchronized void requestPermissionsIfNecessaryForResult(@NonNull Fragment fragment, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                // if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                fragment.requestPermissions(permsToRequest, 1);
            }
        }
    }

    /**
     * This method notifies the PermissionsManager that the permissions have change. If you are making
     * the permissions requests using an Activity, then this method should be called from the
     * Activity callback onRequestPermissionsResult() with the variables passed to that method. If
     * you are passing a Fragment to make the permissions request, then you should call this in
     * the {@link Fragment#onRequestPermissionsResult(int, String[], int[])} method.
     * It will notify all the pending PermissionsResultAction objects currently
     * in the queue, and will remove the permissions request from the list of pending requests.
     *
     * @param permissions the permissions that have changed.
     * @param results     the values for each permission.
     */
    @SuppressWarnings("unused")
    public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {
        int size = permissions.length;
        if (results.length < size) {
            size = results.length;
        }
        Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();

        for (int n = 0; n < size; n++) {
            mPendingRequests.remove(permissions[n]);
        }
    }

    /**
     * When request permissions on devices before Android M (Android 6.0, API Level 23)
     * Do the granted or denied work directly according to the permission status
     *
     * @param activity    the activity to check permissions
     * @param permissions the permissions names
     * @param action      the callback work object, containing what we what to do after
     *                    permission check
     */
    private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        for (String perm : permissions) {
            if (action != null) {
                if (!mPermissions.contains(perm)) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
                    action.onResult(perm, Permissions.DENIED);
                } else {
                    action.onResult(perm, Permissions.GRANTED);
                }
            }
        }
    }

    /**
     * Filter the permissions list:
     * If a permission is not granted, add it to the result list
     * if a permission is granted, do the granted work, do not add it to the result list
     *
     * @param activity    the activity to check permissions
     * @param permissions all the permissions names
     * @param action      the callback work object, containing what we what to do after
     *                    permission check
     * @return a list of permissions names that are not granted yet
     */
    @NonNull
    private List<String> getPermissionsListToRequest(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        List<String> permList = new ArrayList<String>(permissions.length);
        for (String perm : permissions) {
            if (!mPermissions.contains(perm)) {
                if (action != null) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                }
            } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
                if (!mPendingRequests.contains(perm)) {
                    permList.add(perm);
                }
            } else {
                if (action != null) {
                    action.onResult(perm, Permissions.GRANTED);
                }
            }
        }
        return permList;
    }
}