/**
 * The MIT License
 *
 * Copyright (C) 2015 Asterios Raptis
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package de.alpharogroup.xml.tag;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import de.alpharogroup.clone.object.CloneObjectExtensions;
import de.alpharogroup.collections.list.ListFactory;
import de.alpharogroup.collections.map.MapFactory;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;

/**
 * The class Tag represents an tag for xml or html.
 */
@Getter
@Setter
@ToString(exclude = { "attributes", "children" })
@EqualsAndHashCode(exclude = { "attributes", "children" })
@NoArgsConstructor
@AllArgsConstructor
@Builder(toBuilder = true)
@FieldDefaults(level = AccessLevel.PRIVATE)
public class SimpleTag implements Serializable
{

	/**
	 * The Constant serialVersionUID.
	 */
	private static final long serialVersionUID = 1L;

	/** The attributes of the tag. */
	Map<String, String> attributes;

	/** The children. */
	List<SimpleTag> children;

	/** The content of the tag. */
	String content;

	/** The flag endTag signals if this tag has an ending tag. */
	boolean endTag;

	/** The name of the tag. */
	String name;

	/**
	 * Adds the attribute with the given name and value.
	 *
	 * @param name
	 *            the name
	 * @param value
	 *            the value
	 * @return the string
	 */
	public String addAttribute(final String name, final String value)
	{
		if (getAttributes() == null)
		{
			setAttributes(MapFactory.newLinkedHashMap());
		}
		return getAttributes().put(name, value);
	}

	/**
	 * Adds the given child.
	 *
	 * @param child
	 *            the child
	 * @return true, if successful
	 */
	public boolean addChild(final SimpleTag child)
	{
		if (getChildren() == null)
		{
			setChildren(ListFactory.newArrayList());
		}
		return getChildren().add(child);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Object clone() throws CloneNotSupportedException
	{
		try
		{
			return CloneObjectExtensions.clone(this);
		}
		catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException
			| InvocationTargetException | ClassNotFoundException | InstantiationException
			| IOException e)
		{
			throw new CloneNotSupportedException();
		}
	}

	/**
	 * Removes the attribute with the given name.
	 *
	 * @param name
	 *            the name
	 * @return the string
	 */
	public String removeAttribute(final String name)
	{
		if (getAttributes() != null)
		{
			getAttributes().remove(name);
		}
		return null;
	}

	/**
	 * Removes the given child if exists.
	 *
	 * @param child
	 *            the child
	 * @return true, if successful
	 */
	public boolean removeChild(final SimpleTag child)
	{
		if (getChildren() != null)
		{
			return getChildren().remove(child);
		}
		return false;
	}

	/**
	 * Creates from this Tag object an velocity template as String object. It puts children Tag
	 * object in the template recursively.
	 *
	 * @return the string buffer
	 */
	public StringBuilder toVelocityTemplate()
	{
		final StringBuilder buffer = new StringBuilder();
		buffer.append("<");
		buffer.append("${").append(getName()).append(".name}\n");
		if (getAttributes() != null && !getAttributes().isEmpty())
		{
			buffer.append(" #foreach(" + "$attribute in $").append(getName())
				.append(".attributes.keySet()" + ")\n");
			buffer.append("$attribute=\"$").append(getName())
				.append(".getAttributes().get($attribute)\"\n");
			buffer.append(" #end\n");
		}
		buffer.append("#if(${").append(getName()).append(".endTag})>${").append(getName())
			.append(".content}\n");
		if (getChildren() != null && !getChildren().isEmpty())
		{
			buffer.append("#foreach($").append(getChildren().get(0).getName()).append(" in $")
				.append(getName()).append(".children)\n");
			for (final SimpleTag child : getChildren())
			{
				buffer.append(child.toVelocityTemplate().toString());
			}
			buffer.append("#end\n");
		}
		buffer.append("</${").append(getName()).append(".name}>\n");
		buffer.append("#else />\n" + "#end\n");
		return buffer;
	}

	/**
	 * Creates from this Tag object an xml string.
	 *
	 * @return the string
	 */
	public String toXmlString()
	{
		final StringBuilder buffer = new StringBuilder();
		buffer.append("<");
		buffer.append(getName());
		Optional<String> attr = TagExtensions.attributesToString(getAttributes());
		if (attr.isPresent())
		{
			buffer.append(attr.get());
		}
		if (isEndTag())
		{
			buffer.append(">");
			buffer.append(getContent());
			if (getChildren() != null && !getChildren().isEmpty())
			{
				for (final SimpleTag child : getChildren())
				{
					buffer.append(child.toXmlString());
				}
			}
			buffer.append("</");
			buffer.append(getName());
			buffer.append(">");
		}
		else
		{
			buffer.append("/>");
		}
		return buffer.toString();
	}

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Array;
import java.util.Date;

/**
 * A class that allows the conversion of the value of a {@link ProfileField}
 * from/to a String that can be persisted in a database.
 *
 * @author darioarchetti
 *
 * @param <E>
 *            - the type of the {@link ProfileField}
 */
public abstract class ProfileFieldConverter<E extends Object> {

	private static final ProfileFieldConverter<String> STRING_CONVERTER = new StringConverter();
	private static final ProfileFieldConverter<Date> DATE_CONVERTER = new DateConverter();
	private static final ProfileFieldConverter<String[]> STRING_ARRAY_CONVERTER = new ArrayConverter<String>(String.class, STRING_CONVERTER);
	private static final ProfileFieldConverter<Bitmap> BITMAP_CONVERTER = new BitmapConverter();
	private static final String ARRAY_SEPARATOR = ";";

	/**
	 * Returns a {@link ProfileFieldConverter} that can handle the value of a
	 * given {@link ProfileField}
	 *
	 * @param field
	 *            - the field whose values should be handled
	 * @return - the {@link ProfileFieldConverter} that can handle those values
	 */
	@SuppressWarnings("unchecked")
	public static <E> ProfileFieldConverter<E> forField(ProfileField<E> field) {
		Class<?> fieldClass = field.getFieldClass();

		if (fieldClass == String.class) {
			return (ProfileFieldConverter<E>) STRING_CONVERTER;
		} else if (fieldClass == Date.class) {
			return (ProfileFieldConverter<E>) DATE_CONVERTER;
		} else if (fieldClass == String[].class) {
			return (ProfileFieldConverter<E>) STRING_ARRAY_CONVERTER;
		} else if (fieldClass == Bitmap.class) {
			return (ProfileFieldConverter<E>) BITMAP_CONVERTER;
		}

		throw new IllegalArgumentException("Field type " + fieldClass.getSimpleName() + " not supported");
	}

	// public methods
	/**
	 * Converts the value of the {@link ProfileField} to a String
	 *
	 * @param value
	 *            - the value of the {@link ProfileField}
	 * @return the String representation of the given value
	 */
	public abstract String toStorageString(E value);

	/**
	 * Converts the String representation of the value of a {@link ProfileField}
	 * to the original value.
	 *
	 * @param storageString
	 *            - the String representation
	 * @return the original value of the {@link ProfileField}
	 */
	public abstract E fromStorageString(String storageString);

	// Implementations
	private static class StringConverter extends ProfileFieldConverter<String> {

		@Override
		public String toStorageString(String value) {
			return value;
		}

		@Override
		public String fromStorageString(String storageString) {
			return storageString;
		}
	}

	private static class DateConverter extends ProfileFieldConverter<Date> {

		@Override
		public String toStorageString(Date value) {
			return String.valueOf(value.getTime());
		}

		@Override
		public Date fromStorageString(String storageString) {
			return new Date(Long.valueOf(storageString));
		}
	}

	private static class ArrayConverter<E> extends ProfileFieldConverter<E[]> {

		private Class<E> mComponentType;
		private ProfileFieldConverter<E> mBaseConverter;

		public ArrayConverter(Class<E> componentType, ProfileFieldConverter<E> baseConverter) {
			this.mComponentType = componentType;
			this.mBaseConverter = baseConverter;
		}

		@Override
		public String toStorageString(E[] value) {
			StringBuilder b = new StringBuilder();
			for (int i = 0; i < value.length; i++) {
				b.append(mBaseConverter.toStorageString(value[i]));
				if ((i + 1) < value.length) {
					b.append(ARRAY_SEPARATOR);
				}
			}
			return b.toString();
		}

		@SuppressWarnings("unchecked")
		@Override
		public E[] fromStorageString(String storageString) {
			String[] values = storageString.split(ARRAY_SEPARATOR);
			Object array = Array.newInstance(mComponentType, values.length);

			for (int i = 0; i < values.length; i++) {
				Array.set(array, i, mBaseConverter.fromStorageString(values[i]));
			}

			return (E[]) array;
		}

	}

	private static class BitmapConverter extends ProfileFieldConverter<Bitmap> {

		@Override
		public String toStorageString(Bitmap value) {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			value.compress(Bitmap.CompressFormat.JPEG, 100, baos);
			return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
		}

		@Override
		public Bitmap fromStorageString(String storageString) {
			byte[] ba = Base64.decode(storageString.getBytes(), Base64.DEFAULT);
			Bitmap photo = BitmapFactory.decodeByteArray(ba, 0, ba.length);
			return photo;
		}
	}
}


/*
 * Copyright 2015 Stefano Cappa
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package it.polimi.spf.wfd;

import android.net.wifi.p2p.WifiP2pDevice;

import java.util.ArrayList;
import java.util.List;

import it.polimi.spf.wfd.util.WfdLog;
import lombok.Getter;

/**
 * Class that represents a list of {@link it.polimi.spf.wfd.WiFiP2pService }.
 * This list contains all the device found during discovery phase of the wifi direct protocol.
 * This class use Singleton pattern.
 *

 * Created by Stefano Cappa on 07/08/15.
 */
public class ServiceList {
    private static final String TAG = ServiceList.class.getSimpleName();
    private static final String AP_APPENDIX = "AP"; //or GO in case of Wifi direct
    private static final String SLAVE_APPENDIX = "U"; //or client in case of wifi direct

