public class DemoApplication extends Application {
	public static DemoApplication context;
	private static Map<String, AVUser> usersCache = new HashMap<String, AVUser>();
	List<AVUser> friends = new ArrayList<AVUser>();

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		context = this;
		AVOSCloud.initialize(this,
				"h1yik0j4fq0wb7759ruubwdcc7qi4bod880i1joe98jk160s",
				"yl8j5oyb3avv54pbjsl76no4lcnt6lq0z2jngx2dqze4b4h8");
		AVObject.registerSubclass(AddRequest.class);
		SDKInitializer.initialize(this);
		initImageLoader(context);
	}

	public static void initImageLoader(Context context) {
		File cacheDir = StorageUtils.getOwnCacheDirectory(context,
				"leanchat/Cache");
		ImageLoaderConfiguration config = PhotoUtil.getImageLoaderConfig(
				context, cacheDir);
		// Initialize ImageLoader with configuration.
		ImageLoader.getInstance().init(config);
	}

	public List<AVUser> getFriends() {
		return friends;
	}

	public void setFriends(List<AVUser> friends) {
		this.friends = friends;
	}

	public static AVUser lookupUser(String userId) {
		return usersCache.get(userId);
	}

	public static void registerUserCache(String userId, AVUser user) {
		usersCache.put(userId, user);
	}

	public static void registerUserCache(AVUser user) {
		registerUserCache(user.getObjectId(), user);
	}

	public static void registerBatchUserCache(List<AVUser> users) {
		for (AVUser user : users) {
			registerUserCache(user);
		}
	}

} public class ChatMsgAdapter extends BaseListAdapter<Msg> {
	int msgViewTypes = 8;

	public static interface MsgViewType {
		int COME_TEXT = 0;
		int TO_TEXT = 1;
		int COME_IMAGE = 2;
		int TO_IMAGE = 3;
		int COME_AUDIO = 4;
		int TO_AUDIO = 5;
		int COME_LOCATION = 6;
		int TO_LOCATION = 7;
	}

	ChatActivity chatActivity;

	public ChatMsgAdapter(ChatActivity chatActivity, List<Msg> datas) {
		super(chatActivity, datas);
		this.chatActivity = chatActivity;
	}

	public int getItemPosById(String objectId) {
		for (int i = 0; i < getCount(); i++) {
			Msg itemMsg = datas.get(i);
			if (itemMsg.getObjectId().equals(objectId)) {
				return i;
			}
		}
		return -1;
	}

	public Msg getItem(String objectId) {
		for (Msg msg : datas) {
			if (msg.getObjectId().equals(objectId)) {
				return msg;
			}
		}
		return null;
	}

	public int getItemViewType(int position) {
		Msg entity = datas.get(position);
		boolean comeMsg = entity.isComeMessage();
		Msg.Type type = entity.getType();
		switch (type) {
			case Text:
				return comeMsg ? MsgViewType.COME_TEXT : MsgViewType.TO_TEXT;
			case Image:
				return comeMsg ? MsgViewType.COME_IMAGE : MsgViewType.TO_IMAGE;
			case Audio:
				return comeMsg ? MsgViewType.COME_AUDIO : MsgViewType.TO_AUDIO;
			case Location:
				return comeMsg ? MsgViewType.COME_LOCATION : MsgViewType.TO_LOCATION;
		}
		throw new IllegalStateException("position's type is wrong");
	}

	public int getViewTypeCount() {
		return msgViewTypes;
	}

	public View getView(int position, View conView, ViewGroup parent) {
		Msg msg = datas.get(position);
		int itemViewType = getItemViewType(position);
		boolean isComMsg = msg.isComeMessage();
		if (conView == null) {
			conView = createViewByType(itemViewType);
		}
		TextView sendTimeView = ViewHolder.findViewById(conView, R.id.sendTimeView);
		TextView contentView = ViewHolder.findViewById(conView, R.id.textContent);
		ImageView imageView = ViewHolder.findViewById(conView, R.id.imageView);
		ImageView avatarView = ViewHolder.findViewById(conView, R.id.avatar);
		PlayButton playBtn = ViewHolder.findViewById(conView, R.id.playBtn);
		TextView locationView = ViewHolder.findViewById(conView, R.id.locationView);

		View statusSendFailed = ViewHolder.findViewById(conView, R.id.status_send_failed);
		View statusSendSucceed = ViewHolder.findViewById(conView, R.id.status_send_succeed);
		View statusSendStart = ViewHolder.findViewById(conView, R.id.status_send_start);

		// timestamp
		if (position == 0 || TimeUtils.haveTimeGap(datas.get(position - 1).getTimestamp(),
				msg.getTimestamp())) {
			sendTimeView.setVisibility(View.VISIBLE);
			sendTimeView.setText(TimeUtils.millisecs2DateString(msg.getTimestamp()));
		} else {
			sendTimeView.setVisibility(View.GONE);
		}

		String fromPeerId = msg.getFromPeerId();
		AVUser user = DemoApplication.lookupUser(fromPeerId);
		if (user == null) {
			throw new RuntimeException("cannot find user");
		}
		UserService.displayAvatar(getAvatarUrl(user), avatarView);

		Msg.Type type = msg.getType();
		if (type == Msg.Type.Text) {
			contentView.setText(EmotionUtils.replace(ctx, msg.getContent()));
		} else if (type == Msg.Type.Image) {
			String localPath = PathUtils.getChatFileDir() + msg.getObjectId();
			String url = msg.getContent();
			displayImageByUri(imageView, localPath, url);
			setImageOnClickListener(localPath, url, imageView);
		} else if (type == Msg.Type.Audio) {
			initPlayBtn(msg, playBtn);
		} else if (type == Msg.Type.Location) {
			setLocationView(msg, locationView);
		}
		if (isComMsg == false) {
			hideStatusViews(statusSendStart, statusSendFailed, statusSendSucceed);
			setSendFailedBtnListener(statusSendFailed, msg);
			switch (msg.getStatus()) {
				case SendFailed:
					statusSendFailed.setVisibility(View.VISIBLE);
					break;
				case SendSucceed:
					statusSendSucceed.setVisibility(View.VISIBLE);
					break;
				case SendStart:
					statusSendStart.setVisibility(View.VISIBLE);
					break;
			}

		}
		return conView;
	}

	public String getAvatarUrl(AVUser user) {
		AVFile avatar = user.getAVFile("avatar");
		if (avatar != null) {
			return avatar.getUrl();
		} else {
			return null;
		}
	}

	private void setSendFailedBtnListener(View statusSendFailed, final Msg msg) {
		statusSendFailed.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				chatActivity.resendMsg(msg);
			}
		});
	}

	private void hideStatusViews(View statusSendStart, View statusSendFailed, View statusSendSucceed) {
		statusSendFailed.setVisibility(View.GONE);
		statusSendStart.setVisibility(View.GONE);
		statusSendSucceed.setVisibility(View.GONE);
	}

	public void setLocationView(Msg msg, TextView locationView) {
		try {
			String content = msg.getContent();
			if (content != null && !content.equals("")) {
				String[] parts = content.split("&");
				String address = parts[0];
				final String latitude = parts[1];
				final String longtitude = parts[2];
				locationView.setText(address);
				locationView.setOnClickListener(new View.OnClickListener() {

					@Override
					public void onClick(View arg0) {
//            Intent intent = new Intent(ctx, LocationActivity.class);
//            intent.putExtra("type", "scan");
//            intent.putExtra("latitude", Double.parseDouble(latitude));
//            intent.putExtra("longtitude", Double.parseDouble(longtitude));
//            ctx.startActivity(intent);
					}
				});
			}
		} catch (Exception e) {
		}
	}

	private void initPlayBtn(Msg msg, PlayButton playBtn) {
		playBtn.setPath(msg.getAudioPath());
	}

	private void setImageOnClickListener(final String path, final String url, ImageView imageView) {
		imageView.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
//        Intent intent = new Intent(ctx, ImageBrowerActivity.class);
//        intent.putExtra("path", path);
//        intent.putExtra("url", url);
//        ctx.startActivity(intent);
			}
		});
	}

	public static void displayImageByUri(ImageView imageView,
										 String localPath, String url) {
		File file = new File(localPath);
		ImageLoader imageLoader = UserService.imageLoader;
		if (file.exists()) {
			imageLoader.displayImage("file://" + localPath, imageView, PhotoUtil.normalImageOptions);
		} else {
			imageLoader.displayImage(url, imageView, PhotoUtil.normalImageOptions);
		}
	}

	public View createViewByType(int itemViewType) {
		int[] types = new int[]{MsgViewType.COME_TEXT, MsgViewType.TO_TEXT,
				MsgViewType.COME_IMAGE, MsgViewType.TO_IMAGE, MsgViewType.COME_AUDIO,
				MsgViewType.TO_AUDIO, MsgViewType.COME_LOCATION, MsgViewType.TO_LOCATION};
		int[] layoutIds = new int[]{
				R.layout.chat_item_msg_text_left,
				R.layout.chat_item_msg_text_right,
				R.layout.chat_item_msg_image_left,
				R.layout.chat_item_msg_image_right,
				R.layout.chat_item_msg_audio_left,
				R.layout.chat_item_msg_audio_right,
				R.layout.chat_item_msg_location_left,
				R.layout.chat_item_msg_location_right};
		int i;
		for (i = 0; i < types.length; i++) {
			if (itemViewType == types[i]) {
				break;
			}
		}
		return inflater.inflate(layoutIds[i], null);
	}
} public class EmotionGridAdapter extends BaseListAdapter<String> {

	public EmotionGridAdapter(Context ctx) {
		super(ctx);
	}

	@Override
	public View getView(int position, View conView, ViewGroup parent) {
		if (conView == null) {
			LayoutInflater inflater = LayoutInflater.from(ctx);
			conView = inflater.inflate(R.layout.chat_emotion_item, null);
		}
		ImageView emotionImageView = ViewHolder.findViewById(conView, R.id.emotionImageView);
		String emotion = (String) getItem(position);
		emotion = emotion.substring(1);
		Bitmap bitmap = EmotionUtils.getDrawableByName(ctx, emotion);
		emotionImageView.setImageBitmap(bitmap);
		return conView;
	}
} public class EmotionPagerAdapter extends BasePagerAdapter {

	public EmotionPagerAdapter(List<View> views) {
		super(views);
	}
} public class DBHelper extends SQLiteOpenHelper {

	public DBHelper(Context context, String name, int version) {
		super(context, name, null, version);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		DBMsg.createTable(db);
	}

	@Override
	public void onOpen(SQLiteDatabase db) {
		super.onOpen(db);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		switch (newVersion) {
			case 3:
			case 2:
				DBMsg.dropTable(db);  //lazy solution
				DBMsg.createTable(db);
			case 1:
				break;
		}
	}
} public class DBMsg {
	public static final String MESSAGES = "messages";
	public static final String FROM_PEER_ID = "fromPeerId";
	public static final String CONVID = "convid";
	public static final String TIMESTAMP = "timestamp";
	public static final String OBJECT_ID = "objectId";
	public static final String CONTENT = "content";
	public static final String STATUS = "status";
	public static final String TYPE = "type";
	public static final String TO_PEER_ID = "toPeerId";
	public static final String ROOM_TYPE = "roomType";
	public static final String OWNER_ID = "ownerId";

	public static void createTable(SQLiteDatabase db) {
		db.execSQL("create table if not exists messages (id integer primary key, objectId varchar(63) unique not null," +
				"ownerId varchar(255) not null,fromPeerId varchar(255) not null, convid varchar(255) not null ," +
				"toPeerId varchar(255), content varchar(1023)," +
				" status integer,type integer,roomType integer,timestamp varchar(63))");
	}

	public static void dropTable(SQLiteDatabase db) {
		db.execSQL("drop table if exists messages");
	}

	public static int insertMsg(Msg msg) {
		List<Msg> msgs = new ArrayList<Msg>();
		msgs.add(msg);
		return insertMsgs(msgs);
	}

	public static int insertMsgs(List<Msg> msgs) {
		DBHelper dbHelper = new DBHelper(DemoApplication.context, "chat.db", 1);
		if (msgs == null || msgs.size() == 0) {
			return 0;
		}
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		db.beginTransaction();
		int n = 0;
		try {
			for (Msg msg : msgs) {
				ContentValues cv = new ContentValues();
				cv.put(OBJECT_ID, msg.getObjectId());
				cv.put(TIMESTAMP, msg.getTimestamp() + "");
				cv.put(FROM_PEER_ID, msg.getFromPeerId());
				cv.put(STATUS, msg.getStatus().getValue());
				cv.put(ROOM_TYPE, msg.getRoomType().getValue());
				cv.put(CONVID, msg.getConvid());
				cv.put(TO_PEER_ID, msg.getToPeerId());
				AVUser user = AVUser.getCurrentUser();
				cv.put(OWNER_ID, user.getObjectId());
				cv.put(TYPE, msg.getType().getValue());
				cv.put(CONTENT, msg.getContent());
				db.insert(MESSAGES, null, cv);
				n++;
			}
			db.setTransactionSuccessful();
		} finally {
			db.endTransaction();
			db.close();
		}
		return n;
	}

	public static List<Msg> getMsgs(DBHelper dbHelper, String convid, int size) {
		List<Msg> msgs = new ArrayList<Msg>();
		SQLiteDatabase db = dbHelper.getReadableDatabase();
		assert db != null;
		Cursor c = db.query(MESSAGES, null, "convid=?", new String[]{convid}, null, null, TIMESTAMP + " desc", size + "");
		while (c.moveToNext()) {
			Msg msg = createMsgByCursor(c);
			msgs.add(msg);
		}
		c.close();
		Collections.reverse(msgs);
		db.close();
		return msgs;
	}

	public static Msg createMsgByCursor(Cursor c) {
		Msg msg = new Msg();
		msg.setFromPeerId(c.getString(c.getColumnIndex(FROM_PEER_ID)));
		msg.setContent(c.getString(c.getColumnIndex(CONTENT)));
		Msg.Status status = Msg.Status.fromInt(c.getInt(c.getColumnIndex(STATUS)));
		msg.setStatus(status);
		msg.setConvid(c.getString(c.getColumnIndex(CONVID)));
		msg.setObjectId(c.getString(c.getColumnIndex(OBJECT_ID)));
		int roomTypeInt = c.getInt(c.getColumnIndex(ROOM_TYPE));
		RoomType roomType = RoomType.fromInt(roomTypeInt);
		msg.setRoomType(roomType);
		String toPeerId = c.getString(c.getColumnIndex(TO_PEER_ID));
		msg.setToPeerId(toPeerId);
		msg.setTimestamp(Long.parseLong(c.getString(c.getColumnIndex(TIMESTAMP))));
		Msg.Type type = Msg.Type.fromInt(c.getInt(c.getColumnIndex(TYPE)));
		msg.setType(type);
		return msg;
	}

	public static List<Msg> getRecentMsgs(String ownerId) {
		DBHelper dbHelper = new DBHelper(DemoApplication.context, "chat.db", 1);
		SQLiteDatabase db = dbHelper.getReadableDatabase();
		Cursor c = db.query(true, MESSAGES, null, "ownerId=?",
				new String[]{ownerId}, CONVID, null, TIMESTAMP + " desc", null);
		List<Msg> msgs = new ArrayList<Msg>();
		while (c.moveToNext()) {
			Msg msg = createMsgByCursor(c);
			msgs.add(msg);
		}
		c.close();
		db.close();
		return msgs;
	}

	public static int updateStatusAndTimestamp(String objecctId, Msg.Status status, long timestamp) {
		ContentValues cv = new ContentValues();
		cv.put(STATUS, status.getValue());
		cv.put(TIMESTAMP, timestamp + "");
		String objectId = objecctId;
		return updateMessage(objectId, cv);
	}

	public static int updateMessage(String objectId, ContentValues cv) {
		DBHelper dbHelper = new DBHelper(DemoApplication.context, "chat.db", 1);
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int updateN = db.update(MESSAGES, cv, "objectId=?", new String[]{objectId});
		db.close();
		return updateN;
	}

	public static int updateStatus(String objectId, Msg.Status status) {
		ContentValues cv = new ContentValues();
		cv.put(STATUS, status.getValue());
		return updateMessage(objectId, cv);
	}
} public class Msg {
	public static enum Status {
		SendStart(0), SendSucceed(1), SendReceived(2),
		SendFailed(3), HaveRead(4);

		int value;

		Status(int value) {
			this.value = value;
		}

		public int getValue() {
			return value;
		}

		public static Status fromInt(int i) {
			return values()[i];
		}
	}

	public static enum Type {
		Text(0), Image(1), Audio(2), Location(3);
		int value;

		Type(int value) {
			this.value = value;
		}

		public int getValue() {
			return value;
		}

		public static Type fromInt(int i) {
			return values()[i];
		}
	}

	//long timestamp;
	//String fromPeerId;
	//List<String> toPeerIds;
	//isRequestReceipt
	AVMessage internalMessage;
	String content;
	String objectId;
	String convid;


	RoomType roomType = RoomType.Single;
	Status status = Status.SendStart;
	Type type = Type.Text;


	public Msg() {
		internalMessage = new AVMessage();
	}

	public AVMessage getInternalMessage() {
		return internalMessage;
	}

	public void setInternalMessage(AVMessage internalMessage) {
		this.internalMessage = internalMessage;
	}

	public String getToPeerId() {
		List<String> toPeerIds = internalMessage.getToPeerIds();
		if (toPeerIds != null && toPeerIds.size() > 0) {
			return toPeerIds.get(0);
		} else {
			return convid;
		}
	}

	private List<String> getToPeerIds() {
		return internalMessage.getToPeerIds();
	}

	public void setToPeerId(String toPeerId) {
		setToPeerIds(Arrays.asList(toPeerId));
	}

	private void setToPeerIds(List<String> toPeerIds) {
		internalMessage.setToPeerIds(toPeerIds);
	}

	public String getFromPeerId() {
		return internalMessage.getFromPeerId();
	}

	public void setFromPeerId(String fromPeerId) {
		internalMessage.setFromPeerId(fromPeerId);
	}

	public long getTimestamp() {
		return internalMessage.getTimestamp();
	}

	public boolean isRequestReceipt() {
		return internalMessage.isRequestReceipt();
	}

	public void setRequestReceipt(boolean isRequestReceipt) {
		internalMessage.setRequestReceipt(isRequestReceipt);
	}

	public String getConvid() {
		return convid;
	}

	public void setConvid(String convid) {
		this.convid = convid;
	}

	public RoomType getRoomType() {
		return roomType;
	}

	public void setRoomType(RoomType roomType) {
		this.roomType = roomType;
	}

	public Status getStatus() {
		return status;
	}

	public void setStatus(Status status) {
		this.status = status;
	}

	public Type getType() {
		return type;
	}

	public void setType(Type type) {
		this.type = type;
	}

	public void setTimestamp(long timestamp) {
		internalMessage.setTimestamp(timestamp);
	}

	public String getContent() {
		return content;
	}

	public String getStatusDesc() {
		if (status == Status.SendStart) {
			return DemoApplication.context.getString(R.string.sending);
		} else if (status == Status.SendReceived) {
			return DemoApplication.context.getString(R.string.received);
		} else if (status == Status.SendSucceed) {
			return DemoApplication.context.getString(R.string.sent);
		} else if (status == Status.SendFailed) {
			return DemoApplication.context.getString(R.string.failed);
		} else if (status == Status.HaveRead) {
			return DemoApplication.context.getString(R.string.haveRead);
		} else {
			throw new IllegalArgumentException("unknown status");
		}
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getObjectId() {
		return objectId;
	}

	public void setObjectId(String objectId) {
		this.objectId = objectId;
	}

	public boolean isComeMessage() {
		String fromPeerId = getFromPeerId();
		return !fromPeerId.equals(ChatService.getSelfId());
	}

	public String getOtherId() {
		if (getRoomType() == RoomType.Group) {
			return getToPeerId();
		} else {
			String fromPeerId = getFromPeerId();
			String selfId = ChatService.getSelfId();
			if (fromPeerId.equals(selfId)) {
				return getToPeerId();
			} else {
				return fromPeerId;
			}
		}
	}

	public String getFromName() {
		String peerId = getFromPeerId();
		AVUser user = DemoApplication.lookupUser(peerId);
		return user.getUsername();
	}

	public CharSequence getNotifyContent() {
		switch (type) {
			case Audio:
				return DemoApplication.context.getString(R.string.audio);
			case Text:
				if (EmotionUtils.haveEmotion(getContent())) {
					return DemoApplication.context.getString(R.string.emotion);
				} else {
					return getContent();
				}
			case Image:
				return DemoApplication.context.getString(R.string.image);
			case Location:
				return DemoApplication.context.getString(R.string.position);
			default:
				return DemoApplication.context.getString(R.string.newMessage);
		}
	}

	public static Msg fromAVMessage(AVMessage avMsg) {
		Msg msg = new Msg();
		msg.setInternalMessage(avMsg);
		if (!AVUtils.isBlankString(avMsg.getMessage())) {
			HashMap<String, Object> params = JSON.parseObject(avMsg.getMessage(), HashMap.class);
			String objectId = (String) params.get("objectId");
			String content = (String) params.get("content");
			Integer typeInt = (Integer) params.get("type");
			if (objectId == null || content == null ||
					typeInt == null) {
				throwNullException();
			}
			msg.setObjectId(objectId);
			msg.setContent(content);
			Type type = Type.fromInt(typeInt);
			msg.setType(type);
		}
		return msg;
	}

	public AVMessage toAVMessage() {
		if (convid == null || content == null || objectId == null
				|| roomType == null || status == null || type == null) {
			throwNullException();
		}
		HashMap<String, Object> params = new HashMap<String, Object>();
		params.put("objectId", objectId);
		params.put("content", content);
		params.put("type", type.getValue());
		internalMessage.setMessage(JSON.toJSONString(params));
		return internalMessage;
	}

	public static void throwNullException() {
		throw new NullPointerException("at least one of these is null: content,objectId,type");
	}

	@Override
	public String toString() {
		return "{content:" + getContent() + " objectId:" + getObjectId() + " status:" + getStatus() + " fromPeerId:" +
				getFromPeerId() + " toPeerIds:" + getToPeerIds()
				+ " timestamp:" + getTimestamp() + " type=" + getType() + "}";
	}

	public String getAudioPath() {
		return PathUtils.getChatFileDir() + getObjectId();
	}


} public class MsgBuilder {
	Msg msg;

	public MsgBuilder() {
		msg = new Msg();
	}

	public void target(RoomType roomType, String toId) {
		String convid;
		msg.setRoomType(roomType);
		if (roomType == RoomType.Single) {
			msg.setToPeerId(toId);
			msg.setRequestReceipt(true);
			convid = AVOSUtils.convid(ChatService.getSelfId(), toId);
		} else {
			convid = toId;
		}
		msg.setConvid(convid);
	}

	public void text(String content) {
		msg.setType(Msg.Type.Text);
		msg.setContent(content);
	}

	private void file(Msg.Type type, String objectId) {
		msg.setType(type);
		msg.setObjectId(objectId);
	}

	public void image(String objectId) {
		file(Msg.Type.Image, objectId);
	}

	public void location(String address, double latitude, double longitude) {
		String content = address + "&" + latitude + "&" + longitude;
		msg.setContent(content);
		msg.setType(Msg.Type.Location);
	}

	public void audio(String objectId) {
		file(Msg.Type.Audio, objectId);
	}

	public Msg build() {
		msg.setStatus(Msg.Status.SendStart);
		msg.setTimestamp(System.currentTimeMillis());
		msg.setFromPeerId(ChatService.getSelfId());
		if (msg.getObjectId() == null) {
			msg.setObjectId(Utils.myUUID());
		}
		return msg;
	}

	public void upload() throws IOException, AVException {
		if (msg.getType() != Msg.Type.Audio && msg.getType() != Msg.Type.Image) {
			return;
		}
		String objectId = msg.getObjectId();
		if (objectId == null) {
			throw new NullPointerException("objectId mustn't be null");
		}
		String filePath = PathUtils.getChatFilePath(objectId);
		AVFile file = AVFile.withAbsoluteLocalPath(objectId, filePath);
		file.save();
		String url = file.getUrl();
		msg.setContent(url);
	}
} public enum RoomType {
	Single(0), Group(1);

	int value;

	RoomType(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}

	public static RoomType fromInt(int i) {
		return values()[i];
	}
} public class MsgReceiver extends AVMessageReceiver {
	public static StatusListener statusListener;
	public static Set<String> onlineIds = new HashSet<String>();
	public static Set<MsgListener> msgListeners = new HashSet<MsgListener>();
	private static boolean sessionPaused = true;

	@Override
	public void onSessionOpen(Context context, Session session) {
		Log.d("lan", "onSessionOpen");
	/*Intent intent = new Intent(context, MainActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    context.startActivity(intent);*/
		sessionPaused = false;
	}

	@Override
	public void onSessionPaused(Context context, Session session) {
		sessionPaused = true;
	}

	@Override
	public void onSessionResumed(Context context, Session session) {
		sessionPaused = false;
	}


	public static boolean isSessionPaused() {
		return sessionPaused;
	}

	public static void setSessionPaused(boolean sessionPaused) {
		MsgReceiver.sessionPaused = sessionPaused;
	}

	@Override
	public void onPeersWatched(Context context, Session session, List<String> peerIds) {
		if (peerIds.size() != 1) {
			throw new IllegalStateException("the size of watched peers isn't 1");
		}
		Log.d("lan", "watched " + peerIds);
	}

	@Override
	public void onPeersUnwatched(Context context, Session session, List<String> peerIds) {
		Log.d("lan", "unwatch " + peerIds);
	}

	@Override
	public void onMessage(final Context context, Session session, AVMessage avMsg) {
		Log.d("lan", "onMessage");
		AVOSUtils.logAVMessage(avMsg);
		ChatService.onMessage(context, avMsg, msgListeners, null);
	}

	@Override
	public void onMessageSent(Context context, Session session, AVMessage avMsg) {
		Log.d("lan", "onMessageSent");
		AVOSUtils.logAVMessage(avMsg);
		ChatService.onMessageSent(avMsg, msgListeners, null);
	}

	@Override
	public void onMessageDelivered(Context context, Session session, AVMessage msg) {
		Log.d("lan", "onMessageDelivered");
		AVOSUtils.logAVMessage(msg);
		ChatService.onMessageDelivered(msg, msgListeners);
	}

	@Override
	public void onMessageFailure(Context context, Session session, AVMessage avMsg) {
		Log.d("lan", "onMessageFailure");
		AVOSUtils.logAVMessage(avMsg);
		ChatService.onMessageFailure(avMsg, msgListeners, null);
	}

	@Override
	public void onStatusOnline(Context context, Session session, List<String> peerIds) {
		Log.d("lan", "onStatusOnline " + peerIds);
		onlineIds.addAll(peerIds);
		if (statusListener != null) {
			statusListener.onStatusOnline(new ArrayList<String>(onlineIds));
		}
	}

	@Override
	public void onStatusOffline(Context context, Session session, List<String> strings) {
		Log.d("lan", "onStatusOff " + strings);
		onlineIds.removeAll(strings);
		if (statusListener != null) {
			statusListener.onStatusOnline(new ArrayList<String>(onlineIds));
		}
	}

	@Override
	public void onError(Context context, Session session, Throwable throwable) {
		//Utils.toast(context, throwable.getMessage());
		throwable.printStackTrace();
		//ChatService.onMessageError(throwable, msgListeners);
	}

	@Override
	public void onSessionClose(Context context, Session session) {

	}

	public static void registerStatusListener(StatusListener listener) {
		statusListener = listener;
	}

	public static void unregisterSatutsListener() {
		statusListener = null;
	}

	public static void addMsgListener(MsgListener listener) {
		msgListeners.add(listener);
	}

	public static void removeMsgListener(MsgListener listener) {
		msgListeners.remove(listener);
	}

	public static List<String> getOnlineIds() {
		return new ArrayList<String>(onlineIds);
	}
} public class ChatService {
	private static final int REPLY_NOTIFY_ID = 1;

	public static <T extends AVUser> String getPeerId(T user) {
		return user.getObjectId();
	}

	public static String getSelfId() {
		return getPeerId(AVUser.getCurrentUser());
	}

	public static <T extends AVUser> void withUsersToWatch(List<T> users, boolean watch) {
		List<String> peerIds = new ArrayList<String>();
		for (AVUser user : users) {
			peerIds.add(getPeerId(user));
		}
		String selfId = getPeerId(AVUser.getCurrentUser());
		Session session = SessionManager.getInstance(selfId);
		if (watch) {
			session.watchPeers(peerIds);
		} else {
			session.unwatchPeers(peerIds);
		}
	}

	public static <T extends AVUser> void withUserToWatch(T user, boolean watch) {
		List<T> users = new ArrayList<T>();
		users.add(user);
		withUsersToWatch(users, watch);
	}

	public static Session getSession() {
		return SessionManager.getInstance(getPeerId(AVUser.getCurrentUser()));
	}

	public static void openSession(Activity activity) {
		Session session = getSession();
//		session.setSignatureFactory(new SignatureFactory());
		if (session.isOpen() == false) {
			session.open(new LinkedList<String>());
		}
	}

	public static List<Conversation> getConversationsAndCache() throws AVException {
		List<Msg> msgs = DBMsg.getRecentMsgs(AVUser.getCurrentUser().getObjectId());
		cacheUserOrChatGroup(msgs);
		ArrayList<Conversation> conversations = new ArrayList<Conversation>();
		for (Msg msg : msgs) {
			Conversation conversation = new Conversation();
			if (msg.getRoomType() == RoomType.Single) {
				String chatUserId = msg.getOtherId();
				conversation.toUser = DemoApplication.lookupUser(chatUserId);
			}
			conversation.msg = msg;
			conversations.add(conversation);
		}
		return conversations;
	}

	public static void cacheUserOrChatGroup(List<Msg> msgs) throws AVException {
		Set<String> uncachedIds = new HashSet<String>();
		Set<String> uncachedChatGroupIds = new HashSet<String>();
		for (Msg msg : msgs) {
			if (msg.getRoomType() == RoomType.Single) {
				String chatUserId = msg.getOtherId();
				if (DemoApplication.lookupUser(chatUserId) == null) {
					uncachedIds.add(chatUserId);
				}
			}
		}
		UserService.cacheUser(new ArrayList<String>(uncachedIds));
	}

	public static void closeSession() {
		Session session = ChatService.getSession();
		session.close();
	}

	public static Group getGroupById(String groupId) {
		return getSession().getGroup(groupId);
	}

	public static void notifyMsg(Context context, Msg msg, Group group) throws Exception {
		int icon = context.getApplicationInfo().icon;
		Intent intent;
		if (group == null) {
			intent = ChatActivity.getUserChatIntent(context, msg.getFromPeerId());
		} else {
			intent = ChatActivity.getGroupChatIntent(context, group.getGroupId());
		}
		//why Random().nextInt()
		//http://stackoverflow.com/questions/13838313/android-onnewintent-always-receives-same-intent
		PendingIntent pend = PendingIntent.getActivity(context, new Random().nextInt(),
				intent, 0);
		Notification.Builder builder = new Notification.Builder(context);
		CharSequence notifyContent = msg.getNotifyContent();
		CharSequence username = msg.getFromName();
		builder.setContentIntent(pend)
				.setSmallIcon(icon)
				.setWhen(System.currentTimeMillis())
				.setTicker(username + "\n" + notifyContent)
				.setContentTitle(username)
				.setContentText(notifyContent)
				.setAutoCancel(true);
		NotificationManager man = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
		Notification notification = builder.getNotification();
//    PreferenceMap preferenceMap = PreferenceMap.getCurUserPrefDao(context);
//    if (preferenceMap.isVoiceNotify()) {
//      notification.defaults |= Notification.DEFAULT_SOUND;
//    }
//    if (preferenceMap.isVibrateNotify()) {
//      notification.defaults |= Notification.DEFAULT_VIBRATE;
//    }
//    man.notify(REPLY_NOTIFY_ID, notification);
	}

	public static void onMessage(Context context, AVMessage avMsg, Set<MsgListener> listeners, Group group) {
		final Msg msg = Msg.fromAVMessage(avMsg);
		String convid;
		if (group == null) {
			String selfId = getSelfId();
			msg.setToPeerId(selfId);
			convid = AVOSUtils.convid(selfId, msg.getFromPeerId());
			msg.setRoomType(RoomType.Single);
		} else {
			convid = group.getGroupId();
			msg.setRoomType(RoomType.Group);
		}
		msg.setStatus(Msg.Status.SendReceived);
		msg.setConvid(convid);
		handleReceivedMsg(context, msg, listeners, group);
	}

	public static void handleReceivedMsg(final Context context, final Msg msg, final Set<MsgListener> listeners, final Group group) {
		new MyAsyncTask(context, false) {
			@Override
			protected void doInBack() throws Exception {
				if (msg.getType() == Msg.Type.Audio) {
					File file = new File(msg.getAudioPath());
					String url = msg.getContent();
					Utils.downloadFileIfNotExists(url, file);
				}
				if (group != null) {
//          GroupService.cacheChatGroupIfNone(group.getGroupId());
				} else {
					String fromId = msg.getFromPeerId();
					UserService.cacheUserIfNone(fromId);
				}
			}

			@Override
			protected void onSucceed() {
				DBMsg.insertMsg(msg);
				String otherId = getOtherId(msg.getFromPeerId(), group);
				boolean done = false;
				for (MsgListener listener : listeners) {
					if (listener.onMessageUpdate(otherId)) {
						done = true;
						break;
					}
				}
			}

		}.execute();
	}

	private static String getOtherId(String otherId, Group group) {
		assert otherId != null || group != null;
		if (group != null) {
			return group.getGroupId();
		} else {
			return otherId;
		}
	}

	public static void onMessageSent(AVMessage avMsg, Set<MsgListener> listeners, Group group) {
		Msg msg = Msg.fromAVMessage(avMsg);
		DBMsg.updateStatusAndTimestamp(msg.getObjectId(), Msg.Status.SendSucceed, msg.getTimestamp());
		String otherId = getOtherId(msg.getToPeerId(), group);
		for (MsgListener msgListener : listeners) {
			if (msgListener.onMessageUpdate(otherId)) {
				break;
			}
		}
	}

	public static void onMessageFailure(AVMessage avMsg, Set<MsgListener> msgListeners, Group group) {
		Msg msg = Msg.fromAVMessage(avMsg);
		DBMsg.updateStatus(msg.getObjectId(), Msg.Status.SendFailed);
		String otherId = getOtherId(msg.getToPeerId(), group);
		for (MsgListener msgListener : msgListeners) {
			if (msgListener.onMessageUpdate(otherId)) {
				break;
			}
		}
	}

	public static void onMessageError(Throwable throwable, Set<MsgListener> msgListeners) {
		String errorMsg = throwable.getMessage();
		Log.d("lan", "error " + errorMsg);
		if (errorMsg != null && errorMsg.startsWith("{")) {
			AVMessage avMsg = new AVMessage(errorMsg);
			//onMessageFailure(avMsg, msgListeners, group);
		}
	}


	public static Msg resendMsg(Msg msg) {
		String toId;
		if (msg.getRoomType() == RoomType.Group) {
			String groupId = msg.getConvid();
			toId = groupId;
		} else {
			toId = msg.getToPeerId();
			msg.setRequestReceipt(true);
		}
		MsgAgent msgAgent = new MsgAgent(msg.getRoomType(), toId);
		msgAgent.sendMsg(msg);
		DBMsg.updateStatus(msg.getObjectId(), Msg.Status.SendStart);
		return msg;
	}

	public static void cancelNotification(Context ctx) {
//    Utils.cancelNotification(ctx, REPLY_NOTIFY_ID);
	}

	public static void onMessageDelivered(AVMessage avMsg, Set<MsgListener> listeners) {
		Msg msg = Msg.fromAVMessage(avMsg);
		DBMsg.updateStatus(msg.getObjectId(), Msg.Status.SendReceived);
		String otherId = msg.getToPeerId();
		for (MsgListener listener : listeners) {
			if (listener.onMessageUpdate(otherId)) {
				break;
			}
		}
	}

	public static boolean isSessionPaused() {
		return MsgReceiver.isSessionPaused();
	}
} public class MsgAgent {
	RoomType roomType;
	String toId;

	public MsgAgent(RoomType roomType, String toId) {
		this.roomType = roomType;
		this.toId = toId;
	}

	public interface MsgBuilderHelper {
		void specifyType(MsgBuilder msgBuilder);
	}

	public Msg createAndSendMsg(MsgBuilderHelper msgBuilderHelper) throws IOException, AVException {
		MsgBuilder builder = new MsgBuilder();
		builder.target(roomType, toId);
		msgBuilderHelper.specifyType(builder);
		builder.upload();
		Msg msg = builder.build();
		sendMsg(msg);
		DBMsg.insertMsg(msg);
		return msg;
	}

	public Msg sendMsg(Msg msg) {
		AVMessage avMsg = msg.toAVMessage();
		Session session = ChatService.getSession();
		if (roomType == RoomType.Single) {
			session.sendMessage(avMsg);
		} else {
			Group group = session.getGroup(toId);
			group.sendMessage(avMsg);
		}
		return msg;
	}

} public class UserService {
    public static ImageLoader imageLoader = ImageLoader.getInstance();

    public static AVUser findUser(String id) throws AVException {
        AVQuery<AVUser> q = AVUser.getQuery();
        q.setCachePolicy(AVQuery.CachePolicy.NETWORK_ELSE_CACHE);
        return q.get(id);
    }

    public static List<AVUser> findFriends(boolean useCache) throws Exception {
        final List<AVUser> friends = new ArrayList<AVUser>();
        final AVException[] es = new AVException[1];
        final CountDownLatch latch = new CountDownLatch(1);
        findFriendsWithCachePolicy(AVQuery.CachePolicy.CACHE_ELSE_NETWORK, new FindCallback<AVUser>() {
            @Override
            public void done(List<AVUser> avUsers, AVException e) {
                if (e != null) {
                    es[0] = e;
                } else {
                    friends.addAll(avUsers);
                }
                latch.countDown();
            }
        });
        latch.await();
        if (es[0] != null) {
            throw es[0];
        } else {
            List<String> userIds = new ArrayList<String>();
            for (AVUser user : friends) {
                userIds.add(user.getObjectId());
            }
            CacheService.cacheUsers(userIds);
            List<AVUser> newFriends = new ArrayList<>();
            for (AVUser user : friends) {
                newFriends.add(CacheService.lookupUser(user.getObjectId()));
            }
            DemoApplication.registerBatchUserCache(newFriends);
            return newFriends;
        }
    }

    public static List<AVUser> findFriends() throws Exception {
        return findFriends(false);
    }

    public static void findFriendsWithCachePolicy(AVQuery.CachePolicy cachePolicy, FindCallback<AVUser>
            findCallback) {
        AVQuery<AVUser> q = getFriendQuery();
        q.setCachePolicy(cachePolicy);
        q.setMaxCacheAge(TimeUnit.MINUTES.toMillis(1));
        q.findInBackground(findCallback);
    }

    public static AVQuery<AVUser> getFriendQuery() {
        AVUser curUser = AVUser.getCurrentUser();
        AVQuery<AVUser> q = null;
        try {
            q = curUser.followeeQuery(AVUser.class);
        } catch (Exception e) {
            //在 currentUser.objectId 为 null 的时候抛出的，不做处理
            Logger.e(e.getMessage());
        }
        return q;
    }

    public static void displayAvatar(String imageUrl, ImageView avatarView) {
        imageLoader.displayImage(imageUrl, avatarView, PhotoUtil.avatarImageOptions);
    }

    public static void cacheUser(List<String> uncachedIds) throws AVException {
        if (uncachedIds.size() == 0) {
            return;
        }
        findUsers(uncachedIds);
    }

    public static List<AVUser> findUsers(List<String> userIds) throws AVException {
        if (userIds.size() <= 0) {
            return new ArrayList<AVUser>();
        }
        AVQuery<AVUser> q = AVUser.getQuery();
        q.whereContainedIn("objectId", userIds);
        q.setCachePolicy(AVQuery.CachePolicy.NETWORK_ELSE_CACHE);
        List<AVUser> users = q.find();
        DemoApplication.registerBatchUserCache(users);
        return users;
    }

    public static void searchUser(String searchName, int skip, FindCallback<AVUser> findCallback) {
//    AVQuery<AVUser> q = AVUser.getQuery();
//    q.whereContains("username", searchName);
//    q.limit(10);
//    q.skip(skip);
//    User user = User.curUser();
//    List<String> friendIds = getFriendIds();
//    friendIds.add(user.getObjectId());
//    q.whereNotContainedIn(C.OBJECT_ID, friendIds);
//    q.orderByDescending(C.UPDATED_AT);
//    q.setCachePolicy(AVQuery.CachePolicy.NETWORK_ELSE_CACHE);
//    q.findInBackground(findCallback);
    }

    //  private static List<String> getFriendIds() {
////    List<User> friends = App.getInstance().getFriends();
////    List<String> ids = new ArrayList<String>();
////    for (User friend : friends) {
////      ids.add(friend.getObjectId());
////    }
////    return ids;
//  }
//
    public static List<AVObject> findMeetPeople() throws AVException {
        List<AVObject> allMeetInfo = new ArrayList<>();
        AVUser user = AVUser.getCurrentUser();
        AVRelation<AVObject> relation = user.getRelation("MeetingInfo");
        AVQuery<AVObject> query = relation.getQuery();
        query.whereExists("YourUser");
        query.include("YourUser");
        query.setCachePolicy(AVQuery.CachePolicy.NETWORK_ELSE_CACHE);
        query.setLimit(1000);
        int count = query.count();

        for (int i = 0;i < count / 1000f + 1;i++) {
            query.skip(1000 * i);
            allMeetInfo.addAll(query.find());
        }

        return allMeetInfo;

    }

    //
    public static void saveSex(int gender, SaveCallback saveCallback) {
        AVUser user = AVUser.getCurrentUser();
        user.put("gender", gender);
        user.saveInBackground(saveCallback);
    }

    //
//  public static List<String> transformIds(List<? extends AVObject> objects) {
//    List<String> ids = new ArrayList<String>();
//    for (AVObject o : objects) {
//      ids.add(o.getObjectId());
//    }
//    return ids;
//  }
//
//  public static User signUp(String name, String password) throws AVException {
//    User user = new User();
//    user.setUsername(name);
//    user.setPassword(password);
//    user.signUp();
//    return user;
//  }
//
    public static void saveAvatar(String path) throws IOException, AVException {
        AVUser user = AVUser.getCurrentUser();
        if (user.getAVGeoPoint("location") == null) {
            PreferenceMap preferenceMap = new PreferenceMap(DemoApplication.context, user.getObjectId());
            user.put("location", preferenceMap.getLocation());
        }
        final AVFile file = AVFile.withAbsoluteLocalPath(user.getUsername(), path);
        file.save();
        user.put("avatar", file);
        ;

        user.save();
        user.fetch();
    }

    public static void addFriend(String friendId, final SaveCallback saveCallback) {
        AVUser user = AVUser.getCurrentUser();
        user.followInBackground(friendId, new FollowCallback() {
            @Override
            public void done(AVObject object, AVException e) {
                saveCallback.done(e);
            }
        });
    }

    public static void removeFriend(String friendId, final SaveCallback saveCallback) {
        AVUser user = AVUser.getCurrentUser();
        user.unfollowInBackground(friendId, new FollowCallback() {
            @Override
            public void done(AVObject object, AVException e) {
                saveCallback.done(e);
            }
        });
    }

    //
    public static void cacheUserIfNone(String userId) throws AVException {
        if (DemoApplication.lookupUser(userId) == null) {
            DemoApplication.registerUserCache(findUser(userId));
        }
    }
} public class AVOSUtils {
	public static String convid(String myId, String otherId) {
		List<String> ids;
		ids = new ArrayList<String>();
		ids.add(myId);
		ids.add(otherId);
		return convid(ids);
	}

	public static String convid(List<String> peerIds) {
		Collections.sort(peerIds);
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < peerIds.size(); i++) {
			if (i != 0) {
				sb.append(":");
			}
			sb.append(peerIds.get(i));
		}
		return Utils.md5(sb.toString());
	}

	public static void logAVMessage(AVMessage avMsg) {
		Log.d("lam", "avMsg message=" + avMsg.getMessage() + " timestamp=" + avMsg.getTimestamp() + " toPeerIds=" + avMsg.getToPeerIds
				() + " fromPeerId=" + avMsg.getFromPeerId() + " receiptTs=" + avMsg.getReceiptTimestamp() + " groupId=" + avMsg.getGroupId
				() + " isRequestReceipt=" + avMsg.isRequestReceipt());
	}
} public class ChatUtils {
	public static void handleListResult(XListView listView, BaseListAdapter adapter, List datas) {
		if (Utils.isListNotEmpty(datas)) {
			adapter.addAll(datas);
			if (datas.size() == 10) {
				listView.setPullLoadEnable(true);
			} else {
				listView.setPullLoadEnable(false);
			}
		} else {
			listView.setPullLoadEnable(false);
			if (adapter.getCount() == 0) {
				Utils.toast("无结果");
			} else {
				Utils.toast("加载完毕");
			}
		}
	}

	public static void updateUserInfo() {
		AVUser user = AVUser.getCurrentUser();
		if (user != null) {
			user.fetchInBackground("friends", new GetCallback<AVObject>() {
				@Override
				public void done(AVObject avObject, AVException e) {
					if (e == null) {
						AVUser avUser = (AVUser) avObject;
						DemoApplication.registerUserCache(avUser);
					}
				}
			});
		}
	}

	public static void updateUserLocation() {
		PreferenceMap preferenceMap = PreferenceMap.getCurUserPrefDao(DemoApplication.context);
		AVGeoPoint lastLocation = preferenceMap.getLocation();
		if (lastLocation != null) {
			final AVUser user = AVUser.getCurrentUser();
			final AVGeoPoint location = user.getAVGeoPoint("location");
			if (location == null || !Utils.doubleEqual(location.getLatitude(), lastLocation.getLatitude())
					|| !Utils.doubleEqual(location.getLongitude(), lastLocation.getLongitude())) {
				user.put("location", lastLocation);
				user.saveInBackground(new SaveCallback() {
					@Override
					public void done(AVException e) {
						if (e != null) {
							e.printStackTrace();
						} else {
							Log.v("lan", "lastLocation save " + user.getAVGeoPoint("location"));
						}
					}
				});
			}
		}
	}

	public static void stopRefresh(XListView xListView) {
		if (xListView.getPullRefreshing()) {
			xListView.stopRefresh();
		}
	}

	public static void setUserView(View conView, AVUser user) {
//    ImageView avatarView = ViewHolder.findViewById(conView, R.id.avatar);
//    TextView nameView = ViewHolder.findViewById(conView, R.id.username);
////    UserService.displayAvatar(user.getAvatarUrl(), avatarView);
//    nameView.setText(user.getUsername());
	}
} public class Connectivity {
	public static NetworkInfo getNetworkInfo(Context context) {
		ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
		return cm.getActiveNetworkInfo();
	}

	public static boolean isConnected(Context context) {
		NetworkInfo info = getNetworkInfo(context);
		return isConnected(info);
	}

	public static boolean isConnected(NetworkInfo info) {
		return info != null && info.isConnected();
	}

	public static boolean isConnectedWifi(Context context) {
		NetworkInfo info = getNetworkInfo(context);
		return isConnected(info) && info.getType() == ConnectivityManager.TYPE_WIFI;
	}

	public static boolean isConnectedMobile(Context context) {
		NetworkInfo info = getNetworkInfo(context);
		return isConnected(info) && info.getType() == ConnectivityManager.TYPE_MOBILE;
	}
} public class EmotionUtils {
	public static List<String> emotionTexts;
	public static List<String> emotionTexts1;
	public static List<String> emotionTexts2;

	private static List<String> emotions;
	public static int[] emotionCodes = new int[]{0x1F601, 0x1F602, 0x1F603, 0x1F604, 0x1F605, 0x1F606, 0x1F609, 0x1F60A, 0x1F60B, 0x1F60C,
			0x1F60D, 0x1F60F, 0x1F612, 0x1F613, 0x1F614, 0x1F616, 0x1F618, 0x1F61A, 0x1F61C, 0x1F61D, 0x1F61E, 0x1F620, 0x1F621, 0x1F622, 0x1F623, 0x1F624,
			0x1F625, 0x1F628, 0x1F629, 0x1F62A, 0x1F62B, 0x1F62D, 0x1F630, 0x1F631, 0x1F632, 0x1F633, 0x1F635, 0x1F637};
	public static List<String> emotions1, emotions2;
	public static String[] emojiCodes = new String[]{"\\u1f60a", "\\u1f60c",
			"\\u1f60d", "\\u1f60f", "\\u1f61a", "\\u1f61b", "\\u1f61c", "\\u1f61e", "\\u1f62a", "\\u1f601", "\\u1f602", "\\u1f603",
			"\\u1f604", "\\u1f609", "\\u1f612", "\\u1f613", "\\u1f614", "\\u1f616", "\\u1f618", "\\u1f620", "\\u1f621", "\\u1f622",
			"\\u1f621", "\\u1f622", "\\u1f623", "\\u1f625", "\\u1f628", "\\u1f630", "\\u1f631", "\\u1f632", "\\u1f633", "\\u1f637",
			"\\u1f44d", "\\u1f44e", "\\u1f44f"};

	static String getEmojiByUnicode(int unicode) {
		return new String(Character.toChars(unicode));
	}

	private static Pattern pattern;

	static {
		emotions = new ArrayList<String>();
		int i;
		for (i = 0; i < emotionCodes.length; i++) {
			emotions.add(getEmojiByUnicode(emotionCodes[i]));
		}
		emotions1 = emotions.subList(0, 21);
		emotions2 = emotions.subList(21, emotions.size());

		emotionTexts = new ArrayList<String>();
		for (String emojiCode : emojiCodes) {
			emotionTexts.add(emojiCode);
		}
		emotionTexts1 = emotionTexts.subList(0, 21);
		emotionTexts2 = emotionTexts.subList(21, emotionTexts.size());
		pattern = buildPattern();
	}

	private static Pattern buildPattern() {
		return Pattern.compile("\\\\u1f[a-z0-9]{3}");
	}

	public static boolean haveEmotion(String text) {
		Matcher matcher = pattern.matcher(text);
		if (matcher.find()) {
			return true;
		} else {
			return false;
		}
	}

	public static CharSequence scaleEmotions(String text) {
		SpannableString spannableString = new SpannableString(text);
		for (String emotion : emotions) {
			Pattern pattern = Pattern.compile(emotion);
			Matcher matcher = pattern.matcher(text);
			while (matcher.find()) {
				int start = matcher.start();
				int end = matcher.end();
				spannableString.setSpan(new RelativeSizeSpan(1.2f), start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
			}
		}
		return spannableString;
	}

	public static CharSequence replace(Context ctx, String text) {
		SpannableString spannableString = new SpannableString(text);
		Matcher matcher = pattern.matcher(text);
		while (matcher.find()) {
			String factText = matcher.group();
			String key = factText.substring(1);
			if (emotionTexts.contains(factText)) {
				Bitmap bitmap = getDrawableByName(ctx, key);
				ImageSpan image = new ImageSpan(ctx, bitmap);
				int start = matcher.start();
				int end = matcher.end();
				spannableString.setSpan(image, start, end,
						Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
			}
		}
		return spannableString;
	}

	public static Bitmap getDrawableByName(Context ctx, String name) {
		BitmapFactory.Options options = new BitmapFactory.Options();
		Bitmap bitmap = BitmapFactory.decodeResource(ctx.getResources(),
				ctx.getResources().getIdentifier(name, "drawable",
						ctx.getPackageName()), options);
		return bitmap;
	}
} public abstract class MyAsyncTask extends AsyncTask<Void, Void, Void> {
	ProgressDialog dialog;
	protected Context context;
	boolean openDialog = true;
	Exception exception;

	protected MyAsyncTask(Context context) {
		this.context = context;
	}

	protected MyAsyncTask(Context context, boolean openDialog) {
		this.context = context;
		this.openDialog = openDialog;
	}

	public MyAsyncTask setOpenDialog(boolean openDialog) {
		this.openDialog = openDialog;
		return this;
	}

	public ProgressDialog getDialog() {
		return dialog;
	}

	@Override
	protected void onPreExecute() {
		super.onPreExecute();
		if (openDialog) {
			dialog = Utils.showSpinnerDialog((Activity) context);
		}
	}

	@Override
	protected Void doInBackground(Void... params) {
		try {
			doInBack();
		} catch (Exception e) {
			e.printStackTrace();
			exception = e;
		}
		return null;
	}

	@Override
	protected void onPostExecute(Void aVoid) {
		super.onPostExecute(aVoid);
		if (openDialog) {
			if (dialog.isShowing()) {
				dialog.dismiss();
			}
		}
		onPost(exception);
	}



	protected void onPost(Exception e) {
		if (e != null) {
			e.printStackTrace();
			Utils.toast(e.getMessage());
			//Utils.toast(ctx, R.string.pleaseCheckNetwork);
		} else {
			onSucceed();
		}
	}

	protected abstract void doInBack() throws Exception;
	protected abstract void onSucceed();
} public class PathUtils {
	public static String getSDcardDir() {
		return Environment.getExternalStorageDirectory().getPath() + "/";
	}

	public static String checkAndMkdirs(String dir) {
		File file = new File(dir);
		if (file.exists() == false) {
			file.mkdirs();
		}
		return dir;
	}

	public static String getAppPath() {
		String dir = getSDcardDir() + "leanchat/";
		return checkAndMkdirs(dir);
	}

	public static String getAvatarDir() {
		String dir = getAppPath() + "avatar/";
		return checkAndMkdirs(dir);
	}

	public static String getAvatarTmpPath() {
		return getAvatarDir() + "tmp";
	}

	public static String getChatFileDir() {
		String dir = getAppPath() + "files/";
		return checkAndMkdirs(dir);
	}

	public static String getChatFilePath(String id) {
		String dir = getChatFileDir();
		String path = dir + id;
		return path;
	}

	public static String getRecordTmpPath() {
		return getChatFileDir() + "tmp";
	}

	public static String getUUIDFilePath() {
		return getChatFilePath(Utils.myUUID());
	}

	public static String getTmpPath() {
		return getAppPath() + "tmp";
	}
} public class PhotoUtil {

	/**
	 * 回收垃圾 recycle
	 *
	 * @throws
	 */
	public static void recycle(Bitmap bitmap) {
		// 先判断是否已经回收
		if (bitmap != null && !bitmap.isRecycled()) {
			// 回收并且置为null
			bitmap.recycle();
			bitmap = null;
		}
		System.gc();
	}

	/**
	 * 获取指定路径下的图片的指定大小的缩略图 getImageThumbnail
	 *
	 * @return Bitmap
	 * @throws
	 */
	public static Bitmap getImageThumbnail(String imagePath, int width,
										   int height) {
		Bitmap bitmap = null;
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		// 获取这个图片的宽和高，注意此处的bitmap为null
		bitmap = BitmapFactory.decodeFile(imagePath, options);
		options.inJustDecodeBounds = false; // 设为 false
		// 计算缩放比
		int h = options.outHeight;
		int w = options.outWidth;
		int beWidth = w / width;
		int beHeight = h / height;
		int be = 1;
		if (beWidth < beHeight) {
			be = beWidth;
		} else {
			be = beHeight;
		}
		if (be <= 0) {
			be = 1;
		}
		options.inSampleSize = be;
		// 重新读入图片，读取缩放后的bitmap，注意这次要把options.inJustDecodeBounds 设为 false
		bitmap = BitmapFactory.decodeFile(imagePath, options);
		// 利用ThumbnailUtils来创建缩略图，这里要指定要缩放哪个Bitmap对象
		bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
				ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
		return bitmap;
	}

	public static void saveBitmap(String dirpath, String filename,
								  Bitmap bitmap, boolean isDelete) {
		File dir = new File(dirpath);
		if (!dir.exists()) {
			dir.mkdirs();
		}

		File file = new File(dirpath, filename);
		if (isDelete) {
			if (file.exists()) {
				file.delete();
			}
		}

		if (!file.exists()) {
			try {
				file.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(file);
			if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)) {
				out.flush();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static File getFilePath(String filePath, String fileName) {
		File file = null;
		makeRootDirectory(filePath);
		try {
			file = new File(filePath + fileName);
			if (!file.exists()) {
				file.createNewFile();
			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return file;
	}

	public static void makeRootDirectory(String filePath) {
		File file = null;
		try {
			file = new File(filePath);
			if (!file.exists()) {
				file.mkdirs();
			}
		} catch (Exception e) {

		}
	}

	/**
	 * 读取图片属性：旋转的角度
	 *
	 * @param path 图片绝对路径
	 * @return degree旋转的角度
	 */

	public static int readPictureDegree(String path) {
		int degree = 0;
		try {
			ExifInterface exifInterface = new ExifInterface(path);
			int orientation = exifInterface.getAttributeInt(
					ExifInterface.TAG_ORIENTATION,
					ExifInterface.ORIENTATION_NORMAL);
			switch (orientation) {
				case ExifInterface.ORIENTATION_ROTATE_90:
					degree = 90;
					break;
				case ExifInterface.ORIENTATION_ROTATE_180:
					degree = 180;
					break;
				case ExifInterface.ORIENTATION_ROTATE_270:
					degree = 270;
					break;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return degree;

	}

	/**
	 * 旋转图片一定角度
	 * rotaingImageView
	 *
	 * @return Bitmap
	 * @throws
	 */
	public static Bitmap rotaingImageView(int angle, Bitmap bitmap) {
		// 旋转图片 动作
		Matrix matrix = new Matrix();
		matrix.postRotate(angle);
		// 创建新的图片
		Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
				bitmap.getWidth(), bitmap.getHeight(), matrix, true);
		return resizedBitmap;
	}

	/**
	 * 将图片变为圆角
	 *
	 * @param bitmap 原Bitmap图片
	 * @param pixels 图片圆角的弧度(单位:像素(px))
	 * @return 带有圆角的图片(Bitmap 类型)
	 */
	public static Bitmap toRoundCorner(Bitmap bitmap, int pixels) {
		Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
				bitmap.getHeight(), Config.ARGB_8888);
		Canvas canvas = new Canvas(output);

		final int color = 0xff424242;
		final Paint paint = new Paint();
		final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
		final RectF rectF = new RectF(rect);
		final float roundPx = pixels;

		paint.setAntiAlias(true);
		canvas.drawARGB(0, 0, 0, 0);
		paint.setColor(color);
		canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

		paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
		canvas.drawBitmap(bitmap, rect, rect, paint);

		return output;
	}

	/**
	 * 将图片转化为圆形头像
	 *
	 * @throws
	 * @Title: toRoundBitmap
	 */
	public static Bitmap toRoundBitmap(Bitmap bitmap) {
		int width = bitmap.getWidth();
		int height = bitmap.getHeight();
		float roundPx;
		float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
		if (width <= height) {
			roundPx = width / 2;

			left = 0;
			top = 0;
			right = width;
			bottom = width;

			height = width;

			dst_left = 0;
			dst_top = 0;
			dst_right = width;
			dst_bottom = width;
		} else {
			roundPx = height / 2;

			float clip = (width - height) / 2;

			left = clip;
			right = width - clip;
			top = 0;
			bottom = height;
			width = height;

			dst_left = 0;
			dst_top = 0;
			dst_right = height;
			dst_bottom = height;
		}

		Bitmap output = Bitmap.createBitmap(width, height, Config.ARGB_8888);
		Canvas canvas = new Canvas(output);

		final Paint paint = new Paint();
		final Rect src = new Rect((int) left, (int) top, (int) right,
				(int) bottom);
		final Rect dst = new Rect((int) dst_left, (int) dst_top,
				(int) dst_right, (int) dst_bottom);
		final RectF rectF = new RectF(dst);

		paint.setAntiAlias(true);// 设置画笔无锯齿

		canvas.drawARGB(0, 0, 0, 0); // 填充整个Canvas

		// 以下有两种方法画圆,drawRounRect和drawCircle
		canvas.drawRoundRect(rectF, roundPx, roundPx, paint);// 画圆角矩形，第一个参数为图形显示区域，第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
		// canvas.drawCircle(roundPx, roundPx, roundPx, paint);

		paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));// 设置两张图片相交时的模式,参考http://trylovecatch.iteye.com/blog/1189452
		canvas.drawBitmap(bitmap, src, dst, paint); // 以Mode.SRC_IN模式合并bitmap和已经draw了的Circle

		return output;
	}

	public static String simpleCompressImage(String path, String newPath) {
		Bitmap bitmap = BitmapFactory.decodeFile(path);
		FileOutputStream outputStream = null;
		try {
			outputStream = new FileOutputStream(newPath);
			bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		recycle(bitmap);
		return newPath;
	}

	public static String compressImage(String path, String newPath) {
		BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(path, options);
		int inSampleSize = 1;
		int maxSize = 3000;
		Log.d("lan", "outWidth=" + options.outWidth + " outHeight=" + options.outHeight);
		if (options.outWidth > maxSize || options.outHeight > maxSize) {
			int widthScale = (int) Math.ceil(options.outWidth * 1.0 / maxSize);
			int heightScale = (int) Math.ceil(options.outHeight * 1.0 / maxSize);
			inSampleSize = Math.max(widthScale, heightScale);
		}
		Log.d("lan", "inSampleSize=" + inSampleSize);
		options.inJustDecodeBounds = false;
		options.inSampleSize = inSampleSize;
		Bitmap bitmap = BitmapFactory.decodeFile(path, options);
		int w = bitmap.getWidth();
		int h = bitmap.getHeight();
		int newW = w;
		int newH = h;
		if (w > maxSize || h > maxSize) {
			if (w > h) {
				newW = maxSize;
				newH = (int) (newW * h * 1.0 / w);
			} else {
				newH = maxSize;
				newW = (int) (newH * w * 1.0 / h);
			}
		}
		Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, newW, newH, false);
		//recycle(bitmap);
		Log.d("lan", "bitmap width=" + newBitmap.getWidth() + " h=" + newBitmap.getHeight());

		FileOutputStream outputStream = null;
		try {
			outputStream = new FileOutputStream(newPath);
			newBitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				if (outputStream != null) {
					outputStream.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
		recycle(newBitmap);
		recycle(bitmap);
		return newPath;
	}

	public static DisplayImageOptions avatarImageOptions = new DisplayImageOptions.Builder()
			.showImageOnLoading(R.drawable.default_user_avatar)
			.showImageForEmptyUri(R.drawable.default_user_avatar)
			.showImageOnFail(R.drawable.default_user_avatar)
			.cacheInMemory(true)
			.cacheOnDisc(true)
			.considerExifParams(true)
			.imageScaleType(ImageScaleType.EXACTLY)
			.bitmapConfig(Config.RGB_565)
			.resetViewBeforeLoading(true)// 设置图片在下载前是否重置，复位
					//.displayer(new RoundedBitmapDisplayer(20))
					//.displayer(new FadeInBitmapDisplayer(100))// 淡入
			.build();

	public static DisplayImageOptions normalImageOptions = new DisplayImageOptions.Builder()
			.showImageOnLoading(R.drawable.empty_photo)
			.showImageForEmptyUri(R.drawable.empty_photo)
			.showImageOnFail(R.drawable.image_load_fail)
			.cacheInMemory(true)
			.cacheOnDisc(true)
			.considerExifParams(true)
			.imageScaleType(ImageScaleType.EXACTLY)
			.bitmapConfig(Config.RGB_565)
			.resetViewBeforeLoading(true)// 设置图片在下载前是否重置，复位
					//.displayer(new RoundedBitmapDisplayer(20))
					//.displayer(new FadeInBitmapDisplayer(100))// 淡入
			.build();

	public static ImageLoaderConfiguration getImageLoaderConfig(Context context, File cacheDir) {
		return new ImageLoaderConfiguration.Builder(
				context)
				.threadPoolSize(3).threadPriority(Thread.NORM_PRIORITY - 2)
						//.memoryCache(new WeakMemoryCache())
				.denyCacheImageMultipleSizesInMemory()
				.discCacheFileNameGenerator(new Md5FileNameGenerator())
						// 将保存的时候的URI名称用MD5 加密
				.tasksProcessingOrder(QueueProcessingType.LIFO)
				.discCache(new UnlimitedDiscCache(cacheDir))// 自定义缓存路径
						// .defaultDisplayImageOptions(DisplayImageOptions.createSimple())
						//.writeDebugLogs() // Remove for release app
				.build();
	}
} public class SimpleTextWatcher implements TextWatcher {
	@Override
	public void beforeTextChanged(CharSequence s, int start, int count, int after) {

	}

	@Override
	public void onTextChanged(CharSequence s, int start, int before, int count) {

	}

	@Override
	public void afterTextChanged(Editable s) {

	}
} public class Utils {
	public static ProgressDialog showSpinnerDialog(Activity activity) {
		// activity = modifyDialogContext(activity);

		ProgressDialog dialog = new ProgressDialog(activity);
		dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
		dialog.setCancelable(true);
		dialog.setMessage("加载中...");
		if (activity.isFinishing() == false) {
			dialog.show();
		}
		return dialog;
	}

	public static void goActivity(Context context, Class<?> clz) {
		Intent intent = new Intent(context, clz);
		context.startActivity(intent);
	}

	public static void toast(String str) {
		Toast.makeText(DemoApplication.context, str, Toast.LENGTH_SHORT).show();
	}

	public static void toast(int res) {
		Toast.makeText(DemoApplication.context, DemoApplication.context.getResources().getString(res), Toast.LENGTH_SHORT).show();
	}

	public static void updateUserInfo() {
		AVUser user = AVUser.getCurrentUser();
		if (user != null) {
			user.fetchInBackground("friends", new GetCallback<AVObject>() {
				@Override
				public void done(AVObject avObject, AVException e) {
					if (e == null) {
						// User avUser = (User) avObject;
						// App.registerUserCache(avUser);
					}
				}
			});
		}
	}

	public static void updateUserLocation() {
		PreferenceMap preferenceMap = PreferenceMap
				.getCurUserPrefDao(DemoApplication.context);
		AVGeoPoint lastLocation = preferenceMap.getLocation();
		String lastaddress = preferenceMap.getAddress();
		if (lastLocation != null) {
			final AVUser user = AVUser.getCurrentUser();
			final AVGeoPoint location = user.getAVGeoPoint("location");
			final String Address = user.getString("address");
			if (location == null
					|| !Utils.doubleEqual(location.getLatitude(),
					lastLocation.getLatitude())
					|| !Utils.doubleEqual(location.getLongitude(),
					lastLocation.getLongitude())) {
				user.put("location", lastLocation);
				user.put("address", lastaddress);
				user.saveInBackground(new SaveCallback() {
					@Override
					public void done(AVException e) {
						if (e != null) {
							e.printStackTrace();
						} else {
							log.v("lastLocation save "
									+ user.getAVGeoPoint("location"));
						}
					}
				});
			}
		}
	}

	public static boolean doubleEqual(double a, double b) {
		return Math.abs(a - b) < 1E-8;
	}

	public static void handleListResult(XListView listView,
										BaseListAdapter adapter, List datas) {
		if (Utils.isListNotEmpty(datas)) {
			adapter.addAll(datas);
			if (datas.size() == 10) {
				listView.setPullLoadEnable(true);
			} else {
				listView.setPullLoadEnable(false);
			}
		} else {
			listView.setPullLoadEnable(false);
			if (adapter.getCount() == 0) {
				Utils.toast("无结果");
			} else {
				Utils.toast("加载完毕");
			}
		}
	}

	public static boolean isListNotEmpty(Collection<?> collection) {
		if (collection != null && collection.size() > 0) {
			return true;
		}
		return false;
	}

	public static String md5(String string) {
		byte[] hash = null;
		try {
			hash = string.getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("Huh,UTF-8 should be supported?", e);
		}
		return computeMD5(hash);
	}

	public static String computeMD5(byte[] input) {
		try {
			MessageDigest md = MessageDigest.getInstance("MD5");
			md.update(input, 0, input.length);
			byte[] md5bytes = md.digest();

			StringBuffer hexString = new StringBuffer();
			for (int i = 0; i < md5bytes.length; i++) {
				String hex = Integer.toHexString(0xff & md5bytes[i]);
				if (hex.length() == 1)
					hexString.append('0');
				hexString.append(hex);
			}
			return hexString.toString();
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);
		}
	}

	public static String myUUID() {
		StringBuilder sb = new StringBuilder();
		int start = 48, end = 58;
		appendChar(sb, start, end);
		appendChar(sb, 65, 90);
		appendChar(sb, 97, 123);
		String charSet = sb.toString();
		StringBuilder sb1 = new StringBuilder();
		for (int i = 0; i < 24; i++) {
			int len = charSet.length();
			int pos = new Random().nextInt(len);
			sb1.append(charSet.charAt(pos));
		}
		return sb1.toString();
	}

	public static void appendChar(StringBuilder sb, int start, int end) {
		int i;
		for (i = start; i < end; i++) {
			sb.append((char) i);
		}
	}

	public static void downloadFileIfNotExists(String url, File toFile)
			throws IOException {
		if (toFile.exists()) {
		} else {
			downloadFile(url, toFile);
		}
	}

	public static void downloadFile(String url, File toFile) throws IOException {
		toFile.createNewFile();
		FileOutputStream outputStream = new FileOutputStream(toFile);
		InputStream inputStream = Utils.inputStreamFromUrl(url);
		Utils.inputToOutput(outputStream, inputStream);
	}

	public static InputStream inputStreamFromUrl(String url)
			throws IOException, ClientProtocolException {
		DefaultHttpClient client = new DefaultHttpClient();
		HttpGet get = new HttpGet(url);
		HttpResponse response = client.execute(get);
		HttpEntity entity = response.getEntity();
		InputStream stream = entity.getContent();
		return stream;
	}

	public static Bitmap bitmapFromFile(File file) throws FileNotFoundException {
		return BitmapFactory.decodeStream(new BufferedInputStream(
				new FileInputStream(file)));
	}

	public static void inputToOutput(FileOutputStream outputStream,
									 InputStream inputStream) throws IOException {
		byte[] buffer = new byte[1024];
		int len;
		while ((len = inputStream.read(buffer)) != -1) {
			outputStream.write(buffer, 0, len);
		}
		outputStream.close();
		inputStream.close();
	}

	public static void hideSoftInputView(Activity activity) {
		if (activity.getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) {
			InputMethodManager manager = (InputMethodManager) activity
					.getSystemService(Context.INPUT_METHOD_SERVICE);
			View currentFocus = activity.getCurrentFocus();
			if (currentFocus != null) {
				manager.hideSoftInputFromWindow(currentFocus.getWindowToken(),
						InputMethodManager.HIDE_NOT_ALWAYS);
			}
		}
	}

	public static int getColor(int resId) {
		return DemoApplication.context.getResources().getColor(resId);
	}
} public class EmotionEditText extends EditText {

	public EmotionEditText(Context context) {
		super(context);
	}

	public EmotionEditText(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public EmotionEditText(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	@Override
	public void setText(CharSequence text, BufferType type) {
		if (!TextUtils.isEmpty(text)) {
			super.setText(EmotionUtils.replace(getContext(), text.toString()), type);
		} else {
			super.setText(text, type);
		}
	}
} public class XListView extends ListView implements OnScrollListener {
	private float mLastY = -1; // save event y
	private Scroller mScroller; // used for scroll back
	private OnScrollListener mScrollListener; // user's scroll listener

	// the interface to trigger loadMsgsFromDB and load more.
	private IXListViewListener mListViewListener;

	// -- header view
	private XListViewHeader mHeaderView;
	// header view content, use it to calculate the Header's height. And hide it
	// when disable pull loadMsgsFromDB.
	private RelativeLayout mHeaderViewContent;
	private int mHeaderViewHeight; // header view's height
	private boolean mEnablePullRefresh = true;
	private boolean mPullRefreshing = false; // is refreashing.

	// -- footer view
	private XListViewFooter mFooterView = null;
	private boolean mEnablePullLoad = false;
	private boolean mPullLoading = false;

	// total list items, used to detect is at the bottom of listview.
	private int mTotalItemCount;

	// for mScroller, scroll back from header or footer.
	private int mScrollBack;
	private final static int SCROLLBACK_HEADER = 0;
	private final static int SCROLLBACK_FOOTER = 1;

	private final static int SCROLL_DURATION = 400; // scroll back duration
	private final static int PULL_LOAD_MORE_DELTA = 50; // when pull up >= 50px
	// at bottom, trigger
	// load more.
	private final static float OFFSET_RADIO = 1.8f; // support iOS like pull
	// feature.
	private Context m_context;

	/**
	 * @param context
	 */
	public XListView(Context context) {
		super(context);
		initWithContext(context);
	}

	public XListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initWithContext(context);
	}

	public XListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initWithContext(context);
	}

	private void initWithContext(Context context) {
		mScroller = new Scroller(context, new DecelerateInterpolator());
		// XListView need the scroll event, and it will dispatch the event to
		// user's listener (as a proxy).
		super.setOnScrollListener(this);

		m_context = context;
		// init header view
		mHeaderView = new XListViewHeader(context);
		mHeaderViewContent = (RelativeLayout) mHeaderView
				.findViewById(R.id.xlistview_header_content);
		addHeaderView(mHeaderView);

		// init header height
		mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
				new OnGlobalLayoutListener() {
					@Override
					public void onGlobalLayout() {
						mHeaderViewHeight = mHeaderViewContent.getHeight();
						getViewTreeObserver().removeGlobalOnLayoutListener(this);
					}
				});
	}

	public boolean getPullLoading() {
		return this.mPullLoading;
	}

	public boolean getPullRefreshing() {
		return this.mPullRefreshing;
	}

	public void pullRefreshing() {
		if (!mEnablePullRefresh) {
			return;
		}
		mHeaderView.setVisiableHeight(mHeaderViewHeight);
		mPullRefreshing = true;
		mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
	}

	/**
	 * enable or disable pull down loadMsgsFromDB feature.
	 *
	 * @param enable
	 */
	public void setPullRefreshEnable(boolean enable) {
		mEnablePullRefresh = enable;
		if (!mEnablePullRefresh) { // disable, hide the content
			mHeaderViewContent.setVisibility(View.INVISIBLE);
		} else {
			mHeaderViewContent.setVisibility(View.VISIBLE);
		}
	}

	/**
	 * enable or disable pull up load more feature.
	 *
	 * @param enable
	 */
	public void setPullLoadEnable(boolean enable) {
		if (mEnablePullLoad == enable)
			return;

		mEnablePullLoad = enable;
		if (!mEnablePullLoad) {
			if (mFooterView != null) {
				this.removeFooterView(mFooterView);
			}
		} else {
			//mPullLoading = false;
			if (mFooterView == null) {
				mFooterView = new XListViewFooter(m_context);
				mFooterView.setOnClickListener(new OnClickListener() {
					@Override
					public void onClick(View v) {
						startLoadMore();
					}
				});
			}
			this.addFooterView(mFooterView);
			mFooterView.setState(XListViewFooter.STATE_NORMAL);
			// both "pull up" and "click" will invoke load more.
		}
	}

	/**
	 * stop loadMsgsFromDB, reset header view.
	 */
	public void stopRefresh() {
		Time time = new Time();
		time.setToNow();
		if (mPullRefreshing == true) {
			mPullRefreshing = false;
			resetHeaderHeight();
		}
	}

	/**
	 * stop load more, reset footer view.
	 */
	public void stopLoadMore() {
		if (mPullLoading == true) {
			mPullLoading = false;
			mFooterView.setState(XListViewFooter.STATE_NORMAL);
		}
	}

	private void invokeOnScrolling() {
		if (mScrollListener instanceof OnXScrollListener) {
			OnXScrollListener l = (OnXScrollListener) mScrollListener;
			l.onXScrolling(this);
		}
	}

	private void updateHeaderHeight(float delta) {
		mHeaderView.setVisiableHeight((int) delta
				+ mHeaderView.getVisiableHeight());
		if (mEnablePullRefresh && !mPullRefreshing) { // 未处于刷新状态，更新箭头
			if (mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
				mHeaderView.setState(XListViewHeader.STATE_READY);
			} else {
				mHeaderView.setState(XListViewHeader.STATE_NORMAL);
			}
		}
		setSelection(0); // scroll to top each time
	}

	/**
	 * reset header view's height.
	 */
	private void resetHeaderHeight() {
		int height = mHeaderView.getVisiableHeight();
		if (height == 0) // not visible.
			return;
		// refreshing and header isn't shown fully. do nothing.
		if (mPullRefreshing && height <= mHeaderViewHeight) {
			return;
		}
		int finalHeight = 0; // default: scroll back to dismiss header.
		// is refreshing, just scroll back to show all the header.
		if (mPullRefreshing && height > mHeaderViewHeight) {
			finalHeight = mHeaderViewHeight;
		}

		mScrollBack = SCROLLBACK_HEADER;
		mScroller.startScroll(0, height, 0, finalHeight - height,
				SCROLL_DURATION);
		// trigger computeScroll
		invalidate();
	}

	private void updateFooterHeight(float delta) {
		Log.d("lan", "updateFooterHeight");
		int height = mFooterView.getBottomMargin() + (int) delta;
		if (mEnablePullLoad && !mPullLoading) {
			if (height > PULL_LOAD_MORE_DELTA) { // height enough to invoke load
				// more.
				mFooterView.setState(XListViewFooter.STATE_READY);
			} else {
				mFooterView.setState(XListViewFooter.STATE_NORMAL);
			}
		}
		mFooterView.setBottomMargin(height);

//		setSelection(mTotalItemCount - 1); // scroll to bottom
	}

	private void resetFooterHeight() {
		int bottomMargin = mFooterView.getBottomMargin();
		if (bottomMargin > 0) {
			mScrollBack = SCROLLBACK_FOOTER;
			mScroller.startScroll(0, bottomMargin, 0, -bottomMargin,
					SCROLL_DURATION);
			invalidate();
		}
	}

	private void startLoadMore() {
		mPullLoading = true;
		mFooterView.setState(XListViewFooter.STATE_LOADING);
		if (mListViewListener != null) {
			mListViewListener.onLoadMore();
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if (mLastY == -1) {
			mLastY = ev.getRawY();
		}

		switch (ev.getAction()) {
			case MotionEvent.ACTION_DOWN:
				mLastY = ev.getRawY();
				break;
			case MotionEvent.ACTION_MOVE:
				final float deltaY = ev.getRawY() - mLastY;
				mLastY = ev.getRawY();
				if (getFirstVisiblePosition() == 0
						&& (mHeaderView.getVisiableHeight() > 0 || deltaY > 0)) {
					// the first item is showing, header has shown or pull down.
					updateHeaderHeight(deltaY / OFFSET_RADIO);
					invokeOnScrolling();
				} else if (mEnablePullLoad && (getLastVisiblePosition() == mTotalItemCount - 1)
						&& (mFooterView.getBottomMargin() > 0 || deltaY < 0)) {
					// last item, already pulled up or want to pull up.
					updateFooterHeight(-deltaY / OFFSET_RADIO);
				}
				break;
			default:
				mLastY = -1; // reset
				if (getFirstVisiblePosition() == 0) {
					// invoke loadMsgsFromDB
					if (mEnablePullRefresh
							&& mHeaderView.getVisiableHeight() > mHeaderViewHeight) {
						mPullRefreshing = true;
						mHeaderView.setState(XListViewHeader.STATE_REFRESHING);
						if (mListViewListener != null) {
							mListViewListener.onRefresh();
						}
					}
					resetHeaderHeight();
				} else if (getLastVisiblePosition() == mTotalItemCount - 1) {
					// invoke load more.
					if (mEnablePullLoad) {
						if (mFooterView.getBottomMargin() > PULL_LOAD_MORE_DELTA) {
							startLoadMore();
						}
						resetFooterHeight();
					}
				}
				break;
		}
		return super.onTouchEvent(ev);
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			if (mScrollBack == SCROLLBACK_HEADER) {
				mHeaderView.setVisiableHeight(mScroller.getCurrY());
			} else {
				mFooterView.setBottomMargin(mScroller.getCurrY());
			}
			postInvalidate();
			invokeOnScrolling();
		}
		super.computeScroll();
	}

	@Override
	public void setOnScrollListener(OnScrollListener l) {
		mScrollListener = l;
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if (mScrollListener != null) {
			mScrollListener.onScrollStateChanged(view, scrollState);
		}
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
						 int visibleItemCount, int totalItemCount) {
		// send to user's listener

		mTotalItemCount = totalItemCount;
		if (mScrollListener != null) {
			mScrollListener.onScroll(view, firstVisibleItem, visibleItemCount,
					totalItemCount);
		}
	}

	public void setXListViewListener(IXListViewListener l) {
		mListViewListener = l;
	}

	/**
	 * you can listen ListView.OnScrollListener or this one. it will invoke
	 * onXScrolling when header/footer scroll back.
	 */
	public interface OnXScrollListener extends OnScrollListener {
		public void onXScrolling(View view);
	}

	/**
	 * implements this interface to get loadMsgsFromDB/load more event.
	 */
	public interface IXListViewListener {
		public void onRefresh();

		public void onLoadMore();
	}
} public interface MsgListener {
	public boolean onMessageUpdate(String otherId);// true的话 不再传递给下一个Listener
}
import android.accounts.NetworkErrorException;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.support.v4.view.ViewPager;
import android.text.*;
import android.util.Log;
import android.view.*;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.LinearLayout;
import com.avos.avoscloud.AVUser;
import com.example.lovetalk.DemoApplication;
import com.example.lovetalk.R;
import com.example.lovetalk.adapter.ChatMsgAdapter;
import com.example.lovetalk.adapter.EmotionGridAdapter;
import com.example.lovetalk.adapter.EmotionPagerAdapter;
import com.example.lovetalk.chat.db.DBHelper;
import com.example.lovetalk.chat.db.DBMsg;
import com.example.lovetalk.chat.entity.Msg;
import com.example.lovetalk.chat.entity.MsgBuilder;
import com.example.lovetalk.chat.entity.RoomType;
import com.example.lovetalk.receiver.MsgReceiver;
import com.example.lovetalk.service.ChatService;
import com.example.lovetalk.service.MsgAgent;
import com.example.lovetalk.service.UserService;
import com.example.lovetalk.util.AVOSUtils;
import com.example.lovetalk.util.ChatUtils;
import com.example.lovetalk.util.Connectivity;
import com.example.lovetalk.util.EmotionUtils;
import com.example.lovetalk.util.MyAsyncTask;
import com.example.lovetalk.util.PathUtils;
import com.example.lovetalk.util.PhotoUtil;
import com.example.lovetalk.util.SimpleTextWatcher;
import com.example.lovetalk.util.Utils;
import com.example.lovetalk.view.EmotionEditText;
import com.example.lovetalk.view.xlist.XListView;
import com.example.lovetallk.service.listener.MsgListener;
import com.nostra13.universalimageloader.core.listener.PauseOnScrollListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
		turnToAudioBtn.setOnClickListener(this);
//    turnToTextBtn.setOnClickListener(this);
		showAddBtn.setOnClickListener(this);
		showEmotionBtn.setOnClickListener(this);
		addCameraBtn.setOnClickListener(this);
	}

	@Override
	public void onResume() {
		super.onResume();
		if (roomType == RoomType.Single) {
			MsgReceiver.addMsgListener(this);
		} else {
//      GroupMsgReceiver.addMsgListener(this);
		}
	}

	@Override
	public void onPause() {
		super.onPause();
		if (roomType == RoomType.Single) {
			MsgReceiver.removeMsgListener(this);
		} else {
//      GroupMsgReceiver.removeMsgListener(this);
		}
	}

	public void initData(Intent intent) {
		mCurUser = AVUser.getCurrentUser();
		mDbHelper = new DBHelper(ctx, "chat.db", 1);
		int roomTypeInt = intent.getIntExtra(ROOM_TYPE, RoomType.Single.getValue());
		roomType = RoomType.fromInt(roomTypeInt);
		msgSize = PAGE_SIZE;
		if (roomType == RoomType.Single) {
			String chatUserId = intent.getStringExtra(CHAT_USER_ID);
			chatUser = DemoApplication.lookupUser(chatUserId);
			if (chatUser != null) {
				ChatService.withUserToWatch(chatUser, true);
				msgAgent = new MsgAgent(roomType, chatUser.getObjectId());
			} else {
				Log.d("test1", chatUserId);
			}


		} else {
//      String groupId = intent.getStringExtra(GROUP_ID);
//      Session session = ChatService.getSession();
//      group = session.getGroup(groupId);
//      chatGroup = App.lookupChatGroup(groupId);
//      msgAgent = new MsgAgent(roomType, groupId);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.chat_ativity_menu, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {
		int menuId = item.getItemId();
		if (menuId == R.id.people) {
			if (roomType == RoomType.Single) {
				PersonInfoActivity.goPersonInfo(ctx, chatUser.getObjectId());
			} else {
//        GroupDetailActivity.chatGroup = chatGroup;
//        Utils.goActivity(ctx, GroupDetailActivity.class);
			}
		}
		return super.onMenuItemSelected(featureId, item);
	}

	public String currentChatId() {
		if (roomType == RoomType.Single) {
			return chatUser.getObjectId();
		} else {
//      return chatGroup.getObjectId();
		}
		return "";
	}

	public void loadMsgsFromDB(boolean showDialog) {
		new GetDataTask(ctx, showDialog, true).execute();
	}

	@Override
	public void onRefresh() {
		new Handler().postDelayed(new Runnable() {
			@Override
			public void run() {
				msgSize += PAGE_SIZE;
				new GetDataTask(ctx, false, false).execute();
			}
		}, 1000);
	}

	@Override
	public void onLoadMore() {
	}

	@Override
	public boolean onMessageUpdate(String otherId) {
		if (otherId.equals(currentChatId())) {
			loadMsgsFromDB(false);
			return true;
		}
		return false;
	}

	public void resendMsg(final Msg resendMsg) {
		new SendMsgTask(ctx) {
			@Override
			Msg sendMsg() throws Exception {
				return ChatService.resendMsg(resendMsg);
			}
		}.execute();
	}

	class GetDataTask extends MyAsyncTask {