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 ChatActivity extends BaseActivity implements OnClickListener, MsgListener,
		XListView.IXListViewListener {
	private static final int IMAGE_REQUEST = 0;
	public static final int LOCATION_REQUEST = 1;
	private static final int TAKE_CAMERA_REQUEST = 2;
	public static final int PAGE_SIZE = 20;

	private ChatMsgAdapter mAdapter;
	private List<Msg> mMsgs = new ArrayList<Msg>();
	private AVUser mCurUser;
	private DBHelper mDbHelper;
	public static ChatActivity ctx;

	View mChatTextLayout, chatAudioLayout, mChatAddLayout, mChatEmotionLayout;
	View turnToTextBtn, turnToAudioBtn, sendBtn, addImageBtn, showAddBtn, addLocationBtn, showEmotionBtn;
	LinearLayout chatBottomLayout;
	ViewPager emotionPager;
	private EmotionEditText contentEdit;
	private XListView xListView;
	//  RecordButton recordBtn;
	private String localCameraPath = PathUtils.getTmpPath();
	private View addCameraBtn;
	int msgSize;

	public static RoomType roomType;
	public static final String CHAT_USER_ID = "chatUserId";
	public static final String GROUP_ID = "groupId";
	public static final String ROOM_TYPE = "roomType";
	AVUser chatUser;

	String audioId;
	MsgAgent msgAgent;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		ctx = this;
		setContentView(R.layout.chat_layout);
		findView();
		initByIntent(getIntent());
	}

	private void initByIntent(Intent intent) {
		initData(intent);
		initActionBar();
		initEmotionPager();
//    initRecordBtn();
		setEditTextChangeListener();

		initListView();
		setSoftInputMode();
		loadMsgsFromDB(true);
		ChatService.cancelNotification(ctx);
		Log.d("lan", EmotionUtils.emotionCodes.length + " len");
	}

	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		initByIntent(intent);
	}

	private void initListView() {
		mAdapter = new ChatMsgAdapter(ctx, mMsgs);
		mAdapter.setDatas(mMsgs);
		xListView.setAdapter(mAdapter);
		xListView.setPullRefreshEnable(true);
		xListView.setPullLoadEnable(false);
		xListView.setXListViewListener(this);
		xListView.setOnScrollListener(
				new PauseOnScrollListener(UserService.imageLoader, true, true));
	}

	private void initEmotionPager() {
		List<View> views = new ArrayList<View>();
		for (int i = 0; i < 2; i++) {
			views.add(getEmotionGridView(i));
		}
		EmotionPagerAdapter pagerAdapter = new EmotionPagerAdapter(views);
		emotionPager.setAdapter(pagerAdapter);
	}

	private View getEmotionGridView(int pos) {
		LayoutInflater inflater = LayoutInflater.from(ctx);
		View emotionView = inflater.inflate(R.layout.chat_emotion_gridview, null);
		GridView gridView = (GridView) emotionView.findViewById(R.id.gridview);
		final EmotionGridAdapter emotionGridAdapter = new EmotionGridAdapter(ctx);
		List<String> pageEmotions;
		if (pos == 0) {
			pageEmotions = EmotionUtils.emotionTexts1;
		} else {
			pageEmotions = EmotionUtils.emotionTexts2;
		}
		emotionGridAdapter.setDatas(pageEmotions);
		gridView.setAdapter(emotionGridAdapter);
		gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				String emotionText = (String) parent.getAdapter().getItem(position);
				int start = contentEdit.getSelectionStart();
				StringBuffer sb = new StringBuffer(contentEdit.getText());
				sb.replace(contentEdit.getSelectionStart(), contentEdit.getSelectionEnd(), emotionText);
				contentEdit.setText(sb.toString());
				CharSequence info = contentEdit.getText();
				if (info instanceof Spannable) {
					Spannable spannable = (Spannable) info;
					Selection.setSelection(spannable, start + emotionText.length());
				}
			}
		});
		return gridView;
	}