    @Getter
    private final List<WiFiP2pService> serviceList;

    private static final ServiceList instance = new ServiceList();

    /**
     * Method to get the instance of this class.
     *
     * @return instance of this class.
     */
    public static ServiceList getInstance() {
        return instance;
    }

    /**
     * Private constructor, because is a singleton class.
     */
    private ServiceList() {
        serviceList = new ArrayList<>();
    }

    /**
     * Method to add a service inside the list in a secure way.
     * The service is added only if isn't already inside the list.
     *
     * @param service {@link WiFiP2pService} to add.
     */
    public void addServiceIfNotPresent(WiFiP2pService service) {
        WfdLog.d(TAG, "addServiceIfNotPresent BEGIN, with size = " + serviceList.size());

        if (service == null) {
            WfdLog.e(TAG, "Service is null, returning...");
            return;
        }

        boolean add = true;
        for (WiFiP2pService element : serviceList) {
            if (element != null
                    && element.getDevice().equals(service.getDevice())
                    && element.getInstanceName().equals(service.getInstanceName())) {
                add = false; //already in the list
            }
        }

        if (add) {
            serviceList.add(service);
        }

        WfdLog.d(TAG, "addServiceIfNotPresent END, with size = " + serviceList.size());
    }

    /**
     * Method to get a service from the list, using only the device.
     * This method use only the deviceAddress, not the device name, because sometimes Android doesn't
     * get the name, but only the mac address.
     *
     * @param device WifiP2pDevice that you want to use to search the service.
     * @return The WiFiP2pService associated to the device or null, if the device isn't in the list.
     */
    public WiFiP2pService getServiceByDevice(WifiP2pDevice device) {
        if (device == null) {
            return null;
        }

        WfdLog.d(TAG, "groupownerdevice passed to getServiceByDevice: " + device.deviceName + ", " + device.deviceAddress);

        WfdLog.d(TAG, "servicelist size: " + serviceList.size());

        for (WiFiP2pService element : serviceList) {
            WfdLog.d(TAG, "element in list: " + element.getDevice().deviceName + ", " + element.getDevice().deviceAddress);
            WfdLog.d(TAG, "element passed : " + device.deviceName + ", " + device.deviceAddress);

            if (element.getDevice().deviceAddress.equals(device.deviceAddress)) {
                WfdLog.d(TAG, "getServiceByDevice if satisfied : " + device.deviceAddress + ", " + element.getDevice().deviceAddress);
                return element;
            }
        }

        WfdLog.d(TAG, "servicelist size: " + serviceList.size());

        return null;
    }

    public WiFiP2pService getServiceByAddress(String address) {
        if (address == null) {
            return null;
        }

        for (WiFiP2pService element : serviceList) {
            WfdLog.d(TAG, "element in list: " + element.getDevice().deviceName + ", " + element.getDevice().deviceAddress);
            WfdLog.d(TAG, "element passed : " + address);

            if (element.getDevice().deviceAddress.equals(address)) {
                WfdLog.d(TAG, "getServiceByDevice if satisfied : " + address + ", " + element.getDevice().deviceAddress);
                return element;
            }
        }

        WfdLog.d(TAG, "servicelist size: " + serviceList.size());

        return null;
    }

    public List<WiFiP2pService> selectValidServices(String myIdentifier) {
        List<WiFiP2pService> validServiceList = new ArrayList<>();
        for (WiFiP2pService service : ServiceList.getInstance().getServiceList()) {
            if (service != null && service.getPort() != WiFiP2pService.INVALID
                    && service.getIdentifier() != null && !service.getIdentifier().equals(myIdentifier)) {

                validServiceList.add(service);

                WfdLog.d(TAG, "--> ValidServiceList: --OK --: " + service.getIdentifier() + "," + service.getPeerAddress());
            } else {
                if (service != null) {
                    WfdLog.d(TAG, "--> ValidServiceList: --NOT--: " + service.getIdentifier() + "," + service.getPeerAddress());
                }
            }
        }
        return validServiceList;
    }

    public List<WiFiP2pService> getValidGroupOwners(String myIdentifier) {
        List<WiFiP2pService> validServiceList = this.selectValidServices(myIdentifier);
        List<WiFiP2pService> validGoList = new ArrayList<>();
        for (WiFiP2pService service : validServiceList) {
            if (service.getIdentifier().startsWith(AP_APPENDIX)) {
                WfdLog.d(TAG, "--> ValidGroupOwnersList: --OK --: " + service.getIdentifier() + "," + service.getPeerAddress());
                validGoList.add(service);
            }
        }
        return validGoList;
    }

    public List<WiFiP2pService> getValidClients(String myIdentifier) {
        List<WiFiP2pService> validServiceList = this.selectValidServices(myIdentifier);
        List<WiFiP2pService> validGoList = new ArrayList<>();
        for (WiFiP2pService service : validServiceList) {
            if (!service.getIdentifier().startsWith(AP_APPENDIX)) {
                WfdLog.d(TAG, "--> ValidClientsList: --OK --: " + service.getIdentifier() + "," + service.getPeerAddress());
                validGoList.add(service);
            }
        }
        return validGoList;
    }

    public boolean containsDevice(WifiP2pDevice device) {
        for (WiFiP2pService serv : serviceList) {
            if (serv.getDevice().deviceAddress.equals(device.deviceAddress)) {
                return true;
            }
        }
        return false;
    }

    public WiFiP2pService getServiceByDeviceAddress(String deviceAddress) {
        for (WiFiP2pService serv : serviceList) {
            if (serv.getDevice().deviceAddress.equals(deviceAddress)) {
                return serv;
            }
        }
        return null;
    }
}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Interoperable key-value data container describing a potential or completed
 * social action, inspired by the ActivityStreams specification.
 *
 * Each activity features a set of standard fields:
 *

 *
Verb: a string that identifies the type of the social action

 *
SenderIdentifier: the identifier of the user who sent the activity
 *

 *
SenderDisplayName: the display name of the user who sent the
 * activity

 *
ReceivedIdentifier: the identifier of the target of the request

 *
ReceiverDisplayName: the display name of the target of the
 * activity.

 *

 *
 * The personal details of the sender and the receiver are automatically
 * injected by the framework without the need of additional lookups; activities
 * may also contain any custom field needed to describe the action.
 *
 * @see #get(String)
 * @author darioarchetti
 */
public class SPFActivity implements Parcelable {

	/**
	 * Key for the Verb of the activity.
	 *
	 * @see #get(String)
	 */
	public static final String VERB = "verb";

	/**
	 * Key for the identifier of the sender
	 *
	 * @see #get(String)
	 */
	public static final String SENDER_IDENTIFIER = "senderIdentifier";

	/**
	 * Key for the display name of the sender
	 *
	 * @see #get(String)
	 */
	public static final String SENDER_DISPLAY_NAME = "senderDisplayName";

	/**
	 * Key for the identifier of the receiver
	 *
	 * @see #get(String)
	 */
	public static final String RECEIVER_IDENTIFIER = "receiverIdentifier";

	/**
	 * Key for the display name of the receiver
	 *
	 * @see #get(String)
	 */
	public static final String RECEIVER_DISPLAY_NAME = "receiverDisplayName";

	private final Map<String, String> mFields;

	/**
	 * Creates a new activity with a given verb.
	 *
	 * @param verb
	 *            - the verb of the activity
	 */
	public SPFActivity(String verb) {
		this();
		if (verb == null) {
			throw new NullPointerException();
		}

		mFields.put(VERB, verb);
	}

	private SPFActivity() {
		this.mFields = new HashMap<String, String>();
	}

	/**
	 * Returns the verb of the activity
	 *
	 * @return
	 */
	public String getVerb() {
		return mFields.get(VERB);
	}

	/**
	 * Puts a detail in this activity
	 *
	 * @param key
	 *            - the key of the detail
	 * @param value
	 *            - the value of the detail
	 */
	public void put(String key, String value) {
		if (key.equals(VERB)) {
			return;
		}

		mFields.put(key, value);
	}

	/**
	 * Retrieves the value of a detail contained in this activity.
	 *
	 * @param key
	 *            - the key of the detail
	 * @return the value of the detail, or null if the key is not valid
	 */
	public String get(String key) {
		return mFields.get(key);
	}

	/**
	 * @return the set of the detail keys
	 */
	public Set<String> keySet() {
		return mFields.keySet();
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public String toString() {
		return "[SPFActivity " + mFields.toString() + "]";
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		Bundle b = new Bundle();

		for (String k : mFields.keySet()) {
			b.putString(k, mFields.get(k));
		}

		b.writeToParcel(dest, 0);
	}

	public static final Creator<SPFActivity> CREATOR = new Creator<SPFActivity>() {

		@Override
		public SPFActivity[] newArray(int size) {
			return new SPFActivity[size];
		}

		@Override
		public SPFActivity createFromParcel(Parcel source) {
			SPFActivity ac = new SPFActivity();
			ac.readFromParcel(source);
			return ac;
		}
	};

	public void readFromParcel(Parcel parcel) {
		Bundle b = parcel.readBundle();
		for (String k : b.keySet()) {
			mFields.put(k, b.getString(k));
		}
	}
}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 * Copyright 2015 Stefano Cappa
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.framework.local;

import android.app.Notification;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import it.polimi.spf.framework.SPF;
import it.polimi.spf.framework.SPFContext;
import it.polimi.spf.shared.SPFInfo;

/**
 *
 */
public class SPFService extends Service {

