package net.coding.program.maopao.item;
import android.annotation.SuppressLint;
import android.graphics.drawable.AnimationDrawable;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Handler;
import android.text.Html;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.assist.ImageScaleType;
import net.coding.program.MyApp;
import net.coding.program.R;
import net.coding.program.common.Global;
import net.coding.program.common.HtmlContent;
import net.coding.program.common.ImageLoadTool;
import net.coding.program.common.widget.GifMarkImageView;
import net.coding.program.maopao.MaopaoListBaseFragment;
import net.coding.program.maopao.MaopaoListFragment;
import net.coding.program.model.BaseComment;
import net.coding.program.model.Maopao;
import org.apache.http.Header;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Created by chenchao on 15/3/31.
* 有6张图片的控件,比如任务的评论
*
* @update 添加支持语音消息
*/
@SuppressLint("NewApi")
public class ContentAreaImages extends ContentAreaBase {
private static final int[] itemImages = new int[]{
R.id.image0,
R.id.image1,
R.id.image2,
R.id.image3,
R.id.image4,
R.id.image5
};
private static final int itemImagesMaxCount = itemImages.length;
protected ImageLoadTool imageLoad;
protected View imageLayout0;
protected View imageLayout1;
protected ImageView voice_play;
protected LinearLayout voiceLayout;
protected View linearLayout;//气泡
protected boolean isRight;
private AnimationDrawable voicePlayAnim;
protected DisplayImageOptions imageOptions = new DisplayImageOptions
.Builder()
.showImageOnLoading(R.drawable.ic_default_image)
.showImageForEmptyUri(R.drawable.ic_default_image)
.showImageOnFail(R.drawable.ic_default_image)
.cacheInMemory(true)
.cacheOnDisk(true)
.considerExifParams(true)
.imageScaleType(ImageScaleType.EXACTLY)
.build();
private int contentMarginBottom = 0;
private ImageView images[] = new ImageView[itemImagesMaxCount];
private static final HashMap<String, Boolean> mUpdating = new HashMap<>();
public ContentAreaImages(View convertView, View.OnClickListener onClickContent, View.OnClickListener onclickImage, Html.ImageGetter imageGetterParamer, ImageLoadTool loadParams, int pxImageWidth) {
super(convertView, onClickContent, imageGetterParamer);
isRight = R.id.message_list_list_item_right == convertView.getId();
imageLoad = loadParams;
imageLayout0 = convertView.findViewById(R.id.imagesLayout0);
imageLayout1 = convertView.findViewById(R.id.imagesLayout1);
voice_play = (ImageView) convertView.findViewById(R.id.voice_play);
voiceLayout = (LinearLayout) convertView.findViewById(R.id.voiceLayout);
linearLayout = convertView.findViewById(R.id.linearLayout);
for (int i = 0; i < itemImagesMaxCount; ++i) {
images[i] = (ImageView) convertView.findViewById(itemImages[i]);
images[i].setOnClickListener(onclickImage);
images[i].setFocusable(false);
images[i].setLongClickable(true);
ViewGroup.LayoutParams lp = images[i].getLayoutParams();
lp.width = pxImageWidth;
lp.height = pxImageWidth;
}
contentMarginBottom = convertView.getResources().getDimensionPixelSize(R.dimen.message_text_margin_bottom);
}
// 用来设置冒泡的
public void setData(Maopao.MaopaoObject maopaoObject) {
setDataContent(maopaoObject.content, maopaoObject);
}
public void setData(BaseComment comment) {
setDataContent(comment.content, comment);
}
//[voice]{'id':1,'voiceUrl':'/sd/voice/a.amr','voiceDuration':10,'played':1}[voice]
public static Global.MessageParse parseVoice(String s) {
String str = s.substring(7, s.length() - 7);
Global.MessageParse mp = new Global.MessageParse();
mp.text = "";
try {
JSONObject jo = new JSONObject(str);
mp.voiceUrl = jo.getString("voiceUrl");
mp.voiceDuration = jo.getInt("voiceDuration");
mp.isVoice = true;
mp.played = jo.optInt("played");
mp.id = jo.optInt("id");
} catch (JSONException e) {
e.printStackTrace();
}
return mp;
}
private void setDataContent(String data, Object contentObject) {
Global.MessageParse maopaoData = HtmlContent.parseMessage(data);
if (maopaoData.text.isEmpty()) {
content.setVisibility(View.GONE);
} else {
content.setTag(MaopaoListBaseFragment.TAG_COMMENT_TEXT, maopaoData.text);
content.setVisibility(View.VISIBLE);
content.setText(Global.changeHyperlinkColorMaopao(maopaoData.text, imageGetter, Global.tagHandler,
content.getContext().getAssets()), TextView.BufferType.EDITABLE);
content.setTag(contentObject);
}
setImageUrl(maopaoData.uris);
}
public String getVocicePath() {
return voicePath;
}
public void setVoicePlayCallBack(VoicePlayCallBack mVoicePlayCallBack) {
this.mVoicePlayCallBack = mVoicePlayCallBack;
}
private void downVoiceFile() {
//服务器录音文件对应的本地文件路径
final String voiceFilePath = Global.sVoiceDir + File.separator + voicePath.substring(voicePath.lastIndexOf('/') + 1);
//根据录音文件链接查找本地是否存在文件名相同的amr文件,如果不存在,就下载
if (!new File(voiceFilePath).exists()) {
//确保一个录音文件只有一个请求去下载
if (!mUpdating.containsKey(voicePath) || !mUpdating.get(voicePath)) {
mUpdating.put(voicePath, true);
AsyncHttpClient client = new AsyncHttpClient();
client.get(voicePath, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
if (statusCode == 200) {
//保存录音文件到本地
BufferedOutputStream bos = null;
try {
File f = new File(voiceFilePath).getParentFile();
if (!f.exists()) {
f.mkdirs();
}
bos = new BufferedOutputStream(new FileOutputStream(voiceFilePath));
bos.write(responseBody);
bos.flush();
voicePath = voiceFilePath;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
mUpdating.put(voicePath, false);
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
mUpdating.put(voicePath, false);
// Log.e("Voice","Down the voice file failed,statusCode:"+statusCode+",\n error:"+error!=null?error.toString():""+",\n file:"+voicePath);
}
});
}
} else {
voicePath = voiceFilePath;
}
}
private Handler mHandler = new Handler();
int frame = 0;
private boolean isAnimRuning;
private void playVoiceAnim() {
if (mVoicePlayCallBack.getPlayingVoiceId() == id) {
isAnimRuning = true;
frame++;
if (frame > 2) {
frame = 0;
}
if (Build.VERSION.SDK_INT >= 16) {
voice_play.setBackground(voicePlayAnim.getFrame(frame));
} else {
voice_play.setBackgroundDrawable(voicePlayAnim.getFrame(frame));
}
mHandler.postDelayed(task, 200);
}
}
private void stopPlayVoiceAnim() {
isAnimRuning = false;
mHandler.removeCallbacksAndMessages(null);
frame = 0;
if (Build.VERSION.SDK_INT >= 16) {
voice_play.setBackground(voicePlayAnim.getFrame(0));
} else {
voice_play.setBackgroundDrawable(voicePlayAnim.getFrame(0));
}
}
private Runnable task = new Runnable() {
@Override
public void run() {
if (isAnimRuning) {
playVoiceAnim();
}
}
};
public void setVoiceNeedDownload(boolean isNeedDownload) {
this.isNeedDownload = isNeedDownload;
}
private int id;
private boolean isNeedDownload = true;
private String voicePath;
private VoicePlayCallBack mVoicePlayCallBack;
// 用来设置message的
public void setData(String data) {
final Global.MessageParse maopaoData = data.startsWith("[voice]{") && data.endsWith("}[voice]") ? parseVoice(data) : HtmlContent.parseMessage(data);
LinearLayout.LayoutParams lp_voiceLayout = (LinearLayout.LayoutParams) voiceLayout.getLayoutParams();
if (maopaoData.text.isEmpty()) {
content.setVisibility(View.GONE);
content.setText("");
} else {
content.setTag(MaopaoListBaseFragment.TAG_COMMENT_TEXT, maopaoData.text);
content.setVisibility(View.VISIBLE);
content.setText(Global.changeHyperlinkColorMaopao(maopaoData.text, imageGetter,
Global.tagHandler, content.getContext().getAssets()));
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) content.getLayoutParams();
if (maopaoData.uris.size() > 0) {
lp.bottomMargin = contentMarginBottom;
} else {
lp.bottomMargin = 0;
}
content.setLayoutParams(lp);
}
if (maopaoData.isVoice) {
id = maopaoData.id;
voicePath = maopaoData.voiceUrl;
//下载录音文件并缓存到本地
if (voicePath.startsWith("https://") || voicePath.startsWith("http://")) {
if (isNeedDownload) {
downVoiceFile();
} else {
String voiceFilePath = Global.sVoiceDir + File.separator + voicePath.substring(voicePath.lastIndexOf('/') + 1);
if (new File(voiceFilePath).exists()) {
voicePath = voiceFilePath;
}
}
}
voice_play.setVisibility(View.VISIBLE);
content.setVisibility(View.VISIBLE);
content.setText("" + maopaoData.voiceDuration + " \"");
content.setBackgroundDrawable(null);
content.setFocusable(false);
content.setFocusableInTouchMode(false);
//让气泡的宽度随着录音长度变化 为什么还要在减去一个32dp?根据布局文件来算不需要的
int maxWidth = MyApp.sWidthPix - (isRight ? Global.dpToPx(57 + 53 + 36 + 32) : Global.dpToPx(57 + 53 + 24 + 32));
int minWidth = Global.dpToPx(60);
int s = maopaoData.voiceDuration >= 60 ? 60 : maopaoData.voiceDuration;
int width = minWidth + (maxWidth - minWidth) * s / 60;
width = width < minWidth ? minWidth : width;
lp_voiceLayout.width = width;
if (isRight) {
lp_voiceLayout.gravity = Gravity.LEFT;
} else {
lp_voiceLayout.gravity = Gravity.RIGHT;
}
voicePlayAnim = (AnimationDrawable) voice_play.getResources().getDrawable(isRight ? R.drawable.anim_play_voice_right : R.drawable.anim_play_voice_left);
//voicePlayAnim.selectDrawable(0);
//voicePlayAnim。stop();
//这里没有用AnimationDrawable做帧动画,而是自己根据其原理实现帧动画,因为在内存中同一资源id的多个drawable实例的状态好像是一样的
//而上面注释代码由于activity每隔一段时间就会刷新ui,在播放动画时就会执行到这里,从而打断动画,造成语音播放还没完动画就突然停止的现象
//
if (mVoicePlayCallBack == null || mVoicePlayCallBack.getPlayingVoiceId() != id) {
if (Build.VERSION.SDK_INT >= 16) {
voice_play.setBackground(voicePlayAnim.getFrame(0));
} else {
voice_play.setBackgroundDrawable(voicePlayAnim.getFrame(0));
}
} else {
stopPlayVoiceAnim();
if (mVoicePlayCallBack.getPlayingVoicePath() != null) {
playVoiceAnim();
}
}
View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.w("Play Voice", "onClicked");
if (voicePath.startsWith("https://") || voicePath.startsWith("http://")) {
downVoiceFile();
return;
}
if (mVoicePlayCallBack != null) {
if (!isRight && maopaoData.played == 0) {
mVoicePlayCallBack.markVoicePlayed(maopaoData.id);
}
String currentPlayingVoicePath = mVoicePlayCallBack.getPlayingVoicePath();
if (voicePath.equals(currentPlayingVoicePath)) {
mVoicePlayCallBack.onStopPlay();
} else {
if (currentPlayingVoicePath != null) {
mVoicePlayCallBack.onStopPlay();
}
mVoicePlayCallBack.onStartPlay(voicePath, id, new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
playVoiceAnim();
}
}, new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Log.w("Play Voice", "stop anim");
stopPlayVoiceAnim();
}
});
}
}
}
};
linearLayout.setOnClickListener(mOnClickListener);
content.setOnClickListener(mOnClickListener);
} else {
voice_play.setVisibility(View.GONE);
linearLayout.setOnClickListener(null);
content.setOnClickListener(null);
lp_voiceLayout.width = ViewGroup.LayoutParams.WRAP_CONTENT;
lp_voiceLayout.gravity = Gravity.NO_GRAVITY;
}
voiceLayout.setLayoutParams(lp_voiceLayout);
setImageUrl(maopaoData.uris);
// linearLayout.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// View resend = ((ViewGroup)v.getParent()).findViewById(R.id.resend);
// int[] p = new int[2];
// resend.getLocationOnScreen(p);
// int x1 = p[0];
// v.getLocationOnScreen(p);
// int x2 = isRight?p[0]:p[0]+v.getMeasuredWidth();
// Log.w("Margin",""+Global.pxToDp(Math.abs(x1-x2)));
// }
// });
}
protected void setImageUrl(ArrayList<String> uris) {
if (uris.size() == 0) {
imageLayout0.setVisibility(View.GONE);
imageLayout1.setVisibility(View.GONE);
} else if (uris.size() < 3) {
imageLayout0.setVisibility(View.VISIBLE);
imageLayout1.setVisibility(View.GONE);
} else {
imageLayout0.setVisibility(View.VISIBLE);
imageLayout1.setVisibility(View.VISIBLE);
}
int min = Math.min(uris.size(), images.length);
ImageView[] displayImages = images;
if (min == 4) { // 4 张图的时候显示做特殊处理
displayImages = new ImageView[]{
images[0], images[1], images[3], images[4]
};
}
for (ImageView imageView : images) {
imageView.setVisibility(View.GONE);
}
for (int i = 0; i < min; ++i) {
displayImage(displayImages[i], uris, i);
}
}
private void displayImage(ImageView images, ArrayList<String> uris, int pos) {
images.setVisibility(View.VISIBLE);
images.setTag(new MaopaoListFragment.ClickImageParam(uris, pos, false));
if (images instanceof GifMarkImageView) {
((GifMarkImageView) images).showGifFlag(uris.get(pos));
}
imageLoad.loadImage(images, uris.get(pos), imageOptions);
}
/**
* 语音播放回调接口
*/
public interface VoicePlayCallBack {
void onStartPlay(String path, int id, MediaPlayer.OnPreparedListener mOnPreparedListener, MediaPlayer.OnCompletionListener mOnCompletionListener);
String getPlayingVoicePath();
void onStopPlay();
void markVoicePlayed(int id);
int getPlayingVoiceId();
}
}