//  public void initRecordBtn() {
//    setNewRecordPath();
//    recordBtn.setOnFinishedRecordListener(new RecordButton.RecordEventListener() {
//      @Override
//      public void onFinishedRecord(final String audioPath, int secs) {
//        final String objectId = audioId;
//        new SendMsgTask(ctx) {
//          @Override
//          Msg sendMsg() throws Exception {
//            return msgAgent.createAndSendMsg(new MsgAgent.MsgBuilderHelper() {
//              @Override
//              public void specifyType(MsgBuilder msgBuilder) {
//                msgBuilder.audio(objectId);
//              }
//            });
//          }
//        }.execute();
//        setNewRecordPath();
//      }
//
//      @Override
//      public void onStartRecord() {
//      }
//    });
//  }

//  public void setNewRecordPath() {
//    audioId = Utils.myUUID();
//    String audioPath = PathUtils.getChatFilePath(audioId);
//    recordBtn.setSavePath(audioPath);
//  }

	public void setEditTextChangeListener() {
		contentEdit.addTextChangedListener(new SimpleTextWatcher() {

			@Override
			public void onTextChanged(CharSequence s, int start, int before, int count) {
				if (s.length() > 0) {
					sendBtn.setEnabled(true);
					showSendBtn();
				} else {
					sendBtn.setEnabled(false);
					showTurnToRecordBtn();
				}
				super.onTextChanged(s, start, before, count);
			}
		});
	}

	private void showTurnToRecordBtn() {
		sendBtn.setVisibility(View.GONE);
		turnToAudioBtn.setVisibility(View.VISIBLE);
	}

	private void showSendBtn() {
		sendBtn.setVisibility(View.VISIBLE);
		turnToAudioBtn.setVisibility(View.GONE);
	}

	void initActionBar() {
		String title = "";
		if (roomType == RoomType.Single) {
			title = chatUser.getUsername();
		} else {
//      title = chatGroup.getTitle();
		}
		initActionBar(title);
	}


	private void findView() {
		xListView = (XListView) findViewById(R.id.listview);
		addImageBtn = findViewById(R.id.addImageBtn);

		contentEdit = (EmotionEditText) findViewById(R.id.textEdit);
		mChatTextLayout = findViewById(R.id.chatTextLayout);
//    chatAudioLayout = findViewById(R.id.chatRecordLayout);
		chatBottomLayout = (LinearLayout) findViewById(R.id.bottomLayout);
		turnToAudioBtn = findViewById(R.id.turnToAudioBtn);
//    turnToTextBtn = findViewById(R.id.turnToTextBtn);
//    recordBtn = (RecordButton) findViewById(R.id.recordBtn);
		mChatTextLayout = findViewById(R.id.chatTextLayout);
		mChatAddLayout = findViewById(R.id.chatAddLayout);
		addLocationBtn = findViewById(R.id.addLocationBtn);
		mChatEmotionLayout = findViewById(R.id.chatEmotionLayout);
		showAddBtn = findViewById(R.id.showAddBtn);
		showEmotionBtn = findViewById(R.id.showEmotionBtn);
		sendBtn = findViewById(R.id.sendBtn);
		emotionPager = (ViewPager) findViewById(R.id.emotionPager);
		addCameraBtn = findViewById(R.id.addCameraBtn);

		sendBtn.setOnClickListener(this);
		contentEdit.setOnClickListener(this);
		addImageBtn.setOnClickListener(this);
		addLocationBtn.setOnClickListener(this);
		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 {
		List<Msg> msgs;
		boolean scrollToLast = true;

		GetDataTask(Context cxt, boolean openDialog, boolean scrollToLast) {
			super(cxt, openDialog);
			this.scrollToLast = scrollToLast;
		}

		@Override
		protected void doInBack() throws Exception {
			String convid = "";
			if (roomType == RoomType.Single) {
				convid = AVOSUtils.convid(ChatService.getPeerId(mCurUser), ChatService.getPeerId(chatUser));
			} else {
//        convid = group.getGroupId();
			}
			msgs = DBMsg.getMsgs(mDbHelper, convid, msgSize);
			ChatService.cacheUserOrChatGroup(msgs);
		}

		@Override
		protected void onSucceed() {
			ChatUtils.stopRefresh(xListView);
			addMsgsAndRefresh(msgs, scrollToLast);
		}

	}

	public void addMsgsAndRefresh(List<Msg> msgs, boolean scrollToLast) {
		int lastN = mAdapter.getCount();
		int newN = msgs.size();
		this.mMsgs = msgs;
		mAdapter.setDatas(this.mMsgs);
		mAdapter.notifyDataSetChanged();
		if (scrollToLast) {
			scrollToLast();
		} else {
			xListView.setSelection(newN - lastN - 1);
			if (lastN == newN) {
				Utils.toast("加载完毕");
			}
		}
		if (newN < PAGE_SIZE) {
			xListView.setPullRefreshEnable(false);
		} else {
			xListView.setPullRefreshEnable(true);
		}
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch (v.getId()) {
			case R.id.sendBtn:
				sendText();
				break;
			case R.id.addImageBtn:
//        selectImageFromLocal();
				break;
//      case R.id.turnToAudioBtn:
//        showAudioLayout();
//        break;
//      case R.id.turnToTextBtn:
//        showTextLayout();
//        break;
			case R.id.showAddBtn:
//        toggleBottomAddLayout();
				break;
			case R.id.showEmotionBtn:
				toggleEmotionLayout();
				break;
			case R.id.addLocationBtn:
//        selectLocationFromMap();
				break;
			case R.id.textEdit:
				hideBottomLayoutAndScrollToLast();
				break;
			case R.id.addCameraBtn:
				selectImageFromCamera();
				break;
		}
	}

	private void hideBottomLayoutAndScrollToLast() {
		hideBottomLayout();
		scrollToLast();
	}

	private void hideBottomLayout() {
		hideAddLayout();
		mChatEmotionLayout.setVisibility(View.GONE);
	}

	private void selectLocationFromMap() {
//    Intent intent = new Intent(this, LocationActivity.class);
//    intent.putExtra("type", "select");
//    startActivityForResult(intent, LOCATION_REQUEST);
	}

	void hideSoftInputView() {
		Utils.hideSoftInputView(this);
	}

	private void toggleEmotionLayout() {
		if (mChatEmotionLayout.getVisibility() == View.VISIBLE) {
			mChatEmotionLayout.setVisibility(View.GONE);
		} else {
			mChatEmotionLayout.setVisibility(View.VISIBLE);
			hideAddLayout();
			showTextLayout();
			hideSoftInputView();
		}
	}

	private void toggleBottomAddLayout() {
		if (mChatAddLayout.getVisibility() == View.VISIBLE) {
			hideAddLayout();
		} else {
			mChatEmotionLayout.setVisibility(View.GONE);
			hideSoftInputView();
			showAddLayout();
		}
	}

	private void hideAddLayout() {
		mChatAddLayout.setVisibility(View.GONE);
	}

	private void showAddLayout() {
		mChatAddLayout.setVisibility(View.VISIBLE);
	}

	private void showTextLayout() {
		mChatTextLayout.setVisibility(View.VISIBLE);
//    chatAudioLayout.setVisibility(View.GONE);
	}

	private void showAudioLayout() {
		mChatTextLayout.setVisibility(View.GONE);
		chatAudioLayout.setVisibility(View.VISIBLE);
		mChatEmotionLayout.setVisibility(View.GONE);
		hideSoftInputView();
	}

	public void selectImageFromLocal() {
		Intent intent;
		intent = new Intent(Intent.ACTION_GET_CONTENT);
		intent.setType("image/*");
		startActivityForResult(intent, IMAGE_REQUEST);
	}

	public void selectImageFromCamera() {
		Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
		Uri imageUri = Uri.fromFile(new File(localCameraPath));
		openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
		startActivityForResult(openCameraIntent,
				TAKE_CAMERA_REQUEST);
	}

	private void sendText() {
		final String content = contentEdit.getText().toString();
		if (TextUtils.isEmpty(content) == false) {
			new SendMsgTask(ctx) {
				@Override
				Msg sendMsg() throws Exception {
					return msgAgent.createAndSendMsg(new MsgAgent.MsgBuilderHelper() {
						@Override
						public void specifyType(MsgBuilder msgBuilder) {
							msgBuilder.text(content);
						}
					});
				}

				@Override
				protected void onPost(Exception e) {
					super.onPost(e);
					if (e == null) {
						contentEdit.setText("");
					}
				}
			}.execute();
		}
	}

	private String parsePathByReturnData(Intent data) {
		if (data == null) {
			return null;
		}
		String localSelectPath = null;
		Uri selectedImage = data.getData();
		if (selectedImage != null) {
			Cursor cursor = getContentResolver().query(
					selectedImage, null, null, null, null);
			cursor.moveToFirst();
			int columnIndex = cursor.getColumnIndex("_data");
			localSelectPath = cursor.getString(columnIndex);
			cursor.close();
		}
		return localSelectPath;
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		if (resultCode == RESULT_OK) {
			switch (requestCode) {
				case IMAGE_REQUEST:
					String localSelectPath = parsePathByReturnData(data);
					sendImageByPath(localSelectPath);
					break;
				case TAKE_CAMERA_REQUEST:
					sendImageByPath(localCameraPath);
					break;
				case LOCATION_REQUEST:
					sendLocationByReturnData(data);
					break;
			}
		}
		hideBottomLayout();
		super.onActivityResult(requestCode, resultCode, data);
	}

	private void sendLocationByReturnData(Intent data) {
		final double latitude = data.getDoubleExtra("x", 0);
		final double longitude = data.getDoubleExtra("y", 0);
		final String address = data.getStringExtra("address");
		if (address != null && !address.equals("")) {
			new SendMsgTask(ctx) {
				@Override
				Msg sendMsg() throws Exception {
					return msgAgent.createAndSendMsg(new MsgAgent.MsgBuilderHelper() {
						@Override
						public void specifyType(MsgBuilder msgBuilder) {
							msgBuilder.location(address, latitude, longitude);
						}
					});
				}
			}.execute();
		} else {
			Utils.toast("无法连接");
		}
	}

	public abstract class SendMsgTask extends MyAsyncTask {
		Msg msg;

		protected SendMsgTask(Context cxt) {
			super(cxt, false);
		}

		@Override
		protected void doInBack() throws Exception {
			if (Connectivity.isConnected(ctx) == false) {
				throw new NetworkErrorException(DemoApplication.context.getString(R.string.pleaseCheckNetwork));
			} else if (ChatService.isSessionPaused()) {
				throw new NetworkErrorException(DemoApplication.context.getString(R.string.sessionPausedTips));
			} else {
				msg = sendMsg();
			}
		}

		@Override
		protected void onSucceed() {
			loadMsgsFromDB(false);
		}

		abstract Msg sendMsg() throws Exception;
	}

	public String getOtherId() {
		if (roomType == RoomType.Single) {
			return chatUser.getObjectId();
		} else {
//      return group.getGroupId();
			return "";
		}
	}

	private void sendImageByPath(String localSelectPath) {
		final String objectId = Utils.myUUID();
		final String newPath = PathUtils.getChatFilePath(objectId);
		//PhotoUtil.simpleCompressImage(localSelectPath,newPath);
		PhotoUtil.compressImage(localSelectPath, newPath);
		new SendMsgTask(ctx) {
			@Override
			Msg sendMsg() throws Exception {
				return msgAgent.createAndSendMsg(new MsgAgent.MsgBuilderHelper() {
					@Override
					public void specifyType(MsgBuilder msgBuilder) {
						msgBuilder.image(objectId);
					}
				});
			}
		}.execute();
	}

	public void scrollToLast() {
		Log.d("lan", "scrollToLast");
		xListView.smoothScrollToPosition(xListView.getCount() - 1);
		xListView.setSelection(xListView.getCount() - 1);
	}

	@Override
	protected void onDestroy() {
		if (roomType == RoomType.Single) {
			ChatService.withUserToWatch(chatUser, false);
		}
		ctx = null;
		super.onDestroy();
	}

	public static void goUserChat(Activity ctx, String userId) {
		Intent intent = getUserChatIntent(ctx, userId);
		ctx.startActivity(intent);
	}

	public static Intent getUserChatIntent(Context ctx, String userId) {
		Intent intent = new Intent(ctx, ChatActivity.class);
		intent.putExtra(CHAT_USER_ID, userId);
		intent.putExtra(ROOM_TYPE, RoomType.Single.getValue());
		return intent;
	}

	public static void goGroupChat(Activity ctx, String groupId) {
		Intent intent = getGroupChatIntent(ctx, groupId);
		ctx.startActivity(intent);
	}

	public static Intent getGroupChatIntent(Context ctx, String groupId) {
		Intent intent = new Intent(ctx, ChatActivity.class);
		intent.putExtra(GROUP_ID, groupId);
		intent.putExtra(ROOM_TYPE, RoomType.Group.getValue());
		return intent;
	}
} 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 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 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 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 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 TimeUtils {
	public static PrettyTime prettyTime = new PrettyTime();

	public static String getDate(Date date) {
		SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm");
		return format.format(date);
	}

	public static String millisecs2DateString(long timestamp) {
		long gap = System.currentTimeMillis() - timestamp;
		if (gap < 1000 * 60 * 60 * 24) {
			return prettyTime.format(new Date(timestamp));
		} else {
			return getDate(new Date(timestamp));
		}
	}

	public static boolean haveTimeGap(long lastTime, long time) {
		int gap = 1000 * 60 * 10;//10 mins
		return time - lastTime > gap;
	}
} public class PlayButton extends View implements View.OnClickListener {
	MediaPlayer mediaPlayer;
	String path;
	Context ctx;
	boolean prepared = false;
	int backResourceId;

	public PlayButton(Context context) {
		super(context);
		ctx = context;
		init();
	}

	public PlayButton(Context context, AttributeSet attrs) {
		super(context, attrs);
		ctx = context;
		TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PlayBtn);
		boolean left = true;
		for (int i = 0; i < typedArray.getIndexCount(); i++) {
			int attr = typedArray.getIndex(i);
			switch (attr) {
				case R.styleable.PlayBtn_left:
					left = typedArray.getBoolean(attr, true);
					break;
			}
		}
		if (left) {
			backResourceId = R.drawable.voice_left;
		} else {
			backResourceId = R.drawable.voice_right;
		}
		init();
	}

	public void setPath(String path) {
		this.path = path;
		prepared = false;
	}

	public void playAudio() {
		if (prepared == false) {
			try {
				mediaPlayer = new MediaPlayer();
				mediaPlayer.setDataSource(path);
				mediaPlayer.prepare();
				mediaPlayer.start();
				mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
					@Override
					public void onCompletion(MediaPlayer mp) {
						setBackgroundResource(backResourceId);
					}
				});
				setBackgroundDrawable(null);
				prepared = true;
			} catch (IOException e) {
				e.printStackTrace();
				Utils.toast(e.getMessage());
			}
		} else {
			mediaPlayer.start();
			setBackgroundDrawable(null);
		}
	}

	private void init() {
		setBackgroundResource(backResourceId);
		setOnClickListener(this);
	}

	@Override
	protected void onDetachedFromWindow() {
		if (mediaPlayer != null) {
			mediaPlayer.stop();
			mediaPlayer.release();
			mediaPlayer = null;
		}
		super.onDetachedFromWindow();
	}

	@Override
	public void onClick(View v) {
		if (mediaPlayer != null && mediaPlayer.isPlaying()) {
			mediaPlayer.pause();
			setBackgroundResource(backResourceId);
		} else {
			playAudio();
		}
	}
} public class ViewHolder {
	public static <T extends View> T findViewById(View view, int id) {
		SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
		if (viewHolder == null) {
			viewHolder = new SparseArray<View>();
			view.setTag(viewHolder);
		}
		View childView = viewHolder.get(id);
		if (childView == null) {
			childView = view.findViewById(id);
			viewHolder.put(id, childView);
		}
		return (T) childView;
	}
}
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.avos.avoscloud.AVFile;
import com.avos.avoscloud.AVUser;
import com.example.lovetalk.DemoApplication;
import com.example.lovetalk.R;
import com.example.lovetalk.activity.ChatActivity;
import com.example.lovetalk.chat.entity.Msg;
import com.example.lovetalk.chat.entity.RoomType;
import com.example.lovetalk.service.UserService;
import com.example.lovetalk.util.EmotionUtils;
import com.example.lovetalk.util.PathUtils;
import com.example.lovetalk.util.PhotoUtil;
import com.example.lovetalk.util.TimeUtils;
import com.example.lovetalk.view.PlayButton;
import com.example.lovetalk.view.ViewHolder;
import com.nostra13.universalimageloader.core.ImageLoader;
import java.io.File;
import java.util.List;
package com.example.lovetalk.adapter;




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);