    // ## WARNING ##
    // Any modification to the package name or class name of this component
    // should be reflected in SPFInfo.

    private static final String TAG = "SPFService";
    private static final int NOTIFICATION_ID = 0xab01d;
    private static boolean mIsStartedForeground = false;
    private static String ACTION_START_FOREGROUND = "it.polimi.spf.framework.SPFService.START_FOREGROUND";
    private static String ACTION_STOP_FOREGROUND = "it.polimi.spf.framework.SPFService.STOP_FOREGROUND";

    //associated to the button in the notification to catch clicks
    private static final String STOPSPF = "it.polimi.spf.app.stop";

    private static String ACTION_STOP_FOREGROUND_SWITCH = "it.polimi.spf.framework.SPFService.UPDATE_SWITCH";
    private static String CALL_NAVIGATION_FRAGMENT_BROADCAST_INTENT = "it.polimi.spf.SPFService.spfservice-navigationfragment";
    private static final int UPDATESWITCH = 1;

    private final Service service = this;

    /**
     * Sends an intent to SPFService, using the {@link Context} provided, that
     * makes it start in foreground.
     *
     * @param c - the Context used to send the intent;
     * @see Service#startForeground(int, Notification)
     */
    public static void startForeground(Context c) {
        Intent i = new Intent(c, SPFService.class);
        i.setAction(ACTION_START_FOREGROUND);
        c.startService(i);
    }

    /**
     * Sends an intent to SPFService, using the {@link Context} provided, that
     * makes it stop foreground.
     *
     * @param c - the Context used to send the intent;
     * @see Service#stopForeground(boolean)
     */
    public static void stopForeground(Context c) {
        Intent i = new Intent(c, SPFService.class);
        i.setAction(ACTION_STOP_FOREGROUND);
        c.startService(i);
        c.stopService(i);
    }

    /**
     * Check whether the service is started or not. Note that this method return
     * false if the service is bounded.
     *
     * @return true if the service is started, otherwise false
     */
    public static boolean isStarted() {
        return mIsStartedForeground;
    }

    private IBinder mServerBinder;
    private IBinder mProfileBinder;
    private IBinder mLocalServiceBinder;
    private IBinder mNotificationBinder;
    private IBinder mSecurityBinder;

    private Notification mDefaultNotification;

    /*
     * (non-Javadoc)
     *
     * @see android.app.Service#onBind(android.content.Intent)
     */
    @Override
    public IBinder onBind(Intent intent) {
        String action = intent.getAction();
        Log.d(TAG, "External app bound with action " + intent.getAction());

        if (SPFInfo.ACTION_PROXIMITY_SERVER.equalsIgnoreCase(action)) {
            SPF.get().connect();
            // Request the binder that offers access to proximity
            // functionalities
            if (mServerBinder == null) {
                mServerBinder = new SPFProximityServiceImpl();
            }
            return mServerBinder;

        } else if (SPFInfo.ACTION_PROFILE.equals(action)) {
            // Requests the binder that offers the access to the local profile
            if (mProfileBinder == null) {
                mProfileBinder = new SPFProfileServiceImpl(this);
            }
            return mProfileBinder;

        } else if (SPFInfo.ACTION_SERVICE.equalsIgnoreCase(action)) {
            // Request the binder to manage local services
            if (mLocalServiceBinder == null) {
                mLocalServiceBinder = new SPFServiceManagerImpl();
            }
            return mLocalServiceBinder;

        } else if (SPFInfo.ACTION_NOTIFICATION.equalsIgnoreCase(action)) {
            // request the binder to access notification services
            if (mNotificationBinder == null) {
                mNotificationBinder = new SPFNotificationServiceImpl(this);
            }
            return mNotificationBinder;
        } else if (SPFInfo.ACTION_SECURITY.equals(action)) {
            // request the binder to access security services
            if (mSecurityBinder == null) {
                mSecurityBinder = new SPFSecurityServiceImpl(this);
            }
            return mSecurityBinder;
        }

        Log.d(TAG, "Unrecognized action: " + action);
        return null;
    }

    // Triggered by the front end to keep spf service active in foreground
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            return START_STICKY;
        }

        String action = intent.getAction();

        if (ACTION_START_FOREGROUND.equals(action)) {
            if (!SPF.get().isConnected()) {
                SPF.get().connect();
            }
            SPF.get().notifyProximityStatus(false); //false because it's starting

            startInForeground();
            Log.d(TAG, "Started in foreground");
        } else if (ACTION_STOP_FOREGROUND.equals(action)) {

            //notify to the middleware that i killed the proximity service
            SPF.get().notifyProximityStatus(true); //true because it's killing

            stopForeground(true);
            mIsStartedForeground = false;
            Log.d(TAG, "Foreground stopped");
        }

        return START_STICKY;
    }

    private void startInForeground() {
        Notification n = SPFContext.get().getServiceNotification();
        if (n == null) {
            n = mDefaultNotification;
        }
        startForeground(NOTIFICATION_ID, n);
        mIsStartedForeground = true;
    }

    private BroadcastReceiver receivefromServiceNotificationStop = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "Button 'Stop' pressed in Notification");

            //Stop the service
            SPFService.stopForeground(service.getBaseContext());

            //Now i want to call a LocalBroadcast to ask to the NavigationFragment:
            // "Please can you update the switch status?" :)
            Intent intent_call_navigationFragment = new Intent(ACTION_STOP_FOREGROUND_SWITCH);
            intent_call_navigationFragment.putExtra(CALL_NAVIGATION_FRAGMENT_BROADCAST_INTENT, UPDATESWITCH);

            LocalBroadcastManager.getInstance(service).sendBroadcast(intent_call_navigationFragment);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        SPF.get().onServerCreated(this);
        // Default notification is empty, so Android will use its own default
        // notification for this app.
        mDefaultNotification = new Notification.Builder(this).build();
        Log.d(TAG, "Service created");
        this.registerReceiver(receivefromServiceNotificationStop, new IntentFilter(STOPSPF));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Service destroyed");
        this.unregisterReceiver(receivefromServiceNotificationStop);
        SPF.get().onServerDestroy();
        SPF.get().disconnect();
    }
}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.framework.notification;

import it.polimi.spf.shared.model.SPFAction;
import it.polimi.spf.shared.model.SPFQuery;
import it.polimi.spf.shared.model.SPFTrigger;
import it.polimi.spf.shared.model.SPFTrigger.IllegalTriggerException;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;

/**
 * @author Jacopo Aliprandi
 *
 */
/*package*/ class SPFTriggerTable extends SQLiteOpenHelper {

	private static class Contract implements BaseColumns {
		public static final String TABLE_NAME = "triggers";
		public static final String COLUMN_NAME = "name";
		public static final String COLUMN_QUERY = "query";
		public static final String COLUMN_ACTION = "action";
		public static final String COLUMN_ONESHOT = "oneshot";
		public static final String COLUMN_EXPIRATION = "expiration";
		public static final String COLUMN_APP_IDENTIFIER = "app_identifier";
	}

	// If you change the database schema, you must increment the database
	// version.
	public static final int DATABASE_VERSION = 1;
	public static final String DATABASE_NAME = "Trigger.db";
	private static final String TEXT_TYPE = " TEXT";
	private static final String INTEGER_TYPE = " INTEGER";
	private static final String COMMA_SEP = ",";

	private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " +
	// Table name
	Contract.TABLE_NAME + " (" +
	// Trigger ID
	Contract._ID + " INTEGER PRIMARY KEY" + COMMA_SEP +
	// Trigger name
	Contract.COLUMN_NAME + TEXT_TYPE + " UNIQUE ON CONFLICT REPLACE" + COMMA_SEP +
	// Trigger query
	Contract.COLUMN_QUERY + TEXT_TYPE + COMMA_SEP +
	// Trigger action
	Contract.COLUMN_ACTION + TEXT_TYPE + COMMA_SEP +
	// Trigger oneshot
	Contract.COLUMN_ONESHOT + INTEGER_TYPE + COMMA_SEP +
	// Trigger expiration
	Contract.COLUMN_EXPIRATION + INTEGER_TYPE + COMMA_SEP +
	// Trigger owner app identifier
	Contract.COLUMN_APP_IDENTIFIER + TEXT_TYPE + ")";

	private static final String SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + Contract.TABLE_NAME;

	/**
	 *
	 * @param context
	 */
	public SPFTriggerTable(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * android.database.sqlite.SQLiteOpenHelper#onCreate(android.database.sqlite
	 * .SQLiteDatabase)
	 */
	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(SQL_CREATE_ENTRIES);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * android.database.sqlite.SQLiteOpenHelper#onUpgrade(android.database.sqlite
	 * .SQLiteDatabase, int, int)
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL(SQL_DELETE_ENTRIES);
		onCreate(db);
	}

	@Override
	public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		onUpgrade(db, oldVersion, newVersion);
	}

	/**
	 * Save the trigger in the database. If the operation succeeds, return a
	 * trigger with an updated id is returned, otherwise return null. The id is
	 * updated only when a new trigger is created. ( e.g. its id is set to -1)
	 *
	 * @param trigger
	 * @param appIdentifier
	 * @return a trigger, otherwise null
	 */
	SPFTrigger saveTrigger(SPFTrigger trigger, String appIdentifier) {
		if (appIdentifier == null || trigger == null) {
			throw new NullPointerException();
		}

		ContentValues cv = new ContentValues();
		cv.put(Contract.COLUMN_NAME, trigger.getName());
		cv.put(Contract.COLUMN_QUERY, trigger.getQuery().toQueryString());
		cv.put(Contract.COLUMN_ACTION, trigger.getAction().toJSON());
		cv.put(Contract.COLUMN_ONESHOT, trigger.isOneShot() ? 1 : 0);
		cv.put(Contract.COLUMN_EXPIRATION, trigger.getSleepPeriod());
		cv.put(Contract.COLUMN_APP_IDENTIFIER, appIdentifier);

		SQLiteDatabase db = getWritableDatabase();

		if (trigger.getId() >= 0) {
			String where = Contract._ID + " = ?";
			String[] whereArgs = { String.valueOf(trigger.getId()) };
			if (db.update(Contract.TABLE_NAME, cv, where, whereArgs) > 0) {
				return trigger;
			}
			return null;
		}

		long id = db.insert(Contract.TABLE_NAME, null, cv);
		if (id >= 0) {
			trigger.setId(id);
			return trigger;
		}

		return null;
	}

	/**
	 * Return all the triggers of the specified application
	 *
	 * @param appIdentifier
	 *            - the identifier of the application
	 * @return a list of {@link SPFTrigger}
	 */
	List<SPFTrigger> getAllTriggers(String appIdentifier) {
		String where = Contract.COLUMN_APP_IDENTIFIER + " = ?";
		String[] whereArgs = { appIdentifier };
		Cursor c = getReadableDatabase().query(Contract.TABLE_NAME, null, where, whereArgs, null, null, null);
		List<SPFTrigger> triggers = new ArrayList<SPFTrigger>();

		while (c.moveToNext()) {
			triggers.add(triggerFromCursor(c));
		}

		c.close();
		return triggers;
	}

	/**
	 * Return all the saved trigger.
	 *
	 * @return a list of {@link SPFTrigger}
	 */
	List<SPFTrigger> getAllTriggers() {
		String where = null;
		String[] whereArgs = null;
		Cursor c = getReadableDatabase().query(Contract.TABLE_NAME, null, where, whereArgs, null, null, null);
		List<SPFTrigger> triggers = new ArrayList<SPFTrigger>();
		while (c.moveToNext()) {
			triggers.add(triggerFromCursor(c));
		}

		c.close();
		return triggers;
	}

	/**
	 * Delete all the triggers registered with the given application identifier.
	 *
	 * @param appPackageName
	 * @return true if there is at least one row deleted
	 */
	boolean deleteAllTriggerOf(String appPackageName) {
		String where = Contract.COLUMN_APP_IDENTIFIER + " = ?";
		String[] whereArgs = { appPackageName };
		int c = getReadableDatabase().delete(Contract.TABLE_NAME, where, whereArgs);
		return c > 0;
	}

	/**
	 * Delete the trigger with the given id.
	 *
	 * @param id
	 * @param appPackageName
	 * @return true if there is a deletion
	 */
	boolean deleteTrigger(long id, String appPackageName) {
		String where = Contract.COLUMN_APP_IDENTIFIER + " = ? AND " + Contract._ID + " = ?";
		String[] whereArgs = { appPackageName, Long.toString(id) };
		int count = getReadableDatabase().delete(Contract.TABLE_NAME, where, whereArgs);
		return count > 0;

	}

	/**
	 * Return the trigger with the specified id.
	 *
	 * @param triggerId
	 * @param appPackageName
	 * @return
	 */
	SPFTrigger getTrigger(long triggerId, String appPackageName) {
		String where = Contract._ID + " = ? AND " + Contract.COLUMN_APP_IDENTIFIER + " = ?";
		String[] whereArgs = { Long.toString(triggerId), appPackageName };
		Cursor c = getReadableDatabase().query(Contract.TABLE_NAME, null, where, whereArgs, null, null, null);
		if (!c.moveToFirst()) {
			return null;
		}
		SPFTrigger t = triggerFromCursor(c);
		c.close();
		return t;
	}

	private SPFTrigger triggerFromCursor(Cursor c) {
		int idColumnId = c.getColumnIndexOrThrow(Contract._ID);
		int nameColumnId = c.getColumnIndexOrThrow(Contract.COLUMN_NAME);
		int queryColumnId = c.getColumnIndexOrThrow(Contract.COLUMN_QUERY);
		int actionColumnId = c.getColumnIndexOrThrow(Contract.COLUMN_ACTION);
		int oneShotColumnId = c.getColumnIndexOrThrow(Contract.COLUMN_ONESHOT);
		int expirationColumnId = c.getColumnIndexOrThrow(Contract.COLUMN_EXPIRATION);

		long id = c.getLong(idColumnId);
		String name = c.getString(nameColumnId);
		SPFQuery query = SPFQuery.fromQueryString(c.getString(queryColumnId));
		SPFAction action = SPFAction.fromJSON(c.getString(actionColumnId));
		boolean oneShot = c.getInt(oneShotColumnId) == 1;
		long expiration = c.getLong(expirationColumnId);

		try {
			return new SPFTrigger(id, name, query, action, oneShot, expiration);
		} catch (IllegalTriggerException e) {
			// This will never happen
			throw new IllegalStateException("Invalid trigger retrieved from db", e);
		}
	}

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.os.Parcel;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * TODO #Documentation
 *
 * @author Jacopo Aliprandi
 */
public final class SPFActionSendNotification extends SPFAction {

    private static final String KEY_MESSAGE = "message";

    private static final String KEY_TITLE = "title";

    private static final String TAG = null;

    private final int mType = TYPE_NOTIFICATION;

    /**
     * The title to show in the notification.
     */
    private final String mTitle;

    /**
     * the message to show in the notification.
     */
    private final String mMessage;

	/*
     * other properties can be: icon sound intent when the notification is
	 * pressed ...
	 */

    /**
     * TODO comment
     */
    public SPFActionSendNotification(String title, String message) {
        this.mTitle = title;
        this.mMessage = message;
    }

    private SPFActionSendNotification(Parcel source) {
        this.mTitle = source.readString();
        this.mMessage = source.readString();
    }

    public SPFActionSendNotification(JSONObject source) {
        try {
            mTitle = source.getString(KEY_TITLE);
            mMessage = source.getString(KEY_MESSAGE);
        } catch (JSONException e) {
            throw new IllegalArgumentException("JSON does not represent a valid SPFActionNotification");
        }
    }

    public String getTitle() {
        return mTitle;
    }

    public String getMessage() {
        return mMessage;
    }

    /*
     * (non-Javadoc)
     *
     * @see android.os.Parcelable#describeContents()
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /*
     * (non-Javadoc)
     *
     * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(mTitle);
        dest.writeString(mMessage);
    }

    /*
     * (non-Javadoc)
     *
     * @see it.polimi.spf.framework.local.SPFAction#toJSON()
     */
    @Override
    public String toJSON() {
        try {
            JSONObject o = new JSONObject();
            o.put(KEY_TYPE, mType);
            o.put(KEY_TITLE, mTitle);
            o.put(KEY_MESSAGE, mMessage);
            return o.toString();
        } catch (JSONException e) {
            Log.e(TAG, "Error marshalling:", e);
            return "";
        }
    }

    public static Creator<SPFActionSendNotification> CREATOR = new Creator<SPFActionSendNotification>() {

        @Override
        public SPFActionSendNotification[] newArray(int size) {
            return new SPFActionSendNotification[size];
        }

        @Override
        public SPFActionSendNotification createFromParcel(Parcel source) {
            return new SPFActionSendNotification(source);
        }
    };

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Describe the configuration of a search operation. Its use is intended to ease the communication
 * beetween the application and the spf framework. It defines the query, the duration of the search
 * and the frequency of the search signal.
 */
/*
 * TODO Use it directly in the SPFLib ...different from QueryInfo which is an
 * internal data structure! (To favor decoupling )
 */
public final class SPFSearchDescriptor implements Parcelable {

    private long mIntervalBtwSignals;
    private int mNumberOfSignals;
    private SPFQuery mQuery;

    /**
     * @param intervalBtwSignals
     * @param numberOfSignals
     * @param query
     */
    public SPFSearchDescriptor(long intervalBtwSignals, int numberOfSignals, SPFQuery query) {
        super();
        this.mIntervalBtwSignals = intervalBtwSignals;
        this.mNumberOfSignals = numberOfSignals;
        this.mQuery = query;
    }

    /**
     * @return the query string
     */
    public SPFQuery getQuery() {
        return mQuery;
    }

    /**
     * @return the intervalBtwSignals
     */
    public long getIntervalBtwSignals() {
        return mIntervalBtwSignals;
    }

    /**
     * @return the numberOfSignals
     */
    public int getNumberOfSignals() {
        return mNumberOfSignals;
    }

	/*
     * Set of methods an constructor required to implement the Parcelable
	 * interface and the aidl.
	 */

    private SPFSearchDescriptor(Parcel source) {
        this.mIntervalBtwSignals = source.readLong();
        this.mNumberOfSignals = source.readInt();
        this.mQuery = source.readParcelable(((Object) this).getClass().getClassLoader());
    }

    /*
     * (non-Javadoc)
     *
     * @see android.os.Parcelable#describeContents()
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /*
     * (non-Javadoc)
     *
     * @see android.os.Parcelable#writeToParcel(android.os.Parcel, int)
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(mIntervalBtwSignals);
        dest.writeInt(mNumberOfSignals);
        dest.writeParcelable(mQuery, 0);

    }

    public final static Creator<SPFSearchDescriptor> CREATOR = new Creator<SPFSearchDescriptor>() {

        @Override
        public SPFSearchDescriptor[] newArray(int size) {

            return new SPFSearchDescriptor[size];
        }

        @Override
        public SPFSearchDescriptor createFromParcel(Parcel source) {
            return new SPFSearchDescriptor(source);
        }
    };

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.framework.security;

import java.util.List;

import android.content.Context;
import it.polimi.spf.framework.profile.SPFPersona;
import it.polimi.spf.shared.model.AppDescriptor;
import it.polimi.spf.shared.model.Permission;

/**
 * @author darioarchetti
 *
 */
public class SPFSecurityMonitor {

