commit bcb1bd5dff32d2ee861bf01e95eea709c472d086 Author: 李富 Date: Thu Jun 9 14:24:16 2016 +0800 first diff --git a/.gitattributes b/.gitattributes new file mode 100755 index 0000000..bdb0cab --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..9c4de58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..73aee9f --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +NewIM \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..508b3d9 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..5d19981 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..550dc52 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/NewIM.iml b/NewIM.iml new file mode 100755 index 0000000..617a736 --- /dev/null +++ b/NewIM.iml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100755 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/app.iml b/app/app.iml new file mode 100755 index 0000000..b458f5f --- /dev/null +++ b/app/app.iml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100755 index 0000000..a4d3fcc --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,61 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + aaptOptions.cruncherEnabled = false + aaptOptions.useNewCruncher = false + + defaultConfig { + applicationId "cn.bmob.imdemo" + minSdkVersion 14 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + //自动删除 unaligned .apks + applicationVariants.all { variant -> + variant.assemble.doLast { + variant.outputs.each { output -> + File unaligned = output.packageApplication.outputFile; + File aligned = output.outputFile + if (!unaligned.getName().equalsIgnoreCase(aligned.getName())) { + println "deleting " + unaligned.getName() + unaligned.delete() + } + } + } + } + } + } + dexOptions{ + incremental false + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + testCompile 'junit:junit:4.12' + compile('com.android.support:appcompat-v7:22.2.0') { + exclude module: 'support-annotations' + } + compile 'com.android.support:recyclerview-v7:22.2.0' + compile 'com.jakewharton:butterknife:7.0.1' + compile 'com.orhanobut:logger:1.4' + compile 'org.greenrobot:eventbus:3.0.0' + //bmob-im:自2.0.5开始提供aar格式:包含BmobNewIM_xxxx.jar、androidasync_2.1.6.jar、bmob_im_notification_strings.xml + compile 'cn.bmob.android:bmob-im:2.0.5@aar' + //bmob-sdk:3.4.7 + compile 'cn.bmob.android:bmob-sdk:3.4.7-aar' + compile files('libs/baidumapapi_v3_5_0.jar') + compile files('libs/locSDK_6.23.jar') +} diff --git a/app/libs/baidumapapi_v3_5_0.jar b/app/libs/baidumapapi_v3_5_0.jar new file mode 100755 index 0000000..1e22fcf Binary files /dev/null and b/app/libs/baidumapapi_v3_5_0.jar differ diff --git a/app/libs/locSDK_6.23.jar b/app/libs/locSDK_6.23.jar new file mode 100755 index 0000000..1a2a509 Binary files /dev/null and b/app/libs/locSDK_6.23.jar differ diff --git a/app/libs/universal-image-loader-1.9.5.jar b/app/libs/universal-image-loader-1.9.5.jar new file mode 100755 index 0000000..520dac3 Binary files /dev/null and b/app/libs/universal-image-loader-1.9.5.jar differ diff --git a/app/src/androidTest/java/cn/bmob/imdemo/ApplicationTest.java b/app/src/androidTest/java/cn/bmob/imdemo/ApplicationTest.java new file mode 100755 index 0000000..a470807 --- /dev/null +++ b/app/src/androidTest/java/cn/bmob/imdemo/ApplicationTest.java @@ -0,0 +1,13 @@ +package cn.bmob.imdemo; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100755 index 0000000..5ecdd28 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/cn/bmob/imdemo/BmobIMApplication.java b/app/src/main/java/cn/bmob/imdemo/BmobIMApplication.java new file mode 100755 index 0000000..3b88af0 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/BmobIMApplication.java @@ -0,0 +1,65 @@ +package cn.bmob.imdemo; + +import android.app.Application; + +import com.orhanobut.logger.Logger; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; + +import cn.bmob.imdemo.base.UniversalImageLoader; +import cn.bmob.newim.BmobIM; + +/** + * @author :smile + * @project:BmobIMApplication + * @date :2016-01-13-10:19 + */ +public class BmobIMApplication extends Application{ + + private static BmobIMApplication INSTANCE; + public static BmobIMApplication INSTANCE(){ + return INSTANCE; + } + private void setInstance(BmobIMApplication app) { + setBmobIMApplication(app); + } + private static void setBmobIMApplication(BmobIMApplication a) { + BmobIMApplication.INSTANCE = a; + } + + @Override + public void onCreate() { + super.onCreate(); + setInstance(this); + //初始化 + Logger.init("smile"); + //只有主进程运行的时候才需要初始化 + if (getApplicationInfo().packageName.equals(getMyProcessName())){ + //im初始化 + BmobIM.init(this); + //注册消息接收器 + BmobIM.registerDefaultMessageHandler(new DemoMessageHandler(this)); + } + //uil初始化 + UniversalImageLoader.initImageLoader(this); + } + + /** + * 获取当前运行的进程名 + * @return + */ + public static String getMyProcessName() { + try { + File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline"); + BufferedReader mBufferedReader = new BufferedReader(new FileReader(file)); + String processName = mBufferedReader.readLine().trim(); + mBufferedReader.close(); + return processName; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/Config.java b/app/src/main/java/cn/bmob/imdemo/Config.java new file mode 100755 index 0000000..95e0857 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/Config.java @@ -0,0 +1,27 @@ +package cn.bmob.imdemo; + +/** + * @author :smile + * @project:Config + * @date :2016-01-15-18:23 + */ +public class Config { + /** + * Bmob应用key + */ +// public static final String DEFAULT_APPKEY="d6f44e8f1ba9d3dcf4fab7a487fa97dd";//内 + public static final String DEFAULT_APPKEY="87ab0f9bee41bce86dfadd69af692873";//外 + //是否是debug模式 + public static final boolean DEBUG=true; + //好友请求:未读-未添加->接收到别人发给我的好友添加请求,初始状态 + public static final int STATUS_VERIFY_NONE=0; + //好友请求:已读-未添加->点击查看了新朋友,则都变成已读状态 + public static final int STATUS_VERIFY_READED=2; + //好友请求:已添加 + public static final int STATUS_VERIFIED=1; + //好友请求:拒绝 + public static final int STATUS_VERIFY_REFUSE=3; + //好友请求:我发出的好友请求-暂未存储到本地数据库中 + public static final int STATUS_VERIFY_ME_SEND=4; + +} diff --git a/app/src/main/java/cn/bmob/imdemo/DemoMessageHandler.java b/app/src/main/java/cn/bmob/imdemo/DemoMessageHandler.java new file mode 100755 index 0000000..4bd7f2d --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/DemoMessageHandler.java @@ -0,0 +1,176 @@ +package cn.bmob.imdemo; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Log; +import android.widget.Toast; + +import com.orhanobut.logger.Logger; + +import org.greenrobot.eventbus.EventBus; + +import java.util.List; +import java.util.Map; + +import cn.bmob.imdemo.bean.AddFriendMessage; +import cn.bmob.imdemo.bean.AgreeAddFriendMessage; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.imdemo.db.NewFriendManager; +import cn.bmob.imdemo.event.RefreshEvent; +import cn.bmob.imdemo.model.UserModel; +import cn.bmob.imdemo.model.i.UpdateCacheListener; +import cn.bmob.imdemo.ui.MainActivity; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMMessageType; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.event.MessageEvent; +import cn.bmob.newim.event.OfflineMessageEvent; +import cn.bmob.newim.listener.BmobIMMessageHandler; +import cn.bmob.newim.notification.BmobNotificationManager; +import cn.bmob.v3.exception.BmobException; +import cn.bmob.v3.listener.SaveListener; + +/**消息接收器 + * @author smile + * @project DemoMessageHandler + * @date 2016-03-08-17:37 + */ +public class DemoMessageHandler extends BmobIMMessageHandler{ + + private Context context; + + public DemoMessageHandler(Context context) { + this.context = context; + } + + @Override + public void onMessageReceive(final MessageEvent event) { + //当接收到服务器发来的消息时,此方法被调用 + Logger.i(event.getConversation().getConversationTitle() + "," + event.getMessage().getMsgType() + "," + event.getMessage().getContent()); + excuteMessage(event); + } + + @Override + public void onOfflineReceive(final OfflineMessageEvent event) { + //每次调用connect方法时会查询一次离线消息,如果有,此方法会被调用 + Map> map =event.getEventMap(); + Logger.i("离线消息属于" + map.size() + "个用户"); + //挨个检测下离线消息所属的用户的信息是否需要更新 + for (Map.Entry> entry : map.entrySet()) { + List list =entry.getValue(); + int size = list.size(); + for(int i=0;i0){ + showAddNotify(friend); + } + }else if(type.equals("agree")){//接收到的对方同意添加自己为好友,此时需要做的事情:1、添加对方为好友,2、显示通知 + AgreeAddFriendMessage agree = AgreeAddFriendMessage.convert(msg); + addFriend(agree.getFromId());//添加消息的发送方为好友 + //这里应该也需要做下校验--来检测下是否已经同意过该好友请求,我这里省略了 + showAgreeNotify(info,agree); + }else{ + Toast.makeText(context,"接收到的自定义消息:"+msg.getMsgType() + "," + msg.getContent() + "," + msg.getExtra(),Toast.LENGTH_SHORT).show(); + } + } + + /** + * 显示对方添加自己为好友的通知 + * @param friend + */ + private void showAddNotify(NewFriend friend){ + Intent pendingIntent = new Intent(context, MainActivity.class); + pendingIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + //这里可以是应用图标,也可以将聊天头像转成bitmap + Bitmap largetIcon = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher); + BmobNotificationManager.getInstance(context).showNotification(largetIcon, + friend.getName(), friend.getMsg(), friend.getName() + "请求添加你为朋友", pendingIntent); + } + + /** + * 显示对方同意添加自己为好友的通知 + * @param info + * @param agree + */ + private void showAgreeNotify(BmobIMUserInfo info,AgreeAddFriendMessage agree){ + Intent pendingIntent = new Intent(context, MainActivity.class); + pendingIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + Bitmap largetIcon = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher); + BmobNotificationManager.getInstance(context).showNotification(largetIcon,info.getName(),agree.getMsg(),agree.getMsg(),pendingIntent); + } + + /** + * 添加对方为自己的好友 + * @param uid + */ + private void addFriend(String uid){ + User user =new User(); + user.setObjectId(uid); + UserModel.getInstance().agreeAddFriend(user, new SaveListener() { + @Override + public void onSuccess() { + Log.i("bmob", "onSuccess"); + } + + @Override + public void onFailure(int i, String s) { + Log.i("bmob", "onFailure:"+s+"-"+i); + } + }); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/AgreeHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/AgreeHolder.java new file mode 100755 index 0000000..936409f --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/AgreeHolder.java @@ -0,0 +1,43 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.newim.bean.BmobIMMessage; + +/** + * 同意添加好友的agree类型 + */ +public class AgreeHolder extends BaseViewHolder implements View.OnClickListener,View.OnLongClickListener { + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_message) + protected TextView tv_message; + + public AgreeHolder(Context context, ViewGroup root, OnRecyclerViewListener listener) { + super(context, root, R.layout.item_chat_agree, listener); + } + + @Override + public void bindData(Object o) { + final BmobIMMessage message = (BmobIMMessage)o; + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + String time = dateFormat.format(message.getCreateTime()); + String content = message.getContent(); + tv_message.setText(content); + tv_time.setText(time); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ChatAdapter.java b/app/src/main/java/cn/bmob/imdemo/adapter/ChatAdapter.java new file mode 100755 index 0000000..f631eb7 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ChatAdapter.java @@ -0,0 +1,219 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMMessageType; +import cn.bmob.v3.BmobUser; + +/** + * @author :smile + * @project:ChatAdapter + * @date :2016-01-22-14:18 + */ +public class ChatAdapter extends RecyclerView.Adapter{ + + //文本 + private final int TYPE_RECEIVER_TXT = 0; + private final int TYPE_SEND_TXT = 1; + //图片 + private final int TYPE_SEND_IMAGE = 2; + private final int TYPE_RECEIVER_IMAGE = 3; + //位置 + private final int TYPE_SEND_LOCATION = 4; + private final int TYPE_RECEIVER_LOCATION = 5; + //语音 + private final int TYPE_SEND_VOICE =6; + private final int TYPE_RECEIVER_VOICE = 7; + //视频 + private final int TYPE_SEND_VIDEO =8; + private final int TYPE_RECEIVER_VIDEO = 9; + + //同意添加好友成功后的样式 + private final int TYPE_AGREE = 10; + + /** + * 显示时间间隔:10分钟 + */ + private final long TIME_INTERVAL = 10 * 60 * 1000; + + private List msgs = new ArrayList<>(); + + private String currentUid=""; + BmobIMConversation c; + + public ChatAdapter(Context context,BmobIMConversation c) { + try { + currentUid = BmobUser.getCurrentUser(context).getObjectId(); + } catch (Exception e) { + e.printStackTrace(); + } + this.c =c; + } + public int findPosition(BmobIMMessage message) { + int index = this.getCount(); + int position = -1; + while(index-- > 0) { + if(message.equals(this.getItem(index))) { + position = index; + break; + } + } + return position; + } + + public int findPosition(long id) { + int index = this.getCount(); + int position = -1; + while(index-- > 0) { + if(this.getItemId(index) == id) { + position = index; + break; + } + } + return position; + } + + public int getCount() { + return this.msgs == null?0:this.msgs.size(); + } + + public void addMessages(List messages) { + msgs.addAll(0, messages); + notifyDataSetChanged(); + } + + public void addMessage(BmobIMMessage message) { + msgs.addAll(Arrays.asList(message)); + notifyDataSetChanged(); + } + + /**获取消息 + * @param position + * @return + */ + public BmobIMMessage getItem(int position){ + return this.msgs == null?null:(position >= this.msgs.size()?null:this.msgs.get(position)); + } + + /**移除消息 + * @param position + */ + public void remove(int position){ + msgs.remove(position); + notifyDataSetChanged(); + } + + public BmobIMMessage getFirstMessage() { + if (null != msgs && msgs.size() > 0) { + return msgs.get(0); + } else { + return null; + } + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == TYPE_SEND_TXT) { + return new SendTextHolder(parent.getContext(), parent,c,onRecyclerViewListener); + } else if (viewType == TYPE_SEND_IMAGE) { + return new SendImageHolder(parent.getContext(), parent,c,onRecyclerViewListener); + } else if (viewType == TYPE_SEND_LOCATION) { + return new SendLocationHolder(parent.getContext(), parent,c,onRecyclerViewListener); + } else if (viewType == TYPE_SEND_VOICE) { + return new SendVoiceHolder(parent.getContext(), parent,c,onRecyclerViewListener); + } else if (viewType == TYPE_RECEIVER_TXT) { + return new ReceiveTextHolder(parent.getContext(), parent,onRecyclerViewListener); + } else if (viewType == TYPE_RECEIVER_IMAGE) { + return new ReceiveImageHolder(parent.getContext(), parent,onRecyclerViewListener); + } else if (viewType == TYPE_RECEIVER_LOCATION) { + return new ReceiveLocationHolder(parent.getContext(), parent,onRecyclerViewListener); + } else if (viewType == TYPE_RECEIVER_VOICE) { + return new ReceiveVoiceHolder(parent.getContext(), parent,onRecyclerViewListener); + } else if (viewType == TYPE_SEND_VIDEO) { + return new SendVideoHolder(parent.getContext(), parent,c,onRecyclerViewListener); + } else if (viewType == TYPE_RECEIVER_VIDEO) { + return new ReceiveVideoHolder(parent.getContext(), parent,onRecyclerViewListener); + }else if(viewType ==TYPE_AGREE) { + return new AgreeHolder(parent.getContext(),parent,onRecyclerViewListener); + }else{//开发者自定义的其他类型,可自行处理 + return null; + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + ((BaseViewHolder)holder).bindData(msgs.get(position)); + if (holder instanceof ReceiveTextHolder) { + ((ReceiveTextHolder)holder).showTime(shouldShowTime(position)); + } else if (holder instanceof ReceiveImageHolder) { + ((ReceiveImageHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof ReceiveLocationHolder) { + ((ReceiveLocationHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof ReceiveVoiceHolder) { + ((ReceiveVoiceHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof SendTextHolder) { + ((SendTextHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof SendImageHolder) { + ((SendImageHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof SendLocationHolder) { + ((SendLocationHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof SendVoiceHolder) { + ((SendVoiceHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof SendVideoHolder) {//随便模拟的视频类型 + ((SendVideoHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof ReceiveVideoHolder) { + ((ReceiveVideoHolder)holder).showTime(shouldShowTime(position)); + }else if (holder instanceof AgreeHolder) {//同意添加好友成功后的消息 + ((AgreeHolder)holder).showTime(shouldShowTime(position)); + } + } + + @Override + public int getItemViewType(int position) { + BmobIMMessage message = msgs.get(position); + if(message.getMsgType().equals(BmobIMMessageType.IMAGE.getType())){ + return message.getFromId().equals(currentUid) ? TYPE_SEND_IMAGE: TYPE_RECEIVER_IMAGE; + }else if(message.getMsgType().equals(BmobIMMessageType.LOCATION.getType())){ + return message.getFromId().equals(currentUid) ? TYPE_SEND_LOCATION: TYPE_RECEIVER_LOCATION; + }else if(message.getMsgType().equals(BmobIMMessageType.VOICE.getType())){ + return message.getFromId().equals(currentUid) ? TYPE_SEND_VOICE: TYPE_RECEIVER_VOICE; + }else if(message.getMsgType().equals(BmobIMMessageType.TEXT.getType())){ + return message.getFromId().equals(currentUid) ? TYPE_SEND_TXT: TYPE_RECEIVER_TXT; + }else if(message.getMsgType().equals(BmobIMMessageType.VIDEO.getType())){ + return message.getFromId().equals(currentUid) ? TYPE_SEND_VIDEO: TYPE_RECEIVER_VIDEO; + }else if(message.getMsgType().equals("agree")) {//显示欢迎 + return TYPE_AGREE; + }else{ + return -1; + } + } + + @Override + public int getItemCount() { + return msgs.size(); + } + + private OnRecyclerViewListener onRecyclerViewListener; + + public void setOnRecyclerViewListener(OnRecyclerViewListener onRecyclerViewListener) { + this.onRecyclerViewListener = onRecyclerViewListener; + } + + private boolean shouldShowTime(int position) { + if (position == 0) { + return true; + } + long lastTime = msgs.get(position - 1).getCreateTime(); + long curTime = msgs.get(position).getCreateTime(); + return curTime - lastTime > TIME_INTERVAL; + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ContactAdapter.java b/app/src/main/java/cn/bmob/imdemo/adapter/ContactAdapter.java new file mode 100755 index 0000000..a068a9e --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ContactAdapter.java @@ -0,0 +1,48 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; + +import java.util.Collection; + +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseRecyclerAdapter; +import cn.bmob.imdemo.adapter.base.BaseRecyclerHolder; +import cn.bmob.imdemo.adapter.base.IMutlipleItem; +import cn.bmob.imdemo.bean.Friend; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.db.NewFriendManager; + +/**联系人 + * 一种简洁的Adapter实现方式,可用于多种Item布局的recycleView实现,不用再写ViewHolder啦 + * @author :smile + * @project:ContactNewAdapter + * @date :2016-04-27-14:18 + */ +public class ContactAdapter extends BaseRecyclerAdapter { + + public static final int TYPE_NEW_FRIEND = 0; + public static final int TYPE_ITEM = 1; + + public ContactAdapter(Context context, IMutlipleItem items, Collection datas) { + super(context,items,datas); + } + + @Override + public void bindView(BaseRecyclerHolder holder, Friend friend, int position) { + if(holder.layoutId==R.layout.item_contact){ + User user =friend.getFriendUser(); + //好友头像 + holder.setImageView(user == null ? null : user.getAvatar(), R.mipmap.head, R.id.iv_recent_avatar); + //好友名称 + holder.setText(R.id.tv_recent_name,user==null?"未知":user.getUsername()); + }else if(holder.layoutId==R.layout.header_new_friend){ + if(NewFriendManager.getInstance(context).hasNewFriendInvitation()){ + holder.setVisible(R.id.iv_msg_tips,View.VISIBLE); + }else{ + holder.setVisible(R.id.iv_msg_tips, View.GONE); + } + } + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ConversationAdapter.java b/app/src/main/java/cn/bmob/imdemo/adapter/ConversationAdapter.java new file mode 100755 index 0000000..f1073ff --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ConversationAdapter.java @@ -0,0 +1,54 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; + +import java.util.Collection; +import java.util.List; + +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseRecyclerAdapter; +import cn.bmob.imdemo.adapter.base.BaseRecyclerHolder; +import cn.bmob.imdemo.adapter.base.IMutlipleItem; +import cn.bmob.imdemo.bean.Conversation; +import cn.bmob.imdemo.util.TimeUtil; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMMessageType; + +/** + * 使用进一步封装的Conversation,教大家怎么自定义会话列表 + * @author smile + */ +public class ConversationAdapter extends BaseRecyclerAdapter { + + public ConversationAdapter(Context context, IMutlipleItem items, Collection datas) { + super(context,items,datas); + } + + @Override + public void bindView(BaseRecyclerHolder holder, Conversation conversation, int position) { + holder.setText(R.id.tv_recent_msg,conversation.getLastMessageContent()); + holder.setText(R.id.tv_recent_time,TimeUtil.getChatTime(false,conversation.getLastMessageTime())); + //会话图标 + Object obj = conversation.getAvatar(); + if(obj instanceof String){ + String avatar=(String)obj; + holder.setImageView(avatar, R.mipmap.head, R.id.iv_recent_avatar); + }else{ + int defaultRes = (int)obj; + holder.setImageView(null, defaultRes, R.id.iv_recent_avatar); + } + //会话标题 + holder.setText(R.id.tv_recent_name, conversation.getcName()); + //查询指定未读消息数 + long unread = conversation.getUnReadCount(); + if(unread>0){ + holder.setVisible(R.id.tv_recent_unread, View.VISIBLE); + holder.setText(R.id.tv_recent_unread, String.valueOf(unread)); + }else{ + holder.setVisible(R.id.tv_recent_unread, View.GONE); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/NewFriendAdapter.java b/app/src/main/java/cn/bmob/imdemo/adapter/NewFriendAdapter.java new file mode 100755 index 0000000..4e3fe56 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/NewFriendAdapter.java @@ -0,0 +1,137 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import com.orhanobut.logger.Logger; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import cn.bmob.imdemo.Config; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseRecyclerAdapter; +import cn.bmob.imdemo.adapter.base.BaseRecyclerHolder; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.adapter.base.IMutlipleItem; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.imdemo.bean.AgreeAddFriendMessage; +import cn.bmob.imdemo.bean.Friend; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.imdemo.db.NewFriendManager; +import cn.bmob.imdemo.model.UserModel; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.core.BmobIMClient; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.v3.BmobUser; +import cn.bmob.v3.exception.BmobException; +import cn.bmob.v3.listener.SaveListener; + +/** + * @author :smile + * @project:NewFriendAdapter + * @date :2016-04-27-14:18 + */ +public class NewFriendAdapter extends BaseRecyclerAdapter{ + + public NewFriendAdapter(Context context, IMutlipleItem items, Collection datas) { + super(context,items,datas); + } + + @Override + public void bindView(final BaseRecyclerHolder holder, final NewFriend add, int position) { + holder.setImageView(add == null ? null : add.getAvatar(), R.mipmap.head, R.id.iv_recent_avatar); + holder.setText(R.id.tv_recent_name,add==null?"未知":add.getName()); + holder.setText(R.id.tv_recent_msg,add==null?"未知":add.getMsg()); + Integer status =add.getStatus(); + Logger.i("bindData:"+status+","+add.getUid()+","+add.getTime()); + if(status==null || status== Config.STATUS_VERIFY_NONE||status ==Config.STATUS_VERIFY_READED){//未添加/已读未添加 + holder.setText(R.id.btn_aggree,"接受"); + holder.setEnabled(R.id.btn_aggree,true); + holder.setOnClickListener(R.id.btn_aggree,new View.OnClickListener() { + @Override + public void onClick(View v) {//发送消息 + agreeAdd(add, new SaveListener() { + @Override + public void onSuccess() { + holder.setText(R.id.btn_aggree,"已添加"); + holder.setEnabled(R.id.btn_aggree,false); + } + + @Override + public void onFailure(int i, String s) { + holder.setEnabled(R.id.btn_aggree,true); + toast("添加好友失败:" + s); + } + }); + } + }); + }else{ + holder.setText(R.id.btn_aggree,"已添加"); + holder.setEnabled(R.id.btn_aggree,false); + } + } + + /** + * 添加到好友表中... + * @param add + * @param listener + */ + private void agreeAdd(final NewFriend add, final SaveListener listener){ + User user =new User(); + user.setObjectId(add.getUid()); + UserModel.getInstance().agreeAddFriend(user, new SaveListener() { + @Override + public void onSuccess() { + sendAgreeAddFriendMessage(add, listener); + } + + @Override + public void onFailure(int i, String s) { + listener.onFailure(i, s); + } + }); + } + + /** + * 发送同意添加好友的请求 + */ + private void sendAgreeAddFriendMessage(final NewFriend add,final SaveListener listener){ + BmobIMUserInfo info = new BmobIMUserInfo(add.getUid(), add.getName(), add.getAvatar()); + //如果为true,则表明为暂态会话,也就是说该会话仅执行发送消息的操作,不会保存会话和消息到本地数据库中 + BmobIMConversation c = BmobIM.getInstance().startPrivateConversation(info,true,null); + //这个obtain方法才是真正创建一个管理消息发送的会话 + BmobIMConversation conversation = BmobIMConversation.obtain(BmobIMClient.getInstance(),c); + //而AgreeAddFriendMessage的isTransient设置为false,表明我希望在对方的会话数据库中保存该类型的消息 + AgreeAddFriendMessage msg =new AgreeAddFriendMessage(); + User currentUser = BmobUser.getCurrentUser(context, User.class); + msg.setContent("我通过了你的好友验证请求,我们可以开始聊天了!");//---这句话是直接存储到对方的消息表中的 + Map map =new HashMap<>(); + map.put("msg",currentUser.getUsername()+"同意添加你为好友");//显示在通知栏上面的内容 + map.put("uid",add.getUid());//发送者的uid-方便请求添加的发送方找到该条添加好友的请求 + map.put("time", add.getTime());//添加好友的请求时间 + msg.setExtraMap(map); + conversation.sendMessage(msg, new MessageSendListener() { + @Override + public void done(BmobIMMessage msg, BmobException e){ + if (e == null) {//发送成功 + //修改本地的好友请求记录 + NewFriendManager.getInstance(context).updateNewFriend(add,Config.STATUS_VERIFIED); + listener.onSuccess(); + } else {//发送失败 + listener.onFailure(e.getErrorCode(),e.getMessage()); + } + } + }); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/NewRecordPlayClickListener.java b/app/src/main/java/cn/bmob/imdemo/adapter/NewRecordPlayClickListener.java new file mode 100755 index 0000000..d285d52 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/NewRecordPlayClickListener.java @@ -0,0 +1,144 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.graphics.drawable.AnimationDrawable; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnPreparedListener; +import android.view.View; +import android.widget.ImageView; + +import java.io.File; +import java.io.FileInputStream; + +import cn.bmob.imdemo.R; +import cn.bmob.newim.bean.BmobIMAudioMessage; +import cn.bmob.newim.core.BmobDownloadManager; +import cn.bmob.v3.BmobUser; + +public class NewRecordPlayClickListener implements View.OnClickListener { + + BmobIMAudioMessage message; + ImageView iv_voice; + private AnimationDrawable anim = null; + Context mContext; + String currentObjectId = ""; + MediaPlayer mediaPlayer = null; + public static boolean isPlaying = false; + public static NewRecordPlayClickListener currentPlayListener = null; + static BmobIMAudioMessage currentMsg = null; + + public NewRecordPlayClickListener(Context context, BmobIMAudioMessage msg,ImageView voice) { + this.iv_voice = voice; + this.message = msg; + this.mContext = context.getApplicationContext(); + currentMsg = msg; + currentPlayListener = this; + try { + currentObjectId = BmobUser.getCurrentUser(mContext).getObjectId(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("resource") + public void startPlayRecord(String filePath, boolean isUseSpeaker) { + if (!(new File(filePath).exists())) { + return; + } + AudioManager audioManager = (AudioManager) mContext + .getSystemService(Context.AUDIO_SERVICE); + mediaPlayer = new MediaPlayer(); + if (isUseSpeaker) { + audioManager.setMode(AudioManager.MODE_NORMAL); + audioManager.setSpeakerphoneOn(true); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING); + } else { + audioManager.setSpeakerphoneOn(false); + audioManager.setMode(AudioManager.MODE_IN_CALL); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); + } + + try { + mediaPlayer.reset(); + // 单独使用此方法会报错播放错误:setDataSourceFD failed.: status=0x80000000 + // mediaPlayer.setDataSource(filePath); + // 因此采用此方式会避免这种错误 + FileInputStream fis = new FileInputStream(new File(filePath)); + mediaPlayer.setDataSource(fis.getFD()); + mediaPlayer.prepare(); + mediaPlayer.setOnPreparedListener(new OnPreparedListener() { + + @Override + public void onPrepared(MediaPlayer arg0) { + isPlaying = true; + currentMsg = message; + arg0.start(); + startRecordAnimation(); + } + }); + mediaPlayer + .setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + + @Override + public void onCompletion(MediaPlayer mp) { + stopPlayRecord(); + } + + }); + currentPlayListener = this; + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void stopPlayRecord() { + stopRecordAnimation(); + if (mediaPlayer != null) { + mediaPlayer.stop(); + mediaPlayer.release(); + } + isPlaying = false; + } + + private void startRecordAnimation() { + if (message.getFromId().equals(currentObjectId)) { + iv_voice.setImageResource(R.drawable.anim_chat_voice_right); + } else { + iv_voice.setImageResource(R.drawable.anim_chat_voice_left); + } + anim = (AnimationDrawable) iv_voice.getDrawable(); + anim.start(); + } + + private void stopRecordAnimation() { + if (message.getFromId().equals(currentObjectId)) { + iv_voice.setImageResource(R.mipmap.voice_left3); + } else { + iv_voice.setImageResource(R.mipmap.voice_right3); + } + if (anim != null) { + anim.stop(); + } + } + + @Override + public void onClick(View arg0) { + if (isPlaying) { + currentPlayListener.stopPlayRecord(); + if (currentMsg != null + && currentMsg.hashCode() == message.hashCode()) { + currentMsg = null; + return; + } + } + if (message.getFromId().equals(currentObjectId)) {// 如果是自己发送的语音消息,则播放本地地址 + String localPath = message.getContent().split("&")[0]; + startPlayRecord(localPath, true); + } else {// 如果是收到的消息,则需要先下载后播放 + String localPath = BmobDownloadManager.getDownLoadFilePath(message); + startPlayRecord(localPath, true); + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/OnRecyclerViewListener.java b/app/src/main/java/cn/bmob/imdemo/adapter/OnRecyclerViewListener.java new file mode 100755 index 0000000..12cddb5 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/OnRecyclerViewListener.java @@ -0,0 +1,11 @@ +package cn.bmob.imdemo.adapter; + +/**为RecycleView添加点击事件 + * @author smile + * @project OnRecyclerViewListener + * @date 2016-03-03-16:39 + */ +public interface OnRecyclerViewListener { + void onItemClick(int position); + boolean onItemLongClick(int position); +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveImageHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveImageHolder.java new file mode 100755 index 0000000..ced8078 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveImageHolder.java @@ -0,0 +1,111 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.graphics.Bitmap; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMImageMessage; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; + +/** + * 接收到的文本类型 + */ +public class ReceiveImageHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.iv_picture) + protected ImageView iv_picture; + @Bind(R.id.progress_load) + protected ProgressBar progress_load; + + public ReceiveImageHolder(Context context, ViewGroup root,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_received_image,onRecyclerViewListener); + } + + @Override + public void bindData(Object o) { + BmobIMMessage msg = (BmobIMMessage)o; + //用户信息的获取必须在buildFromDB之前,否则会报错'Entity is detached from DAO context' + final BmobIMUserInfo info = msg.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(msg.getCreateTime()); + tv_time.setText(time); + //可使用buildFromDB方法转化为指定类型的消息 + final BmobIMImageMessage message = BmobIMImageMessage.buildFromDB(false,msg); + //显示图片 + ImageLoaderFactory.getLoader().load(iv_picture,message.getRemoteUrl(), R.mipmap.ic_launcher,new ImageLoadingListener(){; + + @Override + public void onLoadingStarted(String s, View view) { + progress_load.setVisibility(View.VISIBLE); + } + + @Override + public void onLoadingComplete(String s, View view, Bitmap bitmap) { + progress_load.setVisibility(View.INVISIBLE); + } + + @Override + public void onLoadingCancelled(String s, View view) { + progress_load.setVisibility(View.INVISIBLE); + } + + @Override + public void onLoadingFailed(String s, View view, FailReason failReason) { + progress_load.setVisibility(View.INVISIBLE); + } + }); + + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + + iv_picture.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击图片:"+message.getRemoteUrl()+""); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + + iv_picture.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveLocationHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveLocationHolder.java new file mode 100755 index 0000000..bb8eaac --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveLocationHolder.java @@ -0,0 +1,81 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMLocationMessage; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; + +/** + * 接收到的位置类型 + */ +public class ReceiveLocationHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_location) + protected TextView tv_location; + + public ReceiveLocationHolder(Context context, ViewGroup root,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_received_location,onRecyclerViewListener); + } + + @Override + public void bindData(Object o) { + BmobIMMessage msg = (BmobIMMessage)o; + //用户信息的获取必须在buildFromDB之前,否则会报错'Entity is detached from DAO context' + final BmobIMUserInfo info = msg.getBmobIMUserInfo(); + //加载头像 + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(msg.getCreateTime()); + tv_time.setText(time); + // + final BmobIMLocationMessage message = BmobIMLocationMessage.buildFromDB(msg); + tv_location.setText(message.getAddress()); + // + tv_location.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("经度:" + message.getLongitude() + ",维度:" + message.getLatitude()); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + tv_location.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击"+info.getName()+"头像"); + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveTextHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveTextHolder.java new file mode 100755 index 0000000..3bce599 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveTextHolder.java @@ -0,0 +1,82 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import butterknife.OnClick; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; + +/** + * 接收到的文本类型 + */ +public class ReceiveTextHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_message) + protected TextView tv_message; + + public ReceiveTextHolder(Context context, ViewGroup root,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_received_message,onRecyclerViewListener); + } + + @OnClick({R.id.iv_avatar}) + public void onAvatarClick(View view) { + + } + + @Override + public void bindData(Object o) { + final BmobIMMessage message = (BmobIMMessage)o; + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(message.getCreateTime()); + tv_time.setText(time); + final BmobIMUserInfo info = message.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + String content = message.getContent(); + tv_message.setText(content); + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + tv_message.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击"+message.getContent()); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + + tv_message.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveVideoHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveVideoHolder.java new file mode 100755 index 0000000..f0151d9 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveVideoHolder.java @@ -0,0 +1,83 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import butterknife.OnClick; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; + +/** + * 接收到的视频类型--这是举个例子,并没有展示出视频缩略图等信息,开发者可自行设置 + */ +public class ReceiveVideoHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_message) + protected TextView tv_message; + + public ReceiveVideoHolder(Context context, ViewGroup root, OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_received_message,onRecyclerViewListener); + } + + @OnClick({R.id.iv_avatar}) + public void onAvatarClick(View view) { + + } + + @Override + public void bindData(Object o) { + final BmobIMMessage message = (BmobIMMessage)o; + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(message.getCreateTime()); + tv_time.setText(time); + final BmobIMUserInfo info = message.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + String content = message.getContent(); + tv_message.setText("接收到的视频文件:"+content); + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + + tv_message.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击"+message.getContent()); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + + tv_message.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveVoiceHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveVoiceHolder.java new file mode 100755 index 0000000..87abd49 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/ReceiveVoiceHolder.java @@ -0,0 +1,118 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMAudioMessage; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.core.BmobDownloadManager; +import cn.bmob.newim.listener.FileDownloadListener; +import cn.bmob.v3.BmobUser; +import cn.bmob.v3.exception.BmobException; + +/** + * 接收到的文本类型 + */ +public class ReceiveVoiceHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_voice_length) + protected TextView tv_voice_length; + @Bind(R.id.iv_voice) + protected ImageView iv_voice; + + @Bind(R.id.progress_load) + protected ProgressBar progress_load; + + private String currentUid=""; + + public ReceiveVoiceHolder(Context context, ViewGroup root,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_received_voice,onRecyclerViewListener); + try { + currentUid = BmobUser.getCurrentUser(context).getObjectId(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void bindData(Object o) { + BmobIMMessage msg = (BmobIMMessage)o; + //用户信息的获取必须在buildFromDB之前,否则会报错'Entity is detached from DAO context' + final BmobIMUserInfo info = msg.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(msg.getCreateTime()); + tv_time.setText(time); + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + //显示特有属性 + final BmobIMAudioMessage message = BmobIMAudioMessage.buildFromDB(false, msg); + boolean isExists = BmobDownloadManager.isAudioExist(currentUid, message); + if(!isExists){//若指定格式的录音文件不存在,则需要下载,因为其文件比较小,故放在此下载 + BmobDownloadManager downloadTask = new BmobDownloadManager(getContext(),msg,new FileDownloadListener() { + + @Override + public void onStart() { + progress_load.setVisibility(View.VISIBLE); + tv_voice_length.setVisibility(View.GONE); + iv_voice.setVisibility(View.INVISIBLE);//只有下载完成才显示播放的按钮 + } + + @Override + public void done(BmobException e) { + if(e==null){ + progress_load.setVisibility(View.GONE); + tv_voice_length.setVisibility(View.VISIBLE); + tv_voice_length.setText(message.getDuration()+"\''"); + iv_voice.setVisibility(View.VISIBLE); + }else{ + progress_load.setVisibility(View.GONE); + tv_voice_length.setVisibility(View.GONE); + iv_voice.setVisibility(View.INVISIBLE); + } + } + }); + downloadTask.execute(message.getContent()); + }else{ + tv_voice_length.setVisibility(View.VISIBLE); + tv_voice_length.setText(message.getDuration() + "\''"); + } + iv_voice.setOnClickListener(new NewRecordPlayClickListener(getContext(), message, iv_voice)); + + iv_voice.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/SearchUserAdapter.java b/app/src/main/java/cn/bmob/imdemo/adapter/SearchUserAdapter.java new file mode 100755 index 0000000..f3ea7d2 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/SearchUserAdapter.java @@ -0,0 +1,64 @@ +package cn.bmob.imdemo.adapter; + +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +import java.util.ArrayList; +import java.util.List; + +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.bean.User; + +/** + * @author :smile + * @project:SearchUserAdapter + * @date :2016-01-22-14:18 + */ +public class SearchUserAdapter extends RecyclerView.Adapter{ + + private List users = new ArrayList<>(); + + public SearchUserAdapter() { + } + + public void setDatas(List list) { + users.clear(); + if (null != list) { + users.addAll(list); + } + } + + /**获取用户 + * @param position + * @return + */ + public User getItem(int position){ + return users.get(position); + } + + private OnRecyclerViewListener onRecyclerViewListener; + + public void setOnRecyclerViewListener(OnRecyclerViewListener onRecyclerViewListener) { + this.onRecyclerViewListener = onRecyclerViewListener; + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new SearchUserHolder(parent.getContext(), parent, onRecyclerViewListener); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + ((BaseViewHolder)holder).bindData(users.get(position)); + } + + @Override + public int getItemViewType(int position) { + return 1; + } + + @Override + public int getItemCount() { + return users.size(); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/SearchUserHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/SearchUserHolder.java new file mode 100755 index 0000000..9405407 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/SearchUserHolder.java @@ -0,0 +1,45 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.ui.UserInfoActivity; + +public class SearchUserHolder extends BaseViewHolder { + + @Bind(R.id.avatar) + public ImageView avatar; + @Bind(R.id.name) + public TextView name; + @Bind(R.id.btn_add) + public Button btn_add; + + public SearchUserHolder(Context context, ViewGroup root,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_search_user,onRecyclerViewListener); + } + + @Override + public void bindData(Object o) { + final User user =(User)o; + ImageLoaderFactory.getLoader().loadAvator(avatar,user.getAvatar(), R.mipmap.head); + name.setText(user.getUsername()); + btn_add.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) {//查看个人详情 + Bundle bundle = new Bundle(); + bundle.putSerializable("u", user); + startActivity(UserInfoActivity.class,bundle); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/SendImageHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/SendImageHolder.java new file mode 100755 index 0000000..9659be3 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/SendImageHolder.java @@ -0,0 +1,144 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMImageMessage; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMSendStatus; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.v3.exception.BmobException; + +/** + * 发送的文本类型 + */ +public class SendImageHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.iv_fail_resend) + protected ImageView iv_fail_resend; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.iv_picture) + protected ImageView iv_picture; + + @Bind(R.id.tv_send_status) + protected TextView tv_send_status; + + @Bind(R.id.progress_load) + protected ProgressBar progress_load; + BmobIMConversation c; + + public SendImageHolder(Context context, ViewGroup root,BmobIMConversation c,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_sent_image,onRecyclerViewListener); + this.c =c; + } + + @Override + public void bindData(Object o) { + BmobIMMessage msg = (BmobIMMessage)o; + //用户信息的获取必须在buildFromDB之前,否则会报错'Entity is detached from DAO context' + final BmobIMUserInfo info = msg.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null,R.mipmap.head); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(msg.getCreateTime()); + tv_time.setText(time); + // + final BmobIMImageMessage message = BmobIMImageMessage.buildFromDB(true, msg); + int status =message.getSendStatus(); + if (status == BmobIMSendStatus.SENDFAILED.getStatus() ||status == BmobIMSendStatus.UPLOADAILED.getStatus()) { + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } else if (status== BmobIMSendStatus.SENDING.getStatus()) { + progress_load.setVisibility(View.VISIBLE); + iv_fail_resend.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } else { + tv_send_status.setVisibility(View.VISIBLE); + tv_send_status.setText("已发送"); + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + } + + //发送的不是远程图片地址,则取本地地址 + ImageLoaderFactory.getLoader().load(iv_picture,TextUtils.isEmpty(message.getRemoteUrl()) ? message.getLocalPath():message.getRemoteUrl(),R.mipmap.ic_launcher,null); +// ViewUtil.setPicture(TextUtils.isEmpty(message.getRemoteUrl()) ? message.getLocalPath():message.getRemoteUrl(), R.mipmap.ic_launcher, iv_picture,null); + + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + iv_picture.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击图片:"+(TextUtils.isEmpty(message.getRemoteUrl()) ? message.getLocalPath():message.getRemoteUrl())+""); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + + iv_picture.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + + //重发 + iv_fail_resend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + c.resendMessage(message, new MessageSendListener() { + @Override + public void onStart(BmobIMMessage msg) { + progress_load.setVisibility(View.VISIBLE); + iv_fail_resend.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + + @Override + public void done(BmobIMMessage msg, BmobException e) { + if (e == null) { + tv_send_status.setVisibility(View.VISIBLE); + tv_send_status.setText("已发送"); + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + } else { + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + } + }); + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/SendLocationHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/SendLocationHolder.java new file mode 100755 index 0000000..c505f17 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/SendLocationHolder.java @@ -0,0 +1,137 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMLocationMessage; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMSendStatus; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.v3.exception.BmobException; + +/** + * 发送的语音类型 + */ +public class SendLocationHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.iv_fail_resend) + protected ImageView iv_fail_resend; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.layout_location) + protected LinearLayout layout_location; + + @Bind(R.id.tv_location) + protected TextView tv_location; + + @Bind(R.id.tv_send_status) + protected TextView tv_send_status; + + @Bind(R.id.progress_load) + protected ProgressBar progress_load; + BmobIMConversation c; + public SendLocationHolder(Context context, ViewGroup root,BmobIMConversation c,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_sent_location,onRecyclerViewListener); + this.c = c; + } + + @Override + public void bindData(Object o) { + BmobIMMessage msg = (BmobIMMessage)o; + //用户信息的获取必须在buildFromDB之前,否则会报错'Entity is detached from DAO context' + final BmobIMUserInfo info = msg.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(msg.getCreateTime()); + tv_time.setText(time); + + final BmobIMLocationMessage message = BmobIMLocationMessage.buildFromDB(msg); + tv_location.setText(message.getAddress()); + int status =message.getSendStatus(); + if (status == BmobIMSendStatus.SENDFAILED.getStatus()) { + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + } else if (status== BmobIMSendStatus.SENDING.getStatus()) { + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.VISIBLE); + } else { + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + } + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + + tv_location.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("经度:" + message.getLongitude() + ",维度:" + message.getLatitude()); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + tv_location.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + //重发 + iv_fail_resend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + c.resendMessage(message, new MessageSendListener() { + @Override + public void onStart(BmobIMMessage msg) { + progress_load.setVisibility(View.VISIBLE); + iv_fail_resend.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + + @Override + public void done(BmobIMMessage msg, BmobException e) { + if (e == null) { + tv_send_status.setVisibility(View.VISIBLE); + tv_send_status.setText("已发送"); + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + } else { + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + } + }); + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/SendTextHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/SendTextHolder.java new file mode 100755 index 0000000..2982037 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/SendTextHolder.java @@ -0,0 +1,135 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMSendStatus; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.v3.exception.BmobException; + +/** + * 发送的文本类型 + */ +public class SendTextHolder extends BaseViewHolder implements View.OnClickListener,View.OnLongClickListener { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.iv_fail_resend) + protected ImageView iv_fail_resend; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_message) + protected TextView tv_message; + @Bind(R.id.tv_send_status) + protected TextView tv_send_status; + + @Bind(R.id.progress_load) + protected ProgressBar progress_load; + + BmobIMConversation c; + + public SendTextHolder(Context context, ViewGroup root,BmobIMConversation c,OnRecyclerViewListener listener) { + super(context, root, R.layout.item_chat_sent_message, listener); + this.c =c; + } + + @Override + public void bindData(Object o) { + final BmobIMMessage message = (BmobIMMessage)o; + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + final BmobIMUserInfo info = message.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + String time = dateFormat.format(message.getCreateTime()); + String content = message.getContent(); + tv_message.setText(content); + tv_time.setText(time); + + int status =message.getSendStatus(); + if (status == BmobIMSendStatus.SENDFAILED.getStatus()) { + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + } else if (status== BmobIMSendStatus.SENDING.getStatus()) { + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.VISIBLE); + } else { + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + } + + tv_message.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击"+message.getContent()); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + + tv_message.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + + //重发 + iv_fail_resend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + c.resendMessage(message, new MessageSendListener() { + @Override + public void onStart(BmobIMMessage msg) { + progress_load.setVisibility(View.VISIBLE); + iv_fail_resend.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + + @Override + public void done(BmobIMMessage msg, BmobException e) { + if(e==null){ + tv_send_status.setVisibility(View.VISIBLE); + tv_send_status.setText("已发送"); + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + }else{ + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + } + }); + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/SendVideoHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/SendVideoHolder.java new file mode 100755 index 0000000..7f879a8 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/SendVideoHolder.java @@ -0,0 +1,136 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMSendStatus; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.v3.exception.BmobException; + +/** + * 发送的视频类型---这是举个例子,并没有展示出视频缩略图等信息,开发者可自行实现 + */ +public class SendVideoHolder extends BaseViewHolder implements View.OnClickListener,View.OnLongClickListener { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.iv_fail_resend) + protected ImageView iv_fail_resend; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_message) + protected TextView tv_message; + @Bind(R.id.tv_send_status) + protected TextView tv_send_status; + + @Bind(R.id.progress_load) + protected ProgressBar progress_load; + + BmobIMConversation c; + + public SendVideoHolder(Context context, ViewGroup root, BmobIMConversation c, OnRecyclerViewListener listener) { + super(context, root, R.layout.item_chat_sent_message, listener); + this.c =c; + } + + @Override + public void bindData(Object o) { + final BmobIMMessage message = (BmobIMMessage)o; + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + final BmobIMUserInfo info = message.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + + String time = dateFormat.format(message.getCreateTime()); + String content = message.getContent(); + tv_message.setText("发送的视频文件:"+content); + tv_time.setText(time); + + int status =message.getSendStatus(); + if (status == BmobIMSendStatus.SENDFAILED.getStatus()) { + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + } else if (status== BmobIMSendStatus.SENDING.getStatus()) { + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.VISIBLE); + } else { + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + } + + tv_message.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击"+message.getContent()); + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + }); + + tv_message.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + + //重发 + iv_fail_resend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + c.resendMessage(message, new MessageSendListener() { + @Override + public void onStart(BmobIMMessage msg) { + progress_load.setVisibility(View.VISIBLE); + iv_fail_resend.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + + @Override + public void done(BmobIMMessage msg, BmobException e) { + if(e==null){ + tv_send_status.setVisibility(View.VISIBLE); + tv_send_status.setText("已发送"); + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + }else{ + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + } + }); + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/SendVoiceHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/SendVoiceHolder.java new file mode 100755 index 0000000..cc4fc47 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/SendVoiceHolder.java @@ -0,0 +1,137 @@ +package cn.bmob.imdemo.adapter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.text.SimpleDateFormat; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.base.BaseViewHolder; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.newim.bean.BmobIMAudioMessage; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMSendStatus; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.v3.exception.BmobException; + +/** + * 发送的语音类型 + */ +public class SendVoiceHolder extends BaseViewHolder { + + @Bind(R.id.iv_avatar) + protected ImageView iv_avatar; + + @Bind(R.id.iv_fail_resend) + protected ImageView iv_fail_resend; + + @Bind(R.id.tv_time) + protected TextView tv_time; + + @Bind(R.id.tv_voice_length) + protected TextView tv_voice_length; + @Bind(R.id.iv_voice) + protected ImageView iv_voice; + + @Bind(R.id.tv_send_status) + protected TextView tv_send_status; + + @Bind(R.id.progress_load) + protected ProgressBar progress_load; + + BmobIMConversation c; + public SendVoiceHolder(Context context, ViewGroup root,BmobIMConversation c,OnRecyclerViewListener onRecyclerViewListener) { + super(context, root, R.layout.item_chat_sent_voice,onRecyclerViewListener); + this.c =c; + } + + @Override + public void bindData(Object o) { + BmobIMMessage msg = (BmobIMMessage)o; + //用户信息的获取必须在buildFromDB之前,否则会报错'Entity is detached from DAO context' + final BmobIMUserInfo info = msg.getBmobIMUserInfo(); + ImageLoaderFactory.getLoader().loadAvator(iv_avatar,info != null ? info.getAvatar() : null, R.mipmap.head); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm"); + String time = dateFormat.format(msg.getCreateTime()); + tv_time.setText(time); + //使用buildFromDB方法转化成指定类型的消息 + final BmobIMAudioMessage message = BmobIMAudioMessage.buildFromDB(true,msg); + tv_voice_length.setText(message.getDuration()+"\''"); + + int status =message.getSendStatus(); + if (status == BmobIMSendStatus.SENDFAILED.getStatus()||status == BmobIMSendStatus.UPLOADAILED.getStatus()) {//发送失败/上传失败 + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + tv_voice_length.setVisibility(View.INVISIBLE); + } else if (status== BmobIMSendStatus.SENDING.getStatus()) { + progress_load.setVisibility(View.VISIBLE); + iv_fail_resend.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + tv_voice_length.setVisibility(View.INVISIBLE); + } else {//发送成功 + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.GONE); + tv_voice_length.setVisibility(View.VISIBLE); + } + + iv_voice.setOnClickListener(new NewRecordPlayClickListener(getContext(),message,iv_voice)); + + iv_voice.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onRecyclerViewListener != null) { + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + }); + + iv_avatar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toast("点击" + info.getName() + "的头像"); + } + }); + //重发 + iv_fail_resend.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + c.resendMessage(message, new MessageSendListener() { + @Override + public void onStart(BmobIMMessage msg) { + progress_load.setVisibility(View.VISIBLE); + iv_fail_resend.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + + @Override + public void done(BmobIMMessage msg, BmobException e) { + if(e==null){ + tv_send_status.setVisibility(View.VISIBLE); + tv_send_status.setText("已发送"); + iv_fail_resend.setVisibility(View.GONE); + progress_load.setVisibility(View.GONE); + }else{ + iv_fail_resend.setVisibility(View.VISIBLE); + progress_load.setVisibility(View.GONE); + tv_send_status.setVisibility(View.INVISIBLE); + } + } + }); + } + }); + } + + public void showTime(boolean isShow) { + tv_time.setVisibility(isShow ? View.VISIBLE : View.GONE); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseRecyclerAdapter.java b/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseRecyclerAdapter.java new file mode 100755 index 0000000..e015b08 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseRecyclerAdapter.java @@ -0,0 +1,221 @@ +package cn.bmob.imdemo.adapter.base; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import cn.bmob.imdemo.adapter.OnRecyclerViewListener; +import cn.bmob.imdemo.base.BaseActivity; + + +/** + * 支持添加自定义头部布局; + * 支持扩展多种item布局; + * 支持设置recycleview点击/长按事件 + * @param + * @author smile + * @link https://github.com/bodismile/BaseRecyclerAdapter + */ +public abstract class BaseRecyclerAdapter extends RecyclerView.Adapter { + /** + * 默认布局 + */ + private final int TYPE_DEFAULT = 0; + /** + * 当list没有值得时候显示的布局 + */ + private final int TYPE_HEADER = 1; + /** + * 多重布局 + */ + private final int TYPE_MUTIPLE = 2; + /** + * 带header的多重布局 + */ + private final int TYPE_MUTIPLE_HEADER = 3; + + protected final Context context; + protected List lists; + protected IMutlipleItem items; + protected OnRecyclerViewListener listener; + + /** + * 支持一种或多种Item布局 + * + * @param context + * @param items + * @param datas + */ + public BaseRecyclerAdapter(Context context, IMutlipleItem items, Collection datas) { + this.context = context; + this.items = items; + this.lists = datas == null ? new ArrayList() : new ArrayList(datas); + } + + /** + * 绑定数据 + * @param datas + * @return + */ + public BaseRecyclerAdapter bindDatas(Collection datas) { + this.lists = datas == null ? new ArrayList() : new ArrayList(datas); + notifyDataSetChanged(); + return this; + } + + /** + * 删除数据 + * @param position + */ + public void remove(int position) { + int more = getItemCount() - lists.size(); + lists.remove(position - more); + notifyDataSetChanged(); + } + + /** + * 获取指定position的Item + * @param position + * @return + */ + public T getItem(int position) { + int more = getItemCount() - lists.size(); + return lists.get(position - more); + } + + @Override + public BaseRecyclerHolder onCreateViewHolder(ViewGroup parent, int viewType) { + int layoutId = items.getItemLayoutId(viewType); + LayoutInflater inflater = LayoutInflater.from(context); + View root = inflater.inflate(layoutId, parent, false); + return new BaseRecyclerHolder(layoutId, root); + } + + @Override + public void onBindViewHolder(BaseRecyclerHolder holder, int position) { + int type = getViewTypeByPosition(position); + if(type==TYPE_HEADER){ + bindView(holder, null, position); + }else if(type==TYPE_MUTIPLE){ + bindView(holder, lists.get(position), position); + }else if(type==TYPE_MUTIPLE_HEADER){ + int headerCount = getItemCount() - lists.size(); + bindView(holder, lists.get(position - headerCount), position); + }else{ + bindView(holder, null, position); + } + holder.itemView.setOnClickListener(getOnClickListener(position)); + holder.itemView.setOnLongClickListener(getOnLongClickListener(position)); + } + + @Override + public int getItemCount() { + if (items != null) {//当有多重布局的时候,则采用多重布局 + return items.getItemCount(lists); + } + return lists.size(); + } + + @Override + public int getItemViewType(int position) { + int type = getViewTypeByPosition(position); + if(type==TYPE_HEADER){ + return items.getItemViewType(position, null); + }else if(type==TYPE_MUTIPLE){ + return items.getItemViewType(position, lists.get(position)); + }else if(type==TYPE_MUTIPLE_HEADER){ + int headerCount = getItemCount() - lists.size(); + return items.getItemViewType(position, lists.get(position - headerCount)); + }else{ + return 0; + } + } + + /**获取指定position的布局类型 + * @param position + */ + private int getViewTypeByPosition(int position) { + if (items == null) {//默认布局 + return TYPE_DEFAULT; + } else {//多布局 + if (lists != null && lists.size() > 0) {//list有值的时候 + if (getItemCount() > lists.size()) {//是否有自定义的Header + int headerCount = getItemCount() - lists.size(); + if (position >= headerCount) {//当前位置大于header个数 + return TYPE_MUTIPLE_HEADER; + } else {//当前点击的是header + return TYPE_HEADER; + } + } else { + return TYPE_MUTIPLE; + } + } else {//list还没有值的时候 + return TYPE_HEADER; + } + } + } + + /** + * 设置点击/长按等事件监听器 + * @param onRecyclerViewListener + */ + public void setOnRecyclerViewListener(OnRecyclerViewListener onRecyclerViewListener) { + this.listener = onRecyclerViewListener; + } + + public View.OnClickListener getOnClickListener(final int position) { + return new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listener != null && v != null) { + listener.onItemClick(position); + } + } + }; + } + + public View.OnLongClickListener getOnLongClickListener(final int position) { + return new View.OnLongClickListener() { + + @Override + public boolean onLongClick(View v) { + if (listener != null && v != null) { + listener.onItemLongClick(position); + } + return true; + } + }; + } + + /** + * 需实现此方法 + * @param holder + * @param item + */ + public abstract void bindView(BaseRecyclerHolder holder, T item, int position); + + private Toast toast; + public void toast(final Object obj) { + try { + ((BaseActivity)context).runOnUiThread(new Runnable() { + + @Override + public void run() { + if (toast == null) + toast = Toast.makeText(context,"", Toast.LENGTH_SHORT); + toast.setText(obj.toString()); + toast.show(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseRecyclerHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseRecyclerHolder.java new file mode 100755 index 0000000..d301bf1 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseRecyclerHolder.java @@ -0,0 +1,123 @@ +package cn.bmob.imdemo.adapter.base; + +import android.graphics.Bitmap; +import android.support.v7.widget.RecyclerView; +import android.util.SparseArray; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import cn.bmob.imdemo.base.ImageLoaderFactory; + +/** + * 与BaseRecyclerAdapter一起使用 + * + */ +public class BaseRecyclerHolder extends RecyclerView.ViewHolder { + + private final SparseArray mViews; + public int layoutId; + + public BaseRecyclerHolder(int layoutId,View itemView) { + super(itemView); + this.layoutId =layoutId; + this.mViews = new SparseArray<>(8); + } + + public SparseArray getAllView() { + return mViews; + } + + /** + * @param viewId + * @return + */ + protected T getView(int viewId) { + View view = mViews.get(viewId); + if (view == null) { + view = itemView.findViewById(viewId); + mViews.put(viewId, view); + } + return (T) view; + } + + /** + * @param viewId + * @param text + * @return + */ + public BaseRecyclerHolder setText(int viewId, String text) { + TextView view = getView(viewId); + view.setText(text); + return this; + } + + /** + * 设置Enabled + * @param viewId + * @param enable + * @return + */ + public BaseRecyclerHolder setEnabled(int viewId,boolean enable){ + View v = getView(viewId); + v.setEnabled(enable); + return this; + } + + /** + * 点击事件 + * @param viewId + * @param listener + * @return + */ + public BaseRecyclerHolder setOnClickListener(int viewId, View.OnClickListener listener){ + View v = getView(viewId); + v.setOnClickListener(listener); + return this; + } + + /** + * @param viewId + * @param visibility + * @return + */ + public BaseRecyclerHolder setVisible(int viewId,int visibility) { + View view = getView(viewId); + view.setVisibility(visibility); + return this; + } + + /** + * @param viewId + * @param drawableId + * @return + */ + public BaseRecyclerHolder setImageResource(int viewId, int drawableId) { + ImageView view = getView(viewId); + view.setImageResource(drawableId); + return this; + } + + /** + * @param viewId + * @param bm + * @return + */ + public BaseRecyclerHolder setImageBitmap(int viewId, Bitmap bm) { + ImageView view = getView(viewId); + view.setImageBitmap(bm); + return this; + } + + /** + * @param avatar + * @param defaultRes + * @param viewId + * @return + */ + public BaseRecyclerHolder setImageView(String avatar, int defaultRes, int viewId) { + ImageView iv = getView(viewId); + ImageLoaderFactory.getLoader().loadAvator(iv,avatar, defaultRes); + return this; + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseViewHolder.java b/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseViewHolder.java new file mode 100755 index 0000000..7b6cf65 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/base/BaseViewHolder.java @@ -0,0 +1,86 @@ +package cn.bmob.imdemo.adapter.base; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import butterknife.ButterKnife; +import cn.bmob.imdemo.adapter.OnRecyclerViewListener; +import cn.bmob.imdemo.base.BaseActivity; + +/** + * 建议使用BaseRecyclerAdapter + * @param + */ +public abstract class BaseViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener,View.OnLongClickListener { + + public OnRecyclerViewListener onRecyclerViewListener; + protected Context context; + + public BaseViewHolder(Context context, ViewGroup root, int layoutRes,OnRecyclerViewListener listener) { + super(LayoutInflater.from(context).inflate(layoutRes, root, false)); + this.context=context; + ButterKnife.bind(this, itemView); + this.onRecyclerViewListener =listener; + itemView.setOnClickListener(this); + itemView.setOnLongClickListener(this); + } + + public Context getContext() { + return itemView.getContext(); + } + + public abstract void bindData(T t); + + private Toast toast; + public void toast(final Object obj) { + try { + ((BaseActivity)context).runOnUiThread(new Runnable() { + + @Override + public void run() { + if (toast == null) + toast = Toast.makeText(context,"", Toast.LENGTH_SHORT); + toast.setText(obj.toString()); + toast.show(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void onClick(View v) { + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemClick(getAdapterPosition()); + } + } + + @Override + public boolean onLongClick(View v) { + if(onRecyclerViewListener!=null){ + onRecyclerViewListener.onItemLongClick(getAdapterPosition()); + } + return true; + } + + /**启动指定Activity + * @param target + * @param bundle + */ + public void startActivity(Class target, Bundle bundle) { + Intent intent = new Intent(); + intent.setClass(getContext(), target); + if (bundle != null) + intent.putExtra(getContext().getPackageName(), bundle); + getContext().startActivity(intent); + } + +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/adapter/base/IMutlipleItem.java b/app/src/main/java/cn/bmob/imdemo/adapter/base/IMutlipleItem.java new file mode 100755 index 0000000..1caeabc --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/adapter/base/IMutlipleItem.java @@ -0,0 +1,31 @@ +package cn.bmob.imdemo.adapter.base; + +import java.util.List; + +/** + * Created by Administrator on 2016/5/6. + */ +public interface IMutlipleItem { + + /** + * 多种布局的layout文件 + * @param viewtype + * @return + */ + int getItemLayoutId(int viewtype); + + /** + * 多种布局类型 + * @param postion + * @param t + * @return + */ + int getItemViewType(int postion, T t); + + /** + * 返回布局个数 + * @param list + * @return + */ + int getItemCount(List list); +} diff --git a/app/src/main/java/cn/bmob/imdemo/base/BaseActivity.java b/app/src/main/java/cn/bmob/imdemo/base/BaseActivity.java new file mode 100755 index 0000000..5b6f0bf --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/base/BaseActivity.java @@ -0,0 +1,139 @@ +package cn.bmob.imdemo.base; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.app.FragmentActivity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.Toast; + +import com.orhanobut.logger.Logger; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import butterknife.ButterKnife; +import cn.bmob.imdemo.Config; + +/**基类 + * @author :smile + * @project:BaseActivity + * @date :2016-01-15-18:23 + */ +public class BaseActivity extends FragmentActivity { + + @Override + protected void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + + @Override + protected void onStop() { + EventBus.getDefault().unregister(this); + super.onStop(); + } + + @Override + public void setContentView(int layoutResID) { + super.setContentView(layoutResID); + ButterKnife.bind(this); + initView(); + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + super.setContentView(view, params); + ButterKnife.bind(this); + initView(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + ButterKnife.unbind(this); + } + + @Subscribe + public void onEvent(Boolean empty){ + + } + + protected void initView() {} + + protected void runOnMain(Runnable runnable) { + runOnUiThread(runnable); + } + + protected final static String NULL = ""; + private Toast toast; + public void toast(final Object obj) { + try { + runOnMain(new Runnable() { + + @Override + public void run() { + if (toast == null) + toast = Toast.makeText(BaseActivity.this, NULL,Toast.LENGTH_SHORT); + toast.setText(obj.toString()); + toast.show(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void startActivity(Class target, Bundle bundle,boolean finish) { + Intent intent = new Intent(); + intent.setClass(this, target); + if (bundle != null) + intent.putExtra(getPackageName(), bundle); + startActivity(intent); + if (finish) + finish(); + } + + public Bundle getBundle() { + if (getIntent() != null && getIntent().hasExtra(getPackageName())) + return getIntent().getBundleExtra(getPackageName()); + else + return null; + } + + /** + * 隐藏软键盘 + */ + public void hideSoftInputView() { + InputMethodManager manager = ((InputMethodManager) this.getSystemService(Activity.INPUT_METHOD_SERVICE)); + if (getWindow().getAttributes().softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) { + if (getCurrentFocus() != null) + manager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + /**隐藏软键盘-一般是EditText.getWindowToken() + * @param token + */ + public void hideSoftInput(IBinder token) { + if (token != null) { + InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + im.hideSoftInputFromWindow(token,InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + /**Log日志 + * @param msg + */ + public void log(String msg){ + if(Config.DEBUG){ + Logger.i(msg); + } + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/base/BaseFragment.java b/app/src/main/java/cn/bmob/imdemo/base/BaseFragment.java new file mode 100755 index 0000000..66a00ca --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/base/BaseFragment.java @@ -0,0 +1,74 @@ +package cn.bmob.imdemo.base; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.app.Fragment; +import android.view.inputmethod.InputMethodManager; +import android.widget.Toast; + +import com.orhanobut.logger.Logger; + +import cn.bmob.imdemo.Config; + +/**基类 + * @author :smile + * @project:BaseActivity + * @date :2016-01-15-18:23 + */ +public class BaseFragment extends Fragment { + + protected void runOnMain(Runnable runnable) { + getActivity().runOnUiThread(runnable); + } + + protected final static String NULL = ""; + private Toast toast; + public void toast(final Object obj) { + try { + runOnMain(new Runnable() { + + @Override + public void run() { + if (toast == null) + toast = Toast.makeText(getActivity(), NULL,Toast.LENGTH_SHORT); + toast.setText(obj.toString()); + toast.show(); + } + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void hideSoftInput(IBinder token) { + if (token != null) { + InputMethodManager im = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + im.hideSoftInputFromWindow(token,InputMethodManager.HIDE_NOT_ALWAYS); + } + } + + /**启动指定Activity + * @param target + * @param bundle + */ + public void startActivity(Class target, Bundle bundle) { + Intent intent = new Intent(); + intent.setClass(getActivity(), target); + if (bundle != null) + intent.putExtra(getActivity().getPackageName(), bundle); + getActivity().startActivity(intent); + } + + /**Log日志 + * @param msg + */ + public void log(String msg){ + if(Config.DEBUG){ + Logger.i(msg); + } + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/base/ILoader.java b/app/src/main/java/cn/bmob/imdemo/base/ILoader.java new file mode 100755 index 0000000..b342cb3 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/base/ILoader.java @@ -0,0 +1,30 @@ +package cn.bmob.imdemo.base; + +import android.widget.ImageView; + +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; + +/** + * 抽象的图片加载接口 + * @author smile + */ +public interface ILoader { + + /** + * 加载圆形头像 + * @param iv + * @param url + * @param defaultRes + */ + void loadAvator(ImageView iv, String url, int defaultRes); + + /** + * 加载图片,添加监听器 + * @param iv + * @param url + * @param defaultRes + * @param listener + */ + void load(ImageView iv,String url,int defaultRes,ImageLoadingListener listener); + +} diff --git a/app/src/main/java/cn/bmob/imdemo/base/ImageLoaderFactory.java b/app/src/main/java/cn/bmob/imdemo/base/ImageLoaderFactory.java new file mode 100755 index 0000000..ffddaf2 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/base/ImageLoaderFactory.java @@ -0,0 +1,22 @@ +package cn.bmob.imdemo.base; + +/** + * Created by Administrator on 2016/5/24. + */ +public class ImageLoaderFactory { + + private static volatile ILoader sInstance; + + private ImageLoaderFactory() {} + + public static ILoader getLoader() { + if (sInstance == null) { + synchronized (ImageLoaderFactory.class) { + if (sInstance == null) { + sInstance = new UniversalImageLoader(); + } + } + } + return sInstance; + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/base/ParentWithNaviActivity.java b/app/src/main/java/cn/bmob/imdemo/base/ParentWithNaviActivity.java new file mode 100755 index 0000000..3d67e2e --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/base/ParentWithNaviActivity.java @@ -0,0 +1,161 @@ +package cn.bmob.imdemo.base; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.bean.User; +import cn.bmob.v3.BmobUser; + +/**封装了导航条的类均需继承该类 + * @author :smile + * @project:ParentWithNaviActivity + * @date :2015-08-18-11:29 + */ +public abstract class ParentWithNaviActivity extends BaseActivity { + + public ToolBarListener listener; + public TextView tv_title; + public ImageView tv_left; + public TextView tv_right; + + /**导航栏标题:必填项 + * @return + */ + protected abstract String title(); + + /**导航栏左边:可以为string或图片资源id,非必须 + * @return + */ + public Object left(){return null;} + + /**导航栏右边:可以为string或图片资源id,非必须 + * @return + */ + public Object right(){return null;} + + /**设置导航栏监听,非必须 + * @return + */ + public ToolBarListener setToolBarListener(){return null;} + + /** + * 初始化导航条 + */ + public void initNaviView(){ + tv_title = getView(R.id.tv_title); + tv_right = getView(R.id.tv_right); + tv_left = getView(R.id.tv_left); + setNaviListener(setToolBarListener()); + tv_left.setOnClickListener(clickListener); + tv_right.setOnClickListener(clickListener); + tv_title.setText(title()); + refreshTop(); + } + + View.OnClickListener clickListener = new View.OnClickListener() { + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.tv_left: + if (listener == null) + finish(); + else{ + listener.clickLeft(); + } + break; + case R.id.tv_right: + if (listener != null) + listener.clickRight(); + break; + + default: + break; + } + } + }; + + protected void refreshTop() { + setLeftView(left()==null ? R.drawable.base_action_bar_back_bg_selector: left()); + setValue(R.id.tv_right, right()); + this.tv_title.setText(title()); + } + + private void setLeftView(Object obj){ + if(obj !=null && !obj.equals("")){ + tv_left.setVisibility(View.VISIBLE); + if(obj instanceof Integer){ + tv_left.setImageResource(Integer.parseInt(obj.toString())); + }else{ + tv_left.setImageResource(R.drawable.base_action_bar_back_bg_selector); + } + }else{ + tv_left.setVisibility(View.INVISIBLE); + } + } + + protected void setValue(int id,Object obj){ + if (obj != null && !obj.equals("")) { + ((TextView) getView(id)).setText(""); + getView(id).setBackgroundDrawable(new BitmapDrawable()); + if (obj instanceof String) { + ((TextView) getView(id)).setText(obj.toString()); + } else if (obj instanceof Integer) { + getView(id).setBackgroundResource(Integer.parseInt(obj.toString())); + } + } else { + ((TextView) getView(id)).setText(""); + getView(id).setBackgroundDrawable(new BitmapDrawable()); + } + } + + protected void setNaviListener(ToolBarListener listener) { + this.listener = listener; + } + + @SuppressWarnings("unchecked") + protected T getView(int id) { + return (T) findViewById(id); + } + + public boolean handleBackPressed(){ + return false; + } + + /**获取Drawable资源 + * @param id + * @return + */ + public Drawable getDrawableResources(int id){ + return getResources().getDrawable(id); + } + + public interface ToolBarListener { + void clickLeft(); + + void clickRight(); + } + + /**启动指定Activity + * @param target + * @param bundle + */ + public void startActivity(Class target, Bundle bundle) { + Intent intent = new Intent(); + intent.setClass(this, target); + if (bundle != null) + intent.putExtra(this.getPackageName(), bundle); + startActivity(intent); + } + + public String getCurrentUid(){ + return BmobUser.getCurrentUser(this,User.class).getObjectId(); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/base/ParentWithNaviFragment.java b/app/src/main/java/cn/bmob/imdemo/base/ParentWithNaviFragment.java new file mode 100755 index 0000000..6b46fa5 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/base/ParentWithNaviFragment.java @@ -0,0 +1,139 @@ +package cn.bmob.imdemo.base; + +import android.graphics.drawable.BitmapDrawable; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.base.ParentWithNaviActivity.ToolBarListener; + +/**封装了导航条的Fragment类均需继承该类 + * @author :smile + * @project:ParentWithNaviFragment + * @date :2015-08-18-14:19 + */ +public abstract class ParentWithNaviFragment extends BaseFragment { + + protected View rootView = null; + private ToolBarListener listener; + private TextView tv_title; + public TextView tv_right; + public ImageView tv_left; + public LinearLayout ll_navi; + + /** + * 初始化导航条 + */ + public void initNaviView(){ + tv_title = getView(R.id.tv_title); + tv_right = getView(R.id.tv_right); + tv_left = getView(R.id.tv_left); + setListener(setToolBarListener()); + tv_left.setOnClickListener(clickListener); + tv_right.setOnClickListener(clickListener); + tv_title.setText(title()); + refreshTop(); + } + + View.OnClickListener clickListener = new View.OnClickListener() { + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.tv_left: + if (listener != null){ + listener.clickLeft(); + } + break; + case R.id.tv_right: + if (listener != null) + listener.clickRight(); + break; + + default: + break; + } + } + }; + + private void refreshTop() { + setLeftView(left()); + setValue(R.id.tv_right, right()); + this.tv_title.setText(title()); + } + + private void setLeftView(Object obj){ + if(obj !=null && !obj.equals("")){ + tv_left.setVisibility(View.VISIBLE); + if(obj instanceof Integer){ + tv_left.setImageResource(Integer.parseInt(obj.toString())); + }else{ + tv_left.setImageResource(R.drawable.base_action_bar_back_bg_selector); + } + }else{ + tv_left.setVisibility(View.INVISIBLE); + } + } + + private void setValue(int id,Object obj){ + if (obj != null && !obj.equals("")) { + ((TextView) getView(id)).setText(""); + getView(id).setBackgroundDrawable(new BitmapDrawable()); + if (obj instanceof String) { + ((TextView) getView(id)).setText(obj.toString()); + } else if (obj instanceof Integer) { + getView(id).setBackgroundResource(Integer.parseInt(obj.toString())); + } + } else { + ((TextView) getView(id)).setText(""); + getView(id).setBackgroundDrawable(new BitmapDrawable()); + } + } + + private void setListener(ToolBarListener listener) { + this.listener = listener; + } + + /**导航栏标题 + * @return + */ + protected abstract String title(); + + /**导航栏右边:可以为string或图片资源id,不是必填项 + * @return + */ + public Object right(){ + return null; + } + + /**导航栏左边 + * @return + */ + public Object left(){return null;} + + /**设置导航条背景色 + * @param color + */ + public void setNavBackground(int color){ + ll_navi.setBackgroundColor(color); + } + + /**设置右边按钮的文字大小 + * @param dimenId + */ + public void setRightTextSize(float dimenId){ + tv_right.setTextSize(dimenId); + } + + /**设置导航栏监听 + * @return + */ + public ToolBarListener setToolBarListener(){return null;} + + protected T getView(int id) { + return (T) rootView.findViewById(id); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/base/UniversalImageLoader.java b/app/src/main/java/cn/bmob/imdemo/base/UniversalImageLoader.java new file mode 100755 index 0000000..4a7b942 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/base/UniversalImageLoader.java @@ -0,0 +1,80 @@ +package cn.bmob.imdemo.base; + +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; +import android.widget.ImageView; + +import com.nostra13.universalimageloader.cache.disc.naming.Md5FileNameGenerator; +import com.nostra13.universalimageloader.cache.memory.impl.WeakMemoryCache; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.assist.QueueProcessingType; +import com.nostra13.universalimageloader.core.download.ImageDownloader; +import com.nostra13.universalimageloader.core.imageaware.ImageAware; +import com.nostra13.universalimageloader.core.imageaware.ImageViewAware; +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; +import com.orhanobut.logger.Logger; + +import cn.bmob.imdemo.util.DisplayConfig; + +/** + * 使用UIL图片框架加载图片,后续方便扩展其他图片框架,比如glide或fresco + * Created by Administrator on 2016/5/24. + */ +public class UniversalImageLoader implements ILoader{ + + public UniversalImageLoader(){} + + @Override + public void loadAvator(ImageView iv, String url, int defaultRes) { + if(!TextUtils.isEmpty(url)){ + display(iv,url,true,defaultRes,null); + } else { + iv.setImageResource(defaultRes); + } + } + + @Override + public void load(ImageView iv, String url, int defaultRes,ImageLoadingListener listener) { + if(!TextUtils.isEmpty(url)){ + display(iv,url.trim(),false,defaultRes,listener); + } else { + iv.setImageResource(defaultRes); + } + } + + /** + * 展示图片 + * @param iv + * @param url + * @param defaultRes + * @param listener + */ + private void display(ImageView iv,String url,boolean isCircle,int defaultRes,ImageLoadingListener listener){ + if(!url.equals(iv.getTag())){//增加tag标记,减少UIL的display次数 + iv.setTag(url); + //不直接display imageview改为ImageAware,解决ListView滚动时重复加载图片 + ImageAware imageAware = new ImageViewAware(iv, false); + ImageLoader.getInstance().displayImage(url, imageAware, DisplayConfig.getDefaultOptions(isCircle,defaultRes),listener); + } + } + + /** + * 初始化ImageLoader + * @param context + */ + public static void initImageLoader(Context context) { + ImageLoaderConfiguration.Builder config = new ImageLoaderConfiguration.Builder(context); + config.threadPoolSize(3); + config.memoryCache(new WeakMemoryCache()); + config.threadPriority(Thread.NORM_PRIORITY - 2); + config.denyCacheImageMultipleSizesInMemory(); + config.diskCacheFileNameGenerator(new Md5FileNameGenerator()); + config.diskCacheSize(50 * 1024 * 1024); // 50 MiB + config.tasksProcessingOrder(QueueProcessingType.LIFO); +// config.writeDebugLogs(); // Remove for release app + // Initialize ImageLoader with configuration. + ImageLoader.getInstance().init(config.build()); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/bean/AddFriendMessage.java b/app/src/main/java/cn/bmob/imdemo/bean/AddFriendMessage.java new file mode 100755 index 0000000..a3c98ed --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/bean/AddFriendMessage.java @@ -0,0 +1,63 @@ +package cn.bmob.imdemo.bean; + +import android.text.TextUtils; + +import org.json.JSONObject; + +import cn.bmob.imdemo.Config; +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.newim.bean.BmobIMExtraMessage; +import cn.bmob.newim.bean.BmobIMMessage; +import com.orhanobut.logger.Logger; + +/**添加好友请求 + * @author :smile + * @project:AddFriendMessage + * @date :2016-01-30-17:28 + */ +public class AddFriendMessage extends BmobIMExtraMessage{ + + public AddFriendMessage(){} + + /**将BmobIMMessage转成NewFriend + * @param msg 消息 + * @return + */ + public static NewFriend convert(BmobIMMessage msg){ + NewFriend add =new NewFriend(); + String content = msg.getContent(); + add.setMsg(content); + add.setTime(msg.getCreateTime()); + add.setStatus(Config.STATUS_VERIFY_NONE); + try { + String extra = msg.getExtra(); + if(!TextUtils.isEmpty(extra)){ + JSONObject json =new JSONObject(extra); + String name = json.getString("name"); + add.setName(name); + String avatar = json.getString("avatar"); + add.setAvatar(avatar); + add.setUid(json.getString("uid")); + }else{ + Logger.i("AddFriendMessage的extra为空"); + } + } catch (Exception e) { + e.printStackTrace(); + } + return add; + } + + + @Override + public String getMsgType() { + return "add"; + } + + @Override + public boolean isTransient() { + //设置为true,表明为暂态消息,那么这条消息并不会保存到本地db中,SDK只负责发送出去 + //设置为false,则会保存到指定会话的数据库中 + return true; + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/bean/AgreeAddFriendMessage.java b/app/src/main/java/cn/bmob/imdemo/bean/AgreeAddFriendMessage.java new file mode 100755 index 0000000..360ddfc --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/bean/AgreeAddFriendMessage.java @@ -0,0 +1,95 @@ +package cn.bmob.imdemo.bean; + +import android.text.TextUtils; + +import com.orhanobut.logger.Logger; + +import org.json.JSONObject; + +import cn.bmob.newim.bean.BmobIMExtraMessage; +import cn.bmob.newim.bean.BmobIMMessage; + +/**同意添加好友请求-仅仅只用于发送同意添加好友的消息 + * @author smile + * @project AgreeAddFriendMessage + * @date 2016-03-04-10:41 + * 接收到对方发送的同意添加自己为好友的请求时,需要做两个事情:1、在本地数据库中新建一个会话,因此需要设置isTransient为false,2、添加对方到自己的好友表中 + */ +public class AgreeAddFriendMessage extends BmobIMExtraMessage{ + + //以下均是从extra里面抽离出来的字段,方便获取 + private String uid;//最初的发送方 + private Long time; + private String msg;//用于通知栏显示的内容 + + @Override + public String getMsgType() { + return "agree"; + } + + @Override + public boolean isTransient() { + //如果需要在对方的会话表中新增一条该类型的消息,则设置为false,表明是非暂态会话 + //此处将同意添加好友的请求设置为false,为了演示怎样向会话表和消息表中新增一个类型 + return false; + } + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public Long getTime() { + return time; + } + + public void setTime(Long time) { + this.time = time; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public AgreeAddFriendMessage(){} + + /** + * 继承BmobIMMessage的属性 + * @param msg + */ + private AgreeAddFriendMessage(BmobIMMessage msg){ + super.parse(msg); + } + + /**将BmobIMMessage转成AgreeAddFriendMessage + * @param msg 消息 + * @return + */ + public static AgreeAddFriendMessage convert(BmobIMMessage msg){ + AgreeAddFriendMessage agree =new AgreeAddFriendMessage(msg); + try { + String extra = msg.getExtra(); + if(!TextUtils.isEmpty(extra)){ + JSONObject json =new JSONObject(extra); + Long time = json.getLong("time"); + String uid =json.getString("uid"); + String m =json.getString("msg"); + agree.setMsg(m); + agree.setUid(uid); + agree.setTime(time); + }else{ + Logger.i("AgreeAddFriendMessage的extra为空"); + } + } catch (Exception e) { + e.printStackTrace(); + } + return agree; + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/bean/Conversation.java b/app/src/main/java/cn/bmob/imdemo/bean/Conversation.java new file mode 100755 index 0000000..e618dee --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/bean/Conversation.java @@ -0,0 +1,106 @@ +package cn.bmob.imdemo.bean; + +import android.content.Context; + +import java.io.Serializable; + +import cn.bmob.newim.bean.BmobIMConversationType; + +/** + * 对BmobIMConveration的抽象封装,方便开发者扩展会话类型 + * @author smile + * @date 2016-05-25 + */ +public abstract class Conversation implements Serializable,Comparable{ + + /** + * 会话id + */ + protected String cId; + /** + * 会话类型 + */ + protected BmobIMConversationType cType; + /** + * 会话名称 + */ + protected String cName; + + /** + * 获取头像-用于会话界面显示 + */ + abstract public Object getAvatar(); + + /** + * 获取最后一条消息的时间 + */ + abstract public long getLastMessageTime(); + + /** + * 获取最后一条消息的时间 + * @return + */ + abstract public String getLastMessageContent(); + + /** + * 获取未读会话个数 + * @return + */ + abstract public int getUnReadCount(); + + /** + * 将所有消息标记为已读 + */ + abstract public void readAllMessages(); + + /** + * 点击事件 + * @param context + */ + abstract public void onClick(Context context); + + /** + * 长按事件 + * @param context + */ + abstract public void onLongClick(Context context); + + public String getcName() { + return cName; + } + + public String getcId(){ + return cId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Conversation that = (Conversation) o; + if (!cId.equals(that.cId)) return false; + return cType == that.cType; + } + + @Override + public int hashCode() { + int result = cId.hashCode(); + result = 31 * result + cType.hashCode(); + return result; + } + + + @Override + public int compareTo(Object another) { + if (another instanceof Conversation){ + Conversation anotherConversation = (Conversation) another; + long timeGap = anotherConversation.getLastMessageTime() - getLastMessageTime(); + if (timeGap > 0) return 1; + else if (timeGap < 0) return -1; + return 0; + }else{ + throw new ClassCastException(); + } + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/bean/Friend.java b/app/src/main/java/cn/bmob/imdemo/bean/Friend.java new file mode 100755 index 0000000..3e902a5 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/bean/Friend.java @@ -0,0 +1,41 @@ +package cn.bmob.imdemo.bean; + +import cn.bmob.v3.BmobObject; + +/**好友表 + * @author smile + * @project Friend + * @date 2016-04-26 + */ +public class Friend extends BmobObject{ + + private User user; + private User friendUser; + + //拼音 + private transient String pinyin; + + public String getPinyin() { + return pinyin; + } + + public void setPinyin(String pinyin) { + this.pinyin = pinyin; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public User getFriendUser() { + return friendUser; + } + + public void setFriendUser(User friendUser) { + this.friendUser = friendUser; + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/bean/NewFriendConversation.java b/app/src/main/java/cn/bmob/imdemo/bean/NewFriendConversation.java new file mode 100755 index 0000000..4420103 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/bean/NewFriendConversation.java @@ -0,0 +1,83 @@ +package cn.bmob.imdemo.bean; + +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; + +import cn.bmob.imdemo.BmobIMApplication; +import cn.bmob.imdemo.Config; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.imdemo.db.NewFriendManager; +import cn.bmob.imdemo.ui.NewFriendActivity; +import cn.bmob.imdemo.util.TimeUtil; + +/** + * 新朋友会话 + * Created by Administrator on 2016/5/25. + */ +public class NewFriendConversation extends Conversation{ + + NewFriend lastFriend; + + public NewFriendConversation(NewFriend friend){ + this.lastFriend=friend; + this.cName="新朋友"; + } + + @Override + public String getLastMessageContent() { + if(lastFriend!=null){ + Integer status =lastFriend.getStatus(); + String name = lastFriend.getName(); + if(TextUtils.isEmpty(name)){ + name = lastFriend.getUid(); + } + //目前的好友请求都是别人发给我的 + if(status==null || status== Config.STATUS_VERIFY_NONE||status ==Config.STATUS_VERIFY_READED){ + return name+"请求添加好友"; + }else{ + return "我已添加"+name; + } + }else{ + return ""; + } + } + + @Override + public long getLastMessageTime() { + if(lastFriend!=null){ + return lastFriend.getTime(); + }else{ + return 0; + } + } + + @Override + public Object getAvatar() { + return R.mipmap.new_friends_icon; + } + + @Override + public int getUnReadCount() { + return NewFriendManager.getInstance(BmobIMApplication.INSTANCE()).getNewInvitationCount(); + } + + @Override + public void readAllMessages() { + //批量更新未读未认证的消息为已读状态 + NewFriendManager.getInstance(BmobIMApplication.INSTANCE()).updateBatchStatus(); + } + + @Override + public void onClick(Context context) { + Intent intent = new Intent(); + intent.setClass(context, NewFriendActivity.class); + context.startActivity(intent); + } + + @Override + public void onLongClick(Context context) { + NewFriendManager.getInstance(context).deleteNewFriend(lastFriend); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/bean/PrivateConversation.java b/app/src/main/java/cn/bmob/imdemo/bean/PrivateConversation.java new file mode 100755 index 0000000..daaeb26 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/bean/PrivateConversation.java @@ -0,0 +1,117 @@ +package cn.bmob.imdemo.bean; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; + +import java.util.List; + +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.ui.ChatActivity; +import cn.bmob.imdemo.util.TimeUtil; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMConversationType; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMMessageType; + +/** + * 私聊会话 + * Created by Administrator on 2016/5/25. + */ +public class PrivateConversation extends Conversation{ + + private BmobIMConversation conversation; + private BmobIMMessage lastMsg; + + public PrivateConversation(BmobIMConversation conversation){ + this.conversation = conversation; + cType = BmobIMConversationType.setValue(conversation.getConversationType()); + cId = conversation.getConversationId(); + if (cType == BmobIMConversationType.PRIVATE){ + cName=conversation.getConversationTitle(); + if (TextUtils.isEmpty(cName)) cName = cId; + }else{ + cName="未知会话"; + } + List msgs =conversation.getMessages(); + if(msgs!=null && msgs.size()>0){ + lastMsg =msgs.get(0); + } + } + + @Override + public void readAllMessages() { + conversation.updateLocalCache(); + } + + @Override + public Object getAvatar() { + if (cType == BmobIMConversationType.PRIVATE){ + String avatar = conversation.getConversationIcon(); + if (TextUtils.isEmpty(avatar)){//头像为空,使用默认头像 + return R.mipmap.head; + }else{ + return avatar; + } + }else{ + return R.mipmap.head; + } + } + + @Override + public String getLastMessageContent() { + if(lastMsg!=null){ + String content =lastMsg.getContent(); + if(lastMsg.getMsgType().equals(BmobIMMessageType.TEXT.getType()) || lastMsg.getMsgType().equals("agree")){ + return content; + }else if(lastMsg.getMsgType().equals(BmobIMMessageType.IMAGE.getType())){ + return "[图片]"; + }else if(lastMsg.getMsgType().equals(BmobIMMessageType.VOICE.getType())){ + return "[语音]"; + }else if(lastMsg.getMsgType().equals(BmobIMMessageType.LOCATION.getType())){ + return"[位置]"; + }else if(lastMsg.getMsgType().equals(BmobIMMessageType.VIDEO.getType())){ + return "[视频]"; + }else{//开发者自定义的消息类型,需要自行处理 + return "[未知]"; + } + }else{//防止消息错乱 + return ""; + } + } + + @Override + public long getLastMessageTime() { + if(lastMsg!=null) { + return lastMsg.getCreateTime(); + }else{ + return 0; + } + } + + @Override + public int getUnReadCount() { + return (int)BmobIM.getInstance().getUnReadCount(conversation.getConversationId()); + } + + @Override + public void onClick(Context context) { + Intent intent = new Intent(); + intent.setClass(context, ChatActivity.class); + Bundle bundle = new Bundle(); + bundle.putSerializable("c", conversation); + if (bundle != null) { + intent.putExtra(context.getPackageName(), bundle); + } + context.startActivity(intent); + } + + @Override + public void onLongClick(Context context) { + //以下两种方式均可以删除会话 +// BmobIM.getInstance().deleteConversation(conversation.getConversationId()); + BmobIM.getInstance().deleteConversation(conversation); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/bean/User.java b/app/src/main/java/cn/bmob/imdemo/bean/User.java new file mode 100755 index 0000000..a5429e9 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/bean/User.java @@ -0,0 +1,30 @@ +package cn.bmob.imdemo.bean; + +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.v3.BmobUser; + +/** + * @author :smile + * @project:User + * @date :2016-01-22-18:11 + */ +public class User extends BmobUser { + + private String avatar; + + public User(){} + + public User(NewFriend friend){ + setObjectId(friend.getUid()); + setUsername(friend.getName()); + setAvatar(friend.getAvatar()); + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/db/NewFriend.java b/app/src/main/java/cn/bmob/imdemo/db/NewFriend.java new file mode 100755 index 0000000..868a301 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/db/NewFriend.java @@ -0,0 +1,99 @@ +package cn.bmob.imdemo.db; + +// THIS CODE IS GENERATED BY greenDAO, EDIT ONLY INSIDE THE "KEEP"-SECTIONS + +// KEEP INCLUDES - put your custom includes here +// KEEP INCLUDES END +/** + * Entity mapped to table "newfriend". + */ +public class NewFriend implements java.io.Serializable { + + private Long id; + private String uid; + private String msg; + private String name; + private String avatar; + private Integer status; + private Long time; + + // KEEP FIELDS - put your custom fields here + // KEEP FIELDS END + + public NewFriend() { + } + + public NewFriend(Long id) { + this.id = id; + } + + public NewFriend(Long id, String uid, String msg, String name, String avatar, Integer status, Long time) { + this.id = id; + this.uid = uid; + this.msg = msg; + this.name = name; + this.avatar = avatar; + this.status = status; + this.time = time; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getUid() { + return uid; + } + + public void setUid(String uid) { + this.uid = uid; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Long getTime() { + return time; + } + + public void setTime(Long time) { + this.time = time; + } + + // KEEP METHODS - put your custom methods here + // KEEP METHODS END + +} diff --git a/app/src/main/java/cn/bmob/imdemo/db/NewFriendManager.java b/app/src/main/java/cn/bmob/imdemo/db/NewFriendManager.java new file mode 100755 index 0000000..48ced84 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/db/NewFriendManager.java @@ -0,0 +1,213 @@ +package cn.bmob.imdemo.db; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; +import android.util.Log; + +import com.orhanobut.logger.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import cn.bmob.imdemo.Config; +import cn.bmob.imdemo.bean.AgreeAddFriendMessage; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.db.dao.DaoMaster; +import cn.bmob.imdemo.db.dao.DaoSession; +import cn.bmob.imdemo.db.dao.NewFriendDao; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMReceiveStatus; +import cn.bmob.newim.db.dao.MessageDao; +import cn.bmob.v3.BmobUser; + + +/** + * Created by Administrator on 2016/4/27. + */ +public class NewFriendManager { + + private DaoMaster.DevOpenHelper openHelper; + Context mContecxt; + String uid=null; + private static HashMap daoMap = new HashMap<>(); + + /**获取DB实例 + * @param context + * @return + */ + public static NewFriendManager getInstance(Context context) { + User user = BmobUser.getCurrentUser(context, User.class); + String loginId=user.getObjectId(); + if(TextUtils.isEmpty(loginId)){ + throw new RuntimeException("you must login."); + } + NewFriendManager dao = daoMap.get(loginId); + if (dao == null) { + dao = new NewFriendManager(context,loginId); + daoMap.put(loginId, dao); + } + return dao; + } + + private NewFriendManager(Context context, String uId){ + clear(); + this.mContecxt =context.getApplicationContext(); + this.uid=uId; + String DBName = uId+".demodb"; + this.openHelper = new DaoMaster.DevOpenHelper(mContecxt, DBName, null); + } + + /** + * 清空资源 + */ + public void clear() { + if(openHelper !=null) { + openHelper.close(); + openHelper = null; + mContecxt=null; + uid =null; + } + } + + private DaoSession openReadableDb() { + checkInit(); + SQLiteDatabase db = openHelper.getReadableDatabase(); + DaoMaster daoMaster = new DaoMaster(db); + DaoSession daoSession = daoMaster.newSession(); + return daoSession; + } + + private DaoSession openWritableDb(){ + checkInit(); + SQLiteDatabase db = openHelper.getWritableDatabase(); + DaoMaster daoMaster = new DaoMaster(db); + DaoSession daoSession = daoMaster.newSession(); + return daoSession; + } + + private void checkInit(){ + if(openHelper ==null){ + throw new RuntimeException("请初始化db"); + } + } + + //------------------------------------------------------------- + + /**获取本地所有的邀请信息 + * @return + */ + public List getAllNewFriend(){ + NewFriendDao dao =openReadableDb().getNewFriendDao(); + return dao.queryBuilder().orderDesc(NewFriendDao.Properties.Time).list(); + } + + /**创建或更新新朋友信息 + * @param info + * @return long:返回插入或修改的id + */ + public long insertOrUpdateNewFriend(NewFriend info){ + NewFriendDao dao = openWritableDb().getNewFriendDao(); + NewFriend local = getNewFriend(info.getUid(), info.getTime()); + if(local==null){ + return dao.insertOrReplace(info); + }else{ + return -1; + } + } + + /** + * 获取本地的好友请求 + * @param uid + * @param time + * @return + */ + private NewFriend getNewFriend(String uid,Long time){ + NewFriendDao dao = openReadableDb().getNewFriendDao(); + return dao.queryBuilder().where(NewFriendDao.Properties.Uid.eq(uid)) + .where(NewFriendDao.Properties.Time.eq(time)).build().unique(); + } + + /** + * 是否有新的好友邀请 + * @return + */ + public boolean hasNewFriendInvitation(){ + List infos =getNoVerifyNewFriend(); + if(infos!=null && infos.size()>0){ + return true; + }else{ + return false; + } + } + + /** + * 获取未读的好友邀请 + * @return + */ + public int getNewInvitationCount(){ + List infos =getNoVerifyNewFriend(); + if(infos!=null && infos.size()>0){ + return infos.size(); + }else{ + return 0; + } + } + /** + * 获取所有未读未验证的好友请求 + * @return + */ + private List getNoVerifyNewFriend(){ + NewFriendDao dao = openReadableDb().getNewFriendDao(); + return dao.queryBuilder().where(NewFriendDao.Properties.Status.eq(Config.STATUS_VERIFY_NONE)) + .build().list(); + } + + /** + * 批量更新未读未验证的状态为已读 + */ + public void updateBatchStatus(){ + List infos =getNoVerifyNewFriend(); + if(infos!=null && infos.size()>0){ + int size =infos.size(); + List all =new ArrayList<>(); + for (int i = 0; i < size; i++) { + NewFriend msg =infos.get(i); + msg.setStatus(Config.STATUS_VERIFY_READED); + all.add(msg); + } + insertBatchMessages(infos); + } + } + + /**批量插入消息 + * @param msgs + */ + public void insertBatchMessages(List msgs){ + NewFriendDao dao =openWritableDb().getNewFriendDao(); + dao.insertOrReplaceInTx(msgs); + } + + /** + * 修改指定好友请求的状态 + * @param friend + * @param status + * @return + */ + public long updateNewFriend(NewFriend friend,int status){ + NewFriendDao dao = openWritableDb().getNewFriendDao(); + friend.setStatus(status); + return dao.insertOrReplace(friend); + } + + /** + * 删除指定的添加请求 + * @param friend + */ + public void deleteNewFriend(NewFriend friend){ + NewFriendDao dao =openWritableDb().getNewFriendDao(); + dao.delete(friend); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/db/dao/DaoMaster.java b/app/src/main/java/cn/bmob/imdemo/db/dao/DaoMaster.java new file mode 100755 index 0000000..f15d716 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/db/dao/DaoMaster.java @@ -0,0 +1,70 @@ +package cn.bmob.imdemo.db.dao; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDatabase.CursorFactory; +import android.database.sqlite.SQLiteOpenHelper; +import android.util.Log; +import de.greenrobot.dao.AbstractDaoMaster; +import de.greenrobot.dao.identityscope.IdentityScopeType; + +import cn.bmob.imdemo.db.dao.NewFriendDao; + +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. +/** + * Master of DAO (schema version 1): knows all DAOs. +*/ +public class DaoMaster extends AbstractDaoMaster { + public static final int SCHEMA_VERSION = 1; + + /** Creates underlying database table using DAOs. */ + public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) { + NewFriendDao.createTable(db, ifNotExists); + } + + /** Drops underlying database table using DAOs. */ + public static void dropAllTables(SQLiteDatabase db, boolean ifExists) { + NewFriendDao.dropTable(db, ifExists); + } + + public static abstract class OpenHelper extends SQLiteOpenHelper { + + public OpenHelper(Context context, String name, CursorFactory factory) { + super(context, name, factory, SCHEMA_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); + createAllTables(db, false); + } + } + + /** WARNING: Drops all table on Upgrade! Use only during development. */ + public static class DevOpenHelper extends OpenHelper { + public DevOpenHelper(Context context, String name, CursorFactory factory) { + super(context, name, factory); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); + dropAllTables(db, true); + onCreate(db); + } + } + + public DaoMaster(SQLiteDatabase db) { + super(db, SCHEMA_VERSION); + registerDaoClass(NewFriendDao.class); + } + + public DaoSession newSession() { + return new DaoSession(db, IdentityScopeType.Session, daoConfigMap); + } + + public DaoSession newSession(IdentityScopeType type) { + return new DaoSession(db, type, daoConfigMap); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/db/dao/DaoSession.java b/app/src/main/java/cn/bmob/imdemo/db/dao/DaoSession.java new file mode 100755 index 0000000..2a8a5ed --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/db/dao/DaoSession.java @@ -0,0 +1,49 @@ +package cn.bmob.imdemo.db.dao; + +import android.database.sqlite.SQLiteDatabase; + +import java.util.Map; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.AbstractDaoSession; +import de.greenrobot.dao.identityscope.IdentityScopeType; +import de.greenrobot.dao.internal.DaoConfig; + +import cn.bmob.imdemo.db.NewFriend; + +import cn.bmob.imdemo.db.dao.NewFriendDao; + +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. + +/** + * {@inheritDoc} + * + * @see de.greenrobot.dao.AbstractDaoSession + */ +public class DaoSession extends AbstractDaoSession { + + private final DaoConfig newFriendDaoConfig; + + private final NewFriendDao newFriendDao; + + public DaoSession(SQLiteDatabase db, IdentityScopeType type, Map>, DaoConfig> + daoConfigMap) { + super(db); + + newFriendDaoConfig = daoConfigMap.get(NewFriendDao.class).clone(); + newFriendDaoConfig.initIdentityScope(type); + + newFriendDao = new NewFriendDao(newFriendDaoConfig, this); + + registerDao(NewFriend.class, newFriendDao); + } + + public void clear() { + newFriendDaoConfig.getIdentityScope().clear(); + } + + public NewFriendDao getNewFriendDao() { + return newFriendDao; + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/db/dao/NewFriendDao.java b/app/src/main/java/cn/bmob/imdemo/db/dao/NewFriendDao.java new file mode 100755 index 0000000..a73c358 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/db/dao/NewFriendDao.java @@ -0,0 +1,160 @@ +package cn.bmob.imdemo.db.dao; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteStatement; + +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.Property; +import de.greenrobot.dao.internal.DaoConfig; + +import cn.bmob.imdemo.db.NewFriend; + +// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. +/** + * DAO for table "newfriend". +*/ +public class NewFriendDao extends AbstractDao { + + public static final String TABLENAME = "newfriend"; + + /** + * Properties of entity NewFriend.
+ * Can be used for QueryBuilder and for referencing column names. + */ + public static class Properties { + public final static Property Id = new Property(0, Long.class, "id", true, "_id"); + public final static Property Uid = new Property(1, String.class, "uid", false, "UID"); + public final static Property Msg = new Property(2, String.class, "msg", false, "MSG"); + public final static Property Name = new Property(3, String.class, "name", false, "NAME"); + public final static Property Avatar = new Property(4, String.class, "avatar", false, "AVATAR"); + public final static Property Status = new Property(5, Integer.class, "status", false, "STATUS"); + public final static Property Time = new Property(6, Long.class, "time", false, "TIME"); + }; + + + public NewFriendDao(DaoConfig config) { + super(config); + } + + public NewFriendDao(DaoConfig config, DaoSession daoSession) { + super(config, daoSession); + } + + /** Creates the underlying database table. */ + public static void createTable(SQLiteDatabase db, boolean ifNotExists) { + String constraint = ifNotExists? "IF NOT EXISTS ": ""; + db.execSQL("CREATE TABLE " + constraint + "\"newfriend\" (" + // + "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id + "\"UID\" TEXT," + // 1: uid + "\"MSG\" TEXT," + // 2: msg + "\"NAME\" TEXT," + // 3: name + "\"AVATAR\" TEXT," + // 4: avatar + "\"STATUS\" INTEGER," + // 5: status + "\"TIME\" INTEGER);"); // 6: time + } + + /** Drops the underlying database table. */ + public static void dropTable(SQLiteDatabase db, boolean ifExists) { + String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"newfriend\""; + db.execSQL(sql); + } + + /** @inheritdoc */ + @Override + protected void bindValues(SQLiteStatement stmt, NewFriend entity) { + stmt.clearBindings(); + + Long id = entity.getId(); + if (id != null) { + stmt.bindLong(1, id); + } + + String uid = entity.getUid(); + if (uid != null) { + stmt.bindString(2, uid); + } + + String msg = entity.getMsg(); + if (msg != null) { + stmt.bindString(3, msg); + } + + String name = entity.getName(); + if (name != null) { + stmt.bindString(4, name); + } + + String avatar = entity.getAvatar(); + if (avatar != null) { + stmt.bindString(5, avatar); + } + + Integer status = entity.getStatus(); + if (status != null) { + stmt.bindLong(6, status); + } + + Long time = entity.getTime(); + if (time != null) { + stmt.bindLong(7, time); + } + } + + /** @inheritdoc */ + @Override + public Long readKey(Cursor cursor, int offset) { + return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0); + } + + /** @inheritdoc */ + @Override + public NewFriend readEntity(Cursor cursor, int offset) { + NewFriend entity = new NewFriend( // + cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id + cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // uid + cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // msg + cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3), // name + cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // avatar + cursor.isNull(offset + 5) ? null : cursor.getInt(offset + 5), // status + cursor.isNull(offset + 6) ? null : cursor.getLong(offset + 6) // time + ); + return entity; + } + + /** @inheritdoc */ + @Override + public void readEntity(Cursor cursor, NewFriend entity, int offset) { + entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0)); + entity.setUid(cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1)); + entity.setMsg(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2)); + entity.setName(cursor.isNull(offset + 3) ? null : cursor.getString(offset + 3)); + entity.setAvatar(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4)); + entity.setStatus(cursor.isNull(offset + 5) ? null : cursor.getInt(offset + 5)); + entity.setTime(cursor.isNull(offset + 6) ? null : cursor.getLong(offset + 6)); + } + + /** @inheritdoc */ + @Override + protected Long updateKeyAfterInsert(NewFriend entity, long rowId) { + entity.setId(rowId); + return rowId; + } + + /** @inheritdoc */ + @Override + public Long getKey(NewFriend entity) { + if(entity != null) { + return entity.getId(); + } else { + return null; + } + } + + /** @inheritdoc */ + @Override + protected boolean isEntityUpdateable() { + return true; + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/event/FinishEvent.java b/app/src/main/java/cn/bmob/imdemo/event/FinishEvent.java new file mode 100755 index 0000000..44a62a4 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/event/FinishEvent.java @@ -0,0 +1,11 @@ +package cn.bmob.imdemo.event; + +/** + * @author :smile + * @project:FinishEvent + * @date :2016-01-25-15:25 + */ +public class FinishEvent { + + public FinishEvent(){} +} diff --git a/app/src/main/java/cn/bmob/imdemo/event/RefreshEvent.java b/app/src/main/java/cn/bmob/imdemo/event/RefreshEvent.java new file mode 100755 index 0000000..ea86c83 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/event/RefreshEvent.java @@ -0,0 +1,8 @@ +package cn.bmob.imdemo.event; + +/** + * Created by Administrator on 2016/4/28. + */ +public class RefreshEvent { + public RefreshEvent(){} +} diff --git a/app/src/main/java/cn/bmob/imdemo/model/BaseModel.java b/app/src/main/java/cn/bmob/imdemo/model/BaseModel.java new file mode 100755 index 0000000..cb8f6fc --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/model/BaseModel.java @@ -0,0 +1,22 @@ +package cn.bmob.imdemo.model; + +import android.content.Context; + +import cn.bmob.imdemo.BmobIMApplication; + +/** + * @author :smile + * @project:BaseModel + * @date :2016-01-23-10:37 + */ +public abstract class BaseModel { + + public int CODE_NULL=1000; + public static int CODE_NOT_EQUAL=1001; + + public static final int DEFAULT_LIMIT=20; + + public Context getContext(){ + return BmobIMApplication.INSTANCE(); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/model/UserModel.java b/app/src/main/java/cn/bmob/imdemo/model/UserModel.java new file mode 100755 index 0000000..bb6bf74 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/model/UserModel.java @@ -0,0 +1,268 @@ +package cn.bmob.imdemo.model; + +import android.text.TextUtils; + +import com.orhanobut.logger.Logger; + +import java.util.ArrayList; +import java.util.List; + +import cn.bmob.imdemo.bean.Friend; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.imdemo.model.i.QueryUserListener; +import cn.bmob.imdemo.model.i.UpdateCacheListener; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.event.MessageEvent; +import cn.bmob.v3.BmobQuery; +import cn.bmob.v3.BmobUser; +import cn.bmob.v3.exception.BmobException; +import cn.bmob.v3.listener.DeleteListener; +import cn.bmob.v3.listener.FindListener; +import cn.bmob.v3.listener.LogInListener; +import cn.bmob.v3.listener.SaveListener; + +/** + * @author :smile + * @project:UserModel + * @date :2016-01-22-18:09 + */ +public class UserModel extends BaseModel { + + private static UserModel ourInstance = new UserModel(); + + public static UserModel getInstance() { + return ourInstance; + } + + private UserModel() {} + + /** 登录 + * @param username + * @param password + * @param listener + */ + public void login(String username, String password, final LogInListener listener) { + if(TextUtils.isEmpty(username)){ + listener.internalDone(new BmobException(CODE_NULL, "请填写用户名")); + return; + } + if(TextUtils.isEmpty(password)){ + listener.internalDone(new BmobException(CODE_NULL, "请填写密码")); + return; + } + final User user =new User(); + user.setUsername(username); + user.setPassword(password); + user.login(getContext(), new SaveListener() { + @Override + public void onSuccess() { + listener.done(getCurrentUser(), null); + } + + @Override + public void onFailure(int i, String s) { + listener.done(user, new BmobException(i, s)); + } + }); + } + + /** + * 退出登录 + */ + public void logout(){ + BmobUser.logOut(getContext()); + } + + public User getCurrentUser(){ + return BmobUser.getCurrentUser(getContext(), User.class); + } + /** + * @param username + * @param password + * @param pwdagain + * @param listener + */ + public void register(String username,String password, String pwdagain, final LogInListener listener) { + if(TextUtils.isEmpty(username)){ + listener.internalDone(new BmobException(CODE_NULL, "请填写用户名")); + return; + } + if(TextUtils.isEmpty(password)){ + listener.internalDone(new BmobException(CODE_NULL, "请填写密码")); + return; + } + if(TextUtils.isEmpty(pwdagain)){ + listener.internalDone(new BmobException(CODE_NULL, "请填写确认密码")); + return; + } + if(!password.equals(pwdagain)){ + listener.internalDone(new BmobException(CODE_NOT_EQUAL, "两次输入的密码不一致,请重新输入")); + return; + } + final User user = new User(); + user.setUsername(username); + user.setPassword(password); + user.signUp(getContext(), new SaveListener() { + @Override + public void onSuccess() { + listener.done(null, null); + } + + @Override + public void onFailure(int i, String s) { + listener.done(null, new BmobException(i, s)); + } + }); + } + + /**查询用户 + * @param username + * @param limit + * @param listener + */ + public void queryUsers(String username,int limit,final FindListener listener){ + BmobQuery query = new BmobQuery<>(); + //去掉当前用户 + try { + BmobUser user = BmobUser.getCurrentUser(getContext()); + query.addWhereNotEqualTo("username",user.getUsername()); + } catch (Exception e) { + e.printStackTrace(); + } + query.addWhereContains("username", username); + query.setLimit(limit); + query.order("-createdAt"); + query.findObjects(getContext(), new FindListener() { + @Override + public void onSuccess(List list) { + if (list != null && list.size() > 0) { + listener.onSuccess(list); + } else { + listener.onError(CODE_NULL, "查无此人"); + } + } + + @Override + public void onError(int i, String s) { + listener.onError(i, s); + } + }); + } + + /**查询用户信息 + * @param objectId + * @param listener + */ + public void queryUserInfo(String objectId, final QueryUserListener listener){ + BmobQuery query = new BmobQuery<>(); + query.addWhereEqualTo("objectId", objectId); + query.findObjects(getContext(), new FindListener() { + @Override + public void onSuccess(List list) { + if(list!=null && list.size()>0){ + listener.internalDone(list.get(0), null); + }else{ + listener.internalDone(new BmobException(000, "查无此人")); + } + } + + @Override + public void onError(int i, String s) { + listener.internalDone(new BmobException(i, s)); + } + }); + } + + /**更新用户资料和会话资料 + * @param event + * @param listener + */ + public void updateUserInfo(MessageEvent event,final UpdateCacheListener listener){ + final BmobIMConversation conversation=event.getConversation(); + final BmobIMUserInfo info =event.getFromUserInfo(); + final BmobIMMessage msg =event.getMessage(); + String username =info.getName(); + String title =conversation.getConversationTitle(); + Logger.i("" + username + "," + title); + //sdk内部,将新会话的会话标题用objectId表示,因此需要比对用户名和会话标题--单聊,后续会根据会话类型进行判断 + if(!username.equals(title)) { + UserModel.getInstance().queryUserInfo(info.getUserId(), new QueryUserListener() { + @Override + public void done(User s, BmobException e) { + if(e==null){ + String name =s.getUsername(); + String avatar = s.getAvatar(); + Logger.i("query success:"+name+","+avatar); + conversation.setConversationIcon(avatar); + conversation.setConversationTitle(name); + info.setName(name); + info.setAvatar(avatar); + //更新用户资料 + BmobIM.getInstance().updateUserInfo(info); + //更新会话资料-如果消息是暂态消息,则不更新会话资料 + if(!msg.isTransient()){ + BmobIM.getInstance().updateConversation(conversation); + } + }else{ + Logger.e(e); + } + listener.done(null); + } + }); + }else{ + listener.internalDone(null); + } + } + + /** + * 同意添加好友:1、发送同意添加的请求,2、添加对方到自己的好友列表中 + */ + public void agreeAddFriend(User friend,SaveListener listener){ + Friend f = new Friend(); + User user =BmobUser.getCurrentUser(getContext(), User.class); + f.setUser(user); + f.setFriendUser(friend); + f.save(getContext(),listener); + } + + /** + * 查询好友 + * @param listener + */ + public void queryFriends(final FindListener listener){ + BmobQuery query = new BmobQuery<>(); + User user =BmobUser.getCurrentUser(getContext(), User.class); + query.addWhereEqualTo("user", user); + query.include("friendUser"); + query.order("-updatedAt"); + query.findObjects(getContext(), new FindListener() { + @Override + public void onSuccess(List list) { + if (list != null && list.size() > 0) { + listener.onSuccess(list); + } else { + listener.onError(0, "暂无联系人"); + } + } + + @Override + public void onError(int i, String s) { + listener.onError(i, s); + } + }); + } + + /** + * 删除好友 + * @param f + * @param listener + */ + public void deleteFriend(Friend f,DeleteListener listener){ + Friend friend =new Friend(); + friend.delete(getContext(),f.getObjectId(),listener); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/model/i/QueryUserListener.java b/app/src/main/java/cn/bmob/imdemo/model/i/QueryUserListener.java new file mode 100755 index 0000000..7105823 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/model/i/QueryUserListener.java @@ -0,0 +1,20 @@ +package cn.bmob.imdemo.model.i; + +import cn.bmob.imdemo.bean.User; +import cn.bmob.v3.exception.BmobException; +import cn.bmob.v3.listener.BmobListener; + +/** + * @author :smile + * @project:QueryUserListener + * @date :2016-02-01-16:23 + */ +public abstract class QueryUserListener extends BmobListener { + + public abstract void done(User s, BmobException e); + + @Override + protected void postDone(User o, BmobException e) { + done(o, e); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/model/i/UpdateCacheListener.java b/app/src/main/java/cn/bmob/imdemo/model/i/UpdateCacheListener.java new file mode 100755 index 0000000..f445c74 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/model/i/UpdateCacheListener.java @@ -0,0 +1,18 @@ +package cn.bmob.imdemo.model.i; + +import cn.bmob.v3.exception.BmobException; +import cn.bmob.v3.listener.BmobListener; + +/** + * @author :smile + * @project:UpdateCacheListener + * @date :2016-02-01-16:23 + */ +public abstract class UpdateCacheListener extends BmobListener { + public abstract void done(BmobException e); + + @Override + protected void postDone(Object o, BmobException e) { + done(e); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/ChatActivity.java b/app/src/main/java/cn/bmob/imdemo/ui/ChatActivity.java new file mode 100755 index 0000000..47810d1 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/ChatActivity.java @@ -0,0 +1,700 @@ +package cn.bmob.imdemo.ui; + +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.orhanobut.logger.Logger; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import butterknife.Bind; +import butterknife.OnClick; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.ChatAdapter; +import cn.bmob.imdemo.adapter.OnRecyclerViewListener; +import cn.bmob.imdemo.base.ParentWithNaviActivity; +import cn.bmob.imdemo.util.Util; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMAudioMessage; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMImageMessage; +import cn.bmob.newim.bean.BmobIMLocationMessage; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMTextMessage; +import cn.bmob.newim.bean.BmobIMVideoMessage; +import cn.bmob.newim.core.BmobIMClient; +import cn.bmob.newim.core.BmobRecordManager; +import cn.bmob.newim.event.MessageEvent; +import cn.bmob.newim.listener.MessageListHandler; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.newim.listener.MessagesQueryListener; +import cn.bmob.newim.listener.ObseverListener; +import cn.bmob.newim.listener.OnRecordChangeListener; +import cn.bmob.newim.notification.BmobNotificationManager; +import cn.bmob.v3.exception.BmobException; + +/**聊天界面 + * @author :smile + * @project:ChatActivity + * @date :2016-01-25-18:23 + */ +public class ChatActivity extends ParentWithNaviActivity implements ObseverListener,MessageListHandler{ + + @Bind(R.id.ll_chat) + LinearLayout ll_chat; + + @Bind(R.id.sw_refresh) + SwipeRefreshLayout sw_refresh; + + @Bind(R.id.rc_view) + RecyclerView rc_view; + + @Bind(R.id.edit_msg) + EditText edit_msg; + + @Bind(R.id.btn_chat_add) + Button btn_chat_add; + @Bind(R.id.btn_chat_emo) + Button btn_chat_emo; + @Bind(R.id.btn_speak) + Button btn_speak; + @Bind(R.id.btn_chat_voice) + Button btn_chat_voice; + @Bind(R.id.btn_chat_keyboard) + Button btn_chat_keyboard; + @Bind(R.id.btn_chat_send) + Button btn_chat_send; + + @Bind(R.id.layout_more) + LinearLayout layout_more; + @Bind(R.id.layout_add) + LinearLayout layout_add; + @Bind(R.id.layout_emo) + LinearLayout layout_emo; + + // 语音有关 + @Bind(R.id.layout_record) + RelativeLayout layout_record; + @Bind(R.id.tv_voice_tips) + TextView tv_voice_tips; + @Bind(R.id.iv_record) + ImageView iv_record; + private Drawable[] drawable_Anims;// 话筒动画 + BmobRecordManager recordManager; + + ChatAdapter adapter; + protected LinearLayoutManager layoutManager; + BmobIMConversation c; + + @Override + protected String title() { + return c.getConversationTitle(); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_chat); + c= BmobIMConversation.obtain(BmobIMClient.getInstance(), (BmobIMConversation) getBundle().getSerializable("c")); + initNaviView(); + initSwipeLayout(); + initVoiceView(); + initBottomView(); + } + + private void initSwipeLayout(){ + sw_refresh.setEnabled(true); + layoutManager = new LinearLayoutManager(this); + rc_view.setLayoutManager(layoutManager); + adapter = new ChatAdapter(this,c); + rc_view.setAdapter(adapter); + ll_chat.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + ll_chat.getViewTreeObserver().removeGlobalOnLayoutListener(this); + sw_refresh.setRefreshing(true); + //自动刷新 + queryMessages(null); + } + }); + //下拉加载 + sw_refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + BmobIMMessage msg = adapter.getFirstMessage(); + queryMessages(msg); + } + }); + //设置RecyclerView的点击事件 + adapter.setOnRecyclerViewListener(new OnRecyclerViewListener() { + @Override + public void onItemClick(int position) { + Logger.i(""+position); + } + + @Override + public boolean onItemLongClick(int position) { + //这里省了个懒,直接长按就删除了该消息 + c.deleteMessage(adapter.getItem(position)); + adapter.remove(position); + return true; + } + }); + } + + private void initBottomView(){ + edit_msg.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if(event.getAction()==MotionEvent.ACTION_DOWN||event.getAction()==MotionEvent.ACTION_UP){ + scrollToBottom(); + } + return false; + } + }); + edit_msg.addTextChangedListener(new TextWatcher() { + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + scrollToBottom(); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + if (!TextUtils.isEmpty(s)) { + btn_chat_send.setVisibility(View.VISIBLE); + btn_chat_keyboard.setVisibility(View.GONE); + btn_chat_voice.setVisibility(View.GONE); + } else { + if (btn_chat_voice.getVisibility() != View.VISIBLE) { + btn_chat_voice.setVisibility(View.VISIBLE); + btn_chat_send.setVisibility(View.GONE); + btn_chat_keyboard.setVisibility(View.GONE); + } + } + } + + @Override + public void afterTextChanged(Editable s) { + } + }); + } + + /** + * 初始化语音布局 + * @param + * @return void + */ + private void initVoiceView() { + btn_speak.setOnTouchListener(new VoiceTouchListener()); + initVoiceAnimRes(); + initRecordManager(); + } + + /** + * 初始化语音动画资源 + * @Title: initVoiceAnimRes + * @param + * @return void + */ + private void initVoiceAnimRes() { + drawable_Anims = new Drawable[] { + getResources().getDrawable(R.mipmap.chat_icon_voice2), + getResources().getDrawable(R.mipmap.chat_icon_voice3), + getResources().getDrawable(R.mipmap.chat_icon_voice4), + getResources().getDrawable(R.mipmap.chat_icon_voice5), + getResources().getDrawable(R.mipmap.chat_icon_voice6) }; + } + + private void initRecordManager(){ + // 语音相关管理器 + recordManager = BmobRecordManager.getInstance(this); + // 设置音量大小监听--在这里开发者可以自己实现:当剩余10秒情况下的给用户的提示,类似微信的语音那样 + recordManager.setOnRecordChangeListener(new OnRecordChangeListener() { + + @Override + public void onVolumnChanged(int value) { + iv_record.setImageDrawable(drawable_Anims[value]); + } + + @Override + public void onTimeChanged(int recordTime, String localPath) { + Logger.i("voice", "已录音长度:" + recordTime); + if (recordTime >= BmobRecordManager.MAX_RECORD_TIME) {// 1分钟结束,发送消息 + // 需要重置按钮 + btn_speak.setPressed(false); + btn_speak.setClickable(false); + // 取消录音框 + layout_record.setVisibility(View.INVISIBLE); + // 发送语音消息 + sendVoiceMessage(localPath, recordTime); + //是为了防止过了录音时间后,会多发一条语音出去的情况。 + new Handler().postDelayed(new Runnable() { + + @Override + public void run() { + btn_speak.setClickable(true); + } + }, 1000); + } + } + }); + } + + /** + * 长按说话 + * @author smile + * @date 2014-7-1 下午6:10:16 + */ + class VoiceTouchListener implements View.OnTouchListener { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + if (!Util.checkSdCard()) { + toast("发送语音需要sdcard支持!"); + return false; + } + try { + v.setPressed(true); + layout_record.setVisibility(View.VISIBLE); + tv_voice_tips.setText(getString(R.string.voice_cancel_tips)); + // 开始录音 + recordManager.startRecording(c.getConversationId()); + } catch (Exception e) { + e.printStackTrace(); + } + return true; + case MotionEvent.ACTION_MOVE: { + if (event.getY() < 0) { + tv_voice_tips.setText(getString(R.string.voice_cancel_tips)); + tv_voice_tips.setTextColor(Color.RED); + } else { + tv_voice_tips.setText(getString(R.string.voice_up_tips)); + tv_voice_tips.setTextColor(Color.WHITE); + } + return true; + } + case MotionEvent.ACTION_UP: + v.setPressed(false); + layout_record.setVisibility(View.INVISIBLE); + try { + if (event.getY() < 0) {// 放弃录音 + recordManager.cancelRecording(); + Logger.i("voice", "放弃发送语音"); + } else { + int recordTime = recordManager.stopRecording(); + if (recordTime > 1) { + // 发送语音文件 + sendVoiceMessage(recordManager.getRecordFilePath(c.getConversationId()),recordTime); + } else {// 录音时间过短,则提示录音过短的提示 + layout_record.setVisibility(View.GONE); + showShortToast().show(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return true; + default: + return false; + } + } + } + + Toast toast; + + /** + * 显示录音时间过短的Toast + * @Title: showShortToast + * @return void + */ + private Toast showShortToast() { + if (toast == null) { + toast = new Toast(this); + } + View view = LayoutInflater.from(this).inflate( + R.layout.include_chat_voice_short, null); + toast.setView(view); + toast.setGravity(Gravity.CENTER, 0, 0); + toast.setDuration(Toast.LENGTH_SHORT); + return toast; + } + + @OnClick(R.id.edit_msg) + public void onEditClick(View view){ + if (layout_more.getVisibility() == View.VISIBLE) { + layout_add.setVisibility(View.GONE); + layout_emo.setVisibility(View.GONE); + layout_more.setVisibility(View.GONE); + } + } + + @OnClick(R.id.btn_chat_emo) + public void onEmoClick(View view){ + if (layout_more.getVisibility() == View.GONE) { + showEditState(true); + } else { + if (layout_add.getVisibility() == View.VISIBLE) { + layout_add.setVisibility(View.GONE); + layout_emo.setVisibility(View.VISIBLE); + } else { + layout_more.setVisibility(View.GONE); + } + } + } + + @OnClick(R.id.btn_chat_add) + public void onAddClick(View view){ + if (layout_more.getVisibility() == View.GONE) { + layout_more.setVisibility(View.VISIBLE); + layout_add.setVisibility(View.VISIBLE); + layout_emo.setVisibility(View.GONE); + hideSoftInputView(); + } else { + if (layout_emo.getVisibility() == View.VISIBLE) { + layout_emo.setVisibility(View.GONE); + layout_add.setVisibility(View.VISIBLE); + } else { + layout_more.setVisibility(View.GONE); + } + } + } + @OnClick(R.id.btn_chat_voice) + public void onVoiceClick(View view){ + edit_msg.setVisibility(View.GONE); + layout_more.setVisibility(View.GONE); + btn_chat_voice.setVisibility(View.GONE); + btn_chat_keyboard.setVisibility(View.VISIBLE); + btn_speak.setVisibility(View.VISIBLE); + hideSoftInputView(); + } + + @OnClick(R.id.btn_chat_keyboard) + public void onKeyClick(View view){ + showEditState(false); + } + + @OnClick(R.id.btn_chat_send) + public void onSendClick(View view){ + sendMessage(); + } + + @OnClick(R.id.tv_picture) + public void onPictureClick(View view){ +// sendLocalImageMessage(); +// sendOtherMessage(); + sendVideoMessage(); + } + @OnClick(R.id.tv_camera) + public void onCameraClick(View view){ + sendRemoteImageMessage(); + } + + @OnClick(R.id.tv_location) + public void onLocationClick(View view){ + sendLocationMessage(); + } + + /** + * 根据是否点击笑脸来显示文本输入框的状态 + * @param isEmo 用于区分文字和表情 + * @return void + */ + private void showEditState(boolean isEmo) { + edit_msg.setVisibility(View.VISIBLE); + btn_chat_keyboard.setVisibility(View.GONE); + btn_chat_voice.setVisibility(View.VISIBLE); + btn_speak.setVisibility(View.GONE); + edit_msg.requestFocus(); + if (isEmo) { + layout_more.setVisibility(View.VISIBLE); + layout_more.setVisibility(View.VISIBLE); + layout_emo.setVisibility(View.VISIBLE); + layout_add.setVisibility(View.GONE); + hideSoftInputView(); + } else { + layout_more.setVisibility(View.GONE); + showSoftInputView(); + } + } + + /** + * 显示软键盘 + */ + public void showSoftInputView() { + if (getWindow().getAttributes().softInputMode == WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN) { + if (getCurrentFocus() != null) + ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)) + .showSoftInput(edit_msg, 0); + } + } + + /** + * 发送文本消息 + */ + private void sendMessage(){ + String text=edit_msg.getText().toString(); + if(TextUtils.isEmpty(text.trim())){ + toast("请输入内容"); + return; + } + BmobIMTextMessage msg =new BmobIMTextMessage(); + msg.setContent(text); + //可设置额外信息 + Map map =new HashMap<>(); + map.put("level", "1");//随意增加信息 + msg.setExtraMap(map); + c.sendMessage(msg, listener); + } + + /** + * 直接发送远程图片地址 + */ + public void sendRemoteImageMessage(){ + BmobIMImageMessage image =new BmobIMImageMessage(); + image.setRemoteUrl("http://img.lakalaec.com/ad/57ab6dc2-43f2-4087-81e2-b5ab5681642d.jpg"); + c.sendMessage(image, listener); + } + + /** + * 发送本地图片地址 + */ + public void sendLocalImageMessage(){ + //正常情况下,需要调用系统的图库或拍照功能获取到图片的本地地址,开发者只需要将本地的文件地址传过去就可以发送文件类型的消息 + BmobIMImageMessage image =new BmobIMImageMessage("/storage/emulated/0/bimagechooser/IMG_20160302_172003.jpg"); + c.sendMessage(image, listener); +// //因此也可以使用BmobIMFileMessage来发送文件消息 +// BmobIMFileMessage file =new BmobIMFileMessage("文件地址"); +// c.sendMessage(file,listener); + } + + /** + * 发送语音消息 + * @Title: sendVoiceMessage + * @param local + * @param length + * @return void + */ + private void sendVoiceMessage(String local, int length) { + BmobIMAudioMessage audio =new BmobIMAudioMessage(local); + //可设置额外信息-开发者设置的额外信息,需要开发者自己从extra中取出来 + Map map =new HashMap<>(); + map.put("from", "优酷"); + audio.setExtraMap(map); + //设置语音文件时长:可选 +// audio.setDuration(length); + c.sendMessage(audio, listener); + } + + /** + * 发送视频文件 + */ + private void sendVideoMessage(){ + BmobIMVideoMessage video =new BmobIMVideoMessage("/storage/sdcard0/bimagechooser/11.png"); + c.sendMessage(video, listener); + } + + /** + * 发送地理位置 + */ + public void sendLocationMessage(){ + //测试数据,真实数据需要从地图SDK中获取 + BmobIMLocationMessage location =new BmobIMLocationMessage("广州番禺区",23.5,112.0); + Map map =new HashMap<>(); + map.put("from", "百度地图"); + location.setExtraMap(map); + c.sendMessage(location, listener); + } + + /** + * 消息发送监听器 + */ + public MessageSendListener listener =new MessageSendListener() { + + @Override + public void onProgress(int value) { + super.onProgress(value); + //文件类型的消息才有进度值 + Logger.i("onProgress:"+value); + } + + @Override + public void onStart(BmobIMMessage msg) { + super.onStart(msg); + adapter.addMessage(msg); + edit_msg.setText(""); + scrollToBottom(); + } + + @Override + public void done(BmobIMMessage msg, BmobException e) { + adapter.notifyDataSetChanged(); + edit_msg.setText(""); + scrollToBottom(); + if (e != null) { + toast(e.getMessage()); + } + } + }; + + /**首次加载,可设置msg为null,下拉刷新的时候,默认取消息表的第一个msg作为刷新的起始时间点,默认按照消息时间的降序排列 + * @param msg + */ + public void queryMessages(BmobIMMessage msg){ + c.queryMessages(msg, 10, new MessagesQueryListener() { + @Override + public void done(List list, BmobException e) { + sw_refresh.setRefreshing(false); + if (e == null) { + if (null != list && list.size() > 0) { + adapter.addMessages(list); + layoutManager.scrollToPositionWithOffset(list.size() - 1, 0); + } + } else { + toast(e.getMessage() + "(" + e.getErrorCode() + ")"); + } + } + }); + } + + private void scrollToBottom() { + layoutManager.scrollToPositionWithOffset(adapter.getItemCount() - 1, 0); + } + + @Override + public void onMessageReceive(List list) { + Logger.i("聊天页面接收到消息:" + list.size()); + //当注册页面消息监听时候,有消息(包含离线消息)到来时会回调该方法 + for (int i=0;i> map =event.getEventMap(); +// if(map!=null&&map.size()>0){ +// //只获取当前聊天对象的离线消息 +// List list = map.get(c.getConversationId()); +// if(list!=null && list.size()>0){ +// for (int i=0;i cache = BmobNotificationManager.getInstance(this).getNotificationCacheList(); + if(cache.size()>0){ + int size =cache.size(); + for(int i=0;i0){ + iv_conversation_tips.setVisibility(View.VISIBLE); + }else{ + iv_conversation_tips.setVisibility(View.GONE); + }*/ + //是否有好友添加的请求 + if(NewFriendManager.getInstance(this).hasNewFriendInvitation()){ + iv_contact_tips.setVisibility(View.VISIBLE); + }else{ + iv_contact_tips.setVisibility(View.GONE); + } + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/NewFriendActivity.java b/app/src/main/java/cn/bmob/imdemo/ui/NewFriendActivity.java new file mode 100755 index 0000000..9bbdbac --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/NewFriendActivity.java @@ -0,0 +1,148 @@ +package cn.bmob.imdemo.ui; + +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.ViewTreeObserver; +import android.widget.LinearLayout; + +import java.util.List; + +import butterknife.Bind; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.ConversationAdapter; +import cn.bmob.imdemo.adapter.NewFriendAdapter; +import cn.bmob.imdemo.adapter.OnRecyclerViewListener; +import cn.bmob.imdemo.adapter.base.IMutlipleItem; +import cn.bmob.imdemo.base.ParentWithNaviActivity; +import cn.bmob.imdemo.bean.Conversation; +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.imdemo.db.NewFriendManager; + +/**新朋友 + * @author :smile + * @project:NewFriendActivity + * @date :2016-01-25-18:23 + */ +public class NewFriendActivity extends ParentWithNaviActivity { + + @Bind(R.id.ll_root) + LinearLayout ll_root; + @Bind(R.id.rc_view) + RecyclerView rc_view; + @Bind(R.id.sw_refresh) + SwipeRefreshLayout sw_refresh; + NewFriendAdapter adapter; + LinearLayoutManager layoutManager; + + @Override + protected String title() { + return "新朋友"; + } + + @Override + public Object right() { + return R.drawable.base_action_bar_add_bg_selector; + } + + @Override + public ParentWithNaviActivity.ToolBarListener setToolBarListener() { + return new ParentWithNaviActivity.ToolBarListener() { + @Override + public void clickLeft() { + finish(); + } + + @Override + public void clickRight() { + startActivity(SearchUserActivity.class,null); + } + }; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.fragment_conversation); + initNaviView(); + //单一布局 + IMutlipleItem mutlipleItem = new IMutlipleItem() { + + @Override + public int getItemViewType(int postion, NewFriend c) { + return 0; + } + + @Override + public int getItemLayoutId(int viewtype) { + return R.layout.item_new_friend; + } + + @Override + public int getItemCount(List list) { + return list.size(); + } + }; + adapter = new NewFriendAdapter(this,mutlipleItem,null); + rc_view.setAdapter(adapter); + layoutManager = new LinearLayoutManager(this); + rc_view.setLayoutManager(layoutManager); + sw_refresh.setEnabled(true); + //批量更新未读未认证的消息为已读状态 + NewFriendManager.getInstance(this).updateBatchStatus(); + setListener(); + } + + private void setListener(){ + ll_root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + ll_root.getViewTreeObserver().removeGlobalOnLayoutListener(this); + sw_refresh.setRefreshing(true); + query(); + } + }); + sw_refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + query(); + } + }); + adapter.setOnRecyclerViewListener(new OnRecyclerViewListener() { + @Override + public void onItemClick(int position) { + log("点击:"+position); + } + + @Override + public boolean onItemLongClick(int position) { + NewFriendManager.getInstance(NewFriendActivity.this).deleteNewFriend(adapter.getItem(position)); + adapter.remove(position); + return true; + } + }); + } + + @Override + public void onResume() { + super.onResume(); + sw_refresh.setRefreshing(true); + query(); + } + + @Override + public void onPause() { + super.onPause(); + } + + /** + 查询本地会话 + */ + public void query(){ + adapter.bindDatas(NewFriendManager.getInstance(this).getAllNewFriend()); + adapter.notifyDataSetChanged(); + sw_refresh.setRefreshing(false); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/RegisterActivity.java b/app/src/main/java/cn/bmob/imdemo/ui/RegisterActivity.java new file mode 100755 index 0000000..abeb7a2 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/RegisterActivity.java @@ -0,0 +1,67 @@ +package cn.bmob.imdemo.ui; + +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import org.greenrobot.eventbus.EventBus; + +import butterknife.Bind; +import butterknife.OnClick; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.base.ParentWithNaviActivity; +import cn.bmob.imdemo.event.FinishEvent; +import cn.bmob.imdemo.model.BaseModel; +import cn.bmob.imdemo.model.UserModel; +import cn.bmob.v3.exception.BmobException; +import cn.bmob.v3.listener.LogInListener; + +/**注册界面 + * @author :smile + * @project:RegisterActivity + * @date :2016-01-15-18:23 + */ +public class RegisterActivity extends ParentWithNaviActivity { + + @Bind(R.id.et_username) + EditText et_username; + @Bind(R.id.et_password) + EditText et_password; + @Bind(R.id.btn_register) + Button btn_register; + + @Bind(R.id.et_password_again) + EditText et_password_again; + + @Override + protected String title() { + return "注册"; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_register); + initNaviView(); + } + + @OnClick(R.id.btn_register) + public void onRegisterClick(View view){ + UserModel.getInstance().register(et_username.getText().toString(), et_password.getText().toString(),et_password_again.getText().toString(),new LogInListener() { + @Override + public void done(Object o, BmobException e) { + if(e==null){ + EventBus.getDefault().post(new FinishEvent()); + startActivity(MainActivity.class, null, true); + }else{ + if(e.getErrorCode()== BaseModel.CODE_NOT_EQUAL){ + et_password_again.setText(""); + } + toast(e.getMessage()+"("+e.getErrorCode()+")"); + } + } + }); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/SearchUserActivity.java b/app/src/main/java/cn/bmob/imdemo/ui/SearchUserActivity.java new file mode 100755 index 0000000..7ad0999 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/SearchUserActivity.java @@ -0,0 +1,115 @@ +package cn.bmob.imdemo.ui; + +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; + +import com.orhanobut.logger.Logger; + +import org.greenrobot.eventbus.Subscribe; + +import java.util.List; + +import butterknife.Bind; +import butterknife.OnClick; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.OnRecyclerViewListener; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.adapter.SearchUserAdapter; +import cn.bmob.imdemo.base.ParentWithNaviActivity; +import cn.bmob.imdemo.model.BaseModel; +import cn.bmob.imdemo.model.UserModel; +import cn.bmob.v3.listener.FindListener; + +/**搜索好友 + * @author :smile + * @project:SearchUserActivity + * @date :2016-01-25-18:23 + */ +public class SearchUserActivity extends ParentWithNaviActivity { + + @Bind(R.id.et_find_name) + EditText et_find_name; + @Bind(R.id.sw_refresh) + SwipeRefreshLayout sw_refresh; + @Bind(R.id.btn_search) + Button btn_search; + @Bind(R.id.rc_view) + RecyclerView rc_view; + LinearLayoutManager layoutManager; + SearchUserAdapter adapter; + + @Override + protected String title() { + return "搜索好友"; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_search_user); + initNaviView(); + adapter =new SearchUserAdapter(); + layoutManager = new LinearLayoutManager(this); + rc_view.setLayoutManager(layoutManager); + rc_view.setAdapter(adapter); + sw_refresh.setEnabled(true); + sw_refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + query(); + } + }); + adapter.setOnRecyclerViewListener(new OnRecyclerViewListener() { + @Override + public void onItemClick(int position) { + Bundle bundle = new Bundle(); + User user = adapter.getItem(position); + bundle.putSerializable("u", user); + startActivity(UserInfoActivity.class, bundle, false); + } + + @Override + public boolean onItemLongClick(int position) { + return true; + } + }); + } + + @OnClick(R.id.btn_search) + public void onSearchClick(View view){ + sw_refresh.setRefreshing(true); + query(); + } + + public void query(){ + String name =et_find_name.getText().toString(); + if(TextUtils.isEmpty(name)){ + toast("请填写用户名"); + sw_refresh.setRefreshing(false); + return; + } + UserModel.getInstance().queryUsers(name, BaseModel.DEFAULT_LIMIT, new FindListener() { + @Override + public void onSuccess(List list) { + sw_refresh.setRefreshing(false); + adapter.setDatas(list); + adapter.notifyDataSetChanged(); + } + + @Override + public void onError(int i, String s) { + sw_refresh.setRefreshing(false); + adapter.setDatas(null); + adapter.notifyDataSetChanged(); + toast(s + "(" + i + ")"); + } + }); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/SplashActivity.java b/app/src/main/java/cn/bmob/imdemo/ui/SplashActivity.java new file mode 100755 index 0000000..a364d9b --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/SplashActivity.java @@ -0,0 +1,37 @@ +package cn.bmob.imdemo.ui; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; + +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.base.BaseActivity; +import cn.bmob.imdemo.model.UserModel; + +/**启动界面 + * @author :smile + * @project:SplashActivity + * @date :2016-01-15-18:23 + */ +public class SplashActivity extends BaseActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_splash); + Handler handler =new Handler(Looper.getMainLooper()); + handler.postDelayed(new Runnable() { + @Override + public void run() { + User user = UserModel.getInstance().getCurrentUser(); + if (user == null) { + startActivity(LoginActivity.class,null,true); + }else{ + startActivity(MainActivity.class,null,true); + } + } + },1000); + + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/UserInfoActivity.java b/app/src/main/java/cn/bmob/imdemo/ui/UserInfoActivity.java new file mode 100755 index 0000000..7f7da88 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/UserInfoActivity.java @@ -0,0 +1,112 @@ +package cn.bmob.imdemo.ui; + +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.HashMap; +import java.util.Map; + +import butterknife.Bind; +import butterknife.OnClick; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.base.ImageLoaderFactory; +import cn.bmob.imdemo.base.ParentWithNaviActivity; +import cn.bmob.imdemo.bean.AddFriendMessage; +import cn.bmob.imdemo.bean.User; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMMessage; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.newim.core.BmobIMClient; +import cn.bmob.newim.listener.MessageSendListener; +import cn.bmob.v3.BmobUser; +import cn.bmob.v3.exception.BmobException; + +/** + * 用户资料 + */ +public class UserInfoActivity extends ParentWithNaviActivity { + + @Bind(R.id.iv_avator) + ImageView iv_avator; + @Bind(R.id.tv_name) + TextView tv_name; + + @Bind(R.id.btn_add_friend) + Button btn_add_friend; + @Bind(R.id.btn_chat) + Button btn_chat; + + User user; + BmobIMUserInfo info; + @Override + protected String title() { + return "个人资料"; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_user_info); + initNaviView(); + user=(User)getBundle().getSerializable("u"); + if(user.getObjectId().equals(getCurrentUid())){ + btn_add_friend.setVisibility(View.GONE); + btn_chat.setVisibility(View.GONE); + }else{ + btn_add_friend.setVisibility(View.VISIBLE); + btn_chat.setVisibility(View.VISIBLE); + } + //构造聊天方的用户信息:传入用户id、用户名和用户头像三个参数 + info = new BmobIMUserInfo(user.getObjectId(),user.getUsername(),user.getAvatar()); + ImageLoaderFactory.getLoader().loadAvator(iv_avator,user.getAvatar(),R.mipmap.head); + tv_name.setText(user.getUsername()); + } + + @OnClick(R.id.btn_add_friend) + public void onAddClick(View view){ + sendAddFriendMessage(); + } + + /** + * 发送添加好友的请求 + */ + private void sendAddFriendMessage(){ + //启动一个会话,如果isTransient设置为true,则不会创建在本地会话表中创建记录, + //设置isTransient设置为false,则会在本地数据库的会话列表中先创建(如果没有)与该用户的会话信息,且将用户信息存储到本地的用户表中 + BmobIMConversation c = BmobIM.getInstance().startPrivateConversation(info, true,null); + //这个obtain方法才是真正创建一个管理消息发送的会话 + BmobIMConversation conversation = BmobIMConversation.obtain(BmobIMClient.getInstance(), c); + AddFriendMessage msg =new AddFriendMessage(); + User currentUser = BmobUser.getCurrentUser(this,User.class); + msg.setContent("很高兴认识你,可以加个好友吗?");//给对方的一个留言信息 + Map map =new HashMap<>(); + map.put("name", currentUser.getUsername());//发送者姓名,这里只是举个例子,其实可以不需要传发送者的信息过去 + map.put("avatar",currentUser.getAvatar());//发送者的头像 + map.put("uid",currentUser.getObjectId());//发送者的uid + msg.setExtraMap(map); + conversation.sendMessage(msg, new MessageSendListener() { + @Override + public void done(BmobIMMessage msg, BmobException e) { + if (e == null) {//发送成功 + toast("好友请求发送成功,等待验证"); + } else {//发送失败 + toast("发送失败:" + e.getMessage()); + } + } + }); + } + + @OnClick(R.id.btn_chat) + public void onChatClick(View view){ + //启动一个会话,设置isTransient设置为false,则会在本地数据库的会话列表中先创建(如果没有)与该用户的会话信息,且将用户信息存储到本地的用户表中 + BmobIMConversation c = BmobIM.getInstance().startPrivateConversation(info,false,null); + Bundle bundle = new Bundle(); + bundle.putSerializable("c", c); + startActivity(ChatActivity.class, bundle, false); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/fragment/ContactFragment.java b/app/src/main/java/cn/bmob/imdemo/ui/fragment/ContactFragment.java new file mode 100755 index 0000000..9f81f28 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/fragment/ContactFragment.java @@ -0,0 +1,221 @@ +package cn.bmob.imdemo.ui.fragment; + +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.ContactAdapter; +import cn.bmob.imdemo.adapter.OnRecyclerViewListener; +import cn.bmob.imdemo.adapter.base.IMutlipleItem; +import cn.bmob.imdemo.base.ParentWithNaviActivity; +import cn.bmob.imdemo.base.ParentWithNaviFragment; +import cn.bmob.imdemo.bean.Friend; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.event.RefreshEvent; +import cn.bmob.imdemo.model.UserModel; +import cn.bmob.imdemo.ui.ChatActivity; +import cn.bmob.imdemo.ui.NewFriendActivity; +import cn.bmob.imdemo.ui.SearchUserActivity; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.bean.BmobIMUserInfo; +import cn.bmob.v3.listener.DeleteListener; +import cn.bmob.v3.listener.FindListener; + +/**联系人界面 + * @author :smile + * @project:ContactFragment + * @date :2016-04-27-14:23 + */ +public class ContactFragment extends ParentWithNaviFragment { + + @Bind(R.id.rc_view) + RecyclerView rc_view; + @Bind(R.id.sw_refresh) + SwipeRefreshLayout sw_refresh; + ContactAdapter adapter; + LinearLayoutManager layoutManager; + + @Override + protected String title() { + return "联系人"; + } + + @Override + public Object right() { + return R.drawable.base_action_bar_add_bg_selector; + } + + @Override + public ParentWithNaviActivity.ToolBarListener setToolBarListener() { + return new ParentWithNaviActivity.ToolBarListener() { + @Override + public void clickLeft() { + + } + + @Override + public void clickRight() { + startActivity(SearchUserActivity.class,null); + } + }; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + rootView =inflater.inflate(R.layout.fragment_conversation, container, false); + initNaviView(); + ButterKnife.bind(this, rootView); + IMutlipleItem mutlipleItem = new IMutlipleItem() { + + @Override + public int getItemViewType(int postion, Friend friend) { + if(postion==0){ + return ContactAdapter.TYPE_NEW_FRIEND; + }else{ + return ContactAdapter.TYPE_ITEM; + } + } + + @Override + public int getItemLayoutId(int viewtype) { + if(viewtype== ContactAdapter.TYPE_NEW_FRIEND){ + return R.layout.header_new_friend; + }else{ + return R.layout.item_contact; + } + } + + @Override + public int getItemCount(List list) { + return list.size()+1; + } + }; + adapter = new ContactAdapter(getActivity(),mutlipleItem,null); + rc_view.setAdapter(adapter); + layoutManager = new LinearLayoutManager(getActivity()); + rc_view.setLayoutManager(layoutManager); + sw_refresh.setEnabled(true); + setListener(); + return rootView; + } + + private void setListener(){ + rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + sw_refresh.setRefreshing(true); + query(); + } + }); + sw_refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + query(); + } + }); + adapter.setOnRecyclerViewListener(new OnRecyclerViewListener() { + @Override + public void onItemClick(int position) { + if (position == 0) {//跳转到新朋友页面 + startActivity(NewFriendActivity.class, null); + } else { + Friend friend = adapter.getItem(position); + User user = friend.getFriendUser(); + BmobIMUserInfo info = new BmobIMUserInfo(user.getObjectId(), user.getUsername(), user.getAvatar()); + //启动一个会话,实际上就是在本地数据库的会话列表中先创建(如果没有)与该用户的会话信息,且将用户信息存储到本地的用户表中 + BmobIMConversation c = BmobIM.getInstance().startPrivateConversation(info, null); + Bundle bundle = new Bundle(); + bundle.putSerializable("c", c); + startActivity(ChatActivity.class, bundle); + } + } + + @Override + public boolean onItemLongClick(final int position) { + log("长按" + position); + if(position==0){ + return true; + } + UserModel.getInstance().deleteFriend(adapter.getItem(position), new DeleteListener() { + @Override + public void onSuccess() { + toast("好友删除成功"); + adapter.remove(position); + } + + @Override + public void onFailure(int i, String s) { + toast("好友删除失败:" + i + ",s =" + s); + } + }); + return true; + } + }); + } + + @Override + public void onResume() { + super.onResume(); + sw_refresh.setRefreshing(true); + query(); + } + + @Override + public void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + + @Override + public void onStop() { + EventBus.getDefault().unregister(this); + super.onStop(); + } + + /**注册自定义消息接收事件 + * @param event + */ + @Subscribe + public void onEventMainThread(RefreshEvent event){ + //重新刷新列表 + log("---联系人界面接收到自定义消息---"); + adapter.notifyDataSetChanged(); + } + + /** + 查询本地会话 + */ + public void query(){ + UserModel.getInstance().queryFriends(new FindListener() { + @Override + public void onSuccess(List list) { + adapter.bindDatas(list); + adapter.notifyDataSetChanged(); + sw_refresh.setRefreshing(false); + } + + @Override + public void onError(int i, String s) { + adapter.bindDatas(null); + adapter.notifyDataSetChanged(); + sw_refresh.setRefreshing(false); + } + }); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/fragment/ConversationFragment.java b/app/src/main/java/cn/bmob/imdemo/ui/fragment/ConversationFragment.java new file mode 100755 index 0000000..6f6ec69 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/fragment/ConversationFragment.java @@ -0,0 +1,236 @@ +package cn.bmob.imdemo.ui.fragment; + +import android.os.Bundle; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.adapter.ConversationAdapter; +import cn.bmob.imdemo.adapter.OnRecyclerViewListener; +import cn.bmob.imdemo.adapter.base.IMutlipleItem; +import cn.bmob.imdemo.base.ParentWithNaviActivity; +import cn.bmob.imdemo.base.ParentWithNaviFragment; +import cn.bmob.imdemo.bean.Conversation; +import cn.bmob.imdemo.bean.NewFriendConversation; +import cn.bmob.imdemo.bean.PrivateConversation; +import cn.bmob.imdemo.db.NewFriend; +import cn.bmob.imdemo.db.NewFriendManager; +import cn.bmob.imdemo.event.RefreshEvent; +import cn.bmob.imdemo.ui.SearchUserActivity; +import cn.bmob.newim.BmobIM; +import cn.bmob.newim.bean.BmobIMConversation; +import cn.bmob.newim.event.MessageEvent; +import cn.bmob.newim.event.OfflineMessageEvent; + +/**会话界面 + * @author :smile + * @project:ConversationFragment + * @date :2016-01-25-18:23 + */ +public class ConversationFragment extends ParentWithNaviFragment { + + @Bind(R.id.rc_view) + RecyclerView rc_view; + @Bind(R.id.sw_refresh) + SwipeRefreshLayout sw_refresh; + ConversationAdapter adapter; + LinearLayoutManager layoutManager; + + @Override + protected String title() { + return "会话"; + } + + @Override + public Object right() { + return R.drawable.base_action_bar_add_bg_selector; + } + + @Override + public ParentWithNaviActivity.ToolBarListener setToolBarListener() { + return new ParentWithNaviActivity.ToolBarListener() { + @Override + public void clickLeft() { + + } + + @Override + public void clickRight() { + startActivity(SearchUserActivity.class,null); + } + }; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + rootView =inflater.inflate(R.layout.fragment_conversation, container, false); + initNaviView(); + ButterKnife.bind(this, rootView); + //单一布局 + IMutlipleItem mutlipleItem = new IMutlipleItem() { + + @Override + public int getItemViewType(int postion, Conversation c) { + return 0; + } + + @Override + public int getItemLayoutId(int viewtype) { + return R.layout.item_conversation; + } + + @Override + public int getItemCount(List list) { + return list.size(); + } + }; + adapter = new ConversationAdapter(getActivity(),mutlipleItem,null); + rc_view.setAdapter(adapter); + layoutManager = new LinearLayoutManager(getActivity()); + rc_view.setLayoutManager(layoutManager); + sw_refresh.setEnabled(true); + setListener(); + return rootView; + } + + private void setListener(){ + rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + rootView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + sw_refresh.setRefreshing(true); + query(); + } + }); + sw_refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + query(); + } + }); + adapter.setOnRecyclerViewListener(new OnRecyclerViewListener() { + @Override + public void onItemClick(int position) { + adapter.getItem(position).onClick(getActivity()); + } + + @Override + public boolean onItemLongClick(int position) { + adapter.getItem(position).onLongClick(getActivity()); + adapter.remove(position); + return true; + } + }); +} + + @Override + public void onResume() { + super.onResume(); + sw_refresh.setRefreshing(true); + query(); + } + + @Override + public void onPause() { + super.onPause(); + } + + @Override + public void onStart() { + super.onStart(); + EventBus.getDefault().register(this); + } + + @Override + public void onStop() { + EventBus.getDefault().unregister(this); + super.onStop(); + } + + /** + 查询本地会话 + */ + public void query(){ +// adapter.bindDatas(BmobIM.getInstance().loadAllConversation()); + adapter.bindDatas(getConversations()); + adapter.notifyDataSetChanged(); + sw_refresh.setRefreshing(false); + } + + /** + * 获取会话列表的数据:增加新朋友会话 + * @return + */ + private List getConversations(){ + //添加会话 + List conversationList = new ArrayList<>(); + conversationList.clear(); + List list =BmobIM.getInstance().loadAllConversation(); + if(list!=null && list.size()>0){ + for (BmobIMConversation item:list){ + switch (item.getConversationType()){ + case 1://私聊 + conversationList.add(new PrivateConversation(item)); + break; + default: + break; + } + } + } + //添加新朋友会话-获取好友请求表中最新一条记录 + List friends = NewFriendManager.getInstance(getActivity()).getAllNewFriend(); + if(friends!=null && friends.size()>0){ + conversationList.add(new NewFriendConversation(friends.get(0))); + } + //重新排序 + Collections.sort(conversationList); + return conversationList; + } + + /**注册自定义消息接收事件 + * @param event + */ + @Subscribe + public void onEventMainThread(RefreshEvent event){ + log("---会话页接收到自定义消息---"); + //因为新增`新朋友`这种会话类型 + adapter.bindDatas(getConversations()); + adapter.notifyDataSetChanged(); + } + + /**注册离线消息接收事件 + * @param event + */ + @Subscribe + public void onEventMainThread(OfflineMessageEvent event){ + //重新刷新列表 + adapter.bindDatas(getConversations()); + adapter.notifyDataSetChanged(); + } + + /**注册消息接收事件 + * @param event + * 1、与用户相关的由开发者自己维护,SDK内部只存储用户信息 + * 2、开发者获取到信息后,可调用SDK内部提供的方法更新会话 + */ + @Subscribe + public void onEventMainThread(MessageEvent event){ + //重新获取本地消息并刷新列表 + adapter.bindDatas(getConversations()); + adapter.notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/ui/fragment/SetFragment.java b/app/src/main/java/cn/bmob/imdemo/ui/fragment/SetFragment.java new file mode 100755 index 0000000..d3bd20c --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/ui/fragment/SetFragment.java @@ -0,0 +1,76 @@ +package cn.bmob.imdemo.ui.fragment; + +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import cn.bmob.imdemo.R; +import cn.bmob.imdemo.base.ParentWithNaviFragment; +import cn.bmob.imdemo.bean.User; +import cn.bmob.imdemo.model.UserModel; +import cn.bmob.imdemo.ui.LoginActivity; +import cn.bmob.imdemo.ui.UserInfoActivity; +import cn.bmob.newim.BmobIM; +import cn.bmob.v3.BmobUser; + +/**设置 + * @author :smile + * @project:SetFragment + * @date :2016-01-25-18:23 + */ +public class SetFragment extends ParentWithNaviFragment { + + @Bind(R.id.tv_set_name) + TextView tv_set_name; + + @Bind(R.id.layout_info) + RelativeLayout layout_info; + + @Override + protected String title() { + return "设置"; + } + + public static SetFragment newInstance() { + SetFragment fragment = new SetFragment(); + Bundle args = new Bundle(); + fragment.setArguments(args); + return fragment; + } + + public SetFragment() {} + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + rootView= inflater.inflate(R.layout.fragment_set,container, false); + initNaviView(); + ButterKnife.bind(this, rootView); + String username = UserModel.getInstance().getCurrentUser().getUsername(); + tv_set_name.setText(TextUtils.isEmpty(username)?"":username); + return rootView; + } + + @OnClick(R.id.layout_info) + public void onInfoClick(View view){ + Bundle bundle = new Bundle(); + bundle.putSerializable("u", BmobUser.getCurrentUser(getActivity(),User.class)); + startActivity(UserInfoActivity.class,bundle); + } + + @OnClick(R.id.btn_logout) + public void onLogoutClick(View view){ + UserModel.getInstance().logout(); + //可断开连接 + BmobIM.getInstance().disConnect(); + getActivity().finish(); + startActivity(LoginActivity.class,null); + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/util/DisplayConfig.java b/app/src/main/java/cn/bmob/imdemo/util/DisplayConfig.java new file mode 100755 index 0000000..9b5f993 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/util/DisplayConfig.java @@ -0,0 +1,40 @@ +package cn.bmob.imdemo.util; + +import android.graphics.Bitmap; + +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.assist.ImageScaleType; +import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; + +/** + * @author :smile + * @project:DisplayConfig + * @date :2016-01-25-09:19 + * 注:由于Picasso圆角处理不够完美,故舍弃 + */ +public class DisplayConfig { + + /**UIL默认的显示配置:圆角 + * @param defaultRes + * @return + */ + public static DisplayImageOptions getDefaultOptions(boolean hasRounded,int defaultRes){ + DisplayImageOptions.Builder builder = new DisplayImageOptions.Builder() + .cacheInMemory(true)//设置下载的图片是否缓存在内存中 + .cacheOnDisc(true)//设置下载的图片是否缓存在SD卡中 + .considerExifParams(true) //是否考虑JPEG图像EXIF参数(旋转,翻转) + .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)//设置图片以如何的编码方式显示 + .bitmapConfig(Bitmap.Config.RGB_565)//设置图片的解码类型:设置为RGB565比起默认的ARGB_8888要节省大量的内存 +// .delayBeforeLoading(100)//载入图片前稍做延时可以提高整体滑动的流畅度 + .resetViewBeforeLoading(true);//设置图片在下载前是否重置,复位 + if(hasRounded){ + builder.displayer(new RoundedBitmapDisplayer(12));//是否设置为圆角,弧度为多少 + } + if(defaultRes!=0){ + builder.showImageForEmptyUri(defaultRes)//设置图片Uri为空或是错误的时候显示的图片 +// .showImageOnLoading(defaultRes) //设置图片在下载期间显示的图片-->应该去掉-会造成ListView中图片闪烁 + .showImageOnFail(defaultRes); //设置图片加载/解码过程中错误时候显示的图片 + } + return builder.build();//构建完成 + } +} diff --git a/app/src/main/java/cn/bmob/imdemo/util/IMMLeaks.java b/app/src/main/java/cn/bmob/imdemo/util/IMMLeaks.java new file mode 100755 index 0000000..b04af1a --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/util/IMMLeaks.java @@ -0,0 +1,178 @@ +package cn.bmob.imdemo.util; + +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.content.ContextWrapper; +import android.os.Bundle; +import android.os.Looper; +import android.os.MessageQueue; +import android.util.Log; +import android.view.View; +import android.view.ViewTreeObserver; +import android.view.inputmethod.InputMethodManager; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import static android.content.Context.INPUT_METHOD_SERVICE; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.KITKAT; + +public class IMMLeaks { + + static class ReferenceCleaner + implements MessageQueue.IdleHandler, View.OnAttachStateChangeListener, + ViewTreeObserver.OnGlobalFocusChangeListener { + + private final InputMethodManager inputMethodManager; + private final Field mHField; + private final Field mServedViewField; + private final Method finishInputLockedMethod; + + ReferenceCleaner(InputMethodManager inputMethodManager, Field mHField, Field mServedViewField, + Method finishInputLockedMethod) { + this.inputMethodManager = inputMethodManager; + this.mHField = mHField; + this.mServedViewField = mServedViewField; + this.finishInputLockedMethod = finishInputLockedMethod; + } + + @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (newFocus == null) { + return; + } + if (oldFocus != null) { + oldFocus.removeOnAttachStateChangeListener(this); + } + Looper.myQueue().removeIdleHandler(this); + newFocus.addOnAttachStateChangeListener(this); + } + + @Override public void onViewAttachedToWindow(View v) { + } + + @Override public void onViewDetachedFromWindow(View v) { + v.removeOnAttachStateChangeListener(this); + Looper.myQueue().removeIdleHandler(this); + Looper.myQueue().addIdleHandler(this); + } + + @Override public boolean queueIdle() { + clearInputMethodManagerLeak(); + return false; + } + + private void clearInputMethodManagerLeak() { + try { + Object lock = mHField.get(inputMethodManager); + if(lock==null){ + return; + } + // This is highly dependent on the InputMethodManager implementation. + synchronized (lock) { + View servedView = (View) mServedViewField.get(inputMethodManager); + if (servedView != null) { + + boolean servedViewAttached = servedView.getWindowVisibility() != View.GONE; + + if (servedViewAttached) { + // The view held by the IMM was replaced without a global focus change. Let's make + // sure we get notified when that view detaches. + + // Avoid double registration. + servedView.removeOnAttachStateChangeListener(this); + servedView.addOnAttachStateChangeListener(this); + } else { + // servedView is not attached. InputMethodManager is being stupid! + Activity activity = extractActivity(servedView.getContext()); + if (activity == null || activity.getWindow() == null) { + // Unlikely case. Let's finish the input anyways. + finishInputLockedMethod.invoke(inputMethodManager); + } else { + View decorView = activity.getWindow().peekDecorView(); + boolean windowAttached = decorView.getWindowVisibility() != View.GONE; + if (!windowAttached) { + finishInputLockedMethod.invoke(inputMethodManager); + } else { + decorView.requestFocusFromTouch(); + } + } + } + } + } + } catch (Exception unexpected) { + Log.e("IMMLeaks", "Unexpected reflection exception", unexpected); + } + } + + private Activity extractActivity(Context context) { + while (true) { + if (context instanceof Application) { + return null; + } else if (context instanceof Activity) { + return (Activity) context; + } else if (context instanceof ContextWrapper) { + Context baseContext = ((ContextWrapper) context).getBaseContext(); + // Prevent Stack Overflow. + if (baseContext == context) { + return null; + } + context = baseContext; + } else { + return null; + } + } + } + } + + /** + * Fix for https://code.google.com/p/android/issues/detail?id=171190 . + * + * When a view that has focus gets detached, we wait for the main thread to be idle and then + * check if the InputMethodManager is leaking a view. If yes, we tell it that the decor view got + * focus, which is what happens if you press home and come back from recent apps. This replaces + * the reference to the detached view with a reference to the decor view. + * + * Should be called from {@link Activity#onCreate(android.os.Bundle)} )}. + */ + public static void fixFocusedViewLeak(Application application) { + + // Don't know about other versions yet. + if (SDK_INT < KITKAT || SDK_INT > 22) { + return; + } + + final InputMethodManager inputMethodManager = + (InputMethodManager) application.getSystemService(INPUT_METHOD_SERVICE); + + final Field mServedViewField; + final Field mHField; + final Method finishInputLockedMethod; + final Method focusInMethod; + try { + mServedViewField = InputMethodManager.class.getDeclaredField("mServedView"); + mServedViewField.setAccessible(true); + mHField = InputMethodManager.class.getDeclaredField("mServedView"); + mHField.setAccessible(true); + finishInputLockedMethod = InputMethodManager.class.getDeclaredMethod("finishInputLocked"); + finishInputLockedMethod.setAccessible(true); + focusInMethod = InputMethodManager.class.getDeclaredMethod("focusIn", View.class); + focusInMethod.setAccessible(true); + } catch (Exception unexpected) { + Log.e("IMMLeaks", "Unexpected reflection exception", unexpected); + return; + } + + application.registerActivityLifecycleCallbacks(new LifecycleCallbacksAdapter() { + @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + ReferenceCleaner cleaner = + new ReferenceCleaner(inputMethodManager, mHField, mServedViewField, + finishInputLockedMethod); + View rootView = activity.getWindow().getDecorView().getRootView(); + ViewTreeObserver viewTreeObserver = rootView.getViewTreeObserver(); + viewTreeObserver.addOnGlobalFocusChangeListener(cleaner); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/util/LifecycleCallbacksAdapter.java b/app/src/main/java/cn/bmob/imdemo/util/LifecycleCallbacksAdapter.java new file mode 100755 index 0000000..1400c11 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/util/LifecycleCallbacksAdapter.java @@ -0,0 +1,36 @@ +package cn.bmob.imdemo.util; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; + +/** Helper to avoid implementing all lifecycle callback methods. */ +public class LifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks { + @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + + } + + @Override public void onActivityStarted(Activity activity) { + + } + + @Override public void onActivityResumed(Activity activity) { + + } + + @Override public void onActivityPaused(Activity activity) { + + } + + @Override public void onActivityStopped(Activity activity) { + + } + + @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + + } + + @Override public void onActivityDestroyed(Activity activity) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/cn/bmob/imdemo/util/TimeUtil.java b/app/src/main/java/cn/bmob/imdemo/util/TimeUtil.java new file mode 100755 index 0000000..315039e --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/util/TimeUtil.java @@ -0,0 +1,78 @@ +package cn.bmob.imdemo.util; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +/** + * @author :smile + * @project:TimeUtil + * @date :2016-01-26-17:27 + */ +public class TimeUtil { + + public final static String FORMAT_TIME = "HH:mm"; + public final static String FORMAT_DATE_TIME = "yyyy-MM-dd HH:mm"; + public final static String FORMAT_DATE_TIME_SECOND = "yyyy-MM-dd HH:mm:ss"; + public final static String FORMAT_MONTH_DAY_TIME = "MM-dd HH:mm"; + + public static String getFormatToday(String dateFormat) { + Date currentTime = new Date(); + SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); + return formatter.format(currentTime); + } + + public static Date stringToDate(String dateStr, String dateFormat) { + SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); + try { + return formatter.parse(dateStr); + } catch (ParseException e) { + return null; + } + } + public static String dateToString(Date date, String dateFormat) { + SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); + return formatter.format(date); + } + + public static String getChatTime(boolean hasYear,long timesamp) { + long clearTime = timesamp; + String result; + SimpleDateFormat sdf = new SimpleDateFormat("dd"); + Date today = new Date(System.currentTimeMillis()); + Date otherDay = new Date(clearTime); + int temp = Integer.parseInt(sdf.format(today)) + - Integer.parseInt(sdf.format(otherDay)); + switch (temp) { + case 0: + result = "今天 " + getHourAndMin(clearTime); + break; + case 1: + result = "昨天 " + getHourAndMin(clearTime); + break; + case 2: + result = "前天 " + getHourAndMin(clearTime); + break; + default: + result = getTime(hasYear,clearTime); + break; + } + return result; + } + + public static String getTime(boolean hasYear,long time) { + String pattern=FORMAT_DATE_TIME; + if(!hasYear){ + pattern = FORMAT_MONTH_DAY_TIME; + } + SimpleDateFormat format = new SimpleDateFormat(pattern); + return format.format(new Date(time)); + } + + public static String getHourAndMin(long time) { + SimpleDateFormat format = new SimpleDateFormat(FORMAT_TIME); + return format.format(new Date(time)); + } + +} diff --git a/app/src/main/java/cn/bmob/imdemo/util/Util.java b/app/src/main/java/cn/bmob/imdemo/util/Util.java new file mode 100755 index 0000000..36d4803 --- /dev/null +++ b/app/src/main/java/cn/bmob/imdemo/util/Util.java @@ -0,0 +1,27 @@ +package cn.bmob.imdemo.util; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; + +/** + * @author smile + * @project Util + * @date 2016-03-01-14:55 + */ +public class Util { + public static boolean checkSdCard() { + if (android.os.Environment.getExternalStorageState().equals( + android.os.Environment.MEDIA_MOUNTED)) + return true; + else + return false; + } +} diff --git a/app/src/main/java/com.baidu.location.service/LocationService.java b/app/src/main/java/com.baidu.location.service/LocationService.java new file mode 100755 index 0000000..33de07d --- /dev/null +++ b/app/src/main/java/com.baidu.location.service/LocationService.java @@ -0,0 +1,111 @@ +package com.baidu.location.service; + +import android.content.Context; + +import com.baidu.location.BDLocationListener; +import com.baidu.location.LocationClient; +import com.baidu.location.LocationClientOption; +import com.baidu.location.LocationClientOption.LocationMode; + +/** + * + * @author baidu + * + */ +public class LocationService { + private LocationClient client = null; + private LocationClientOption mOption,DIYoption; + private Object objLock = new Object(); + + /*** + * + * @param locationContext + */ + public LocationService(Context locationContext){ + synchronized (objLock) { + if(client == null){ + client = new LocationClient(locationContext); + client.setLocOption(getDefaultLocationClientOption()); + } + } + } + + /*** + * + * @param listener + * @return + */ + + public boolean registerListener(BDLocationListener listener){ + boolean isSuccess = false; + if(listener != null){ + client.registerLocationListener(listener); + isSuccess = true; + } + return isSuccess; + } + + public void unregisterListener(BDLocationListener listener){ + if(listener != null){ + client.unRegisterLocationListener(listener); + } + } + + /*** + * + * @param option + * @return isSuccessSetOption + */ + public boolean setLocationOption(LocationClientOption option){ + boolean isSuccess = false; + if(option != null){ + if(client.isStarted()) + client.stop(); + DIYoption = option; + client.setLocOption(option); + isSuccess = true; + } + return isSuccess; + } + + public LocationClientOption getOption(){ + return DIYoption; + } + /*** + * + * @return DefaultLocationClientOption + */ + public LocationClientOption getDefaultLocationClientOption(){ + if(mOption == null){ + mOption = new LocationClientOption(); + mOption.setLocationMode(LocationMode.Hight_Accuracy);//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备 + mOption.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系,如果配合百度地图使用,建议设置为bd09ll; + mOption.setScanSpan(3000);//可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的 + mOption.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要 + mOption.setIsNeedLocationDescribe(true);//可选,设置是否需要地址描述 + mOption.setNeedDeviceDirect(false);//可选,设置是否需要设备方向结果 + mOption.setLocationNotify(false);//可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果 + mOption.setIgnoreKillProcess(true);//可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死 + mOption.setIsNeedLocationDescribe(true);//可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近” + mOption.setIsNeedLocationPoiList(true);//可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到 + mOption.SetIgnoreCacheException(false);//可选,默认false,设置是否收集CRASH信息,默认收集 + } + return mOption; + } + + public void start(){ + synchronized (objLock) { + if(client != null && !client.isStarted()){ + client.start(); + } + } + } + public void stop(){ + synchronized (objLock) { + if(client != null && client.isStarted()){ + client.stop(); + } + } + } + +} diff --git a/app/src/main/java/com.baidu.location.service/Utils.java b/app/src/main/java/com.baidu.location.service/Utils.java new file mode 100755 index 0000000..3d116b1 --- /dev/null +++ b/app/src/main/java/com.baidu.location.service/Utils.java @@ -0,0 +1,28 @@ +package com.baidu.location.service; + +public class Utils { + public final static String CoorType_GCJ02 = "gcj02"; + public final static String CoorType_BD09LL= "bd09ll"; + public final static String CoorType_BD09MC= "bd09"; + /*** + *61 : GPS定位结果,GPS定位成功。 + *62 : 无法获取有效定位依据,定位失败,请检查运营商网络或者wifi网络是否正常开启,尝试重新请求定位。 + *63 : 网络异常,没有成功向服务器发起请求,请确认当前测试手机网络是否通畅,尝试重新请求定位。 + *65 : 定位缓存的结果。 + *66 : 离线定位结果。通过requestOfflineLocaiton调用时对应的返回结果。 + *67 : 离线定位失败。通过requestOfflineLocaiton调用时对应的返回结果。 + *68 : 网络连接失败时,查找本地离线定位时对应的返回结果。 + *161: 网络定位结果,网络定位定位成功。 + *162: 请求串密文解析失败。 + *167: 服务端定位失败,请您检查是否禁用获取位置信息权限,尝试重新请求定位。 + *502: key参数错误,请按照说明文档重新申请KEY。 + *505: key不存在或者非法,请按照说明文档重新申请KEY。 + *601: key服务被开发者自己禁用,请按照说明文档重新申请KEY。 + *602: key mcode不匹配,您的ak配置过程中安全码设置有问题,请确保:sha1正确,“;”分号是英文状态;且包名是您当前运行应用的包名,请按照说明文档重新申请KEY。 + *501~700:key验证失败,请按照说明文档重新申请KEY。 + */ + + public static float[] EARTH_WEIGHT = {0.1f,0.2f,0.4f,0.6f,0.8f}; // 推算计算权重_地球 + //public static float[] MOON_WEIGHT = {0.0167f,0.033f,0.067f,0.1f,0.133f}; + //public static float[] MARS_WEIGHT = {0.034f,0.068f,0.152f,0.228f,0.304f}; +} diff --git a/app/src/main/jnilibs/arm64-v8a/libBaiduMapSDK_v3_5_0_9.so b/app/src/main/jnilibs/arm64-v8a/libBaiduMapSDK_v3_5_0_9.so new file mode 100755 index 0000000..31ac11f Binary files /dev/null and b/app/src/main/jnilibs/arm64-v8a/libBaiduMapSDK_v3_5_0_9.so differ diff --git a/app/src/main/jnilibs/arm64-v8a/liblocSDK6a.so b/app/src/main/jnilibs/arm64-v8a/liblocSDK6a.so new file mode 100755 index 0000000..1bbcc59 Binary files /dev/null and b/app/src/main/jnilibs/arm64-v8a/liblocSDK6a.so differ diff --git a/app/src/main/jnilibs/armeabi-v7a/libBaiduMapSDK_v3_5_0_9.so b/app/src/main/jnilibs/armeabi-v7a/libBaiduMapSDK_v3_5_0_9.so new file mode 100755 index 0000000..bfd7b27 Binary files /dev/null and b/app/src/main/jnilibs/armeabi-v7a/libBaiduMapSDK_v3_5_0_9.so differ diff --git a/app/src/main/jnilibs/armeabi-v7a/liblocSDK6a.so b/app/src/main/jnilibs/armeabi-v7a/liblocSDK6a.so new file mode 100755 index 0000000..56278b2 Binary files /dev/null and b/app/src/main/jnilibs/armeabi-v7a/liblocSDK6a.so differ diff --git a/app/src/main/jnilibs/armeabi/libBaiduMapSDK_v3_5_0_9.so b/app/src/main/jnilibs/armeabi/libBaiduMapSDK_v3_5_0_9.so new file mode 100755 index 0000000..fce5a50 Binary files /dev/null and b/app/src/main/jnilibs/armeabi/libBaiduMapSDK_v3_5_0_9.so differ diff --git a/app/src/main/jnilibs/armeabi/liblocSDK6a.so b/app/src/main/jnilibs/armeabi/liblocSDK6a.so new file mode 100755 index 0000000..83f7f12 Binary files /dev/null and b/app/src/main/jnilibs/armeabi/liblocSDK6a.so differ diff --git a/app/src/main/jnilibs/mips/liblocSDK6a.so b/app/src/main/jnilibs/mips/liblocSDK6a.so new file mode 100755 index 0000000..fb3d18c Binary files /dev/null and b/app/src/main/jnilibs/mips/liblocSDK6a.so differ diff --git a/app/src/main/jnilibs/mips64/liblocSDK6a.so b/app/src/main/jnilibs/mips64/liblocSDK6a.so new file mode 100755 index 0000000..9951a24 Binary files /dev/null and b/app/src/main/jnilibs/mips64/liblocSDK6a.so differ diff --git a/app/src/main/jnilibs/x86/libBaiduMapSDK_v3_5_0_9.so b/app/src/main/jnilibs/x86/libBaiduMapSDK_v3_5_0_9.so new file mode 100755 index 0000000..7e41146 Binary files /dev/null and b/app/src/main/jnilibs/x86/libBaiduMapSDK_v3_5_0_9.so differ diff --git a/app/src/main/jnilibs/x86/liblocSDK6a.so b/app/src/main/jnilibs/x86/liblocSDK6a.so new file mode 100755 index 0000000..2809bc1 Binary files /dev/null and b/app/src/main/jnilibs/x86/liblocSDK6a.so differ diff --git a/app/src/main/jnilibs/x86_64/libBaiduMapSDK_v3_5_0_9.so b/app/src/main/jnilibs/x86_64/libBaiduMapSDK_v3_5_0_9.so new file mode 100755 index 0000000..a9ef1c3 Binary files /dev/null and b/app/src/main/jnilibs/x86_64/libBaiduMapSDK_v3_5_0_9.so differ diff --git a/app/src/main/jnilibs/x86_64/liblocSDK6a.so b/app/src/main/jnilibs/x86_64/liblocSDK6a.so new file mode 100755 index 0000000..ec3175e Binary files /dev/null and b/app/src/main/jnilibs/x86_64/liblocSDK6a.so differ diff --git a/app/src/main/res/drawable/about_bottom_bg.9.png b/app/src/main/res/drawable/about_bottom_bg.9.png new file mode 100755 index 0000000..190b024 Binary files /dev/null and b/app/src/main/res/drawable/about_bottom_bg.9.png differ diff --git a/app/src/main/res/drawable/about_mid_bg.9.png b/app/src/main/res/drawable/about_mid_bg.9.png new file mode 100755 index 0000000..c29789f Binary files /dev/null and b/app/src/main/res/drawable/about_mid_bg.9.png differ diff --git a/app/src/main/res/drawable/about_top_bg.9.png b/app/src/main/res/drawable/about_top_bg.9.png new file mode 100755 index 0000000..25c5fdb Binary files /dev/null and b/app/src/main/res/drawable/about_top_bg.9.png differ diff --git a/app/src/main/res/drawable/anim_chat_voice_left.xml b/app/src/main/res/drawable/anim_chat_voice_left.xml new file mode 100755 index 0000000..5b2d040 --- /dev/null +++ b/app/src/main/res/drawable/anim_chat_voice_left.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/anim_chat_voice_right.xml b/app/src/main/res/drawable/anim_chat_voice_right.xml new file mode 100755 index 0000000..8d8b132 --- /dev/null +++ b/app/src/main/res/drawable/anim_chat_voice_right.xml @@ -0,0 +1,16 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/base_action_bar_add_bg_selector.xml b/app/src/main/res/drawable/base_action_bar_add_bg_selector.xml new file mode 100755 index 0000000..b795039 --- /dev/null +++ b/app/src/main/res/drawable/base_action_bar_add_bg_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/base_action_bar_back_bg_selector.xml b/app/src/main/res/drawable/base_action_bar_back_bg_selector.xml new file mode 100755 index 0000000..ba180f4 --- /dev/null +++ b/app/src/main/res/drawable/base_action_bar_back_bg_selector.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/app/src/main/res/drawable/base_action_bar_more_bg_selector.xml b/app/src/main/res/drawable/base_action_bar_more_bg_selector.xml new file mode 100755 index 0000000..c4d6539 --- /dev/null +++ b/app/src/main/res/drawable/base_action_bar_more_bg_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/base_action_bar_true_bg_selector.xml b/app/src/main/res/drawable/base_action_bar_true_bg_selector.xml new file mode 100755 index 0000000..fa2b486 --- /dev/null +++ b/app/src/main/res/drawable/base_action_bar_true_bg_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/base_comment_emo_bar.9.png b/app/src/main/res/drawable/base_comment_emo_bar.9.png new file mode 100755 index 0000000..7abf2b8 Binary files /dev/null and b/app/src/main/res/drawable/base_comment_emo_bar.9.png differ diff --git a/app/src/main/res/drawable/base_dialog_bg.9.png b/app/src/main/res/drawable/base_dialog_bg.9.png new file mode 100755 index 0000000..df86014 Binary files /dev/null and b/app/src/main/res/drawable/base_dialog_bg.9.png differ diff --git a/app/src/main/res/drawable/base_horizontal_line.9.png b/app/src/main/res/drawable/base_horizontal_line.9.png new file mode 100755 index 0000000..97bf003 Binary files /dev/null and b/app/src/main/res/drawable/base_horizontal_line.9.png differ diff --git a/app/src/main/res/drawable/base_horizontal_line_red.9.png b/app/src/main/res/drawable/base_horizontal_line_red.9.png new file mode 100755 index 0000000..601ea6b Binary files /dev/null and b/app/src/main/res/drawable/base_horizontal_line_red.9.png differ diff --git a/app/src/main/res/drawable/btn_chat_add_camera_selector.xml b/app/src/main/res/drawable/btn_chat_add_camera_selector.xml new file mode 100755 index 0000000..7045475 --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_add_camera_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_add_location_selector.xml b/app/src/main/res/drawable/btn_chat_add_location_selector.xml new file mode 100755 index 0000000..38019b2 --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_add_location_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_add_picture_selector.xml b/app/src/main/res/drawable/btn_chat_add_picture_selector.xml new file mode 100755 index 0000000..9356d80 --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_add_picture_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_add_selector.xml b/app/src/main/res/drawable/btn_chat_add_selector.xml new file mode 100755 index 0000000..9678d70 --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_add_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_emo_selector.xml b/app/src/main/res/drawable/btn_chat_emo_selector.xml new file mode 100755 index 0000000..a1c0d43 --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_emo_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_fail_resend.xml b/app/src/main/res/drawable/btn_chat_fail_resend.xml new file mode 100755 index 0000000..25c7cea --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_fail_resend.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_keyboard_selector.xml b/app/src/main/res/drawable/btn_chat_keyboard_selector.xml new file mode 100755 index 0000000..f9ac40a --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_keyboard_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_record_selector.xml b/app/src/main/res/drawable/btn_chat_record_selector.xml new file mode 100755 index 0000000..75f0dc4 --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_record_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_send_selector.xml b/app/src/main/res/drawable/btn_chat_send_selector.xml new file mode 100755 index 0000000..628e25b --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_send_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_chat_voice_selector.xml b/app/src/main/res/drawable/btn_chat_voice_selector.xml new file mode 100755 index 0000000..af54f9f --- /dev/null +++ b/app/src/main/res/drawable/btn_chat_voice_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_login_n.9.png b/app/src/main/res/drawable/btn_login_n.9.png new file mode 100755 index 0000000..3b078c8 Binary files /dev/null and b/app/src/main/res/drawable/btn_login_n.9.png differ diff --git a/app/src/main/res/drawable/btn_login_p.9.png b/app/src/main/res/drawable/btn_login_p.9.png new file mode 100755 index 0000000..99d2362 Binary files /dev/null and b/app/src/main/res/drawable/btn_login_p.9.png differ diff --git a/app/src/main/res/drawable/btn_login_selector.xml b/app/src/main/res/drawable/btn_login_selector.xml new file mode 100755 index 0000000..88ffebd --- /dev/null +++ b/app/src/main/res/drawable/btn_login_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_logout_n.9.png b/app/src/main/res/drawable/btn_logout_n.9.png new file mode 100755 index 0000000..838ec0e Binary files /dev/null and b/app/src/main/res/drawable/btn_logout_n.9.png differ diff --git a/app/src/main/res/drawable/btn_logout_p.9.png b/app/src/main/res/drawable/btn_logout_p.9.png new file mode 100755 index 0000000..81b9606 Binary files /dev/null and b/app/src/main/res/drawable/btn_logout_p.9.png differ diff --git a/app/src/main/res/drawable/btn_logout_selector.xml b/app/src/main/res/drawable/btn_logout_selector.xml new file mode 100755 index 0000000..89762c7 --- /dev/null +++ b/app/src/main/res/drawable/btn_logout_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/btn_selector.xml b/app/src/main/res/drawable/btn_selector.xml new file mode 100755 index 0000000..84500a0 --- /dev/null +++ b/app/src/main/res/drawable/btn_selector.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/button_normal_shape.xml b/app/src/main/res/drawable/button_normal_shape.xml new file mode 100755 index 0000000..14c0b64 --- /dev/null +++ b/app/src/main/res/drawable/button_normal_shape.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/chat_left_qp.9.png b/app/src/main/res/drawable/chat_left_qp.9.png new file mode 100755 index 0000000..0f2e37c Binary files /dev/null and b/app/src/main/res/drawable/chat_left_qp.9.png differ diff --git a/app/src/main/res/drawable/chat_right_qp.9.png b/app/src/main/res/drawable/chat_right_qp.9.png new file mode 100755 index 0000000..2eaa7dd Binary files /dev/null and b/app/src/main/res/drawable/chat_right_qp.9.png differ diff --git a/app/src/main/res/drawable/chat_top_voice_bg.9.png b/app/src/main/res/drawable/chat_top_voice_bg.9.png new file mode 100755 index 0000000..b5678bc Binary files /dev/null and b/app/src/main/res/drawable/chat_top_voice_bg.9.png differ diff --git a/app/src/main/res/drawable/chat_voice_bg.9.png b/app/src/main/res/drawable/chat_voice_bg.9.png new file mode 100755 index 0000000..72c6d01 Binary files /dev/null and b/app/src/main/res/drawable/chat_voice_bg.9.png differ diff --git a/app/src/main/res/drawable/chat_voice_bg_press.9.png b/app/src/main/res/drawable/chat_voice_bg_press.9.png new file mode 100755 index 0000000..a443809 Binary files /dev/null and b/app/src/main/res/drawable/chat_voice_bg_press.9.png differ diff --git a/app/src/main/res/drawable/contact_list_buddy_item_bg.9.png b/app/src/main/res/drawable/contact_list_buddy_item_bg.9.png new file mode 100755 index 0000000..5fd9ca6 Binary files /dev/null and b/app/src/main/res/drawable/contact_list_buddy_item_bg.9.png differ diff --git a/app/src/main/res/drawable/drawable_edit_normal.xml b/app/src/main/res/drawable/drawable_edit_normal.xml new file mode 100755 index 0000000..835e99b --- /dev/null +++ b/app/src/main/res/drawable/drawable_edit_normal.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/input_bg.9.png b/app/src/main/res/drawable/input_bg.9.png new file mode 100755 index 0000000..a489026 Binary files /dev/null and b/app/src/main/res/drawable/input_bg.9.png differ diff --git a/app/src/main/res/drawable/list_conversation_press.9.png b/app/src/main/res/drawable/list_conversation_press.9.png new file mode 100755 index 0000000..2d236fc Binary files /dev/null and b/app/src/main/res/drawable/list_conversation_press.9.png differ diff --git a/app/src/main/res/drawable/list_newmessage2.9.png b/app/src/main/res/drawable/list_newmessage2.9.png new file mode 100755 index 0000000..3da6326 Binary files /dev/null and b/app/src/main/res/drawable/list_newmessage2.9.png differ diff --git a/app/src/main/res/drawable/location_default.9.png b/app/src/main/res/drawable/location_default.9.png new file mode 100755 index 0000000..8890623 Binary files /dev/null and b/app/src/main/res/drawable/location_default.9.png differ diff --git a/app/src/main/res/drawable/pop_bg.9.png b/app/src/main/res/drawable/pop_bg.9.png new file mode 100755 index 0000000..f1d8003 Binary files /dev/null and b/app/src/main/res/drawable/pop_bg.9.png differ diff --git a/app/src/main/res/drawable/pop_bg_press.9.png b/app/src/main/res/drawable/pop_bg_press.9.png new file mode 100755 index 0000000..3fdfd20 Binary files /dev/null and b/app/src/main/res/drawable/pop_bg_press.9.png differ diff --git a/app/src/main/res/drawable/tab_contact_btn.xml b/app/src/main/res/drawable/tab_contact_btn.xml new file mode 100755 index 0000000..a81378a --- /dev/null +++ b/app/src/main/res/drawable/tab_contact_btn.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_message_btn.xml b/app/src/main/res/drawable/tab_message_btn.xml new file mode 100755 index 0000000..364337a --- /dev/null +++ b/app/src/main/res/drawable/tab_message_btn.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_set_btn.xml b/app/src/main/res/drawable/tab_set_btn.xml new file mode 100755 index 0000000..9d3ca54 --- /dev/null +++ b/app/src/main/res/drawable/tab_set_btn.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/tab_textcolor.xml b/app/src/main/res/drawable/tab_textcolor.xml new file mode 100755 index 0000000..7d518fd --- /dev/null +++ b/app/src/main/res/drawable/tab_textcolor.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/top_bar.9.png b/app/src/main/res/drawable/top_bar.9.png new file mode 100755 index 0000000..f58c960 Binary files /dev/null and b/app/src/main/res/drawable/top_bar.9.png differ diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml new file mode 100755 index 0000000..cae5253 --- /dev/null +++ b/app/src/main/res/layout/activity_chat.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100755 index 0000000..c4db6aa --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + +