public class FacebookFQLFriend implements Friend {
	private JSONObject json;
	private List<String> defaultURLs = Arrays.asList("https://fbcdn-profile-a.akamaihd.net/static-ak/rsrc.php/v2/yL/r/HsTZSDw4avx.gif", "https://fbcdn-profile-a.akamaihd.net/static-ak/rsrc.php/v2/yp/r/yDnr5YfbJCH.gif");
	
	public FacebookFQLFriend(JSONObject json){
		this.json = json;
	}
	
	@Override
	public String toString(){
		return "Name: "+this.getName(true) +", FB-ID: "+this.getUserName();
	}

	@Override
	public String getName(boolean ignoreMiddleNames) {
		String name = null;
		try {
			name = json.getString("name");
		} catch (JSONException e) {}
		return 	name;
	}
	
	//returns lame @facebook.com email because the API doesn't allow anything else
	public String getEmail(){
		try {
			return json.getString("username") + "@facebook.com";
		} catch (JSONException e) {
			return null;
		}	}

	@Override
	public String getUserName() {
		String uid = null;
		try {
			uid = json.getString("uid");
		} catch (JSONException e) {}
		return 	uid;
	}

	@Override
	public String getPicURL() {
		String url = null;
		try {
			url = defaultURLs.contains(json.getString("pic_big")) ? null : json.getString("pic_big");
		} catch (JSONException e) {}
		return 	url;
	}

	@Override
	public long getPicTimestamp() {
		long timestamp = 0;
		try {
			timestamp = json.getLong("pic_modified");
		} catch (JSONException e) {}
		return 	timestamp;
	}
	
	public String getCountry(){
		String country = null;
		try {
			country = json.getJSONObject("current_location").getString("country");
		} catch (JSONException e) {}
		return 	country;
	}
	
	public String getState(){
		String state = null;
		try {
			state = json.getJSONObject("current_location").getString("state");
		} catch (JSONException e) {}
		return 	state;
	}
	
	public String getCity(){
		String city = null;
		try {
			city = json.getJSONObject("current_location").getString("city");
		} catch (JSONException e) {}
		return 	city;
	}
	
	public String getBirthday(){
		try {
			String birthday = json.getString("birthday_date");
			String[] birthdayArray = birthday.split("/");
			if (birthdayArray.length == 3){
				return birthdayArray[2] + "-" + birthdayArray[0] + "-" + birthdayArray[1];
			} else if (birthdayArray.length == 2){
				return "--" + birthdayArray[0]  + "-" + birthdayArray[1];
			}
					
		} catch (JSONException e) {}
		return 	null;
	}