	private final ApplicationRegistry mAppRegistry;
	private final PersonRegistry mPersonRegistry;

	public SPFSecurityMonitor(Context context) {
		this.mAppRegistry = new ApplicationRegistry(context);
		this.mPersonRegistry = new PersonRegistry(context);
	}

	public AppAuth getAppAuthorization(String accessToken) throws TokenNotValidException {
		return mAppRegistry.getAppAuthorization(accessToken);
	}

	// Local app security
	public AppAuth validateAccess(String accessToken, Permission permission) throws TokenNotValidException, PermissionDeniedException {
		AppAuth appAuth;
		if (accessToken == null) {
			throw new TokenNotValidException();
		}

		appAuth = mAppRegistry.getAppAuthorization(accessToken);

		if ((appAuth.getPermissionCode() & permission.getCode()) == 0) {
			throw new PermissionDeniedException();
		}

		return appAuth;
	}

	public boolean isAppRegistered(String appIdentifier) {
		return mAppRegistry.isAppRegistered(appIdentifier);
	}

	public String registerApplication(AppDescriptor descriptor, SPFPersona persona) {
		return mAppRegistry.registerApplication(descriptor, persona);
	}

	public boolean unregisterApplication(String appIdentifier) {
		return mAppRegistry.unregisterApplication(appIdentifier);
	}

	public List<AppAuth> getAvailableApplications() {
		return mAppRegistry.getAvailableApplications();
	}

	/**
	 * Returns the SPFPersona associated to the specified application. If the
	 * application does not exist returns the default SPFPersona.
	 *
	 * @param appIdentifier
	 *            - the identifier of the application
	 * @return a SPFPersona
	 */
	public SPFPersona getPersonaOf(String appIdentifier) {
		return mAppRegistry.getPersonaOf(appIdentifier);
	}

	// Remote instances security
	public PersonRegistry getPersonRegistry() {
		return mPersonRegistry;
	}

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.graphics.Bitmap;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Instances of this class represents the type of fields supported by the local
 * profile, and are used to specify which field to write/read in components to
 * access profile. Instances are available as static methods of this class. The
 * list of available fields, with their type, are
 *

 *
IDENTIFIER:string

 *
DISPLAY_NAME:string

 *
PHOTO:bitmap

 *
BIRTHDAY:Date

 *
ABOUT_ME:string

 *
EMAILS:string[]

 *
LOCATION:string

 *
STATUS:string

 *
GENDER:string

 *
INTERESTS:string[]

 *

 */
public class ProfileField<E> {

	private static List<ProfileField<?>> sFields = new ArrayList<>();

	// Available fields
	public static ProfileField<String> IDENTIFIER = new ProfileField<>("identifier", String.class);
	public static ProfileField<String> DISPLAY_NAME = new ProfileField<>("display_name", String.class);
	public static ProfileField<Bitmap> PHOTO = new ProfileField<>("photo", Bitmap.class);
	public static ProfileField<Date> BIRTHDAY = new DateProfileField("birthday");
	public static ProfileField<String> ABOUT_ME = new ProfileField<>("about_me", String.class);
	public static ProfileField<String[]> EMAILS = new ProfileField<>("emails", String[].class);
	public static ProfileField<String> LOCATION = new ProfileField<>("location", String.class);
	public static ProfileField<String> STATUS = new ProfileField<>("status", String.class);
	public static ProfileField<String> GENDER = new MultipleChoicheProfileField<>("gender", String.class, new String[] { "male", "female", "other" });
	public static ProfileField<String[]> INTERESTS = new TagProfileField("interests");

	public static List<ProfileField<?>> getDefaultFields() {
		return new ArrayList<>(sFields);
	}

	/**
	 * Converts an array of {@link ProfileField} to an array of String,
	 * containing the identifier of the original fields, in the same order.
	 *
	 * @param fields
	 *            - the array to convert
	 * @return the array of identifiers
	 */
	public static String[] toIdentifierList(ProfileField<?>[] fields) {
		if (fields == null) {
			return null;
		}

		String[] identifiers = new String[fields.length];
		for (int i = 0; i < fields.length; i++) {
			identifiers[i] = fields[i].getIdentifier();
		}

		return identifiers;
	}

	/**
	 * Converts an array of profile field identifier to an array of
	 * {@link ProfileField}
	 *
	 * @param identifiers
	 *            the array of identifier
	 * @return the converted array of {@link ProfileField}
	 */
	public static ProfileField<?>[] fromIdentifierList(String[] identifiers) {
		if (identifiers == null) {
			return null;
		}

		ProfileField<?>[] fields = new ProfileField<?>[identifiers.length];
		for (int i = 0; i < identifiers.length; i++) {
			fields[i] = lookup(identifiers[i]);

			if (fields[i] == null) {
				throw new IllegalArgumentException("Unknown field " + identifiers[i]);
			}
		}

		return fields;
	}

	/**
	 * Lookups the {@link ProfileField} identified by the given identifier
	 *
	 * @param identifier
	 *            - the identifier of the field to lookup
	 * @return the {@link ProfileField} identified by the given identifier, or
	 *         null if none matches
	 */
	public static ProfileField<?> lookup(String identifier) {
		for (ProfileField<?> field : sFields) {
			if (field.getIdentifier().equals(identifier)) {
				return field;
			}
		}

		return null;
	}

	private String mIdentifier;
	private Class<E> mClass;

	private ProfileField(String identifier, Class<E> fieldClass) {
		this.mIdentifier = identifier;
		this.mClass = fieldClass;
		sFields.add(this);
	}

	/**
	 * @return the identifier
	 */
	public String getIdentifier() {
		return mIdentifier;
	}

	/**
	 * @return the {@link Class} of the field
	 */
	public Class<E> getFieldClass() {
		return mClass;
	}

	@Override
	public String toString() {
		return "[ProfileField: " + mIdentifier + "]";
	}

	/**
	 * A profile field whose value can be chosen among a set of possible values
	 *
	 * @author darioarchetti
	 *
	 * @param <E>
	 *            - the type of the field
	 */
	public static class MultipleChoicheProfileField<E> extends ProfileField<E> {

		private final E[] mChoiches;

		public MultipleChoicheProfileField(String identifier, Class<E> fieldClass, E[] choiches) {
			super(identifier, fieldClass);
			this.mChoiches = choiches;
		}

		/**
		 * @return the possible values of this field
		 */
		public E[] getChoiches() {
			return mChoiches;
		}
	}

	/**
	 * A field whoase value is an unordered list of String
	 *
	 * @author darioarchetti
	 *
	 */
	public static class TagProfileField extends ProfileField<String[]> {
		public TagProfileField(String identifier) {
			super(identifier, String[].class);
		}
	}

	/**
	 * A profile field of {@link Date} type.
	 *
	 * @author darioarchetti
	 *
	 */
	public static class DateProfileField extends ProfileField<Date> {
		public DateProfileField(String identifier) {
			super(identifier, Date.class);
		}
	}
}


/**
 * The MIT License
 *
 * Copyright (C) 2015 Asterios Raptis
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package de.alpharogroup.xml;

import java.beans.XMLDecoder;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Map;

import com.thoughtworks.xstream.XStream;

import lombok.experimental.UtilityClass;

/**
 * The class {@link XmlToObjectExtensions}.
 */
@UtilityClass
public final class XmlToObjectExtensions
{

	/**
	 * Creates from the given xml string an java object.
	 *
	 * @param <T>
	 *            the generic type
	 * @param xmlString
	 *            the xml string to transform to an java object.
	 * @return the xml string
	 */
	@SuppressWarnings("unchecked")
	public static <T> T toObjectWithXMLDecoder(final String xmlString)
	{

		XMLDecoder dec = null;
		T obj = null;
		try
		{
			final InputStream is = new ByteArrayInputStream(xmlString.getBytes());
			dec = new XMLDecoder(is);

			obj = (T)dec.readObject();

		}
		finally
		{
			if (dec != null)
			{
				dec.close();
			}
		}
		return obj;
	}

	/**
	 * Creates from the given xml string an java object.
	 *
	 * @param <T>
	 *            the generic type of the return type
	 * @param xmlString
	 *            the xml as string object
	 * @return the xml string
	 */
	public static <T> T toObjectWithXStream(final String xmlString)
	{
		return toObjectWithXStream(null, xmlString);
	}

	/**
	 * Creates from the given xml string an Object. The given map hold the aliases. For more
	 * information with aliasing see documation of xstream.
	 *
	 * @param <T>
	 *            the generic type of the return type
	 * @param xmlString
	 *            the xml as string object
	 * @param aliases
	 *            the aliases
	 * @return the created object from the given xml string.
	 */
	public static <T> T toObjectWithXStream(final String xmlString,
		final Map<String, Class<?>> aliases)
	{
		return toObjectWithXStream(null, xmlString, aliases);
	}

	/**
	 * Creates from the given xml string an java object.
	 *
	 * @param <T>
	 *            the generic type of the return type
	 * @param xstream
	 *            the xstream object.
	 * @param xmlString
	 *            the xml as string object
	 * @return the object
	 */
	public static <T> T toObjectWithXStream(final XStream xstream, final String xmlString)
	{
		return toObjectWithXStream(xstream, xmlString, null);
	}

	/**
	 * Creates from the given xml string an java object. The given map hold the aliases. For more
	 * information with aliasing see documation of xstream.
	 *
	 * @param <T>
	 *            the generic type of the return type
	 * @param xstream
	 *            the xstream object.
	 * @param xmlString
	 *            the xml
	 * @param aliases
	 *            the aliases
	 * @return the object
	 */
	@SuppressWarnings("unchecked")
	public static <T> T toObjectWithXStream(XStream xstream, final String xmlString,
		final Map<String, Class<?>> aliases)
	{
		if (xstream == null)
		{
			xstream = new XStream();
		}
		if (aliases != null)
		{
			for (final Map.Entry<String, Class<?>> alias : aliases.entrySet())
			{
				xstream.alias(alias.getKey(), alias.getValue());
			}
		}
		return (T)xstream.fromXML(xmlString);
	}

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 * Copyright 2015 Stefano Cappa
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.app;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CompoundButton;
import android.widget.FrameLayout;
import android.widget.Spinner;
import android.widget.Toast;

import com.mikepenz.aboutlibraries.LibsBuilder;
import com.mikepenz.fontawesome_typeface_library.FontAwesome;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.materialdrawer.AccountHeader;
import com.mikepenz.materialdrawer.AccountHeaderBuilder;
import com.mikepenz.materialdrawer.Drawer;
import com.mikepenz.materialdrawer.DrawerBuilder;
import com.mikepenz.materialdrawer.holder.BadgeStyle;
import com.mikepenz.materialdrawer.holder.StringHolder;
import com.mikepenz.materialdrawer.interfaces.OnCheckedChangeListener;
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem;
import com.mikepenz.materialdrawer.model.ProfileDrawerItem;
import com.mikepenz.materialdrawer.model.SwitchDrawerItem;
import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem;
import com.mikepenz.materialdrawer.model.interfaces.IProfile;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import butterknife.Bind;
import butterknife.ButterKnife;
import it.polimi.spf.app.fragments.ActivityFragment;
import it.polimi.spf.app.fragments.NotificationFragment;
import it.polimi.spf.app.fragments.advertising.AdvertisingFragment;
import it.polimi.spf.app.fragments.appmanager.AppManagerFragment;
import it.polimi.spf.app.fragments.contacts.ContactsFragment;
import it.polimi.spf.app.fragments.groupinfo.GroupInfoFragment;
import it.polimi.spf.app.fragments.personas.PersonasFragment;
import it.polimi.spf.app.fragments.profile.ProfileFragment;
import it.polimi.spf.app.permissiondisclaimer.PermissionDisclaimerDialogFragment;
import it.polimi.spf.framework.SPF;
import it.polimi.spf.framework.SPFContext;
import it.polimi.spf.framework.local.SPFService;
import it.polimi.spf.framework.profile.SPFPersona;

public class MainActivity extends ToolbarActivity implements
        PermissionDisclaimerDialogFragment.PermissionDisclaimerListener,
        SPFContext.OnEventListener,
        ProfileFragment.ProfilePhotoListener {

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

    private static final int OVERLAY_PERMISSION_REQ_CODE = 1234;
    private static final String DIAG_TAG = "DIAG_TAG";
    private static final String PREFERENCES_FILE = "it.polimi.spf.navigationdrawer";
    private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned";
    private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";

    public enum Entry {
        PROFILE, PERSONAS, CONTACTS, NOTIFICATIONS, ADVERTISING, APPS, ACTIVITIES;
    }

    private Map<Entry, PrimaryDrawerItem> mEntries;

    @Bind(R.id.toolbar)
    Toolbar toolbar;

    private FrameLayout mContentFrame;
    private boolean mUserLearnedDrawer;
    private int mCurrentSelectedPosition;
    private boolean mFromSavedInstanceState;
    private Fragment currentFragment;
    private boolean tabletSize;
    private Drawer drawer;
    private DrawerBuilder drawerBuilder;
    private SwitchDrawerItem goSwitch;
    private SwitchDrawerItem autonomousSwitch;
    private SwitchDrawerItem proximitySwitch;
    private PrimaryDrawerItem contactsDrawerItem;
    private PrimaryDrawerItem notificationsDrawerItem;
    private PrimaryDrawerItem advertisingDrawerItem;
    private ProfileDrawerItem profileDrawerItem;
    private AccountHeader headerResult;

    /**
     * Array that contains the names of sections
     */
    private String[] mSectionNames;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        /*
         * Workaround to fix the crash:
		 * android.view.WindowManager$BadTokenException: Unable to add window
		 * android.view.ViewRootImpl$W@3d67307 -- permission denied for this window type
		 * that appears only on Android 6.0 Marshmallow or greater.
		 * Start a dialog fragment to explain the procedure to the user.
		 * When the user accepts, onClickOnUnderstoodButton() will be called.
		 */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            PermissionDisclaimerDialogFragment diagFrag =
                    (PermissionDisclaimerDialogFragment) getSupportFragmentManager().findFragmentByTag(DIAG_TAG);
            if (diagFrag == null) {
                diagFrag = PermissionDisclaimerDialogFragment.newInstance();
                diagFrag.show(getSupportFragmentManager(), DIAG_TAG);
                getSupportFragmentManager().executePendingTransactions();
            }
        }

        mSectionNames = getResources().getStringArray(R.array.content_fragments_titles);

        //this var says if we are on a tablet (true) or a smartphone (false)
        this.tabletSize = getResources().getBoolean(R.bool.isTablet);

        this.setupToolbar(toolbar, R.string.app_name, R.color.toolbar_text_color);

        mUserLearnedDrawer = Boolean.valueOf(readSharedSetting(this, PREF_USER_LEARNED_DRAWER, "false"));

        if (savedInstanceState != null) {
            mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
            mFromSavedInstanceState = true;
        }

        mContentFrame = (FrameLayout) findViewById(R.id.container);

        goSwitch = new SwitchDrawerItem().withName("Group Owner")/*.withIcon(Octicons.Icon.oct_tools)*/
                .withChecked(false).withEnabled(true).withOnCheckedChangeListener(goSwitchListener)
                .withSelectable(false)
                .withIdentifier(10);
        autonomousSwitch = new SwitchDrawerItem().withName("Autonomous GO")/*.withIcon(Octicons.Icon.oct_tools)*/
                .withChecked(true).withEnabled(true).withOnCheckedChangeListener(autonomousSwitchListener)
                .withSelectable(false)
                .withIdentifier(11);
        proximitySwitch = new SwitchDrawerItem().withName("Proximity")/*.withIcon(Octicons.Icon.oct_tools)*/
                .withChecked(false).withEnabled(true).withOnCheckedChangeListener(proximitySwitchListener)
                .withSelectable(false)
                .withIdentifier(12);

        contactsDrawerItem = new PrimaryDrawerItem().withName(mSectionNames[2]);
        notificationsDrawerItem = new PrimaryDrawerItem().withName(mSectionNames[3]);
        advertisingDrawerItem = new PrimaryDrawerItem().withName(mSectionNames[4]);

        profileDrawerItem = new ProfileDrawerItem().withIcon(getResources().getDrawable(R.drawable.empty_profile_picture)).withIdentifier(100);

        // Create the AccountHeader
        headerResult = new AccountHeaderBuilder()
                .withActivity(this)
                .withHeaderBackground(R.drawable.drawer_header_new)
                .addProfiles(profileDrawerItem)
                .withOnAccountHeaderListener(new AccountHeader.OnAccountHeaderListener() {
                    @Override
                    public boolean onProfileChanged(View view, IProfile profile, boolean currentProfile) {
                        return false;
                    }
                })
                .withSelectionListEnabledForSingleProfile(false)
                .withSavedInstance(savedInstanceState)
                .build();

        drawerBuilder = new DrawerBuilder()
                .withActivity(this)
                .withAccountHeader(headerResult)
                .withHasStableIds(true)
                .withToolbar(toolbar)
                .addDrawerItems(
                        new PrimaryDrawerItem().withName(mSectionNames[0]).withIdentifier(0).withIcon(FontAwesome.Icon.faw_user),
                        new PrimaryDrawerItem().withName(mSectionNames[1]).withIdentifier(1).withIcon(FontAwesome.Icon.faw_users),
                        contactsDrawerItem.withIdentifier(2).withIcon(GoogleMaterial.Icon.gmd_book),
                        notificationsDrawerItem.withIdentifier(3).withIcon(FontAwesome.Icon.faw_exclamation_circle),
                        advertisingDrawerItem.withIdentifier(4).withIcon(FontAwesome.Icon.faw_eye),
                        new PrimaryDrawerItem().withName(mSectionNames[5]).withIdentifier(5).withIcon(GoogleMaterial.Icon.gmd_apps),
                        new PrimaryDrawerItem().withName(mSectionNames[6]).withIdentifier(6).withIcon(FontAwesome.Icon.faw_check_circle),
                        new PrimaryDrawerItem().withName(mSectionNames[7]).withIdentifier(7).withIcon(FontAwesome.Icon.faw_globe),
                        new PrimaryDrawerItem().withName(mSectionNames[8]).withIdentifier(8).withIcon(FontAwesome.Icon.faw_info_circle)
                )
                .addStickyDrawerItems(
                        goSwitch,
                        proximitySwitch
                )
                .withOnDrawerItemClickListener(drawerItemClickListener)
                .withSavedInstance(savedInstanceState);