	@Override
	public ArrayList<Status> getStatuses() {
		JSONObject status = null;
		ArrayList<Status> statuses = new ArrayList<Status>();
		try {
			status = json.getJSONObject("status");
		} catch (JSONException e) {	}
		if (status != null) {
			statuses.add(new FacebookStatus(status));
			return statuses;
		} else {
			String uid = this.getUserName();
			if (uid != null) {
				return FacebookUtil.getStatuses(uid, false);
			}

		}
		return statuses;
	}

} public class FacebookGraphFriend implements  Friend{

    private  JSONObject json;
    private static final String TAG = "FacebookGraphFriend";

    @Override
    public String toString(){
        return "Name: "+this.getName(false) +", FB-ID: "+this.getUserName();
    }

    public FacebookGraphFriend(JSONObject json){
        this.json = json;
    }

    @Override
    public String getName(boolean ignoreMiddleNames) {
        String name = null;
        try{
            if(!ignoreMiddleNames){
                name = json.getString("name");
                Log.i(TAG, "name without middlename" + name);
            }

            else{
                name = json.getString("first_name") + " " + json.getString("last_name");
             }
        }catch (JSONException e){
            Log.e(TAG, e.toString());
         }
        return 	name;

    }
    //returns lame @facebook.com email because the API doesn't allow anything else
    public String getEmail(){
        try {
            return json.getString("username") + "@facebook.com";
        } catch (JSONException e) {
            return null;
        }
    }

    public String getLocation(){
        String location = null;
        try {
            JSONObject loc = json.getJSONObject("location");
            location = loc.getString("name");
        } catch (JSONException e) {
            Log.e(TAG, e.toString());
        }
        return location;
    }

    @Override
    public String getUserName() {
        String username = null;
        try {
            username = json.getString("id");
        } catch (JSONException e) {
            Log.e(TAG, e.toString());
        }
        return 	username;
    }

    @Override
    public String getPicURL() {
        String picUrl = null;
        try {
            JSONObject pic = json.getJSONObject("picture").getJSONObject("data");
            if (pic.getBoolean("is_silhouette"))
                return picUrl;
            picUrl = pic.getString("url");
        } catch (JSONException e){
            Log.e(TAG, e.toString());
        }
        return picUrl;
    }

    public String getBirthday(){
        try {
            String birthday = json.getString("birthday");
            String[] birthdayArray = birthday.split("/");
            if (birthdayArray.length == 3){
                return birthdayArray[2] + "-" + birthdayArray[0] + "-" + birthdayArray[1];
            } else if (birthdayArray.length == 2){
                return "--" + birthdayArray[0]  + "-" + birthdayArray[1];
            }

        } catch (JSONException e) {}
        return 	null;
    }

    //graph api doesn't seem to support this :/
    @Override
    public long getPicTimestamp() {
        return 0;
    }

    @Override
    public ArrayList<Status> getStatuses() {
        return FacebookUtil.getStatuses(getUserName(), false);
    }
} public interface Status {
	public String getMessage();
	public long getTimestamp();
	public String getPermalink();
	public String getID();
} public class BitmapUtil {
	
	public static int getMaxSize(Context context) {
		  // Note that this URI is safe to call on the UI thread.
		  Cursor c = context.getContentResolver().query(DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI,
		      new String[]{ DisplayPhoto.DISPLAY_MAX_DIM }, null, null, null);
		  try {
		    c.moveToFirst();
		    return c.getInt(0);
		  } finally {
		    c.close();
		  }
		}
	
	private static Bitmap scale(Bitmap in, int size){
		int width = in.getWidth();
		int height = in.getHeight();
		float scalew;
		float scaleh;
		Matrix matrix = new Matrix();
		//scale smaller axis to size (if necessary)
		if (width > height){
			scaleh = (float) size / height;
			scalew = scaleh;
			if ((width * scalew) % 2 != 0){
				scalew = ((width * scalew) + 1) / (float) width;
			}
			
		}else{
			scalew = (float) size / width;
			scaleh = scalew;
			if ((height * scaleh) % 2 != 0){
				scaleh = ((height * scaleh) + 1) / (float) height;
			}
		}
		Log.i("RESIZING", "old width: " + width + "old height: " + height + "new size: " + size);
		matrix.postScale(scalew, scaleh);
		Bitmap scaledPic = Bitmap.createBitmap(in, 0, 0, width, height, matrix, true);
		return scaledPic;		
	}
	
	private static PointF findFaceMid(Bitmap in){
		PointF mid = new PointF();
		Bitmap bitmap565 = in.copy(Bitmap.Config.RGB_565, true);

		FaceDetector fd = new FaceDetector(in.getWidth(), in.getHeight(), 1);
		FaceDetector.Face[] faces = new FaceDetector.Face[1];
		fd.findFaces(bitmap565, faces);
		

		FaceDetector.Face face = faces[0];
		if (face != null){
			try{
				face.getMidPoint(mid);
				return mid;
			} catch (NullPointerException n){}
		}
		return null;

	}
	
	private static byte[] bitmapToBytes(Bitmap in){
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		in.compress(CompressFormat.PNG, 0, bos);
		return bos.toByteArray();
	}

	
	public static byte[] resize(byte[] picArray, int size, boolean faceDetect){
		if (picArray == null){
			return null;
		}
		Bitmap pic = BitmapFactory.decodeByteArray(picArray, 0, picArray.length);
		if (pic == null){
			return null;
		}
		
		size = Math.min(size, Math.min(pic.getHeight(), pic.getWidth()));
		if (size % 2 != 0)
			size--;
		Log.i("sizes", "old width:"+ pic.getWidth() + " old height:" + pic.getHeight() + " new size:" + size);
		Bitmap scaledPic = scale(pic, size);

		int width = scaledPic.getWidth();
		int height = scaledPic.getHeight();
		
		//if pic is already square, we are done now
		if (width == height){
			return bitmapToBytes(scaledPic);
		}
		
		PointF mid = null;
		int cropcenter;
				
		if (faceDetect)
			mid = findFaceMid(scaledPic);
		
		
		Bitmap out;
		if (width > height){
			if (mid != null)
				cropcenter = Math.max(size/2, Math.min((int) Math.floor(mid.y), width - size/2));
			else
				cropcenter = width/2;
			Log.i("CROPPING", "width:" + width +" center:" + cropcenter + " size:" + size + " left edge:" +  (cropcenter - size/2) + " right edge:" +  (cropcenter + size/2));
			out = Bitmap.createBitmap(scaledPic, cropcenter - size/2, 0, size, size);
		} else {
			if (mid != null)
				cropcenter = Math.max(size/2, Math.min((int) Math.floor(mid.x), height - size/2));
			else
				cropcenter = height/2;
			out = Bitmap.createBitmap(scaledPic, 0, 0, size, size);
		}
		
		return bitmapToBytes(out);
	}

} public class ContactUtil {
    public static class Contact{
    	public long ID;
    	public String name;
    	public String accountType;
    }
    
    public static class Photo{
    	public byte[] data;
    	public String url;
    	public long timestamp;
    }
    
    public static final String TAG = "ContactUtil";
    
    private static HashMap<String, Drawable> accountIcons = new HashMap<String, Drawable>();
    
    public static Contact getMergedContact(Context c, long contactID, Account account){
    	
		Uri ContactUri = RawContacts.CONTENT_URI.buildUpon()
				.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
				.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type)
				.build();
    	
		Cursor cursor = c.getContentResolver().query(ContactUri, new String[] { BaseColumns._ID, RawContacts.DISPLAY_NAME_PRIMARY}, RawContacts.CONTACT_ID +" = '" + contactID + "'", null, null);
		if (cursor.getCount() > 0){
			cursor.moveToFirst();
			Contact contact = new Contact();
			contact.ID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
			contact.name = cursor.getString(cursor.getColumnIndex(RawContacts.DISPLAY_NAME_PRIMARY));
			cursor.close();
			return contact;
		}
		cursor.close();
		return null;

    }
    
    private static void fillIcons(Context c){
        AccountManager am = AccountManager.get(c);
        AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
        PackageManager pm = c.getPackageManager();
        for (AuthenticatorDescription auth : auths){
        	accountIcons.put(auth.type, pm.getDrawable(auth.packageName,  auth.iconId, null));
        /*	Log.i("Account:", auth.type);
        	Log.i("pkg:", auth.packageName);
        	Log.i("icon:", String.valueOf(auth.smallIconId));*/

        }
    }
    
    
    
    public static List<HashMap<String, Object>> getMergedContacts(Context c, long rawContactID){
    	
    	
		Cursor cursor = c.getContentResolver().query(RawContacts.CONTENT_URI, new String[] { RawContacts.CONTACT_ID}, RawContacts._ID +" = '" + rawContactID + "'", null, null);
		ArrayList<HashMap<String, Object>> contacts = new ArrayList<HashMap<String, Object>>();
		
		if (cursor.getCount() > 0){
			cursor.moveToFirst();
			long contactID = cursor.getLong(cursor.getColumnIndex(RawContacts.CONTACT_ID));
			Cursor c2 = c.getContentResolver().query(RawContacts.CONTENT_URI, new String[] { BaseColumns._ID, RawContacts.DISPLAY_NAME_PRIMARY, RawContacts.ACCOUNT_TYPE}, RawContacts.CONTACT_ID +" = '" + contactID + "'" + " AND " + BaseColumns._ID + " != " +rawContactID , null, null);
			while(c2.moveToNext()){
				HashMap<String, Object> contact = new HashMap<String, Object>();
				contact.put("name", c2.getString(c2.getColumnIndex(RawContacts.DISPLAY_NAME_PRIMARY)));
				contact.put("id", c2.getString(c2.getColumnIndex(BaseColumns._ID)));
				Drawable icon;
				//todo: cache this shit
				fillIcons(c);
				try{
					icon = accountIcons.get(c2.getString(c2.getColumnIndex(RawContacts.ACCOUNT_TYPE)));
				} catch (Exception e){
					fillIcons(c);
					icon = accountIcons.get(c2.getString(c2.getColumnIndex(RawContacts.ACCOUNT_TYPE)));
				}
				contact.put("icon", icon);
				contacts.add(contact);
			}
			c2.close();

		}
		cursor.close();
		return contacts;

    }

	public static void merge(Context c, long id1, long id2){
		 Log.i("MERGING", id1 +","+id2);
		 ContentValues values = new ContentValues();
	     values.put(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, id1);
	     values.put(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, id2);
	     values.put(ContactsContract.AggregationExceptions.TYPE,ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER);
	     c.getContentResolver().update(ContactsContract.AggregationExceptions.CONTENT_URI, values, null, null);
	}
	
	private static Set<Long> getRawContacts(Context c, long contactID, long rawContactID){
		HashSet<Long> ids = new HashSet<Long>();
			Cursor c2 = c.getContentResolver().query(RawContacts.CONTENT_URI, new String[] { BaseColumns._ID}, RawContacts.CONTACT_ID +" = '" + contactID + "'" + " AND " + BaseColumns._ID + " != " +rawContactID , null, null);
			while (c2.moveToNext()){
				ids.add(c2.getLong(c2.getColumnIndex(BaseColumns._ID)));
			}
			c2.close();
		return ids;
	}
	
	public static Set<Long> getRawContacts(ContentResolver c, long rawContactID, String accountType){
		HashSet<Long> ids = new HashSet<Long>();
		Cursor cursor = c.query(RawContacts.CONTENT_URI, new String[] { RawContacts.CONTACT_ID}, RawContacts._ID +" = '" + rawContactID + "'", null, null);	
		if (cursor.getCount() > 0){
			cursor.moveToFirst();
			long contactID = cursor.getLong(cursor.getColumnIndex(RawContacts.CONTACT_ID));
			//	Log.i("QUERY", RawContacts.CONTACT_ID +" = '" + contactID + "'" + " AND " + BaseColumns._ID + " != " +rawContactID + " AND " + RawContacts.ACCOUNT_TYPE + " = '" + accountType+"'");
				Cursor c2 = c.query(RawContacts.CONTENT_URI, new String[] { BaseColumns._ID}, RawContacts.CONTACT_ID +" = '" + contactID + "'" + " AND " + BaseColumns._ID + " != " +rawContactID + " AND " + RawContacts.ACCOUNT_TYPE + " = '" + accountType+"'", null, null);
			//	Log.i("CURSOR SIZE", String.valueOf(c2.getCount()));
				while (c2.moveToNext()){
					ids.add(c2.getLong(c2.getColumnIndex(BaseColumns._ID)));
				}
				c2.close();
		}
		cursor.close();
		return ids;
	}
	
	public static void mergeWithContact(Context c, long rawContactID, long contactID){
		ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
		Set<Long> ids = getRawContacts(c, contactID, rawContactID);
		for (long id: ids){
			ContentProviderOperation.Builder builder = ContentProviderOperation
					.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI);
			builder.withValue(
					ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, rawContactID);
			builder.withValue(
					ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, id);
			builder.withValue(
					ContactsContract.AggregationExceptions.TYPE,ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER);
			operationList.add(builder.build());
		}
		if(operationList.size() > 0)
			try {
				c.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (OperationApplicationException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

	}
	
	public static void seperate(Context c, long rawContactID){
		ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
		Cursor cursor = c.getContentResolver().query(RawContacts.CONTENT_URI, new String[] { RawContacts.CONTACT_ID}, RawContacts._ID +" = '" + rawContactID + "'", null, null);
		if (cursor.moveToFirst()){
			long contactID = cursor.getLong(cursor.getColumnIndex(RawContacts.CONTACT_ID));
			Set<Long> ids = getRawContacts(c, contactID, rawContactID);
			for (long id: ids){
				ContentProviderOperation.Builder builder = ContentProviderOperation
						.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI);
				builder.withValue(
						ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, rawContactID);
				builder.withValue(
						ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, id);
				builder.withValue(
						ContactsContract.AggregationExceptions.TYPE,ContactsContract.AggregationExceptions.TYPE_KEEP_SEPARATE);
				operationList.add(builder.build());
			}
			
			if(operationList.size() > 0)
				try {
					c.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList);
				} catch (RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (OperationApplicationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
		cursor.close();
		}
	
	public static Photo getPhoto(ContentResolver c, long rawContactId){
		Photo photo = new Photo();
		String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId 
				+ "' AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "'";



		Cursor c1 = c.query(ContactsContract.Data.CONTENT_URI, new String[] {ContactsContract.CommonDataKinds.Photo.PHOTO, ContactsContract.Data.SYNC2, ContactsContract.Data.SYNC3 }, where , null, null);
		if (c1.getCount() > 0){
			c1.moveToLast();
			photo.data = c1.getBlob(c1.getColumnIndex(ContactsContract.CommonDataKinds.Photo.PHOTO));
			photo.timestamp = Long.valueOf(c1.getString(c1.getColumnIndex(ContactsContract.Data.SYNC2)));
			photo.url = c1.getString(c1.getColumnIndex(ContactsContract.Data.SYNC3));
		}
		c1.close();
		return photo;
	}
	
	public static void updateContactPhoto(ContentResolver c, long rawContactId, Photo pic, boolean primary){
		ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
		

		
		//insert new picture
		try {
			if(pic.data != null) {
                //delete old picture
                String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId
                        + "' AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "'";
                Log.i(TAG, "Deleting picture: "+where);

                ContentProviderOperation.Builder builder = ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI);
                builder.withSelection(where, null);
                operationList.add(builder.build());
				builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
				builder.withValue(ContactsContract.CommonDataKinds.Photo.RAW_CONTACT_ID, rawContactId);
				builder.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
				builder.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, pic.data);
				builder.withValue(ContactsContract.Data.SYNC2, String.valueOf(pic.timestamp));
				builder.withValue(ContactsContract.Data.SYNC3, pic.url);
                if (primary)
				    builder.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
				operationList.add(builder.build());

				
			}
			c.applyBatch(ContactsContract.AUTHORITY,	operationList);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			Log.e("ERROR:" , e.toString());
		}


	}
	
	public static void removeBirthdays(Context c, long rawContactId){
		String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId 
				+ "' AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE
				+ "' AND " + ContactsContract.CommonDataKinds.Event.TYPE + " = '" + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + "'";
		c.getContentResolver().delete(ContactsContract.Data.CONTENT_URI, where, null);
	}
	
	public static void removeEmails(Context c, long rawContactId){
		String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId 
				+ "' AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE+ "'";
		c.getContentResolver().delete(ContactsContract.Data.CONTENT_URI, where, null);
	}
	
	public static void addEmail(Context c, long rawContactId, String email){
		DeviceUtil.log(c, "adding email", email);
		String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId 
				+ "' AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE+ "'";
		Cursor cursor = c.getContentResolver().query(ContactsContract.Data.CONTENT_URI, new String[] { RawContacts.CONTACT_ID}, where, null, null);
		if (cursor.getCount() == 0){
			ContentValues contentValues = new ContentValues();
			//op.put(ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID, );
			contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
			contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
			contentValues.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);
			c.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, contentValues);
		}
		cursor.close();

	}

	public static void addBirthday(long rawContactId, String birthday){
		String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId 
				+ "' AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE
				+ "' AND " + ContactsContract.CommonDataKinds.Event.TYPE + " = '" + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY + "'";
		Cursor cursor = ContactsSyncAdapterService.mContentResolver.query(ContactsContract.Data.CONTENT_URI, null, where, null, null);
		int count = cursor.getCount();
		cursor.close();
		if (count <= 0){
			ContentValues contentValues = new ContentValues();
			contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
			contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
			contentValues.put(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY);
			contentValues.put(ContactsContract.CommonDataKinds.Event.START_DATE, birthday);
			
			try {
				ContactsSyncAdapterService.mContentResolver.insert(ContactsContract.Data.CONTENT_URI, contentValues);
			//	mContentResolver.applyBatch(ContactsContract.AUTHORITY,	operationList);
			} catch (Exception e) {
				e.printStackTrace();
				//Log.e("ERROR:" , e.^);
			}
		}
	}

	public static void removeContactLocations(Context c, Account account){
		ContactsSyncAdapterService.mContentResolver = c.getContentResolver();
		HashMap<String, ContactsSyncAdapterService.SyncEntry> localContacts = ContactsSyncAdapterService.getLocalContacts(account);
		for (ContactsSyncAdapterService.SyncEntry s : localContacts.values()){
			ContactsSyncAdapterService.mContentResolver.delete(ContactsContract.Data.CONTENT_URI, ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.Data.RAW_CONTACT_ID + " = " + s.raw_id, null); 
		}			
		
	}

	public static void updateContactLocation(long rawContactId, String country, String region, String city){
		if ((country == null || country.equals("")) && (region == null || region.equals("")) && (city == null || city.equals(""))){
			return;
		}
		String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId 
				+ "' AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE + "'";
		String[] projection = {ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, ContactsContract.CommonDataKinds.StructuredPostal.REGION, ContactsContract.CommonDataKinds.StructuredPostal.CITY};
		
		Cursor cursor = ContactsSyncAdapterService.mContentResolver.query(ContactsContract.Data.CONTENT_URI, projection, where, null, null);
		boolean insert = false;
		if (cursor.getCount() == 0){
			insert = true; 
		} else{
			cursor.moveToFirst();
			String oldCountry = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY));
			String oldRegion = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.REGION));
			String oldCity = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.CITY));
			if ((oldCountry != null && !oldCountry.equals(country)) || (oldRegion != null && !oldRegion.equals(region)) || (oldCity != null && oldCity.equals(city))){
				ContactsSyncAdapterService.mContentResolver.delete(ContactsContract.Data.CONTENT_URI, where, null);
				insert = true;
			}
		}
		cursor.close();
		if (insert){
			ContentValues contentValues = new ContentValues();
			//op.put(ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID, );
			contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
			contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
			if (country != null && ! country.equals("")){
				contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, country);
			} if (region != null && ! region.equals("")){
			contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, region);
			} if (city != null && ! city.equals("")){
			contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, city);
			}
			try {
				ContactsSyncAdapterService.mContentResolver.insert(ContactsContract.Data.CONTENT_URI, contentValues);
			//	mContentResolver.applyBatch(ContactsContract.AUTHORITY,	operationList);
			} catch (Exception e) {
				e.printStackTrace();
				//Log.e("ERROR:" , e.^);
			}
		}
	
	}

    public static void updateContactLocation(long rawContactId, String location){
        if ((location == null || location.equals(""))){
            return;
        }
        String where = ContactsContract.Data.RAW_CONTACT_ID + " = '" + rawContactId
                + "' AND " + ContactsContract.Data.MIMETYPE + " = '" + StructuredPostal.CONTENT_ITEM_TYPE + "'";
        String[] projection = {StructuredPostal.FORMATTED_ADDRESS};

        Cursor cursor = ContactsSyncAdapterService.mContentResolver.query(ContactsContract.Data.CONTENT_URI, projection, where, null, null);
        boolean insert = false;
        if (cursor.getCount() == 0){
            insert = true;
        } else{
            cursor.moveToFirst();
            String oldloc = cursor.getString(cursor.getColumnIndex(StructuredPostal.FORMATTED_ADDRESS));
            if ((oldloc == null) || (!oldloc.equals(location))){
                ContactsSyncAdapterService.mContentResolver.delete(ContactsContract.Data.CONTENT_URI, where, null);
                insert = true;
            }
        }
        cursor.close();
        if (insert){
            ContentValues contentValues = new ContentValues();
            //op.put(ContactsContract.CommonDataKinds.StructuredPostal.CONTACT_ID, );
            contentValues.put(ContactsContract.Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
            contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
            contentValues.put(StructuredPostal.FORMATTED_ADDRESS, location);
            try {
                ContactsSyncAdapterService.mContentResolver.insert(ContactsContract.Data.CONTENT_URI, contentValues);
                //	mContentResolver.applyBatch(ContactsContract.AUTHORITY,	operationList);
            } catch (Exception e) {
                e.printStackTrace();
                //Log.e("ERROR:" , e.^);
            }
        }

    }
	

} public static class Photo{
	public byte[] data;
	public String url;
	public long timestamp;
} public class DeviceUtil {

	public static boolean isOnline(Context c) {
		ConnectivityManager connectionManager = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
		if (connectionManager.getActiveNetworkInfo() != null && connectionManager.getActiveNetworkInfo().isConnected()){
			try {
				InetAddress addr = InetAddress.getByName("graph.facebook.com");
				addr = InetAddress.getByName("api.facebook.com");
			} catch (UnknownHostException e) {
				return false;
			}
			return true;
		}
		return false;
	}

	public static boolean isWifi(Context c){
	    ConnectivityManager cm = (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
	        NetworkInfo networkInfo = null;
	        if (cm != null) {
	            networkInfo = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
	        }
	        return networkInfo != null && networkInfo.isConnected();
	}

	public static boolean isCharging(Context context){
		IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
		Intent batteryStatus = context.registerReceiver(null, ifilter);
		// Are we charging / charged?
		int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
		return (status == BatteryManager.BATTERY_STATUS_CHARGING) || (status == BatteryManager.BATTERY_STATUS_FULL);
	}
	
	public static boolean hasAccount(Context context){
		AccountManager am = AccountManager.get(context);
		return (am.getAccountsByType("org.mots.haxsync.account").length > 0);
	}
	
	public static boolean needsWorkaround(Context context){
        //workaround is required on 4.1 and all samsung devices <4.3
        if(android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1){
          return false;
        }


        if ((android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
                && (android.os.Build.VERSION.SDK_INT < 15 || Build.MANUFACTURER.toLowerCase().contains("samsung"))
                && (android.os.Build.VERSION.SDK_INT < 15 || Build.MODEL.toLowerCase().contains("maxx"))){
			SharedPreferences prefs = context.getSharedPreferences(context.getPackageName() + "_preferences", Activity.MODE_MULTI_PROCESS);
			if (!prefs.getBoolean("workaround_ignore", false)){
				try
				{
					context.getPackageManager().getApplicationInfo("com.haxsync.facebook.workaround", 0);
				}catch (PackageManager.NameNotFoundException e){
					return true;
				}
			}
		}
		return false;
	}
	
	public static Account getAccount(Context c){
		AccountManager am = AccountManager.get(c);
		Account[] accounts = am.getAccountsByType(c.getString(R.string.ACCOUNT_TYPE));
		if (accounts.length == 0)
			return null;
		return accounts[0];
	}
	
	public static void toggleWizard(Context c, boolean display){
		PackageManager pm = c.getPackageManager();
		pm.setComponentEnabledSetting(new ComponentName(c, WelcomeActivity.class), display ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
				PackageManager.DONT_KILL_APP);
	}
	
	public static boolean isWizardShown(Context c){
		PackageManager pm = c.getPackageManager();
		return (pm.getComponentEnabledSetting(new ComponentName(c, WelcomeActivity.class)) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
	}

	
	@SuppressLint("NewApi")
	public static void showJellyBeanNotification(Context context){
		if (needsWorkaround(context)){
			Intent intent = new Intent(Intent.ACTION_VIEW);
			intent.setData(Uri.parse("market://details?id=com.haxsync.facebook.workaround"));


			Intent cancelIntent = new Intent(context, WorkaroundConfirmation.class);

			PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);


			PendingIntent cancelContentIntent = PendingIntent.getActivity(context, 1, cancelIntent, PendingIntent.FLAG_CANCEL_CURRENT);

			NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);


			Resources res = context.getResources();
			Notification.Builder builder = new Notification.Builder(context);


			builder.setContentIntent(contentIntent)
			.setSmallIcon(android.R.drawable.stat_notify_more)
			.setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.icon))
			.setTicker(res.getString(R.string.jb_warning_ticker))
			.setWhen(System.currentTimeMillis())
			.setAutoCancel(true)
			.setContentTitle(res.getString(R.string.jb_warning_title))
			.setContentText(res.getString(R.string.jb_warning_description))
			.addAction(android.R.drawable.ic_menu_info_details, context.getString(android.R.string.yes), contentIntent)
			.addAction(android.R.drawable.ic_menu_close_clear_cancel, context.getString(android.R.string.no), cancelContentIntent);
			Notification n = new Notification.BigTextStyle(builder).bigText(context.getString(R.string.workaround_description)).build();


			nm.notify(1, n);
				
			}
		}
	

	public static String saveBytes(byte[] file, File dir){
		try {
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(dir, "photo.png")));
			bos.write(file);
			bos.flush();
			bos.close();
		} catch (Exception e) {
			return null;
		}
		return dir.getAbsolutePath()+"/photo.png";
	}

	public static boolean isCallable(Context c, Intent intent1) {    
		List<ResolveInfo> list = c.getPackageManager().queryIntentActivities(intent1,     
				PackageManager.MATCH_DEFAULT_ONLY);
        return list.size() > 0;
	
	}
	
	public static boolean isTouchWiz(Context c){
		PackageManager pm = c.getPackageManager();
		try{
			pm.getPackageInfo("com.sec.android.app.twlauncher", 0);
		} catch (PackageManager.NameNotFoundException e){
			try {
				pm.getPackageInfo("com.sec.android.app.launcher", 0);
			}  catch (PackageManager.NameNotFoundException y){
				try {
					pm.getPackageInfo("com.sec.android.app.samsungapps", 0);
				} catch (PackageManager.NameNotFoundException x){
					return false;
				}
			}
		}
		return true;
	}
	
	public static boolean isSense(Context c){
		PackageManager pm = c.getPackageManager();
		try{
			pm.getPackageInfo("com.htc", 0);
		} catch (PackageManager.NameNotFoundException e){
			return false;
		}
		return true;
	}
	
	public static void log(Context c, String tag, String message){
		SharedPreferences prefs = c.getSharedPreferences(c.getPackageName() + "_preferences", Context.MODE_MULTI_PROCESS);
		if (prefs.getBoolean("debug_logging", false))
			Log.i(tag, message);
	}
	


} public class FacebookUtil extends Application{
//	public static final String fbID = "176550339110536";
	public static final String fbID = "366633546716469";
	public static Facebook facebook = new Facebook(fbID);
	public static AsyncFacebookRunner mAsyncRunner = new AsyncFacebookRunner(facebook);
	public static Activity mActivity;
	public static String accessToken;
	private static final String TAG = "Utility";
	public static final int PERMISSION_LEVEL = 1;
	private static JSONObject selfInfo = null;
	private static final int FQL_LIMIT = 20;
	private static HashMap<String, Long> birthdays = new HashMap<String, Long>();
	public static boolean isExtendingToken = false;

	public static final boolean RESPECT_FACEBOOK_POLICY = true;

	private static JSONObject realSelfInfo = null;

	public static class PicInfo{
		public String url;
		public long timestamp;
	}

    public static boolean authorize(Context context, Account account) {
        if (isExtendingToken) {
            return false;
        }
        String token;
        AccountManager am = AccountManager.get(context);
        try {
            token = am.getPassword(account);
        } catch (SecurityException e) {
            Log.e(TAG, "error getting token");
            return false;
        }
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        long access_expires = prefs.getLong("access_expires", 0);
        Log.i("TOKEN EXPIRES", String.valueOf(access_expires));
        facebook.setAccessToken(token);
        if (access_expires != 0)
            facebook.setAccessExpires(access_expires);
        //Log.i("Facebook valid?", String.valueOf());

        DeviceUtil.showJellyBeanNotification(context);

        if (!DeviceUtil.isOnline(context))
            return false;

        boolean shouldExtend = false;
        try {
            shouldExtend = !checkToken() || facebook.shouldExtendAccessToken();
        } catch (IOException i) {
            Log.e(TAG, "error: " + i.getMessage());
            return false;
        }
        if (shouldExtend) {
            notifyToken(context);
            return false;
        }
        return true;
    }

    private static void notifyToken(Context context) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setClassName("org.mots.haxsync", "org.mots.haxsync.activities.AuthorizationActivity");
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        isExtendingToken = true;

        PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);

        NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        Resources res = context.getResources();
        Notification.Builder builder = new Notification.Builder(context);

        builder.setContentIntent(contentIntent)
                    .setSmallIcon(android.R.drawable.stat_notify_error)
                    .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.icon))
                    .setTicker(res.getString(R.string.token_notification_ticker))
                    .setWhen(System.currentTimeMillis())
                    .setAutoCancel(true)
                    .setContentTitle(res.getString(R.string.token_notification_title))
                    .setContentText(res.getString(R.string.token_notification_description));
        Notification n = builder.getNotification();

        nm.notify(0, n);
    }

    private static boolean checkToken() throws IOException {
			String response = facebook.request("me");
			//Log.i("response", response);
			return (!response.contains("\"type\":\"OAuthException\"") && facebook.isSessionValid());
	}


	public static void refreshPermissions(Context c){

		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c);

		int permissionLevel = prefs.getInt("permission_level", 0);
		Log.i("Permission Level", String.valueOf(permissionLevel));

		if (permissionLevel < FacebookUtil.PERMISSION_LEVEL){
			Intent intent = new Intent(Intent.ACTION_VIEW);
			intent.setClassName("org.mots.haxsync", "org.mots.haxsync.activities.AuthorizationActivity");
			intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
			c.startActivity(intent);
			SharedPreferences.Editor editor = prefs.edit();
			editor.putInt("permission_level", FacebookUtil.PERMISSION_LEVEL);
			editor.commit();
		}
	}


    public static List<FacebookGraphFriend> getFriends(int maxsize, boolean addMeToFriends){
        Bundle params = new Bundle();
        params.putString("fields", "picture.width("+maxsize+").height("+maxsize+"), name, first_name, last_name, username, birthday, location, updated_time");
        List<FacebookGraphFriend> friends = new ArrayList<FacebookGraphFriend>();
        List<String> ids = new ArrayList<String>();


        try {
            Bundle idParam = new Bundle();
            idParam.putString("fields", "id");
            if(addMeToFriends){
                JSONObject resp =  new JSONObject(facebook.request("me",idParam));
                Log.i(TAG, "id response " + resp);
                Log.i(TAG, "add Me to friends:" + resp.getString("id"));
                ids.add(resp.getString("id"));


            }

            JSONObject resp =  new JSONObject(facebook.request("me/friends", idParam));
            Log.i(TAG, "id response:" + resp);
            JSONArray friendsjson = resp.getJSONArray("data");
            for(int i = 0; i < friendsjson.length(); i++){
                ids.add(friendsjson.getJSONObject(i).getString("id"));

            }
            for(String id : ids){
                JSONObject response = new JSONObject(facebook.request(id,params));
                FacebookGraphFriend  friend = new FacebookGraphFriend(response);
                friends.add(friend);

            }
        } catch (JSONException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            Log.e(TAG, "Error loading friends: " + e.toString());
        }
        return friends;

    }


	public static ArrayList<FacebookFQLFriend> getFriendInfo(boolean status){
        //me/friends?fields=picture.height(720).width(720),name,username,birthday,location



    //old fql way
	JSONArray friendarray = new JSONArray();
	JSONArray timestamps = new JSONArray();
	int fetched = FQL_LIMIT;
	int callNo = 0;
	try{
		while (fetched == FQL_LIMIT){
		JSONObject jsonFQL = new JSONObject();
		String query1;
		if (status){
			query1 = "select name,  username, status, uid, profile_update_time, birthday_date, pic_big, current_location from user where uid in (select uid2 from friend where uid1=me()) order by name limit " + FQL_LIMIT + " offset " + callNo * FQL_LIMIT;
		} else {
			query1 = "select name,  username, uid, profile_update_time, birthday_date, pic_big, current_location from user where uid in (select uid2 from friend where uid1=me()) order by name limit " +  FQL_LIMIT + " offset " + callNo * FQL_LIMIT;
		}
		//Log.i("query1", query1);
		jsonFQL.put("query1", query1);
	    jsonFQL.put("query2", "SELECT modified, src_big, owner FROM photo WHERE pid IN (SELECT cover_pid FROM album WHERE owner IN (SELECT uid FROM #query1) AND type = 'profile')");
	    Bundle params = new Bundle();
	    params.putString("method", "fql.multiquery");
	    params.putString("queries", jsonFQL.toString());
	    String friendstring = facebook.request(params);
		JSONArray friendInfo = new JSONArray(friendstring);
		JSONArray newFriends = friendInfo.getJSONObject(0).getJSONArray("fql_result_set");
		Log.i("friendstring", friendstring);
		fetched = newFriends.length();
		Log.i("no of friends fetched", String.valueOf(fetched));
		friendarray = concatArray(friendarray, newFriends);
		timestamps = concatArray(timestamps, friendInfo.getJSONObject(1).getJSONArray("fql_result_set"));
		callNo++;
		}
	} catch (Exception e) {
		Log.e("ERROR:", e.toString());
	}
	//Log.i("friendarray", friendarray.toString());
	Log.i("total number of friends fetched", String.valueOf(friendarray.length()));
	Log.i("timestamps", timestamps.toString());
	return mergeArrays(friendarray, timestamps);

	}



	public static PicInfo getProfilePicInfo(String uid){
		String query = "SELECT modified, src_big, owner FROM photo WHERE pid IN (SELECT cover_pid FROM album WHERE owner = "+uid+" AND type = 'profile')";

		Bundle params = new Bundle();
		params.putString("method", "fql.query");
		params.putString("query", query);
		PicInfo pic = new PicInfo();
		try {
			JSONArray hires = new JSONArray(facebook.request(params));
			if (hires.length() >= 1){
				pic.url = hires.getJSONObject(0).getString("src_big");
				pic.timestamp = hires.getJSONObject(0).getLong("modified");
			}
			else {
				query = "select pic_big from user where uid =" +uid;
				params.putString("query", query);
				//Log.i("json", facebook.request(params));
				pic.url = new JSONArray(facebook.request(params)).getJSONObject(0).getString("pic_big");

			}

		} catch (Exception e){
			e.printStackTrace();
		}
		return pic;
	}




	private static JSONArray concatArray(JSONArray old, JSONArray add) throws JSONException {
	   for (int i = 0; i < add.length(); i++) {
	     old.put(add.get(i));
    }
	      return old;
	}

	public static ArrayList<Status> getStatuses(String uid, boolean fullTimeline){
		JSONArray statusarray = getStatusJSON(uid, fullTimeline);
		ArrayList<Status> statuses = new ArrayList<Status>();
		if (statusarray != null){
			for (int j = statusarray.length() -1 ; j >= 0 ; j--) {
				JSONObject statusjson = null;
				try {
					statusjson = statusarray.getJSONObject(j);
				} catch (JSONException e) {	}
				if (statusjson != null) {
					statuses.add(new FacebookStatus(statusjson));
				}
			}
		}
		return statuses;

	}

	private static JSONArray getStatusJSON(String uid, boolean fullTimeline){
		String query = "SELECT message, post_id, type, created_time, actor_id, app_data, source_id, likes, comments, permalink FROM stream WHERE source_id="+uid+"  ORDER BY created_time DESC LIMIT 50";
		if (!fullTimeline){
			query = "SELECT message, post_id, type, created_time, actor_id, app_data, source_id, likes, comments, permalink FROM stream WHERE source_id="+uid+" AND actor_id="+uid+" ORDER BY created_time DESC LIMIT 50";
		}
		Bundle params = new Bundle();
		params.putString("method", "fql.query");
		params.putString("query", query);
		JSONArray result = null;
		try {
			String response = facebook.request(params);
		//	Log.i("response", response);
			result = new JSONArray(response);
		} catch (Exception e){}
		//Log.i("response", result.toString());
		return result;
	}


	public static List<Event> getEvents(String status){
		ArrayList<Event> events = new ArrayList<Event>();
	try{
	    Bundle params = new Bundle();
	    params.putString("method", "fql.query");
	    String statusString = "";
	    String[] statusArray = status.split("\\|");
	    for (int i = 0; i < statusArray.length; i++){
	    	statusString += "'"+statusArray[i]+"'";
	    	if (i != statusArray.length-1)
	    		statusString += ", ";
	    }
	    String request = "SELECT name, eid, start_time, end_time, location, description FROM event WHERE eid IN ( SELECT eid FROM event_member WHERE uid =  me() AND rsvp_status in ("+statusString+"))";
	    Log.i("request", request);
	    params.putString("query", request);
	    JSONArray eventarray = new JSONArray(facebook.request(params));
	    for (int i = 0; i < eventarray.length(); i++){
	    	events.add(new Event(eventarray.getJSONObject(i)));
	    }
	} catch (Exception e) {
		Log.e("ERROR:", e.toString());
	}
	return events;
	}

	private static void addBirthday(String name, String date){
		int month = Integer.valueOf(date.split("/")[0]);
		int day = Integer.valueOf(date.split("/")[1]);
		GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
		cal.set(Calendar.getInstance().get(Calendar.YEAR), month-1, day, 0, 0 , 0);
		long millis = cal.getTimeInMillis();
		birthdays.put(name, millis);
	}

	public static HashMap<String, Long> getBirthdays(){
		if (birthdays.size() > 0){
			return birthdays;
		}
		String query = "select name, birthday_date from user where uid in (select uid2 from friend where uid1=me()) order by name";
		Bundle params = new Bundle();
		params.putString("method", "fql.query");
		params.putString("query", query);
		JSONArray result = null;
		try {
			result = new JSONArray(facebook.request(params));
		} catch (Exception e) {
			return birthdays;
		}for(int i = 0; i < result.length(); i++){
			try{
				JSONObject friend = result.getJSONObject(i);
				if (friend.getString("birthday_date") != null){
					addBirthday(friend.getString("name"), friend.getString("birthday_date"));
				}} catch (Exception e) {}

		}
		return birthdays;
	}



	private static ArrayList<FacebookFQLFriend> mergeArrays(JSONArray friendarray, JSONArray timestamps){
		ArrayList<FacebookFQLFriend> friends = new ArrayList<FacebookFQLFriend>();
		try{
		for(int i = 0; i < friendarray.length(); i++){
			JSONObject friend = friendarray.getJSONObject(i);
			String uid = friend.getString("uid");

			//write birthday hashmap
			String birthdayDate = friend.getString("birthday_date");
			if (birthdayDate != null && !birthdayDate.equals("") && ! birthdayDate.equals("null")){
				addBirthday(friend.getString("name"), friend.getString("birthday_date"));
			}

			long modified = 0;
			for(int j = 0; j < timestamps.length(); j++){
				JSONObject timestamp = timestamps.getJSONObject(j);
				if (timestamp.getString("owner").equals(uid)){
					modified = timestamp.getLong("modified");
					String src = timestamp.getString("src_big");
					if (!src.equalsIgnoreCase("")){
						friend.put("pic_big", src);
					}
					break;
				}
			}
			friend.put("pic_modified", modified);
			friends.add(new FacebookFQLFriend(friend));
		}
		} catch (Exception e) {
			Log.e("ERROR:", e.toString());
		}
	//	Log.i("JSON2", merged.toString());
		return friends;
	}

    public static JSONObject getPicInfo(long picID){
		String query = "SELECT owner, src_big, pid, aid FROM photo WHERE object_id="+picID;
	    Bundle params = new Bundle();
	    params.putString("method", "fql.query");
	    params.putString("query", query);
	    JSONObject result = null;
		try {
			result = new JSONArray(facebook.request(params)).getJSONObject(0);
			//Log.i("PIC_ID"+picID, result);
		} catch (Exception e) {
			Log.e(TAG, "error getting pic for id: " + picID);
			return null;
		}
		return result;
	}



	public static JSONObject getProfilePic(String uid){
		String query = "SELECT modified, src_big FROM photo WHERE pid IN (SELECT cover_pid FROM album WHERE owner="+uid+" AND type = 'profile')";
		Log.i("query", query);
	    Bundle params = new Bundle();
	    params.putString("method", "fql.query");
	    params.putString("query", query);
	    JSONObject result = null;
		try {
			result = new JSONArray(facebook.request(params)).getJSONObject(0);
		} catch (Exception e) {	}
		//Log.i("result", result.toString());
		return result;
	}



	public static String getSelfID(){
		FacebookGraphFriend selfInfo = getSelfInfo();
        return selfInfo.getUserName();
	}


	public static FacebookGraphFriend getSelfInfo(){
        FacebookGraphFriend friend = null;
        Bundle params = new Bundle();
        params.putString("fields", "picture.width(720).height(720), name, username, birthday, location, updated_time");
        try {
            JSONObject response = new JSONObject(facebook.request("me", params));
            friend = new FacebookGraphFriend(response);
            //Log.i("GRAPH FRIENDS", friendsjson.toString());
        } catch (IOException e) {
            Log.e(TAG, "Error loading friends: " + e.toString());
        }catch (JSONException e) {
            Log.e(TAG, "Error loading friends: " + e.toString());
        }
    	return  friend;
	}

	public static JSONObject getSelfInfoAsync(){
    	Bundle params = new Bundle();
    	params.putString("fields", "name, picture");
    	mAsyncRunner.request("me", params, new UserRequestListener());
    	while (selfInfo == null){
    		try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	return selfInfo;

	}


	public static String getFriendName(String uid){
		String name = null;
		try{
			String query = "select name from user where uid = " + uid;
		    Bundle params = new Bundle();
		    params.putString("method", "fql.query");
		    params.putString("query", query);
		    name = new JSONArray(facebook.request(params)).getJSONObject(0).getString("name");
		} catch (Exception e) {
			Log.e("ERROR:", e.toString());
		}
	   // Log.i("JSON1", friendInfo.toString());
		return name;

	}






	public static JSONArray getFriendNames(){
	JSONArray friendInfo = null;
	try{
		String query = "select name from user where uid in (select uid2 from friend where uid1=me()) order by name";
	    Bundle params = new Bundle();
	    params.putString("method", "fql.query");
	    params.putString("query", query);
	    String friendstring = facebook.request(params);
		friendInfo = new JSONArray(friendstring);
	} catch (Exception e) {
		Log.e("ERROR:", e.toString());
	}
   // Log.i("JSON1", friendInfo.toString());
	return friendInfo;

	}

	public static int convertStatus(String statusString){
		if (statusString.equals("attending")){
			return Attendees.ATTENDEE_STATUS_ACCEPTED;
		} else if (statusString.equals("unsure")){
			return  Attendees.ATTENDEE_STATUS_TENTATIVE;
		} else if (statusString.equals("declined")){
			return Attendees.ATTENDEE_STATUS_DECLINED;
		} else {
			return Attendees.ATTENDEE_STATUS_INVITED;
		}
	}

	public static List<EventAttendee> getEventAttendees(long eid){
	JSONObject jsonFQL = new JSONObject();
	String query1 = "SELECT uid, rsvp_status FROM event_member WHERE eid = " +eid + " AND uid IN (select uid2 from friend where uid1=me())";
	List<EventAttendee> attendees = new ArrayList<EventAttendee>();
	try {
		jsonFQL.put("query1", query1);
	    jsonFQL.put("query2", "SELECT name, username, uid FROM user WHERE uid IN (SELECT uid FROM #query1)");
	    Bundle params = new Bundle();
	    params.putString("method", "fql.multiquery");
	    params.putString("queries", jsonFQL.toString());
	    JSONArray response = new JSONArray(facebook.request(params));
	    attendees = mergeAttendees(response.getJSONObject(0).getJSONArray("fql_result_set"), response.getJSONObject(1).getJSONArray("fql_result_set"));
	   // Log.i("respnse", response.toString());
	} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	/*try{
		JSONObject self = getSelfInfo();
		String query = "SELECT rsvp_status FROM event_member WHERE eid = " + eid +" AND uid = me()";
	    Bundle params = new Bundle();
	    params.putString("method", "fql.query");
	    params.putString("query", query);
	    int status = convertStatus(new JSONArray(facebook.request(params)).getJSONObject(0).getString("rsvp_status"));
		EventAttendee selfAttendee = new EventAttendee(self.getString("name"), self.getString("email"), status);
		Log.i("selfattendee", selfAttendee.toString());
	    attendees.add(selfAttendee);
	}catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}*/
    return attendees;
	}


	public static String getSelfAttendance(long eid){
	try{
		String query = "SELECT rsvp_status FROM event_member WHERE eid = " + eid +" AND uid = me()";
	    Bundle params = new Bundle();
	    params.putString("method", "fql.query");
	    params.putString("query", query);
	    return new JSONArray(facebook.request(params)).getJSONObject(0).getString("rsvp_status");
	}catch (Exception e) {
		// TODO Auto-generated catch block
		Log.e(TAG, e.getMessage());
		return "";
	}
	}

	private static List<EventAttendee> mergeAttendees(JSONArray statuses, JSONArray attendees){
		ArrayList<EventAttendee> attendeeList = new ArrayList<EventAttendee>();
		try{
		for(int i = 0; i < attendees.length(); i++){
			JSONObject friend = attendees.getJSONObject(i);
			String uid = friend.getString("uid");

			//write birthday hashmap

			long modified = 0;
			for(int j = 0; j < statuses.length(); j++){
				JSONObject status = statuses.getJSONObject(j);
				if (status.getString("uid").equals(uid)){
					friend.put("rsvp_status", status.getString("rsvp_status"));
					break;
				}
			}
			attendeeList.add(new EventAttendee(friend));
		}
		} catch (Exception e) {
			Log.e("ERROR:", e.toString());
		}
	//	Log.i("JSON2", merged.toString());
		return attendeeList;
	}





	public static JSONObject getFriendNamesAsync(){
    	Bundle params = new Bundle();
    	params.putString("fields", "name, picture");
    	mAsyncRunner.request("me", params, new UserRequestListener());
    	while (selfInfo == null){
    		try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
    	}
    	return selfInfo;

	}

	public static class TokenListener implements ServiceListener{

		@Override
		public void onComplete(Bundle values) {
			//Log.i("got values", values.toString());
			accessToken = values.getString(Facebook.TOKEN);
			// TODO Auto-generated method stub

		}

		@Override
		public void onFacebookError(FacebookError e) {
			Log.e(TAG, e.toString());
		}

		@Override
		public void onError(Error e) {
			Log.e(TAG, e.toString());
		}

	}

	public static class UserRequestListener extends BaseRequestListener{
		public void onComplete(final String response, final Object state){
			JSONObject jsonobject;
				try{
					jsonobject = new JSONObject(response);
					selfInfo = jsonobject;
				} catch (JSONException e){
					e.printStackTrace();
				}
		}
	}

} public class RootUtil {
	private static final String PICTURE_DIR = "/data/data/com.android.providers.contacts/files/photos/";
	
	public static boolean isRoot(){
		return Shell.su();
	}
	
	public static String movePic(String path, String file) throws ShellException{
		String newpath = PICTURE_DIR + file;
		String command = "mv " + path + " " + PICTURE_DIR + file;
		Log.i("COMMAND", command);
		Shell.sudo(command);
		return newpath;
	}
	
	public static void changeOwner(String file) throws ShellException{
		String command = "chown app_1.app_1 " + file;
		Log.i("COMMAND", command);
		Shell.sudo(command);
	}
	
	public static void refreshContacts() throws ShellException{
		Shell.sudo("pm disable com.android.providers.contacts");
		Shell.sudo("pm enable com.android.providers.contacts");
	}
	
	public static String listPics() throws ShellException{
		return Shell.sudo("ls " + PICTURE_DIR);
	}
	
	

} public class WebUtil {

	public static byte[] download(String urlString){
	    ByteArrayOutputStream bais = new ByteArrayOutputStream();
	    InputStream is = null;
	    URL url = null;
		try {
			url = new URL(urlString);
	      is = url.openStream ();
	      byte[] byteChunk = new byte[4096];
	      int n;
	
	      while ( (n = is.read(byteChunk)) > 0 ) {
	        bais.write(byteChunk, 0, n);
	      }
	    }
	    catch (Exception e) {
	    	e.printStackTrace();
	    	if (urlString.contains("fbcdn_sphotos_")){
	    		String alt = urlString;
	    		urlString.replace("fbcdn_sphotos_", "fbcdn-sphotos-");
	    		alt.replace("fbcdn_sphotos_a-a.akamaihd.net", "a1.sphotos.ak.fbcdn.net");
	    		byte[] res = download(urlString);
	    		return (res == null) ? download(alt) : res;
	    	}
	    	return null;
	    }
	    finally {
	      if (is != null) { try {
			is.close();
		} catch (IOException e) {
			return null;
		} }
	    }
	
	    return bais.toByteArray();
	}

} public static class ShellException extends Exception {
	private static final long serialVersionUID = 4820332926695755116L;
		
	public ShellException() {
		super();
	}
		
	public ShellException(String msg) {
		super(msg);
	}
}
import java.io.File;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.mots.haxsync.R;
import org.mots.haxsync.provider.FacebookFQLFriend;
import org.mots.haxsync.provider.FacebookGraphFriend;
import org.mots.haxsync.provider.Status;
import org.mots.haxsync.utilities.BitmapUtil;
import org.mots.haxsync.utilities.ContactUtil;
import org.mots.haxsync.utilities.ContactUtil.Photo;
import org.mots.haxsync.utilities.DeviceUtil;
import org.mots.haxsync.utilities.FacebookUtil;
import org.mots.haxsync.utilities.RootUtil;
import org.mots.haxsync.utilities.WebUtil;
import com.jjnford.android.util.Shell.ShellException;
import android.accounts.Account;
import android.accounts.OperationCanceledException;
import android.app.Service;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.SyncResult;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.BaseColumns;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.RawContacts.Entity;
import android.util.Log;


			HashMap<String, SyncEntry> localContacts = getLocalContacts(account);
			HashMap<String, Long> names = loadPhoneContacts(context);
			HashMap<String, Long> uids = loadHTCData(context);
			//Log.i("CONTACTS", names.toString());
			boolean phoneOnly = prefs.getBoolean("phone_only", true);
			/*if (phoneOnly){
				names = loadPhoneContacts(context);
			}*/
			boolean wifiOnly = prefs.getBoolean("wifi_only", false);
			boolean syncEmail = prefs.getBoolean("sync_facebook_email", false);
			boolean syncBirthday = prefs.getBoolean("sync_contact_birthday", true);
			boolean force = prefs.getBoolean("force_dl", false);
			boolean google = prefs.getBoolean("update_google_photos", false);
            boolean ignoreMiddleaNames = prefs.getBoolean("ignore_middle_names",false);
            boolean addMeToFriends = prefs.getBoolean("add_me_to_friends", false);
			Log.i("google", String.valueOf(google));
			int fuzziness = Integer.parseInt(prefs.getString("fuzziness", "2"));
			Set<String> addFriends = prefs.getStringSet("add_friends", new HashSet<String>());
			Log.i(TAG, "phone_only: " + Boolean.toString(phoneOnly));
			Log.i(TAG, "wifi_only: " + Boolean.toString(wifiOnly));
			Log.i(TAG, "is wifi: " + Boolean.toString(DeviceUtil.isWifi(context)));
			Log.i(TAG, "phone contacts: " + names.toString()); 
			Log.i(TAG, "using old status api: " +String.valueOf(oldStatus));
            Log.i(TAG, "ignoring middle names : " + String.valueOf(ignoreMiddleaNames));
            Log.i(TAG, "add me to friends : "+  String.valueOf(addMeToFriends));
			boolean chargingOnly = prefs.getBoolean("charging_only", false);
			int maxsize = BitmapUtil.getMaxSize(context);
			File cacheDir = context.getCacheDir();
			Log.i("CACHE DIR", cacheDir.getAbsolutePath());
			Log.i("MAX IMAGE SIZE", String.valueOf(maxsize));
			if (!((wifiOnly && !DeviceUtil.isWifi(context)) || (chargingOnly && !DeviceUtil.isCharging(context)))){
			try {
				if (syncSelf){
					addSelfContact(account, maxsize, cropPhotos, faceDetect, force, root, rootSize, cacheDir, google);
				}	
				//ArrayList<FacebookFQLFriend> friends = FacebookUtil.getFriendInfo(oldStatus);
                List<FacebookGraphFriend> friends = FacebookUtil.getFriends(maxsize, addMeToFriends);
				for (FacebookGraphFriend friend : friends) {
					String uid = friend.getUserName();
					String friendName = friend.getName(ignoreMiddleaNames);
					if (friendName != null && uid != null){
					String match = matches(names.keySet(), friendName , fuzziness);
					
					if (!(phoneOnly && (match == null) && !uids.containsKey(uid)) || addFriends.contains(friendName)){
					//add contact
					if (localContacts.get(uid) == null) {
						//String name = friend.getString("name");
						//Log.i(TAG, name + " already on phone: " + Boolean.toString(names.contains(name)));
						
						addContact(account, friendName, uid);
						
						SyncEntry entry = new SyncEntry();
						Uri rawContactUr = RawContacts.CONTENT_URI.buildUpon()
								.appendQueryParameter(RawContacts.ACCOUNT_NAME, account.name)
								.appendQueryParameter(RawContacts.ACCOUNT_TYPE, account.type)
								.appendQueryParameter(RawContacts.Data.DATA1, uid)
								.build();
						Cursor c =mContentResolver.query(rawContactUr, new String[] {
								BaseColumns._ID}, null,
								null, null);
						c.moveToLast();
						long id = c.getLong(c.getColumnIndex(BaseColumns._ID));
						c.close();
					//	Log.i("ID", Long.toString(id));
						entry.raw_id = id;
						localContacts.put(uid, entry);
						if (uids.containsKey(uid)){
							ContactUtil.merge(context, uids.get(uid), id);
						} else if (names.containsKey(match)){
							ContactUtil.merge(context, names.get(match), id);
						}
						//localContacts = loadContacts(accounts, context);
					}
					
	
					// set contact photo
	
					SyncEntry contact = localContacts.get(uid);
					
					updateContactPhoto(contact.raw_id, friend.getPicTimestamp(), maxsize, cropPhotos, friend.getPicURL(), faceDetect, force, root, rootSize, cacheDir, google, imageDefault);
					
					if (syncEmail && !FacebookUtil.RESPECT_FACEBOOK_POLICY)
						ContactUtil.addEmail(context, contact.raw_id, friend.getEmail());


					if (syncLocation  && !FacebookUtil.RESPECT_FACEBOOK_POLICY){
						ContactUtil.updateContactLocation(contact.raw_id, friend.getLocation());
					}
					
					
					if (oldStatus  && !FacebookUtil.RESPECT_FACEBOOK_POLICY){
						ArrayList<Status> statuses = friend.getStatuses();
						if (statuses.size() >= 1){
							updateContactStatus(contact.raw_id,
									statuses.get(0).getMessage(), statuses.get(0).getTimestamp());
						}
						
					}
					if (syncBirthday  && !FacebookUtil.RESPECT_FACEBOOK_POLICY){
						String birthday = friend.getBirthday();
						
						if (birthday != null){
							ContactUtil.addBirthday(contact.raw_id, birthday);
						}
					}
	
				}
				}
			} }catch (Exception e) {
				// TODO: handle exception
				Log.e("ERROR", e.toString());
				e.printStackTrace();
			
			}
			
			if (root){
				try {
					RootUtil.refreshContacts();
				} catch (ShellException e) {