        if (tabletSize) {
            //on tablet like explained to me here:
            //https://github.com/mikepenz/MaterialDrawer/issues/743
            drawer = drawerBuilder.buildView();
            ((ViewGroup) findViewById(R.id.nav_tablet)).addView(drawer.getSlider());
        } else {
            //on smartphones
            //i want to show the hamburger icon into the toolbar
            //with the onlick listner enabled and the material-animations
            if (getSupportActionBar() != null) {
                getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            }
            drawerBuilder.withActionBarDrawerToggle(true);
            drawerBuilder.withActionBarDrawerToggleAnimated(true);
            drawer = drawerBuilder.build();

            //call this only after the getSupportActionBar().setDisplayHomeAsUpEnabled(false);
            //7to prevent exceptions, like specified by the developer of MaterialDrawer
            drawer.getActionBarDrawerToggle().setDrawerIndicatorEnabled(true);
        }

        //default
        getSupportFragmentManager().
                beginTransaction().
                replace(R.id.container, createFragment(0)).
                commit();

        this.mEntries = new HashMap<>();
        this.mEntries.put(Entry.CONTACTS, contactsDrawerItem);
        this.mEntries.put(Entry.NOTIFICATIONS, notificationsDrawerItem);
        this.mEntries.put(Entry.ADVERTISING, advertisingDrawerItem);

        //update the navigation drawer badges
        Iterator it = this.mEntries.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry el = (Map.Entry) it.next();
            updateViewNotification((Entry) el.getKey(), (PrimaryDrawerItem) el.getValue());
        }

        SPFContext.get().registerEventListener(this);

        //check if called after a click on a notification into the notification drawer
        if (SPFService.isStarted()) {
            //update the drawer
            drawer.removeAllStickyFooterItems();
            //TODO add a method in SPF to get is a device is a go or not directly calling directly
            //TODO SPF.get or SPFService. Do the same thing with an isAutonomous method.
            //TODO Because i must update the switch on the drawer if i reopened the app.
            proximitySwitch.withChecked(true);
            drawer.updateStickyFooterItem(proximitySwitch);
        }
    }

    @Override
    protected void setupToolbar(Toolbar toolbar, int titleStringId, int colorId) {
        super.setupToolbar(toolbar, titleStringId, colorId);
    }

    /**
     * Method defined in
     * {@link it.polimi.spf.app.permissiondisclaimer.PermissionDisclaimerDialogFragment.PermissionDisclaimerListener}
     * and called by
     * {@link it.polimi.spf.app.permissiondisclaimer.PermissionDisclaimerDialogFragment}
     */
    @Override
    public void onClickConfirmButton() {
        /*
         * Workaround to fix the crash:
		 * android.view.WindowManager$BadTokenException: Unable to add window
		 * android.view.ViewRootImpl$W@3d67307 -- permission denied for this window type
		 * that appears only on Android 6.0 Marshmallow or greater.
		 * See onActivityResult in this class!!!
		 */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            Log.d(TAG, "canDrawOverlay not enabled. Requesting... ");
            startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
        }
    }

    /**
     * Method defined in
     * {@link it.polimi.spf.app.permissiondisclaimer.PermissionDisclaimerDialogFragment.PermissionDisclaimerListener}
     * and called by
     * {@link it.polimi.spf.app.permissiondisclaimer.PermissionDisclaimerDialogFragment}
     */
    @Override
    public void onClickDenyButton() {
        Toast.makeText(this, "You must confirm to activate SPF", Toast.LENGTH_SHORT).show();
        Log.d(TAG, "User clicked on DenyButton in Permission Disclaimer Dialog Fragment");
        finish();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        /*
         * Workaround to fix the crash:
		 * android.view.WindowManager$BadTokenException: Unable to add window
		 * android.view.ViewRootImpl$W@3d67307 -- permission denied for this window type
		 * that appears only on Android 6.0 Marshmallow or greater.
		 * This code will be executed at the end of the onCreate() in this activity!!!
		 */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && requestCode == OVERLAY_PERMISSION_REQ_CODE) {
            if (!Settings.canDrawOverlays(this)) {
                Toast.makeText(this, "Required permission not enabled!", Toast.LENGTH_SHORT).show();
                Log.e(TAG, "Required permission not enabled!");
                finish();
            } else {
                Log.d(TAG, "WOW!!! Required permission enabled! Thank you ;)");
                this.setUpFirstStartNavDrawer();
            }
        }
    }

    private Fragment createFragment(int position) {
        switch (position) {
            case 0:
                // Displays the profile
                currentFragment = ProfileFragment.createViewSelfProfileFragment();
                break;
            case 1:
                // Displays available personas
                currentFragment = PersonasFragment.newInstance();
                break;
            case 2:
                // Displays the list of friends
                currentFragment = ContactsFragment.newInstance();
                break;
            case 3:
                // Displays the list of notifications
                currentFragment = NotificationFragment.newInstance();
                break;
            case 4:
                // Displays advertising options
                currentFragment = AdvertisingFragment.newInstance();
                break;
            case 5:
                // Displays the list of apps authorized to interact with SPF
                currentFragment = AppManagerFragment.newInstance();
                break;
            case 6:
                currentFragment = ActivityFragment.newInstance();
                break;
            case 7:
                currentFragment = GroupInfoFragment.newInstance();
                break;
            case 8:
                //string and other settings are defined into values/strings.xml
                currentFragment = new LibsBuilder()
                        .withAboutDescription("Social Proximity Framework 2")
                        .withAboutAppName(getString(R.string.app_name) + " 2")
                        .withLibraries("android-crop", "butterknife", "circleimageview",
                                "appcompat-v7", "support-v4", "recyclerview-v7", "cardview-v7",
                                "design", "materialdrawer", "aboutlibraries", "iconics-core")
                        .withAutoDetect(false)
                        .withLicenseShown(true)
                        .withAboutIconShown(true)
                        .withAboutVersionShown(true)
                        .withAboutVersionShownCode(true)
                        .withAboutVersionShownName(true)
                        .withAnimations(false)
                        .withSortEnabled(true)
                        .fragment();
                break;
        }
        return currentFragment;
    }

    @Override
    public void onPhotoReady(Bitmap bitmap) {
        if (bitmap == null) {
            Log.d(TAG, "onPhotoReady, but photo is null");
            return;
        }
        Log.d(TAG, "onPhotoReady!!!");
        profileDrawerItem.withIcon(bitmap);
        headerResult.updateProfile(profileDrawerItem);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.notifications_delete_all:
                if (currentFragment instanceof NotificationFragment) {
                    ((NotificationFragment) currentFragment).clickedOptionItemDeleteAll();
                }
                break;
            case R.id.profileview_edit:
                if (currentFragment instanceof ProfileFragment) {
                    ((ProfileFragment) currentFragment).clickedOptionItemEdit();
                }
                break;
            case R.id.profileview_send_contact_request:
                if (currentFragment instanceof ProfileFragment) {
                    ((ProfileFragment) currentFragment).clickedOptionItemSendContactRequest();
                }
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        //add the values which need to be saved from the drawer to the bundle
        outState = drawer.saveInstanceState(outState);
        //add the values which need to be saved from the accountHeader to the bundle
        outState = headerResult.saveInstanceState(outState);
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION, 0);
    }

    private void setUpFirstStartNavDrawer() {
        if (!tabletSize) {
            if (!mUserLearnedDrawer) {
                drawer.openDrawer();
                mUserLearnedDrawer = true;
                saveSharedSetting(this, PREF_USER_LEARNED_DRAWER, "true");
            }
        }
    }

    public static void saveSharedSetting(Context ctx, String settingName, String settingValue) {
        SharedPreferences sharedPref = ctx.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putString(settingName, settingValue);
        editor.apply();
    }

    public static String readSharedSetting(Context ctx, String settingName, String defaultValue) {
        SharedPreferences sharedPref = ctx.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
        return sharedPref.getString(settingName, defaultValue);
    }

    private Drawer.OnDrawerItemClickListener drawerItemClickListener = new Drawer.OnDrawerItemClickListener() {
        @Override
        public boolean onItemClick(View view, int position, IDrawerItem drawerItem) {
            // do something with the clicked item :D
            getSupportFragmentManager().
                    beginTransaction().
                    replace(R.id.container, createFragment(drawerItem.getIdentifier())).
                    commit();

            //to prevent multiple items added every time that this method will be called
            toolbar.getMenu().clear();

            switch (drawerItem.getIdentifier()) {
                case 0:
                    initProfileFragmentToolbar();
                    break;
                case 3:
                    toolbar.inflateMenu(R.menu.menu_notifications);
                    break;
                case 10:
                    goSwitch.withChecked(true);
                    drawer.updateItem(goSwitch);
                    break;
                case 11:
                    autonomousSwitch.withChecked(true);
                    drawer.updateItem(autonomousSwitch);
                    break;
                case 12:
                    proximitySwitch.withChecked(true);
                    drawer.updateItem(proximitySwitch);
                    break;
            }

            drawer.closeDrawer();
            return true;
        }
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        this.initProfileFragmentToolbar();
        return super.onCreateOptionsMenu(menu);
    }

    private void initProfileFragmentToolbar() {
        toolbar.inflateMenu(R.menu.menu_view_self_profile);
        MenuItem item = toolbar.getMenu().findItem(R.id.profileview_persona_selector);
        Spinner spinner = (Spinner) MenuItemCompat.getActionView(item);
        List<SPFPersona> personas = SPF.get().getProfileManager().getAvailablePersonas();
        ArrayAdapter adapter = new ArrayAdapter<>(getBaseContext(), R.layout.spinner_toolbar_item, personas);
        adapter.setDropDownViewResource(R.layout.spinner_toolbar_item_dropdown);
        spinner.setAdapter(adapter);
        spinner.setSelection(personas.indexOf(((ProfileFragment) currentFragment).getMCurrentPersona()), false);
        spinner.setOnItemSelectedListener((ProfileFragment) currentFragment);

    }

    private OnCheckedChangeListener goSwitchListener = new OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) {
            if (isChecked) {
                Log.d(TAG, "connectSwitch checked -> gointent=15");
                drawer.removeAllStickyFooterItems();
                drawer.addStickyFooterItem(goSwitch);
                drawer.addStickyFooterItem(autonomousSwitch);
                drawer.addStickyFooterItem(proximitySwitch);
                autonomousSwitch.withChecked(true);
                ((SPFApp) getApplication()).updateIdentifier(15);
            } else {
                Log.d(TAG, "connectSwitch unchecked -> gointent=0");
                drawer.removeAllStickyFooterItems();
                drawer.addStickyFooterItem(goSwitch);
                drawer.addStickyFooterItem(proximitySwitch);
                ((SPFApp) getApplication()).updateIdentifier(0);
            }
            drawer.updateItem(autonomousSwitch);
        }
    };

    private OnCheckedChangeListener autonomousSwitchListener = new OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) {
            Log.d(TAG, "goSwitch status = " + isChecked);
        }
    };

    private OnCheckedChangeListener proximitySwitchListener = new OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(IDrawerItem drawerItem, CompoundButton buttonView, boolean isChecked) {
            if (isChecked) {
                if (goSwitch.isChecked()) {
                    Log.d(TAG, "connectSwitch checked -> gointent=15");
                    ((SPFApp) getApplication()).initSPF(15, autonomousSwitch.isChecked());
                } else {
                    ((SPFApp) getApplication()).initSPF(0, false);

                }
                SPFService.startForeground(getBaseContext());
                drawer.removeAllStickyFooterItems();
                drawer.addStickyFooterItem(proximitySwitch);
            } else {
                SPFService.stopForeground(getBaseContext());
                drawer.removeAllStickyFooterItems();
                drawer.addStickyFooterItem(goSwitch);
                if (goSwitch.isChecked()) {
                    drawer.addStickyFooterItem(autonomousSwitch);
                }
                drawer.addStickyFooterItem(proximitySwitch);
            }
            drawer.updateItem(goSwitch);
            drawer.updateItem(autonomousSwitch);
            drawer.updateItem(proximitySwitch);
        }
    };

    @Override
    public void onEvent(int eventCode, Bundle payload) {
        Entry entry;

        switch (eventCode) {
            case SPFContext.EVENT_ADVERTISING_STATE_CHANGED: {
                entry = Entry.ADVERTISING;
                break;
            }
            case SPFContext.EVENT_NOTIFICATION_MESSAGE_RECEIVED: {
                entry = Entry.NOTIFICATIONS;
                break;
            }
            case SPFContext.EVENT_CONTACT_REQUEST_RECEIVED: {
                entry = Entry.CONTACTS;
                break;
            }
            default:
                return;
        }

        PrimaryDrawerItem item = mEntries.get(entry);
        updateViewNotification(entry, item);
    }

    private void updateViewNotification(Entry entry, PrimaryDrawerItem item) {
        switch (entry) {
            case NOTIFICATIONS:
                int notifCount = SPF.get().getNotificationManager().getAvailableNotificationCount();
                showNotificationOrClear(item, notifCount > 0 ? String.valueOf(notifCount) : null);
                break;
            case CONTACTS:
                int msgCount = SPF.get().getSecurityMonitor().getPersonRegistry().getPendingRequestCount();
                showNotificationOrClear(item, msgCount > 0 ? String.valueOf(msgCount) : null);
                break;
            case ADVERTISING:
                boolean active = SPF.get().getAdvertiseManager().isAdvertisingEnabled();
                showNotificationOrClear(item, active ? "ON" : null);
                break;
            default:
                break;
        }
    }

    private void showNotificationOrClear(PrimaryDrawerItem item, String text) {
        if (item == null) {
            return;
        }
        item.withBadgeStyle(new BadgeStyle()
                .withTextColor(Color.WHITE)
                .withColorRes(R.color.md_red_700));
        StringHolder badge;
        if (text == null) {
            badge = null; //to remove the badge, use null
        } else {
            badge = new StringHolder(text);
        }
        drawer.updateBadge(item.getIdentifier(), badge);
    }

    @Override
    public void onBackPressed() {
        //handle the back press :D close the drawer first and if the drawer is closed close the activity
        if (drawer != null && drawer.isDrawerOpen()) {
            drawer.closeDrawer();
        } else {
            super.onBackPressed();
        }
    }
}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.os.Parcelable;

import org.json.JSONException;
import org.json.JSONObject;

/**
 * TODO #Documentation
 *
 * @author Jacopo Aliprandi
 */
public abstract class SPFAction implements Parcelable {

	protected static final int TYPE_NOTIFICATION = 0;
	protected static final int TYPE_INTENT = 1;
	protected static final int TYPE_SERVICE_CALL = 2;

	protected static final String KEY_TYPE = "type";

	/**
	 * TODO comment
	 *
	 * @return
	 */
	public abstract String toJSON();

	/**
	 * TODO comment
	 *
	 * @return
	 */
	public static SPFAction fromJSON(String json) {
		try {
			JSONObject o = new JSONObject(json);
			int type = o.getInt(KEY_TYPE);
			switch (type) {
			case TYPE_INTENT:
				return new SPFActionIntent(o);
			case TYPE_NOTIFICATION:
				return new SPFActionSendNotification(o);
			default:
				throw new IllegalArgumentException("Unknown action type: " + type);
			}

		} catch (JSONException e) {
			throw new IllegalArgumentException("JSON does not represent a valid SPFAction");
		}
	}

}


/*
 * Copyright 2014 Jacopo Aliprandi, Dario Archetti
 *
 * This file is part of SPF.
 *
 * SPF is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * SPF is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
 * more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with SPF.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package it.polimi.spf.shared.model;

import android.database.CursorJoiner.Result;
import android.os.Parcel;
import android.os.Parcelable;

/**
 * Container for the result of an invocation. It may be in two state:
 * {@link #RESULT}, if the invocation completed successfully, or {@link #ERROR},
 * if an error prevented the invocation from completing normally.
 *
 * In case of {@link Result}, the container holds the result in case of non void
 * methods. Otherwise, it may contain an error message detailing the error.
 *
 * @author darioarchetti
 *
 */
public class InvocationResponse implements Parcelable {

	/**
	 * State for successful invocation
	 */
	public final static int RESULT = 0;

	/**
	 * State for erroneous invocation
	 */
	public final static int ERROR = 1;

	private final int type;
	private String errorMessage;
	private String resultPayload;

	/**
	 * Creates a new {@link InvocationResponse} to hold the result of an
	 * invocation.
	 *
	 * @param result
	 *            - the result of the invocation, or null if the method returns
	 *            void.
	 * @return the invocation request.
	 */
	public static InvocationResponse result(String payload) {
		InvocationResponse resp = new InvocationResponse(RESULT);
		resp.resultPayload = payload;
		return resp;
	}

	/**
	 * Creates a new {@link InvocationResponse} to hold the error that occurred
	 * during the execution of the service.
	 *
	 * @param t
	 *            - the throwable that caused the error.
	 * @return the invocation request.
	 */
	public static InvocationResponse error(Throwable t) {
		InvocationResponse resp = new InvocationResponse(ERROR);
		resp.errorMessage = t.getClass().getName() + ":" + t.getLocalizedMessage();
		return resp;
	}

	/**
	 * Creates a new {@link InvocationResponse} to hold the error that occurred
	 * during the execution of the service.
	 *
	 * @param errorMessage
	 *            - the error message that describes the occurred error.
	 * @return the invocation request.
	 */
	public static InvocationResponse error(String errorMessage) {
		InvocationResponse resp = new InvocationResponse(ERROR);
		resp.errorMessage = errorMessage;
		return resp;
	}

	private InvocationResponse(int type) {
		this.type = type;
	}

	private InvocationResponse(Parcel source) {
		type = source.readInt();
		switch (type) {
		case ERROR:
			errorMessage = source.readString();
			break;
		case RESULT:
			resultPayload = source.readString();
			break;
		}
	}

	/**
	 * @return the error message
	 */
	public String getErrorMessage() {
		return errorMessage;
	}

	/**
	 * Sets the result
	 *
	 * @param errorMessage
	 */
	public void setErrorMessage(String errorMessage) {
		this.errorMessage = errorMessage;
	}

	/**
	 * @return the result
	 */
	public String getPayload() {
		return resultPayload;
	}

	/**
	 * Sets the result
	 *
	 * @param result
	 */
	public void setPayload(String payload) {
		this.resultPayload = payload;
	}

	/**
	 * @return the type of the container
	 */
	public int getType() {
		return type;
	}

	/**
	 * @return true if the type is {@link #RESULT}
	 */
	public boolean isResult() {
		return type == RESULT;
	}

	@Override
	public String toString() {
		return "Invocation response: " + (type == RESULT ? "RESULT\n" + resultPayload : "ERROR\n" + errorMessage);
	}

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flag) {
		dest.writeInt(type);
		switch (type) {
		case ERROR:
			dest.writeString(errorMessage);
			break;
		case RESULT:
			dest.writeString(resultPayload);
			break;
		}
	}

	public final static Creator<InvocationResponse> CREATOR = new Creator<InvocationResponse>() {

		@Override
		public InvocationResponse[] newArray(int arg0) {
			return new InvocationResponse[arg0];
		}

		@Override
		public InvocationResponse createFromParcel(Parcel arg0) {
			return new InvocationResponse(arg0);
		}
	};
}