Repository: YiChat/android_YiChat_Lite Branch: master Commit: 13e0915862e0 Files: 319 Total size: 1.2 MB Directory structure: gitextract_zpgm32r8/ ├── .gitignore ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── libs/ │ │ ├── htmessage_open.jar │ │ ├── zbardecoder.jar │ │ └── zxing.jar │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── htmessage/ │ │ └── yichatopen/ │ │ ├── HTApp.java │ │ ├── HTClientHelper.java │ │ ├── HTConstant.java │ │ ├── IMAction.java │ │ ├── activity/ │ │ │ ├── BaseActivity.java │ │ │ ├── BasePresenter.java │ │ │ ├── BaseView.java │ │ │ ├── ScanCaptureActivity.java │ │ │ ├── SettingsActivity.java │ │ │ ├── ShowBigImageActivity.java │ │ │ ├── SplashActivity.java │ │ │ ├── addfriends/ │ │ │ │ ├── add/ │ │ │ │ │ ├── end/ │ │ │ │ │ │ └── AddFriendsFinalActivity.java │ │ │ │ │ ├── next/ │ │ │ │ │ │ ├── AddFriendNextBasePrestener.java │ │ │ │ │ │ ├── AddFriendNextFragment.java │ │ │ │ │ │ ├── AddFriendNextPrestener.java │ │ │ │ │ │ ├── AddFriendNextView.java │ │ │ │ │ │ └── AddFriendsNextActivity.java │ │ │ │ │ └── pre/ │ │ │ │ │ └── AddFriendsPreActivity.java │ │ │ │ └── newfriend/ │ │ │ │ ├── NewFriendBasePresenter.java │ │ │ │ ├── NewFriendFragment.java │ │ │ │ ├── NewFriendPrestener.java │ │ │ │ ├── NewFriendView.java │ │ │ │ ├── NewFriendsActivity.java │ │ │ │ └── NewFriendsAdapter.java │ │ │ ├── chat/ │ │ │ │ ├── ChatActivity.java │ │ │ │ ├── ChatAdapter.java │ │ │ │ ├── ChatContract.java │ │ │ │ ├── ChatFragment.java │ │ │ │ ├── ChatPresenter.java │ │ │ │ ├── activity/ │ │ │ │ │ ├── ChatSettingActivity.java │ │ │ │ │ ├── ChooseContactActivity.java │ │ │ │ │ └── ChooseContactAdapter.java │ │ │ │ └── weight/ │ │ │ │ ├── ChatExtendMenu.java │ │ │ │ ├── ChatInputView.java │ │ │ │ ├── VoicePlayClickListener.java │ │ │ │ ├── VoiceRecorder.java │ │ │ │ ├── VoiceRecorderView.java │ │ │ │ ├── emoji/ │ │ │ │ │ ├── DefaultEmojiconDatas.java │ │ │ │ │ ├── EmojiFragment.java │ │ │ │ │ ├── Emojicon.java │ │ │ │ │ ├── EmojiconGridAdapter.java │ │ │ │ │ └── SmileUtils.java │ │ │ │ └── loadmore/ │ │ │ │ ├── DensityUtil.java │ │ │ │ ├── Pacman.java │ │ │ │ ├── ProgressView.java │ │ │ │ ├── ProgressViewController.java │ │ │ │ ├── PullToLoadMoreListView.java │ │ │ │ └── RefreshHeader.java │ │ │ ├── country/ │ │ │ │ ├── CharacterParserUtil.java │ │ │ │ ├── CountryCodeUtil.java │ │ │ │ ├── CountryComparator.java │ │ │ │ ├── CountryModel.java │ │ │ │ ├── CountrySortAdapter.java │ │ │ │ ├── CountrySortModel.java │ │ │ │ ├── CountrySortToken.java │ │ │ │ ├── GetCountryNameSort.java │ │ │ │ └── SideBar.java │ │ │ ├── login/ │ │ │ │ ├── LoginActivity.java │ │ │ │ ├── LoginContract.java │ │ │ │ ├── LoginFragment.java │ │ │ │ └── LoginPresenter.java │ │ │ ├── main/ │ │ │ │ ├── MainActivity.java │ │ │ │ ├── MainBasePrester.java │ │ │ │ ├── MainPrestener.java │ │ │ │ ├── MainView.java │ │ │ │ ├── contacts/ │ │ │ │ │ ├── BaseContactsPresenter.java │ │ │ │ │ ├── ContactsAdapter.java │ │ │ │ │ ├── ContactsPresenter.java │ │ │ │ │ ├── ContactsView.java │ │ │ │ │ ├── FragmentContacts.java │ │ │ │ │ └── Sidebar.java │ │ │ │ ├── conversation/ │ │ │ │ │ ├── BaseConversationPresenter.java │ │ │ │ │ ├── ConversationAdapter.java │ │ │ │ │ ├── ConversationFragment.java │ │ │ │ │ ├── ConversationPresenter.java │ │ │ │ │ └── ConversationView.java │ │ │ │ ├── details/ │ │ │ │ │ ├── UserDetailesFragment.java │ │ │ │ │ ├── UserDetailsActivity.java │ │ │ │ │ ├── UserDetailsBasePrester.java │ │ │ │ │ ├── UserDetailsPrester.java │ │ │ │ │ └── UserDetailsView.java │ │ │ │ ├── find/ │ │ │ │ │ ├── FragmentFind.java │ │ │ │ │ └── recentlypeople/ │ │ │ │ │ ├── PeopleRecentlyActivity.java │ │ │ │ │ ├── PeopleRecentlyAdapter.java │ │ │ │ │ ├── PeopleRecentlyBasePrestener.java │ │ │ │ │ ├── PeopleRecentlyFragment.java │ │ │ │ │ ├── PeopleRecentlyPrestener.java │ │ │ │ │ └── PeopleRecentlyView.java │ │ │ │ ├── password/ │ │ │ │ │ ├── PasswordBasePrester.java │ │ │ │ │ ├── PasswordPrester.java │ │ │ │ │ ├── PasswordResetActivity.java │ │ │ │ │ ├── PasswordResetFragment.java │ │ │ │ │ └── PasswordView.java │ │ │ │ ├── profile/ │ │ │ │ │ ├── FragmentProfile.java │ │ │ │ │ └── info/ │ │ │ │ │ ├── profile/ │ │ │ │ │ │ ├── ProfileActivity.java │ │ │ │ │ │ ├── ProfileBasePrester.java │ │ │ │ │ │ ├── ProfileFragment.java │ │ │ │ │ │ ├── ProfilePrester.java │ │ │ │ │ │ └── ProfileView.java │ │ │ │ │ └── update/ │ │ │ │ │ ├── ProfileUpdateActivity.java │ │ │ │ │ ├── ProfileUpdateFragment.java │ │ │ │ │ ├── UpdateProfileBasePrester.java │ │ │ │ │ ├── UpdateProfilePrestener.java │ │ │ │ │ └── UpdateProfileView.java │ │ │ │ ├── qrcode/ │ │ │ │ │ ├── QrCodeActivity.java │ │ │ │ │ ├── QrCodeBasePrester.java │ │ │ │ │ ├── QrCodeFragment.java │ │ │ │ │ ├── QrCodePrester.java │ │ │ │ │ └── QrCodeView.java │ │ │ │ └── region/ │ │ │ │ ├── RegionActivity.java │ │ │ │ ├── RegionBasePrestener.java │ │ │ │ ├── RegionFragment.java │ │ │ │ ├── RegionPresenter.java │ │ │ │ └── RegionView.java │ │ │ └── register/ │ │ │ ├── RegisterActivity.java │ │ │ ├── RegisterContract.java │ │ │ ├── RegisterFragment.java │ │ │ └── RegisterPresenter.java │ │ ├── domain/ │ │ │ ├── InviteMessage.java │ │ │ ├── InviteMessgeDao.java │ │ │ ├── User.java │ │ │ └── UserDao.java │ │ ├── manager/ │ │ │ ├── ContactsManager.java │ │ │ ├── DBManager.java │ │ │ ├── DbOpenHelper.java │ │ │ ├── LocalUserManager.java │ │ │ ├── Manager.java │ │ │ ├── MyNotification.java │ │ │ ├── Notifier.java │ │ │ ├── NotifierManager.java │ │ │ ├── PreferenceManager.java │ │ │ └── SettingsManager.java │ │ ├── runtimepermissions/ │ │ │ ├── Permissions.java │ │ │ ├── PermissionsManager.java │ │ │ └── PermissionsResultAction.java │ │ ├── utils/ │ │ │ ├── ACache.java │ │ │ ├── CommonUtils.java │ │ │ ├── DateUtils.java │ │ │ ├── HTMessageUtils.java │ │ │ ├── ImageUtils.java │ │ │ ├── OkHttpUtils.java │ │ │ ├── Param.java │ │ │ ├── PathUtils.java │ │ │ ├── UpdateLastLoginTimeUtils.java │ │ │ └── Validator.java │ │ └── widget/ │ │ ├── HTAlertDialog.java │ │ ├── SwitchButton.java │ │ ├── photoview/ │ │ │ ├── Compat.java │ │ │ ├── IPhotoView.java │ │ │ ├── PhotoView.java │ │ │ ├── PhotoViewAttacher.java │ │ │ ├── SDK16.java │ │ │ ├── ScrollerProxy.java │ │ │ └── VersionedGestureDetector.java │ │ ├── scan/ │ │ │ ├── CameraConfigurationManager.java │ │ │ ├── CameraManager.java │ │ │ └── CameraPreview.java │ │ ├── swipyrefresh/ │ │ │ ├── CircleImageView.java │ │ │ ├── MaterialProgressDrawable.java │ │ │ ├── SwipyRefreshLayout.java │ │ │ └── SwipyRefreshLayoutDirection.java │ │ └── zxing/ │ │ ├── activity/ │ │ │ ├── CaptureActivity.java │ │ │ └── DensityUtil.java │ │ ├── camera/ │ │ │ ├── AutoFocusCallback.java │ │ │ ├── CameraConfigurationManager.java │ │ │ ├── CameraManager.java │ │ │ ├── FlashlightManager.java │ │ │ ├── PlanarYUVLuminanceSource.java │ │ │ └── PreviewCallback.java │ │ ├── decoding/ │ │ │ ├── CaptureActivityHandler.java │ │ │ ├── DecodeFormatManager.java │ │ │ ├── DecodeHandler.java │ │ │ ├── DecodeThread.java │ │ │ ├── FinishListener.java │ │ │ ├── InactivityTimer.java │ │ │ └── Intents.java │ │ ├── encoding/ │ │ │ └── EncodingHandler.java │ │ └── view/ │ │ ├── ViewfinderResultPointCallback.java │ │ └── ViewfinderView.java │ └── res/ │ ├── anim/ │ │ ├── fade_in.xml │ │ ├── fade_out.xml │ │ ├── head_in.xml │ │ ├── head_out.xml │ │ ├── hold.xml │ │ ├── loading_animation.xml │ │ ├── push_bottom_in.xml │ │ ├── push_bottom_out.xml │ │ ├── push_top_in.xml │ │ ├── push_top_in2.xml │ │ ├── push_top_out.xml │ │ ├── push_top_out2.xml │ │ ├── slide_in_from_left.xml │ │ ├── slide_in_from_right.xml │ │ ├── slide_out_to_left.xml │ │ ├── slide_out_to_right.xml │ │ ├── voice_from_icon.xml │ │ └── voice_to_icon.xml │ ├── color/ │ │ ├── login_btn_text_color.xml │ │ └── main_botton_text_color.xml │ ├── drawable/ │ │ ├── bg_btn_gray.xml │ │ ├── bg_btn_green.xml │ │ ├── bg_dialog.xml │ │ ├── bg_et.xml │ │ ├── btn_bottom_selector.xml │ │ ├── btn_more_type_msg.xml │ │ ├── chat_error_item_bg.xml │ │ ├── chat_image_selector.xml │ │ ├── chat_press_speak_btn.xml │ │ ├── chat_takepic_selector.xml │ │ ├── chatfrom_bg.xml │ │ ├── chatting_setmode_keyboard_btn.xml │ │ ├── chatting_setmode_voice_btn.xml │ │ ├── chatto_bg.xml │ │ ├── divider_horizontal.xml │ │ ├── divider_vertical.xml │ │ ├── dot_emoji.xml │ │ ├── edit_text_bg.xml │ │ ├── emoji_bottom_bg.xml │ │ ├── item_pre_videocall_selector.xml │ │ ├── list_item_bg_gray.xml │ │ ├── list_item_bg_white.xml │ │ ├── msg_state_failed_resend.xml │ │ ├── progressbar_white.xml │ │ ├── recording_hint_bg.xml │ │ ├── recording_text_hint_bg.xml │ │ ├── register_phone_bg.xml │ │ ├── sidebar_background_pressed.xml │ │ ├── sign_bg.xml │ │ ├── tab_chat_bg.xml │ │ ├── tab_contact_list_bg.xml │ │ ├── tab_find_bg.xml │ │ ├── tab_profile_bg.xml │ │ ├── timestampe_bg.xml │ │ ├── top_bar_back.xml │ │ └── topbar_back.xml │ ├── layout/ │ │ ├── activity_addfriends_final.xml │ │ ├── activity_addfriends_next.xml │ │ ├── activity_addfriends_pre.xml │ │ ├── activity_base.xml │ │ ├── activity_base_input.xml │ │ ├── activity_base_main.xml │ │ ├── activity_chat_setting_single.xml │ │ ├── activity_check_people.xml │ │ ├── activity_new_friends.xml │ │ ├── activity_people_recently.xml │ │ ├── activity_psw_reset.xml │ │ ├── activity_qrcode_generate.xml │ │ ├── activity_region.xml │ │ ├── activity_show_big_image.xml │ │ ├── activity_splash.xml │ │ ├── activity_update_info.xml │ │ ├── activity_userinfo.xml │ │ ├── chat_menu_item.xml │ │ ├── chat_neterror_item.xml │ │ ├── coogame_country_item.xml │ │ ├── dialog_alert.xml │ │ ├── emoji_gridview.xml │ │ ├── fragment_chat.xml │ │ ├── fragment_contactlist.xml │ │ ├── fragment_conversation_settings.xml │ │ ├── fragment_emoji.xml │ │ ├── fragment_find.xml │ │ ├── fragment_home.xml │ │ ├── fragment_login.xml │ │ ├── fragment_profile.xml │ │ ├── fragment_profile_info.xml │ │ ├── fragment_register.xml │ │ ├── item_contact_list.xml │ │ ├── item_contact_list_footer.xml │ │ ├── item_contact_list_header.xml │ │ ├── item_conversation_single.xml │ │ ├── item_dialog_gridview.xml │ │ ├── item_diaolog_gridview.xml │ │ ├── item_newfriend_msg.xml │ │ ├── item_people_recently.xml │ │ ├── item_region.xml │ │ ├── latout_pre_videocall_item.xml │ │ ├── layout_alert_dialog_delete.xml │ │ ├── layout_pup.xml │ │ ├── layout_title_bar.xml │ │ ├── loading_dialog.xml │ │ ├── row_big_expression.xml │ │ ├── row_expression.xml │ │ ├── row_received_message.xml │ │ ├── row_received_picture.xml │ │ ├── row_received_voice.xml │ │ ├── row_sent_message.xml │ │ ├── row_sent_picture.xml │ │ ├── row_sent_voice.xml │ │ ├── widget_input_view.xml │ │ ├── widget_main_button.xml │ │ ├── widget_switch_button.xml │ │ ├── widget_voice_recorder.xml │ │ └── widget_zbar_scan_capture.xml │ ├── menu/ │ │ └── menu_main.xml │ ├── values/ │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-hdpi/ │ │ └── dimens.xml │ └── values-v21/ │ └── styles.xml ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Built application files *.apk *.ap_ # Files for the ART/Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ out/ # Gradle files .gradle/ build/ .idea # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # Log Files *.log # Android Studio Navigation editor temp files .navigation/ # Android Studio captures folder captures/ # Intellij *.iml # Keystore files *.jks gotye.keystore ================================================ FILE: README.md ================================================ # Android 项目配置# public class HTConstant { //IM服务器相关 public static final String HOST_IM = "xxx.xxx.xxx.xxx";//示例ip: 119.125.523.153 此IP不可用 //api服务器 public static final String HOST_API = "http://xxx.xxx.xxx.xxx/api/";//示例ip: 119.125.523.153 此IP不可用 //阿里云OSS信息配置 public static final String endpoint = "oss-cn-hangzhou.aliyuncs.com"; public static final String accessKeyId = "xxxxxxxx"; public static final String accessKeySecret = "xxxxxxxxxxxx"; ..... } ---------- # 相关工程 # ---------- #最具诚意的开源IM系统 YiChat# 1. github地址:[https://github.com/YiChat](https://github.com/YiChat) 2. 开源中国地址:[https://git.oschina.net/zhangfeng_tech](https://git.oschina.net/zhangfeng_tech) ##本项目的开源内容## ###已开源的所有源码:### 1. IM服务器(负责即时通讯消息)-直接部署,无需修改参数 2. api服务器(非IM模块相关的其他业务逻辑)-修改一处参数,详见工程下文档 3. android客户端-配置参数,连接自己的服务器ip.详见工程文档 ###待开源的工程源码:### - iOS客户端:前面三个工程的github star数超过3000马上开源 ##这个开源项目的意义在于## 1. 拥有自己的IM服务器,不再受制于第三方通讯云的限制. 2. 提供了一个完善优化的客户端源码,具体参见体验包: - Android:[https://www.pgyer.com/YiChatLite](https://www.pgyer.com/YiChatLite) - iOS:[https://www.pgyer.com/9sVQ](https://www.pgyer.com/9sVQ) ##QQ:84543217 (技术相关请提交Issus,商务合作可联系QQ) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.0" defaultConfig { applicationId "com.htmessage.yichatopen" minSdkVersion 16 targetSdkVersion 23 versionCode 1 versionName "1.0" multiDexEnabled true ndk { abiFilter 'armeabi-v7a' // 例如:abiFilter 'armeabi, x86' } sourceSets { main { jniLibs.srcDir 'libs' } instrumentTest.setRoot('tests') debug.setRoot('build-types/debug') release.setRoot('build-types/release') } lintOptions { abortOnError false } aaptOptions{ cruncherEnabled=false useNewCruncher=false } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } repositories { flatDir { dirs 'libs' } } } ext { smackVersion = '4.1.9' } dependencies { compile 'com.android.support:multidex:1.0.1' compile fileTree(include: '*.jar' , dir: 'libs') compile 'com.android.support:appcompat-v7:25.0.1' compile 'com.tencent.bugly:crashreport:2.2.0' // compile 'com.android.support:design:25.0.1' compile 'com.android.support:cardview-v7:25.0.1' compile 'com.tbruyelle.rxpermissions:rxpermissions:0.9.1@aar' compile 'com.github.promeg:tinypinyin:1.0.0' compile 'com.squareup.okhttp3:okhttp:3.3.1' compile "org.igniterealtime.smack:smack-android-extensions:$smackVersion" compile "org.igniterealtime.smack:smack-experimental:$smackVersion" compile "org.igniterealtime.smack:smack-tcp:$smackVersion" compile "org.igniterealtime.smack:smack-legacy:$smackVersion" compile 'io.reactivex:rxandroid:1.2.1' compile 'io.reactivex:rxjava:1.1.6' compile 'com.android.support:support-v4:25.0.0' compile 'com.alibaba:fastjson:1.2.24' compile 'com.alibaba:fastjson:1.1.56.android' compile 'top.zibin:Luban:1.0.9' //图片裁剪库 compile 'com.soundcloud.android:android-crop:1.0.1@aar' //图片处理库和毛玻璃特效 compile 'com.github.bumptech.glide:glide:3.7.0' compile 'jp.wasabeef:glide-transformations:2.0.1' compile 'com.google.android.gms:play-services-appindexing:8.4.0' compile 'com.mabeijianxi:small-video-record:1.2.0' //视频播放器 compile 'fm.jiecao:jiecaovideoplayer:5.5.4' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in C:\Users\Administrator\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/HTApp.java ================================================ package com.htmessage.yichatopen; import android.app.Activity; import android.app.ActivityManager; import android.app.Application; import android.app.Dialog; import android.content.Context; import android.content.pm.PackageManager; import android.support.multidex.MultiDex; import android.view.LayoutInflater; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.manager.MyNotification; import com.htmessage.yichatopen.manager.LocalUserManager; import com.htmessage.yichatopen.manager.Manager; import com.tencent.bugly.crashreport.CrashReport; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class HTApp extends Application { private static Context applicationContext; private static HTApp instance; private JSONObject userJson = null; private List activities = new ArrayList<>(); public static boolean isCalling = false; @Override public void onCreate() { //sdk采用双进程守护,因此不要在守护进程中初始Application int pid = android.os.Process.myPid(); String processAppName = getAppName(pid, this); if (processAppName == null || !processAppName.equalsIgnoreCase(this.getPackageName())) { return; } super.onCreate(); applicationContext = this; instance = this; MyNotification.init(applicationContext); LocalUserManager.init(applicationContext); if (getUserJson() != null) { Manager.initManagerList(applicationContext); } //异常上报 CrashReport.initCrashReport(applicationContext, "a7b4566fd9", false); HTClientHelper.init(applicationContext); } public static Context getContext() { return applicationContext; } public static HTApp getInstance() { return instance; } public String getUsername() { String username = null; if (getUserJson() != null) { username = getUserJson().getString(HTConstant.JSON_KEY_HXID); } return username; } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); MultiDex.install(this); } public void setUserJson(JSONObject userJson) { this.userJson = userJson; if (userJson != null) { Manager.initManagerList(applicationContext); } LocalUserManager.getInstance().setUserJson(userJson); } public JSONObject getUserJson() { if (userJson == null) { userJson = LocalUserManager.getInstance().getUserJson(); } return userJson; } public void saveActivity(Activity activity) { if (activity != null) { activities.add(activity); } } public void finishActivities() { for (Activity activity : activities) { if (activity != null && !activity.isFinishing()) { activity.finish(); } } } /** * check the application process name if process name is not qualified, then we think it is a service process and we will not init SDK * * @param pID * @return */ private static String getAppName(int pID, Context appContext) { String processName = null; ActivityManager am = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE); List l = am.getRunningAppProcesses(); Iterator i = l.iterator(); PackageManager pm = appContext.getPackageManager(); while (i.hasNext()) { ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next()); try { if (info.pid == pID) { CharSequence c = pm.getApplicationLabel(pm.getApplicationInfo(info.processName, PackageManager.GET_META_DATA)); processName = info.processName; return processName; } } catch (Exception e) { } } return processName; } /** * 得到自定义的progressDialog * * @param context * @param msg * @return */ public Dialog createLoadingDialog(Context context, String msg) { LayoutInflater inflater = LayoutInflater.from(context); View v = inflater.inflate(R.layout.loading_dialog, null);// 得到加载view LinearLayout layout = (LinearLayout) v.findViewById(R.id.dialog_view);// 加载布局 // main.xml中的ImageView ImageView spaceshipImage = (ImageView) v.findViewById(R.id.img); TextView tipTextView = (TextView) v.findViewById(R.id.tipTextView);// 提示文字 // 加载动画 Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation( context, R.anim.loading_animation); // 使用ImageView显示动画 spaceshipImage.startAnimation(hyperspaceJumpAnimation); tipTextView.setText(msg);// 设置加载信息 Dialog loadingDialog = new Dialog(context, R.style.loading_dialog);// 创建自定义样式dialog loadingDialog.setCancelable(false);// 不可以用“返回键”取消 loadingDialog.setContentView(layout, new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));// 设置布局 return loadingDialog; } public String getDirFilePath() { File file = new File(HTConstant.DIR_AVATAR); if (!file.exists()) { file.getParentFile().mkdirs(); file.mkdir(); } return file.getAbsolutePath() + File.separator; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/HTClientHelper.java ================================================ package com.htmessage.yichatopen; import android.content.Context; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import com.alibaba.fastjson.JSONObject; import com.htmessage.sdk.client.HTOptions; import com.htmessage.sdk.manager.HTChatManager; import com.htmessage.yichatopen.activity.main.MainActivity; import com.htmessage.yichatopen.activity.chat.ChatActivity; import com.htmessage.yichatopen.manager.MyNotification; import com.htmessage.yichatopen.domain.InviteMessage; import com.htmessage.yichatopen.domain.InviteMessgeDao; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.manager.NotifierManager; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.listener.HTConnectionListener; import com.htmessage.sdk.model.CallMessage; import com.htmessage.sdk.model.CmdMessage; import com.htmessage.sdk.model.HTMessage; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.utils.HTMessageUtils; import java.util.List; /** * Created by huangfangyi on 2017/3/3. * qq 84543217 */ public class HTClientHelper { private NotifierManager notifierManager; private static Context applicationContext; private static HTClientHelper htClientHelper; public static void init(Context context) { htClientHelper = new HTClientHelper(context); } public HTClientHelper(Context context) { this.applicationContext = context; HTOptions options=new HTOptions(); //IM相关配置 options.setHost(HTConstant.HOST_IM); //IP地址 options.setOssInfo(HTConstant.endpoint,HTConstant.bucket,HTConstant.accessKeyId,HTConstant.accessKeySecret);//阿里云OSS相关 options.setSinglePointUrl(HTConstant.DEVICE_URL_UPDATE,HTConstant.DEVICE_URL_GET);//设置单APP端登录 options.setDebug(false); //是否打印log false不打印 options.setKeepAlive(false); //是否开启保活 false 不开启 HTClient.init(applicationContext,options); HTClient.getInstance().setMessageLisenter(messageLisenter); HTClient.getInstance().addConnectionListener(htConnectionListener); notifierManager = new NotifierManager(applicationContext); } public static HTClientHelper getInstance() { if (htClientHelper == null) { throw new RuntimeException("please init first!"); } return htClientHelper; } private HTConnectionListener htConnectionListener = new HTConnectionListener() { @Override public void onConnected() { // Toast.makeText(applicationContext,"连上啦",Toast.LENGTH_SHORT).show(); notifyConnection(true); } @Override public void onDisconnected() { notifyConnection(false); // Toast.makeText(applicationContext,"断连啦",Toast.LENGTH_SHORT).show(); } @Override public void onConflict() { // Toast.makeText(applicationContext,"被踢啦",Toast.LENGTH_SHORT).show(); notifyConflict(); } }; private HTClient.MessageLisenter messageLisenter = new HTClient.MessageLisenter() { @Override public void onHTMessage(HTMessage htMessage) { handleHTMessage(htMessage); } @Override public void onCMDMessgae(CmdMessage cmdMessage) { handleCmdMessage(cmdMessage); } @Override public void onCallMessgae(CallMessage callMessage) { //lite版没有音视频消息 } }; private void handleHTMessage(HTMessage htMessage){ Intent intent=new Intent(IMAction.ACTION_NEW_MESSAGE); intent.putExtra("message",htMessage); LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent); if (ChatActivity.activityInstance != null && htMessage.getUsername().equals(ChatActivity.activityInstance.getToChatUsername())) { } else { MyNotification.getInstance().onNewMessage(htMessage); } notifyHTMessage(); } private void handleCmdMessage(CmdMessage cmdMessage) { InviteMessgeDao inviteMessgeDao = new InviteMessgeDao(applicationContext); String data = cmdMessage.getBody(); if (data != null) { JSONObject dataJSON = JSONObject.parseObject(data); if (dataJSON != null && dataJSON.containsKey("action")) { int action = dataJSON.getInteger("action"); if (action == 1000) { //收到好友申请的请求 List msgs = inviteMessgeDao.getMessagesList(); for (InviteMessage inviteMessage : msgs) { if ( inviteMessage.getFrom().equals(cmdMessage.getFrom())) { inviteMessgeDao.deleteMessage(cmdMessage.getFrom()); } } InviteMessage msg = new InviteMessage(); msg.setFrom(cmdMessage.getFrom()); msg.setTime(System.currentTimeMillis()); msg.setReason(dataJSON.getJSONObject("data").toJSONString()); // set invitation status msg.setStatus(InviteMessage.Status.BEINVITEED); notifyNewInviteMessage(msg,null); } else if (action == 1001) { //收到好友同意的透传消息 List msgs = inviteMessgeDao.getMessagesList(); for (InviteMessage inviteMessage : msgs) { if (inviteMessage.getFrom().equals(cmdMessage.getFrom())) { inviteMessgeDao.deleteMessage(cmdMessage.getFrom()); } } // save invitation as message InviteMessage msg = new InviteMessage(); msg.setFrom(cmdMessage.getFrom()); msg.setReason(dataJSON.getJSONObject("data").toJSONString()); msg.setTime(System.currentTimeMillis()); // Log.d(TAG, message.getFrom() + "accept your request"); msg.setStatus(InviteMessage.Status.BEAGREED); notifyNewInviteMessage(msg,dataJSON.getJSONObject("data")); } else if (action == 1002) { //收到好友拒绝的透传消息 //Lite版没有拒绝好友申请的处理 } else if (action == 1003) { //收到删除好友的透传消息 //发送广播 if (HTApp.getInstance().getUsername().equals(cmdMessage.getTo())){ LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(new Intent(IMAction.CMD_DELETE_FRIEND).putExtra(HTConstant.JSON_KEY_HXID, cmdMessage.getFrom())); } } else if (action == 6000) {//收到撤回消息的透传 String msgId = dataJSON.getString("msgId"); String chatTo = cmdMessage.getTo(); if (cmdMessage.getChatType() == ChatType.singleChat) { chatTo = cmdMessage.getFrom(); } HTMessage htMessage = HTClient.getInstance().messageManager().getMssage(chatTo, msgId); HTMessageUtils.creatWithDrowMsg(htMessage); LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(new Intent(IMAction.ACTION_MESSAGE_WITHDROW).putExtra("msgId", msgId)); } } } } /** * user has logged into another device */ protected void notifyConflict() { Intent intent = new Intent(applicationContext, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra(IMAction.ACTION_CONFLICT, true); applicationContext.startActivity(intent); } /** * user has logged into another device */ protected void notifyConnection(boolean isConnected) { Intent intent = new Intent(IMAction.ACTION_CONNECTION_CHANAGED); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("state", isConnected); LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(intent); } /** * save and notify invitation message * * @param msg */ private void notifyNewInviteMessage(final InviteMessage msg, final JSONObject jsonObject) { new Thread(new Runnable() { @Override public void run() { InviteMessgeDao inviteMessgeDao = new InviteMessgeDao(applicationContext); inviteMessgeDao.saveMessage(msg); inviteMessgeDao.saveUnreadMessageCount(1); LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(new Intent(IMAction.ACTION_INVITE_MESSAGE)); notifierManager.getNotifier().vibrateAndPlayTone(null); if(jsonObject!=null){ User user = CommonUtils.Json2User(jsonObject); ContactsManager.getInstance().saveContact(user); LocalBroadcastManager.getInstance(applicationContext).sendBroadcast(new Intent(IMAction.ACTION_CONTACT_CHANAGED)); } } }).start(); } /** * save and notify invitation message */ private void notifyHTMessage() { notifierManager.getNotifier().vibrateAndPlayTone(null); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/HTConstant.java ================================================ package com.htmessage.yichatopen; import android.os.Environment; /** * Created by ustc on 2016/6/27. */ public class HTConstant { // IM服务器相关 public static final String HOST_IM = "xxx.xxx.xxx.xxx";//示例ip: 119.125.523.153 此IP不可用 //api服务器 public static final String HOST_API = "http://xxx.xxx.xxx.xxx/api/";//示例ip: 119.125.523.153 此IP不可用 //阿里云OSS信息配置 public static final String endpoint = "oss-cn-hangzhou.aliyuncs.com"; public static final String accessKeyId = "xxxxxxxx"; public static final String accessKeySecret = "xxxxxxxxxxxx"; public static final String bucket = "xxxxxxxxxx"; public static final String baseOssUrl = "http://"+ bucket+"."+endpoint+"/"; //登记设备id,实现单点登陆 public static final String DEVICE_URL_UPDATE = HOST_API+"updateDeviceId.php"; public static final String DEVICE_URL_GET = HOST_API+"getDeviceId.php"; //查询更新 public static final String URL_CHECK_UPDATE = HOST_API+"version.php"; //应用层使用到的api接口 public static final String URL_AVATAR= HOST_API + "upload/"; public static final String URL_REGISTER = HOST_API + "register";//注册 public static final String URL_LOGIN = HOST_API + "login";//登录 public static final String URL_FriendList = HOST_API + "fetchFriends";//获取好友列表 public static final String URL_Search_User = HOST_API + "searchUser";//查询好友 public static final String URL_Get_UserInfo = HOST_API + "getUserInfo";//获取详情 public static final String URL_UPDATE = HOST_API + "update";//更新 public static final String URL_RESETPASSWORD = HOST_API + "resetPassword";//更新密码 public static final String URL_ADD_FRIEND=HOST_API + "addFriend"; //添加好友 public static final String URL_DELETE_FRIEND=HOST_API + "removeFriend";//删除好友 public static final String URL_ADD_BLACKLIST=HOST_API +"addBlackList";//添加黑名单 public static final String URL_GET_RECENTLY_PEOPLE= HOST_API + "getRecentlyUser";//获取最近上线的人 public static final String URL_SEND_LOCAL_LOGIN_TIME= HOST_API + "updateLocalTimestamp";//获取最近上线的人 // 缩略图处理---等高宽-请查看阿里云官方文档oss图片处理文档 public static final String baseImgUrl_set = "?x-oss-process=image/resize,m_fill,h_300,w_300"; //jsonobject常用key值 public static final String JSON_KEY_NICK ="nick"; public static final String JSON_KEY_HXID ="userId"; public static final String JSON_KEY_FXID ="fxid"; public static final String JSON_KEY_SEX ="sex"; public static final String JSON_KEY_AVATAR ="avatar"; public static final String JSON_KEY_CITY ="city"; public static final String JSON_KEY_PASSWORD ="password"; public static final String JSON_KEY_PROVINCE ="province"; public static final String JSON_KEY_TEL ="tel"; public static final String JSON_KEY_SIGN ="sign"; public static final String JSON_KEY_ROLE ="role"; public static final String JSON_KEY_SESSION ="session"; //添加好友的原因 public static final String CMD_ADD_REASON="ADD_REASON"; //进入用户详情页传递json字符串 public static final String KEY_USER_INFO="userInfo"; //修改用户资料的广播 public static final String KEY_CHANGE_TYPE="type"; //开源地址 public static final String GITHUBURL = "https://github.com/YiChat"; public static final String OSCHINAURL = "http://git.oschina.net/zhangfeng_tech"; //Pro版本体验 public static final String YICHATPROURL = "https://www.pgyer.com/yichat_android"; public static final String DIR_AVATAR = Environment.getExternalStorageDirectory().toString()+"/yiChat/"; } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/IMAction.java ================================================ package com.htmessage.yichatopen; /** * Created by huangfangyi on 2017/2/10. * qq 84543217 */ public class IMAction { public static final String ACTION_INVITE_MESSAGE="action_invite_message"; public static final String ACTION_NEW_MESSAGE="action_new_message"; public static final String ACTION_REMOVED_FROM_GROUP="action_removed_from_group"; public static final String ACTION_CONTACT_CHANAGED = "action_contact_changed"; public static final String ACTION_CONFLICT = "action_conflict"; public static final String ACTION_CONNECTION_CHANAGED="action_connection_changed"; //消息撤回 public static final String ACTION_MESSAGE_WITHDROW="action_message_withdrow"; //转发消息 public static final String ACTION_MESSAGE_FORWORD="action_message_forword"; //清空消息 public static final String ACTION_MESSAGE_EMPTY="ACTION_MESSAGE_EMPTY"; //删除好友通知 public static final String CMD_DELETE_FRIEND ="DELETE_FRIEND"; //资料更新的通知 public static final String ACTION_UPDATE_INFO ="ACTION_UPDATE_INFO"; } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/BaseActivity.java ================================================ package com.htmessage.yichatopen.activity; import android.os.Build; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.Gravity; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; public class BaseActivity extends AppCompatActivity { public String TAG=this.getClass().getName().toString(); @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); HTApp.getInstance().saveActivity(this); } public void back(View view) { finish(); } public void setTitle(int title){ TextView textView= (TextView) this.findViewById(R.id.tv_title); // RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); // layoutParams.addRule(); // imageView.setLayoutParams(layoutParams); if(textView!=null){ textView.setText(title); } } public void setTitleCenter(){ TextView textView= (TextView) this.findViewById(R.id.tv_title); textView.setGravity(Gravity.CENTER); } public void setTitle(String title){ TextView textView= (TextView) this.findViewById(R.id.tv_title); if(textView!=null){ textView.setText(title); } } public void hideBackView(){ ImageView iv_back= (ImageView) this.findViewById(R.id.iv_back); View view=this.findViewById(R.id.view_temp); if(iv_back!=null&&view!=null){ iv_back.setVisibility(View.GONE); view.setVisibility(View.GONE); } } public void showRightView(int res,View.OnClickListener onClickListener){ ImageView ivRight= (ImageView) this.findViewById(R.id.iv_right); if(ivRight!=null ){ ivRight.setImageResource(res); ivRight.setVisibility(View.VISIBLE); if(onClickListener!=null){ ivRight.setOnClickListener(onClickListener); } } } public void showRightTextView(int res,View.OnClickListener onClickListener){ TextView ivRight= (TextView) this.findViewById(R.id.btn_rtc); if(ivRight!=null ){ ivRight.setText(res); ivRight.setVisibility(View.VISIBLE); if(onClickListener!=null){ ivRight.setOnClickListener(onClickListener); } } } public void showRightTextView(String res,View.OnClickListener onClickListener){ TextView ivRight= (TextView) this.findViewById(R.id.btn_rtc); if(ivRight!=null ){ ivRight.setText(res); ivRight.setVisibility(View.VISIBLE); if(onClickListener!=null){ ivRight.setOnClickListener(onClickListener); } } } protected boolean isCompatible(int apiLevel) { return Build.VERSION.SDK_INT >= apiLevel; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/BasePresenter.java ================================================ package com.htmessage.yichatopen.activity; /** * Created by huangfangyi on 2017/6/21. * qq 84543217 */ public interface BasePresenter { void start(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/BaseView.java ================================================ package com.htmessage.yichatopen.activity; import android.app.Activity; import android.content.Context; /** * Created by huangfangyi on 2017/6/21. * qq 84543217 */ public interface BaseView { void setPresenter(T presenter); Context getBaseContext(); Activity getBaseActivity(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/ScanCaptureActivity.java ================================================ package com.htmessage.yichatopen.activity; import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.PreviewCallback; import android.hardware.Camera.Size; import android.os.Bundle; import android.os.Handler; import android.text.TextUtils; import android.view.Display; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.activity.main.details.UserDetailsActivity; import com.htmessage.yichatopen.widget.scan.CameraManager; import com.htmessage.yichatopen.widget.scan.CameraPreview; import net.sourceforge.zbar.Config; import net.sourceforge.zbar.Image; import net.sourceforge.zbar.ImageScanner; import net.sourceforge.zbar.Symbol; import net.sourceforge.zbar.SymbolSet; import java.io.IOException; import java.lang.reflect.Field; public class ScanCaptureActivity extends BaseActivity { private Camera mCamera; private CameraPreview mPreview; private Handler autoFocusHandler; private CameraManager mCameraManager; private TextView scanResult; private FrameLayout scanPreview; private Button scanRestart; private RelativeLayout scanContainer; private RelativeLayout scanCropView; private ImageView scanLine; private TextView tv_title; private Rect mCropRect = null; private boolean barcodeScanned = false; private boolean previewing = true; private ImageScanner mImageScanner = null; static { System.loadLibrary("iconv"); } public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.widget_zbar_scan_capture); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); findViewById(); addEvents(); initViews(); } private void findViewById() { scanPreview = (FrameLayout) findViewById(R.id.capture_preview); scanResult = (TextView) findViewById(R.id.capture_scan_result); scanRestart = (Button) findViewById(R.id.capture_restart_scan); scanContainer = (RelativeLayout) findViewById(R.id.capture_container); scanCropView = (RelativeLayout) findViewById(R.id.capture_crop_view); scanLine = (ImageView) findViewById(R.id.capture_scan_line); tv_title = (TextView) findViewById(R.id.tv_title); tv_title.setText(R.string.scan_title); } private void addEvents() { scanRestart.setOnClickListener(new OnClickListener() { public void onClick(View v) { if (barcodeScanned) { barcodeScanned = false; scanResult.setText(R.string.Scanning); mCamera.setPreviewCallback(previewCb); mCamera.startPreview(); previewing = true; mCamera.autoFocus(autoFocusCB); } } }); } private void initViews() { mImageScanner = new ImageScanner(); mImageScanner.setConfig(0, Config.X_DENSITY, 3); mImageScanner.setConfig(0, Config.Y_DENSITY, 3); autoFocusHandler = new Handler(); mCameraManager = new CameraManager(this); try { mCameraManager.openDriver(); } catch (IOException e) { e.printStackTrace(); } // 调整扫描框大小,自适应屏幕 Display display = this.getWindowManager().getDefaultDisplay(); int width = display.getWidth(); int height = display.getHeight(); RelativeLayout.LayoutParams linearParams = (RelativeLayout.LayoutParams) scanCropView .getLayoutParams(); linearParams.height = (int) (width * 0.8); linearParams.width = (int) (width * 0.8); scanCropView.setLayoutParams(linearParams); // ** mCamera = mCameraManager.getCamera(); mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB); scanPreview.addView(mPreview); TranslateAnimation animation = new TranslateAnimation( Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.85f); animation.setDuration(5000); animation.setRepeatCount(-1); animation.setRepeatMode(Animation.REVERSE); scanLine.startAnimation(animation); } public void onPause() { super.onPause(); releaseCamera(); } private void releaseCamera() { if (mCamera != null) { previewing = false; mCamera.setPreviewCallback(null); mCamera.release(); mCamera = null; } } private Runnable doAutoFocus = new Runnable() { public void run() { if (previewing) mCamera.autoFocus(autoFocusCB); } }; PreviewCallback previewCb = new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera camera) { Size size = camera.getParameters().getPreviewSize(); // 这里需要将获取的data翻转一下,因为相机默认拿的的横屏的数据 byte[] rotatedData = new byte[data.length]; for (int y = 0; y < size.height; y++) { for (int x = 0; x < size.width; x++) rotatedData[x * size.height + size.height - y - 1] = data[x + y * size.width]; } // 宽高也要调整 int tmp = size.width; size.width = size.height; size.height = tmp; initCrop(); Image barcode = new Image(size.width, size.height, "Y800"); barcode.setData(rotatedData); barcode.setCrop(mCropRect.left, mCropRect.top, mCropRect.width(), mCropRect.height()); int result = mImageScanner.scanImage(barcode); String resultStr = null; if (result != 0) { SymbolSet syms = mImageScanner.getResults(); for (Symbol sym : syms) { resultStr = sym.getData(); } } if (!TextUtils.isEmpty(resultStr)) { previewing = false; mCamera.setPreviewCallback(null); mCamera.stopPreview(); releaseCamera(); barcodeScanned = true; if (resultStr.contains(":")) { String userType= resultStr .substring(0, resultStr.indexOf(":")); String value = resultStr.substring(resultStr.indexOf(":") + 1); System.out.println("type----------->>>>" + userType); System.out.println("value----------->>>>" + value); if (userType.equals("userInfo")) { JSONObject object = JSONObject.parseObject(value);//.putExtra(HTConstant.JSON_KEY_HXID, object.getString(HTConstant.JSON_KEY_HXID)) startActivity(new Intent(ScanCaptureActivity.this, UserDetailsActivity.class).putExtra(HTConstant.KEY_USER_INFO,object.toJSONString())); } else { Toast.makeText(getApplicationContext(), R.string.code_is_not_invlide, Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(getApplicationContext(), R.string.code_is_not_invlide, Toast.LENGTH_SHORT).show(); } finish(); } } }; // Mimic continuous auto-focusing AutoFocusCallback autoFocusCB = new AutoFocusCallback() { public void onAutoFocus(boolean success, Camera camera) { autoFocusHandler.postDelayed(doAutoFocus, 1000); } }; /** * 初始化截取的矩形区域 */ private void initCrop() { int cameraWidth = mCameraManager.getCameraResolution().y; int cameraHeight = mCameraManager.getCameraResolution().x; /** 获取布局中扫描框的位置信息 */ int[] location = new int[2]; scanCropView.getLocationInWindow(location); int cropLeft = location[0]; int cropTop = location[1] - getStatusBarHeight(); int cropWidth = scanCropView.getWidth(); int cropHeight = scanCropView.getHeight(); /** 获取布局容器的宽高 */ int containerWidth = scanContainer.getWidth(); int containerHeight = scanContainer.getHeight(); /** 计算最终截取的矩形的左上角顶点x坐标 */ int x = cropLeft * cameraWidth / containerWidth; /** 计算最终截取的矩形的左上角顶点y坐标 */ int y = cropTop * cameraHeight / containerHeight; /** 计算最终截取的矩形的宽度 */ int width = cropWidth * cameraWidth / containerWidth; /** 计算最终截取的矩形的高度 */ int height = cropHeight * cameraHeight / containerHeight; /** 生成最终的截取的矩形 */ mCropRect = new Rect(x, y, width + x, height + y); } private int getStatusBarHeight() { try { Class c = Class.forName("com.android.internal.R$dimen"); Object obj = c.newInstance(); Field field = c.getField("status_bar_height"); int x = Integer.parseInt(field.get(obj).toString()); return getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printStackTrace(); } return 0; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/SettingsActivity.java ================================================ package com.htmessage.yichatopen.activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.view.View; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.manager.SettingsManager; import com.htmessage.yichatopen.activity.login.LoginActivity; import com.htmessage.yichatopen.activity.main.password.PasswordResetActivity; import com.htmessage.yichatopen.widget.SwitchButton; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import com.htmessage.sdk.client.HTClient; import com.htmessage.yichatopen.widget.HTAlertDialog; import java.util.ArrayList; import java.util.List; /** * Created by huangfangyi on 2016/7/4.\ * QQ:84543217 */ public class SettingsActivity extends BaseActivity implements View.OnClickListener { /** * new message notification */ private RelativeLayout rl_switch_notification; /** * sound */ private RelativeLayout rl_switch_sound; /** * vibration */ private RelativeLayout rl_switch_vibrate; /** * speaker */ private RelativeLayout rl_switch_speaker; /** * line between sound and vibration */ private TextView textview1, textview2; private LinearLayout blacklistContainer; private LinearLayout userProfileContainer; /** * logout */ private RelativeLayout rl_logout; private RelativeLayout rl_switch_chatroom_leave; private RelativeLayout rl_switch_delete_msg_when_exit_group; private RelativeLayout rl_switch_auto_accept_group_invitation; private RelativeLayout rl_switch_adaptive_video_encode, rl_keep_live, re_resetpassword; private RelativeLayout rl_update, rl_about_us;//检查更新 /** * Diagnose */ private LinearLayout llDiagnose, llChange, ll_numal_set; private View view_devider; /** * display name for APNs */ private LinearLayout pushNick, ll_new_msg; private SwitchButton notifiSwitch; private SwitchButton soundSwitch; private SwitchButton vibrateSwitch; private SwitchButton speakerSwitch; private SwitchButton ownerLeaveSwitch; private SwitchButton switch_delete_msg_when_exit_group; private SwitchButton switch_auto_accept_group_invitation; private SwitchButton switch_adaptive_video_encode; private SettingsManager settingsModel; @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.fragment_conversation_settings); initView(); initData(); setListener(); } private void setListener() { blacklistContainer.setOnClickListener(this); userProfileContainer.setOnClickListener(this); rl_switch_notification.setOnClickListener(this); rl_switch_sound.setOnClickListener(this); rl_switch_vibrate.setOnClickListener(this); rl_switch_speaker.setOnClickListener(this); rl_logout.setOnClickListener(this); llDiagnose.setOnClickListener(this); pushNick.setOnClickListener(this); rl_switch_chatroom_leave.setOnClickListener(this); rl_switch_delete_msg_when_exit_group.setOnClickListener(this); rl_switch_auto_accept_group_invitation.setOnClickListener(this); rl_switch_adaptive_video_encode.setOnClickListener(this); rl_update.setOnClickListener(this); rl_keep_live.setOnClickListener(this); re_resetpassword.setOnClickListener(this); rl_about_us.setOnClickListener(this); ll_new_msg.setOnClickListener(this); llChange.setOnClickListener(this); } private void initData() { settingsModel = SettingsManager.getInstance(); // the vibrate and sound notification are allowed or not? if (settingsModel.getSettingMsgNotification()) { notifiSwitch.openSwitch(); rl_switch_sound.setVisibility(View.VISIBLE); rl_switch_vibrate.setVisibility(View.VISIBLE); textview1.setVisibility(View.VISIBLE); textview2.setVisibility(View.VISIBLE); } else { notifiSwitch.closeSwitch(); rl_switch_sound.setVisibility(View.GONE); rl_switch_vibrate.setVisibility(View.GONE); textview1.setVisibility(View.GONE); textview2.setVisibility(View.GONE); } // sound notification is switched on or not? if (settingsModel.getSettingMsgSound()) { soundSwitch.openSwitch(); } else { soundSwitch.closeSwitch(); } // vibrate notification is switched on or not? if (settingsModel.getSettingMsgVibrate()) { vibrateSwitch.openSwitch(); } else { vibrateSwitch.closeSwitch(); } // the speaker is switched on or not? if (settingsModel.getSettingMsgSpeaker()) { speakerSwitch.openSwitch(); } else { speakerSwitch.closeSwitch(); } // if allow owner leave if (settingsModel.isChatroomOwnerLeaveAllowed()) { ownerLeaveSwitch.openSwitch(); } else { ownerLeaveSwitch.closeSwitch(); } // delete messages when exit group? if (settingsModel.isDeleteMessagesAsExitGroup()) { switch_delete_msg_when_exit_group.openSwitch(); } else { switch_delete_msg_when_exit_group.closeSwitch(); } if (settingsModel.isAutoAcceptGroupInvitation()) { switch_auto_accept_group_invitation.openSwitch(); } else { switch_auto_accept_group_invitation.closeSwitch(); } } private void initView() { rl_switch_notification = (RelativeLayout) findViewById(R.id.rl_switch_notification); rl_switch_sound = (RelativeLayout) findViewById(R.id.rl_switch_sound); rl_switch_vibrate = (RelativeLayout) findViewById(R.id.rl_switch_vibrate); rl_switch_speaker = (RelativeLayout) findViewById(R.id.rl_switch_speaker); rl_switch_chatroom_leave = (RelativeLayout) findViewById(R.id.rl_switch_chatroom_owner_leave); rl_switch_delete_msg_when_exit_group = (RelativeLayout) findViewById(R.id.rl_switch_delete_msg_when_exit_group); rl_switch_auto_accept_group_invitation = (RelativeLayout) findViewById(R.id.rl_switch_auto_accept_group_invitation); rl_switch_adaptive_video_encode = (RelativeLayout) findViewById(R.id.rl_switch_adaptive_video_encode); rl_update = (RelativeLayout) findViewById(R.id.rl_update);//rl_keep_live,re_resetpassword rl_keep_live = (RelativeLayout) findViewById(R.id.rl_keep_live); re_resetpassword = (RelativeLayout) findViewById(R.id.re_resetpassword); rl_about_us = (RelativeLayout) findViewById(R.id.rl_about_us); notifiSwitch = (SwitchButton) findViewById(R.id.switch_notification); soundSwitch = (SwitchButton) findViewById(R.id.switch_sound); vibrateSwitch = (SwitchButton) findViewById(R.id.switch_vibrate); speakerSwitch = (SwitchButton) findViewById(R.id.switch_speaker); ownerLeaveSwitch = (SwitchButton) findViewById(R.id.switch_owner_leave); switch_delete_msg_when_exit_group = (SwitchButton) findViewById(R.id.switch_delete_msg_when_exit_group); switch_auto_accept_group_invitation = (SwitchButton) findViewById(R.id.switch_auto_accept_group_invitation); switch_adaptive_video_encode = (SwitchButton) findViewById(R.id.switch_adaptive_video_encode); llChange = (LinearLayout) findViewById(R.id.ll_change); ll_new_msg = (LinearLayout) findViewById(R.id.ll_new_msg); ll_numal_set = (LinearLayout) findViewById(R.id.ll_numal_set); rl_logout = (RelativeLayout) findViewById(R.id.rl_logout); textview1 = (TextView) findViewById(R.id.textview1); textview2 = (TextView) findViewById(R.id.textview2); blacklistContainer = (LinearLayout) findViewById(R.id.ll_black_list); userProfileContainer = (LinearLayout) findViewById(R.id.ll_user_profile); llDiagnose = (LinearLayout) findViewById(R.id.ll_diagnose); pushNick = (LinearLayout) findViewById(R.id.ll_set_push_nick); view_devider = findViewById(R.id.view_devider); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.ll_change: //新消息设置 if (ll_new_msg.getVisibility() == View.GONE) { ll_new_msg.setVisibility(View.VISIBLE); view_devider.setVisibility(View.GONE); } else { ll_new_msg.setVisibility(View.GONE); view_devider.setVisibility(View.VISIBLE); } break; case R.id.rl_switch_notification: if (notifiSwitch.isSwitchOpen()) { notifiSwitch.closeSwitch(); rl_switch_sound.setVisibility(View.GONE); rl_switch_vibrate.setVisibility(View.GONE); textview1.setVisibility(View.GONE); textview2.setVisibility(View.GONE); settingsModel.setSettingMsgNotification(false); } else { notifiSwitch.openSwitch(); rl_switch_sound.setVisibility(View.VISIBLE); rl_switch_vibrate.setVisibility(View.VISIBLE); textview1.setVisibility(View.VISIBLE); textview2.setVisibility(View.VISIBLE); settingsModel.setSettingMsgNotification(true); } break; case R.id.rl_switch_sound: if (soundSwitch.isSwitchOpen()) { soundSwitch.closeSwitch(); settingsModel.setSettingMsgSound(false); } else { soundSwitch.openSwitch(); settingsModel.setSettingMsgSound(true); } break; case R.id.rl_switch_vibrate: if (vibrateSwitch.isSwitchOpen()) { vibrateSwitch.closeSwitch(); settingsModel.setSettingMsgVibrate(false); } else { vibrateSwitch.openSwitch(); settingsModel.setSettingMsgVibrate(true); } break; case R.id.rl_switch_speaker: if (speakerSwitch.isSwitchOpen()) { speakerSwitch.closeSwitch(); settingsModel.setSettingMsgSpeaker(false); } else { speakerSwitch.openSwitch(); settingsModel.setSettingMsgSpeaker(true); } break; case R.id.rl_switch_chatroom_owner_leave: if (ownerLeaveSwitch.isSwitchOpen()) { ownerLeaveSwitch.closeSwitch(); settingsModel.allowChatroomOwnerLeave(false); } else { ownerLeaveSwitch.openSwitch(); settingsModel.allowChatroomOwnerLeave(true); } break; case R.id.rl_switch_delete_msg_when_exit_group: if (switch_delete_msg_when_exit_group.isSwitchOpen()) { switch_delete_msg_when_exit_group.closeSwitch(); settingsModel.setDeleteMessagesAsExitGroup(false); } else { switch_delete_msg_when_exit_group.openSwitch(); settingsModel.setDeleteMessagesAsExitGroup(true); } break; case R.id.rl_switch_auto_accept_group_invitation: if (switch_auto_accept_group_invitation.isSwitchOpen()) { switch_auto_accept_group_invitation.closeSwitch(); settingsModel.setAutoAcceptGroupInvitation(false); } else { switch_auto_accept_group_invitation.openSwitch(); settingsModel.setAutoAcceptGroupInvitation(true); } break; case R.id.rl_switch_adaptive_video_encode: if (switch_adaptive_video_encode.isSwitchOpen()) { switch_adaptive_video_encode.closeSwitch(); settingsModel.setAdaptiveVideoEncode(false); } else { switch_adaptive_video_encode.openSwitch(); settingsModel.setAdaptiveVideoEncode(true); } break; case R.id.rl_logout: // logout(); logOutDialog(); break; case R.id.ll_black_list: // startActivity(new Intent(SettingsActivity.this, BlacklistActivity.class)); break; case R.id.ll_diagnose: // startActivity(new Intent(SettingsActivity.this, DiagnoseActivity.class)); break; case R.id.ll_set_push_nick: // startActivity(new Intent(SettingsActivity.this, OfflinePushNickActivity.class)); break; case R.id.ll_user_profile: // startActivity(new Intent(SettingsActivity.this, UserProfileActivity.class).putExtra("setting", true) // .putExtra("username", EMClient.getInstance().getCurrentUser())); break; case R.id.rl_update: //检查更新 getAppUpdate(); break; //rl_keep_live,re_resetpassword case R.id.re_resetpassword: //重置密码 startActivity(new Intent(SettingsActivity.this, PasswordResetActivity.class).putExtra("isReset", true)); break; case R.id.rl_keep_live: //后台保活 Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS); startActivity(intent); break; case R.id.rl_about_us: break; default: break; } } private void logOutDialog() { HTAlertDialog dialog = new HTAlertDialog(this, null, new String[]{getString(R.string.exit_this_user), getString(R.string.close_app)}); dialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { switch (position) { case 0: logout(); break; case 1: HTApp.getInstance().finishActivities(); //杀死该应用进程 android.os.Process.killProcess(android.os.Process.myPid()); System.exit(0); break; } } }); } void logout() { final ProgressDialog pd = new ProgressDialog(SettingsActivity.this); String st = getResources().getString(R.string.Are_logged_out); pd.setMessage(st); pd.setCanceledOnTouchOutside(false); pd.show(); HTClient.getInstance().logout(new HTClient.HTCallBack() { @Override public void onSuccess() { pd.dismiss(); HTApp.getInstance().setUserJson(null); HTApp.getInstance().finishActivities(); startActivity(new Intent(SettingsActivity.this, LoginActivity.class)); finish(); } @Override public void onError() { pd.dismiss(); Toast.makeText(getApplicationContext(), R.string.logout_failed, Toast.LENGTH_SHORT).show(); } }); // HTClientManager.getInstance().logout(new HTClientManager.LogoutCallBack() { // @Override // public void onSuccess() { // // HTApp.getInstance().finishActivities(); // runOnUiThread(new Runnable() { // @Override // public void run() { // pd.dismiss(); // startActivity(new Intent(SettingsActivity.this, LoginActivity.class)); // finish(); // } // }); // // } // // @Override // public void onFailure(String errorMessage) { // // } // }); // // } public void back(View view) { finish(); } /** * 获取VersionCode * * @return 当前应用的VersionCode */ public String getVersionCode() { try { PackageManager manager = getPackageManager(); PackageInfo info = manager.getPackageInfo(getPackageName(), 0); String version = String.valueOf(info.versionCode); return version; } catch (Exception e) { e.printStackTrace(); return null; } } private void getAppUpdate() { final ProgressDialog dialog = new ProgressDialog(SettingsActivity.this); dialog.setMessage(getString(R.string.are_checking_update)); dialog.show(); final String version = getVersionCode(); List params = new ArrayList<>(); params.add(new Param("system", "0")); new OkHttpUtils(SettingsActivity.this).post(params, HTConstant.URL_CHECK_UPDATE, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { if (jsonObject != null) { String serviceVersion = jsonObject.getString("newVersion"); String url = jsonObject.getString("url"); String info = jsonObject.getString("info"); String statue = jsonObject.getString("statue"); if (!version.equals(serviceVersion) && (Integer.valueOf(version) < Integer.valueOf(serviceVersion))) { showUpdateDialog(SettingsActivity.this, getString(R.string.has_update), info, url); } else { Toast.makeText(SettingsActivity.this, R.string.just_new_version, Toast.LENGTH_SHORT).show(); } } dialog.dismiss(); } @Override public void onFailure(String errorMsg) { dialog.dismiss(); } }); } private void showUpdateDialog(final Context context, String title, String message, final String url) { AlertDialog.Builder builder = new AlertDialog.Builder(context); View dialogView = View.inflate(context, R.layout.layout_alert_dialog_delete, null); TextView tv_delete_people = (TextView) dialogView.findViewById(R.id.tv_delete_people); TextView tv_delete_title = (TextView) dialogView.findViewById(R.id.tv_delete_title); TextView tv_cancle = (TextView) dialogView.findViewById(R.id.tv_cancle); TextView tv_ok = (TextView) dialogView.findViewById(R.id.tv_ok); tv_delete_title.setText(title); tv_delete_people.setText(message); tv_cancle.setText(R.string.update_later); tv_ok.setText(R.string.update_now); builder.setView(dialogView); final AlertDialog dialog = builder.show(); // dialog.setCancelable(false);//点击屏幕外不取消 返回键也没用 // dialog.setCanceledOnTouchOutside(false); //点击屏幕外取消,返回键有用 tv_cancle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); tv_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); Uri uri = Uri.parse(url); intent.setData(uri); SettingsActivity.this.startActivity(intent); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/ShowBigImageActivity.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.activity; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.widget.photoview.PhotoView; import java.io.File; /** * download and show original image * */ public class ShowBigImageActivity extends BaseActivity { private PhotoView image; @Override protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_show_big_image); super.onCreate(savedInstanceState); String localPath=this.getIntent().getStringExtra("localPath"); Uri uri=Uri.fromFile(new File(localPath)); image = (PhotoView) findViewById(R.id.image); // image.setImageURI(uri); Glide.with(ShowBigImageActivity.this).load(uri.getPath()).diskCacheStrategy(DiskCacheStrategy.ALL).error(R.drawable.default_image).into(image); image.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { finish(); } }); } @Override public void onBackPressed() { setResult(RESULT_OK); finish(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/SplashActivity.java ================================================ package com.htmessage.yichatopen.activity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.widget.RelativeLayout; import com.htmessage.yichatopen.R; import com.htmessage.sdk.client.HTClient; import com.htmessage.yichatopen.activity.login.LoginActivity; import com.htmessage.yichatopen.activity.main.MainActivity; import com.htmessage.yichatopen.utils.UpdateLastLoginTimeUtils; /** * 开屏页 * */ public class SplashActivity extends Activity { //继承Activity是为动画启动前无白屏卡顿 @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.activity_splash); RelativeLayout rootLayout = (RelativeLayout) findViewById(R.id.splash_root); AlphaAnimation animation = new AlphaAnimation(0.5f, 1.0f); animation.setDuration(2000); rootLayout.startAnimation(animation); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (HTClient.getInstance().isLogined()) { //上传最近登录时间 UpdateLastLoginTimeUtils.sendLocalTimeToService(SplashActivity.this); Intent intent=new Intent(SplashActivity.this, MainActivity.class); startActivity(intent); finish(); }else { startActivity(new Intent(SplashActivity.this, LoginActivity.class)); finish(); } } @Override public void onAnimationRepeat(Animation animation) { } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/add/end/AddFriendsFinalActivity.java ================================================ package com.htmessage.yichatopen.activity.addfriends.add.end; import android.app.ProgressDialog; import android.os.Bundle; import android.text.Selection; import android.text.Spannable; import android.view.View; import android.view.View.OnClickListener; import android.widget.EditText; import android.widget.Toast; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.HTConstant; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.manager.HTChatManager; import com.htmessage.sdk.model.CmdMessage; import java.util.UUID; public class AddFriendsFinalActivity extends BaseActivity { private ProgressDialog progressDialog; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_addfriends_final); String userInfo = getIntent().getStringExtra(HTConstant.KEY_USER_INFO); JSONObject jsonObject = null; try { jsonObject = JSONObject.parseObject(userInfo); } catch (JSONException e) { } if (jsonObject == null) { finish(); return; } initView(jsonObject); } private void initView(final JSONObject jsonObject) { final EditText etReason = (EditText) this.findViewById(R.id.et_reason); etReason.setText(getString(R.string.i_am)+HTApp.getInstance().getUserJson().getString(HTConstant.JSON_KEY_NICK)); if (etReason.getText() instanceof Spannable) { Spannable spanText = (Spannable)etReason.getText(); Selection.setSelection(spanText, etReason.getText().length()); } findViewById(R.id.tv_send).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { addContact(jsonObject.getString(HTConstant.JSON_KEY_HXID), etReason.getText().toString().trim()); } }); } /** * 添加contact * * @param */ public void addContact(final String hxid, String reason) { progressDialog = new ProgressDialog(this); progressDialog.setMessage(getResources().getString(R.string.Is_sending_a_request)); progressDialog.setCanceledOnTouchOutside(false); progressDialog.show(); JSONObject userJson = HTApp.getInstance().getUserJson(); JSONObject data = new JSONObject(); data.put("ADD_REASON", reason); data.put("userId", userJson.getString("userId")); data.put("nick",userJson.getString("nick")); data.put("avatar", userJson.getString("avatar")); data.put("role",userJson.getString(HTConstant.JSON_KEY_ROLE)); data.put("teamId",userJson.getString("teamId")); JSONObject bodyJson = new JSONObject(); bodyJson.put("action", 1000); bodyJson.put("data", data); CmdMessage customMessage = new CmdMessage(); customMessage.setBody(bodyJson.toJSONString()); customMessage.setFrom(HTApp.getInstance().getUsername()); customMessage.setTime(System.currentTimeMillis()); customMessage.setTo(hxid); customMessage.setMsgId( UUID.randomUUID().toString()); customMessage.setChatType(ChatType.singleChat); HTClient.getInstance().chatManager().sendCmdMessage(customMessage, new HTChatManager.HTMessageCallBack() { @Override public void onProgress() { } @Override public void onSuccess() { runOnUiThread(new Runnable() { @Override public void run() { if (progressDialog != null) { progressDialog.dismiss(); } Toast.makeText(AddFriendsFinalActivity.this, R.string.send_successful, Toast.LENGTH_SHORT) .show(); finish(); } }); } @Override public void onFailure() { runOnUiThread(new Runnable() { @Override public void run() { if (progressDialog != null) { progressDialog.dismiss(); } Toast.makeText(AddFriendsFinalActivity.this, getResources().getString( R.string.Request_add_buddy_failure), Toast.LENGTH_SHORT) .show(); finish(); } }); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/add/next/AddFriendNextBasePrestener.java ================================================ package com.htmessage.yichatopen.activity.addfriends.add.next; import com.htmessage.yichatopen.activity.BasePresenter; /** * 项目名称:HTOpen * 类描述:AddFriendNextBasePrestener 描述: * 创建人:songlijie * 创建时间:2017/7/7 17:22 * 邮箱:814326663@qq.com */ public interface AddFriendNextBasePrestener extends BasePresenter { void searchUser(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/add/next/AddFriendNextFragment.java ================================================ package com.htmessage.yichatopen.activity.addfriends.add.next; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.main.details.UserDetailsActivity; import com.htmessage.yichatopen.HTConstant; /** * 项目名称:HTOpen * 类描述:AddFriendNextFragment 描述: * 创建人:songlijie * 创建时间:2017/7/7 17:33 * 邮箱:814326663@qq.com */ public class AddFriendNextFragment extends Fragment implements AddFriendNextView ,TextWatcher,View.OnClickListener{ private RelativeLayout re_search; private TextView tv_search; private EditText et_search; private AddFriendNextPrestener prestener; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View nextView = inflater.inflate(R.layout.activity_addfriends_next, container, false); initView(nextView); setListener(); return nextView; } private void initView(View nextView) { re_search = (RelativeLayout) nextView.findViewById(R.id.re_search); tv_search = (TextView) nextView.findViewById(R.id.tv_search); et_search = (EditText) nextView.findViewById(R.id.et_search); } private void setListener() { et_search.addTextChangedListener(this); re_search.setOnClickListener(this); } @Override public String getInputString() { return et_search.getText().toString().trim(); } @Override public void onSearchSuccess(JSONObject object) { getBaseActivity().startActivity(new Intent(getBaseActivity(), UserDetailsActivity.class).putExtra(HTConstant.KEY_USER_INFO,object.toJSONString())); } @Override public void onSearchFailed(String error) { Toast.makeText(getBaseContext(), error, Toast.LENGTH_SHORT).show(); } @Override public void setPresenter(AddFriendNextPrestener presenter) { this.prestener = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.length() > 0) { re_search.setVisibility(View.VISIBLE); tv_search.setText(et_search.getText().toString().trim()); } else { re_search.setVisibility(View.GONE); tv_search.setText(""); } } @Override public void afterTextChanged(Editable s) { } public void back(View view) { getBaseActivity().finish(); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.re_search: prestener.searchUser(); break; } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/add/next/AddFriendNextPrestener.java ================================================ package com.htmessage.yichatopen.activity.addfriends.add.next; import android.app.ProgressDialog; import android.text.TextUtils; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import java.util.ArrayList; import java.util.List; /** * 项目名称:HTOpen * 类描述:AddFriendNextPrestener 描述: * 创建人:songlijie * 创建时间:2017/7/7 17:23 * 邮箱:814326663@qq.com */ public class AddFriendNextPrestener implements AddFriendNextBasePrestener { private AddFriendNextView nextView; public AddFriendNextPrestener(AddFriendNextView nextView) { this.nextView = nextView; this.nextView.setPresenter(this); } @Override public void searchUser() { if (TextUtils.isEmpty(nextView.getInputString())){ return; } final ProgressDialog dialog = new ProgressDialog(nextView.getBaseContext()); dialog.setMessage(nextView.getBaseContext().getString(R.string.are_finding_contact)); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); dialog.show(); List paramList=new ArrayList<>(); paramList.add(new Param("userId", nextView.getInputString())); new OkHttpUtils(nextView.getBaseContext()).post(paramList, HTConstant.URL_Search_User, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { dialog.dismiss(); int code = jsonObject.getInteger("code"); if (code == 1) { JSONObject json = jsonObject.getJSONObject("user"); nextView.onSearchSuccess(json); } else if (code == -1) { nextView.onSearchFailed(nextView.getBaseContext().getString(R.string.User_does_not_exis)); } else if (code == 0) { nextView.onSearchFailed(nextView.getBaseContext().getString(R.string.server_is_busy_try_again)); } else { nextView.onSearchFailed(nextView.getBaseContext().getString(R.string.server_is_busy_try_again)); } } @Override public void onFailure(String errorMsg) { nextView.onSearchFailed(errorMsg); } }); } @Override public void start() { } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/add/next/AddFriendNextView.java ================================================ package com.htmessage.yichatopen.activity.addfriends.add.next; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.activity.BaseView; /** * 项目名称:HTOpen * 类描述:AddFriendNextView 描述: * 创建人:songlijie * 创建时间:2017/7/7 17:24 * 邮箱:814326663@qq.com */ public interface AddFriendNextView extends BaseView{ String getInputString(); void onSearchSuccess(JSONObject object); void onSearchFailed(String error); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/add/next/AddFriendsNextActivity.java ================================================ package com.htmessage.yichatopen.activity.addfriends.add.next; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; public class AddFriendsNextActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base_input); AddFriendNextFragment fragment = (AddFriendNextFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (fragment == null){ fragment = new AddFriendNextFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame,fragment); transaction.commit(); } new AddFriendNextPrestener(fragment); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/add/pre/AddFriendsPreActivity.java ================================================ package com.htmessage.yichatopen.activity.addfriends.add.pre; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.activity.main.qrcode.QrCodeActivity; import com.htmessage.yichatopen.activity.ScanCaptureActivity; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.activity.addfriends.add.next.AddFriendsNextActivity; public class AddFriendsPreActivity extends BaseActivity implements OnClickListener{ private TextView tv_search,tv_fxid; private RelativeLayout rl_leida,rl_jianqun,rl_sacn,rl_lianxiren,rl_ggh; private ImageView iv_scode; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_addfriends_pre); initUI(); setOnClick(); } private void initUI() { tv_search = (TextView) findViewById(R.id.tv_search); tv_fxid = (TextView) findViewById(R.id.tv_fxid); rl_leida = (RelativeLayout) findViewById(R.id.rl_leida); rl_jianqun = (RelativeLayout) findViewById(R.id.rl_jianqun); rl_sacn = (RelativeLayout) findViewById(R.id.rl_sacn); rl_lianxiren = (RelativeLayout) findViewById(R.id.rl_lianxiren); rl_ggh = (RelativeLayout) findViewById(R.id.rl_ggh); iv_scode = (ImageView) findViewById(R.id.iv_scode); JSONObject userJson = HTApp.getInstance().getUserJson(); if (userJson!=null){ String fxid = userJson.getString(HTConstant.JSON_KEY_FXID); if (!TextUtils.isEmpty(fxid)){ tv_fxid.setText(getString(R.string.me_yichat_number)+fxid); }else{ tv_fxid.setText(getString(R.string.me_yichat_number)+getString(R.string.not_set)); } } } private void setOnClick() { tv_search.setOnClickListener(this); rl_leida.setOnClickListener(this); rl_jianqun.setOnClickListener(this); rl_sacn.setOnClickListener(this); rl_lianxiren.setOnClickListener(this); rl_ggh.setOnClickListener(this); iv_scode.setOnClickListener(this); } @Override public void onClick(View view) { switch(view.getId()){ case R.id.tv_search: startActivity(new Intent(AddFriendsPreActivity.this,AddFriendsNextActivity.class)); break; case R.id.rl_leida: // startActivity(new Intent(AddFriendsPreActivity.this,RadarActivity.class)); break; case R.id.rl_jianqun: break; case R.id.rl_sacn: startActivity(new Intent(AddFriendsPreActivity.this,ScanCaptureActivity.class)); break; case R.id.rl_lianxiren: break; case R.id.rl_ggh: break; case R.id.iv_scode: startActivity(new Intent(AddFriendsPreActivity.this,QrCodeActivity.class)); break; } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/newfriend/NewFriendBasePresenter.java ================================================ package com.htmessage.yichatopen.activity.addfriends.newfriend; import android.content.Context; import com.htmessage.yichatopen.domain.InviteMessage; import com.htmessage.yichatopen.activity.BasePresenter; import java.util.List; /** * 项目名称:yichat0504 * 类描述:NewFriendBasePresenter 描述: * 创建人:songlijie * 创建时间:2017/7/4 9:40 * 邮箱:814326663@qq.com */ public interface NewFriendBasePresenter extends BasePresenter{ List getAllInviteMessage(); void registerRecivier(); void startActivity(Context context, Class clazz); void saveUnreadMessageCount(int count); void refresh(); void onDestory(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/newfriend/NewFriendFragment.java ================================================ package com.htmessage.yichatopen.activity.addfriends.newfriend; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.TextView; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.addfriends.add.next.AddFriendsNextActivity; import com.htmessage.yichatopen.activity.addfriends.add.pre.AddFriendsPreActivity; /** * 项目名称:yichat0504 * 类描述:NewFriendFragment 描述: * 创建人:songlijie * 创建时间:2017/7/4 17:17 * 邮箱:814326663@qq.com */ public class NewFriendFragment extends Fragment implements NewFriendView,View.OnClickListener{ private ListView listView; private TextView et_search, tv_add; private NewFriendsAdapter adapter; private NewFriendPrestener friendPrestener; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View friendView = inflater.inflate(R.layout.activity_new_friends, container, false); initView(friendView); initData(); setListener(); return friendView; } private void initData() { friendPrestener.registerRecivier(); // 设置adapter adapter = new NewFriendsAdapter(getBaseContext(),friendPrestener.getAllInviteMessage()); listView.setAdapter(adapter); friendPrestener.saveUnreadMessageCount(0); } private void initView(View view) { listView = (ListView) view.findViewById(R.id.listview); et_search = (TextView) view.findViewById(R.id.et_search); tv_add = (TextView) view.findViewById(R.id.tv_add); } private void setListener() { et_search.setOnClickListener(this); tv_add.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.et_search: friendPrestener.startActivity(getBaseActivity(), AddFriendsNextActivity.class); break; case R.id.tv_add: friendPrestener.startActivity(getBaseActivity(), AddFriendsPreActivity.class); break; } } @Override public void refresh() { friendPrestener.refresh(); adapter.notifyDataSetChanged(); friendPrestener.saveUnreadMessageCount(0); } @Override public void setPresenter(NewFriendPrestener presenter) { this.friendPrestener = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void onDestroy() { friendPrestener.onDestory(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/newfriend/NewFriendPrestener.java ================================================ package com.htmessage.yichatopen.activity.addfriends.newfriend; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.support.v4.content.LocalBroadcastManager; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.domain.InviteMessage; import com.htmessage.yichatopen.domain.InviteMessgeDao; import java.util.List; /** * 项目名称:yichat0504 * 类描述:NewFriendPrestener 描述: * 创建人:songlijie * 创建时间:2017/7/4 9:48 * 邮箱:814326663@qq.com */ public class NewFriendPrestener implements NewFriendBasePresenter { private NewFriendView newFriendView; private InviteMessgeDao dao; private NewFriendRecivier friendRecivier; private List allInviteMessage; public NewFriendPrestener(NewFriendView newFriendView) { this.newFriendView = newFriendView; this.newFriendView.setPresenter(this); dao = new InviteMessgeDao(newFriendView.getBaseContext()); allInviteMessage = dao.getMessagesList(); } @Override public List getAllInviteMessage() { return allInviteMessage; } @Override public void registerRecivier() { friendRecivier = new NewFriendRecivier(); IntentFilter intent = new IntentFilter(IMAction.ACTION_INVITE_MESSAGE); LocalBroadcastManager.getInstance(newFriendView.getBaseContext()).registerReceiver(friendRecivier, intent); } @Override public void startActivity(Context context, Class clazz) { newFriendView.getBaseActivity().startActivity(new Intent(context, clazz)); } private void unRegisterRecivier() { if (friendRecivier != null) { LocalBroadcastManager.getInstance(newFriendView.getBaseContext()).unregisterReceiver(friendRecivier); } } @Override public void saveUnreadMessageCount(int count) { dao.saveUnreadMessageCount(count); } @Override public void refresh() { allInviteMessage.clear(); if (dao == null){ dao = new InviteMessgeDao(newFriendView.getBaseContext()); } allInviteMessage.addAll(dao.getMessagesList()); } @Override public void onDestory() { unRegisterRecivier(); newFriendView = null; } @Override public void start() { } private class NewFriendRecivier extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (IMAction.ACTION_INVITE_MESSAGE.equals(intent.getAction())) { newFriendView.refresh(); } } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/newfriend/NewFriendView.java ================================================ package com.htmessage.yichatopen.activity.addfriends.newfriend; import com.htmessage.yichatopen.activity.BaseView; /** * 项目名称:yichat0504 * 类描述:NewFriendView 描述: * 创建人:songlijie * 创建时间:2017/7/4 9:49 * 邮箱:814326663@qq.com */ public interface NewFriendView extends BaseView { void refresh(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/newfriend/NewFriendsActivity.java ================================================ package com.htmessage.yichatopen.activity.addfriends.newfriend; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import android.view.View; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.addfriends.add.next.AddFriendsNextActivity; import com.htmessage.yichatopen.activity.BaseActivity; /** * 申请与通知 */ public class NewFriendsActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); setTitle(R.string.new_friend); NewFriendFragment newFriendFragment = (NewFriendFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (newFriendFragment == null) { // Create the fragment newFriendFragment =new NewFriendFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame, newFriendFragment); transaction.commit(); } final NewFriendPrestener presenter=new NewFriendPrestener(newFriendFragment); showRightView(R.drawable.add_icon, new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(NewFriendsActivity.this, AddFriendsNextActivity.class)); } }); } public void back(View v) { setResult(Activity.RESULT_OK); finish(); } @Override public void onBackPressed() { setResult(Activity.RESULT_OK); finish(); super.onBackPressed(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/addfriends/newfriend/NewFriendsAdapter.java ================================================ package com.htmessage.yichatopen.activity.addfriends.newfriend; import android.app.Activity; import android.app.ProgressDialog; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.activity.main.details.UserDetailsActivity; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.domain.InviteMessgeDao; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.domain.UserDao; import com.htmessage.yichatopen.domain.InviteMessage; import com.htmessage.yichatopen.domain.InviteMessage.Status; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.manager.HTChatManager; import com.htmessage.sdk.model.CmdMessage; import com.htmessage.yichatopen.widget.HTAlertDialog; import java.util.ArrayList; import java.util.List; import java.util.UUID; public class NewFriendsAdapter extends BaseAdapter { private Context context; private List msgs; private InviteMessgeDao messgeDao; int total = 0; public NewFriendsAdapter(Context _context, List msgs) { this.context = _context; this.msgs = msgs; messgeDao = new InviteMessgeDao(context); total = msgs.size(); } @Override public int getCount() { return msgs.size(); } @Override public InviteMessage getItem(int position) { return msgs.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = View.inflate(context, R.layout.item_newfriend_msg, null); } ViewHolder holder = (ViewHolder) convertView.getTag(); if (holder == null) { holder = new ViewHolder(); holder.iv_avatar = (ImageView) convertView.findViewById(R.id.iv_avatar); holder.tv_name = (TextView) convertView.findViewById(R.id.tv_name); holder.tv_reason = (TextView) convertView.findViewById(R.id.tv_reason); holder.tv_added = (TextView) convertView.findViewById(R.id.tv_added); holder.btn_add = (Button) convertView.findViewById(R.id.btn_add); holder.rl_item_add = (RelativeLayout) convertView.findViewById(R.id.rl_item_add); convertView.setTag(holder); } // final InviteMessage msg = getItem(total - 1 - position); final InviteMessage msg = getItem(position); // String reason = context.getString(R.string.request_add_friend); String reason = context.getString(R.string.Reasons); String nick = msg.getFrom(); JSONObject userInfo = null; try { userInfo = JSONObject.parseObject(msg.getReason()); Log.d("slj", "----获取好友信息:" + userInfo.toJSONString()); if (userInfo != null) { nick = userInfo.getString("nick"); String avatar = userInfo.getString("avatar"); if (!TextUtils.isEmpty(avatar)) { if (!avatar.contains("http")) { avatar = HTConstant.URL_AVATAR + avatar; } } Glide.with(context).load(avatar).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(holder.iv_avatar); //TODO 在申请消息的jsonobject里面是传有申请理由的,后续开发者可以按照需求处理这个申请理由 String reasonTemp = userInfo.getString(HTConstant.CMD_ADD_REASON); if (!TextUtils.isEmpty(reasonTemp)) { reason = reason + reasonTemp; } else { reason = context.getString(R.string.Request_to_add_you_as_a_friend); } } } catch (JSONException e) { e.printStackTrace(); } holder.tv_name.setText(nick); holder.tv_reason.setText(reason); if (msg.getStatus() == InviteMessage.Status.AGREED) { holder.tv_added.setVisibility(View.VISIBLE); holder.btn_add.setVisibility(View.GONE); } else if (msg.getStatus() == InviteMessage.Status.REFUSED) { holder.tv_added.setText(context.getString(R.string.Refused)); holder.tv_added.setVisibility(View.VISIBLE); holder.btn_add.setVisibility(View.GONE); } else if (msg.getStatus() == InviteMessage.Status.BEREFUSED) { holder.tv_added.setText(context.getString(R.string.has_rejected)); holder.tv_reason.setText(context.getString(R.string.already_rejected)); holder.tv_added.setVisibility(View.VISIBLE); holder.btn_add.setVisibility(View.GONE); } else if (msg.getStatus() == InviteMessage.Status.BEAGREED) { holder.tv_reason.setText(context.getString(R.string.Has_agreed_to_your_friend_request)); holder.tv_added.setVisibility(View.VISIBLE); holder.btn_add.setVisibility(View.GONE); } else { holder.tv_added.setVisibility(View.GONE); holder.btn_add.setVisibility(View.VISIBLE); holder.btn_add.setTag(msg); final ViewHolder finalHolder = holder; final JSONObject finalUserInfo = userInfo; if (finalUserInfo != null) { holder.btn_add.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { acceptInvitation(finalHolder.btn_add, msg, finalHolder.tv_added, finalUserInfo); } }); } } holder.rl_item_add.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String from = msg.getFrom(); context.startActivity(new Intent(context, UserDetailsActivity.class).putExtra(HTConstant.JSON_KEY_HXID,from)); } }); holder.rl_item_add.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { deleteInviteMessge(msg); return true; } }); return convertView; } private static class ViewHolder { ImageView iv_avatar; TextView tv_name; TextView tv_reason; TextView tv_added; Button btn_add; RelativeLayout rl_item_add; } /** * 同意好友请求 * * @param button * @param */ private void acceptInvitation(final Button button, final InviteMessage msg, final TextView textview, final JSONObject userInfo) { final ProgressDialog pd = new ProgressDialog(context); pd.setMessage(context.getString(R.string.Are_agree_with)); pd.setCanceledOnTouchOutside(false); pd.show(); List params = new ArrayList<>(); params.add(new Param("userId", msg.getFrom())); new OkHttpUtils(context).post(params, HTConstant.URL_ADD_FRIEND, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { int code = jsonObject.getInteger("code"); switch (code){ case 1: User user = CommonUtils.Json2User(userInfo); // 存入内存 ContactsManager.getInstance().getContactList().put(user.getUsername(), user); // 存入db UserDao dao = new UserDao(context); dao.saveContact(user); LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent(IMAction.ACTION_CONTACT_CHANAGED)); sendCmdAgreeMsg(button, msg, textview, pd); break; default: pd.dismiss(); Toast.makeText(context, context.getString(R.string.add_friend_failed), Toast.LENGTH_SHORT) .show(); break; } } @Override public void onFailure(String errorMsg) { pd.dismiss(); } }); } private void sendCmdAgreeMsg(final Button button, final InviteMessage msg, final TextView textview, final ProgressDialog pd) { JSONObject userJson = HTApp.getInstance().getUserJson(); JSONObject jsonObject = new JSONObject(); jsonObject.put("action", 1001); JSONObject data = new JSONObject(); data.put("userId", userJson.getString("userId")); data.put("nick", userJson.getString("nick")); data.put("avatar",userJson.getString("avatar")); data.put("role",userJson.getString(HTConstant.JSON_KEY_ROLE)); data.put("teamId",userJson.getString("teamId")); jsonObject.put("data", data); CmdMessage customMessage = new CmdMessage(); customMessage.setMsgId(UUID.randomUUID().toString()); customMessage.setFrom(HTApp.getInstance().getUsername()); customMessage.setTime(System.currentTimeMillis()); customMessage.setTo(msg.getFrom()); customMessage.setBody(jsonObject.toJSONString()); customMessage.setChatType(ChatType.singleChat); HTClient.getInstance().chatManager().sendCmdMessage(customMessage, new HTChatManager.HTMessageCallBack() { @Override public void onProgress() { } @Override public void onSuccess() { ((Activity)context).runOnUiThread(new Runnable() { @Override public void run() { if (pd != null) { pd.dismiss(); } Toast.makeText(context, context.getString(R.string.add_friend_success), Toast.LENGTH_SHORT) .show(); } }); } @Override public void onFailure() { ((Activity)context).runOnUiThread(new Runnable() { @Override public void run() { if (pd != null) { pd.dismiss(); } Toast.makeText(context, context.getString(R.string.add_friend_failed), Toast.LENGTH_SHORT) .show(); } }); } }); textview.setVisibility(View.VISIBLE); button.setEnabled(false); button.setVisibility(View.GONE); msg.setStatus(Status.AGREED); // 更新db ContentValues values = new ContentValues(); values.put(InviteMessgeDao.COLUMN_NAME_STATUS, msg .getStatus().ordinal()); messgeDao.updateMessage(msg.getId(), values); } /** * 删除透传消息 * @param msg */ private void deleteInviteMessge(final InviteMessage msg){ HTAlertDialog dialog = new HTAlertDialog(context,null,new String[]{context.getString(R.string.delete)}); dialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { switch (position){ case 0: msgs.remove(msg); messgeDao.deleteMessage(msg.getFrom()); notifyDataSetChanged(); break; } } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/ChatActivity.java ================================================ package com.htmessage.yichatopen.activity.chat; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.view.View; import com.htmessage.sdk.utils.MessageUtils; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.manager.MyNotification; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.activity.chat.activity.ChatSettingActivity; import com.htmessage.yichatopen.activity.main.MainActivity; import java.util.List; /** * */ public class ChatActivity extends BaseActivity { public static ChatActivity activityInstance; private ChatFragment chatFragment; public String toChatUsername; public int chatType; @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.activity_base); activityInstance = this; toChatUsername = getIntent().getExtras().getString("userId"); chatType = getIntent().getExtras().getInt("chatType", MessageUtils.CHAT_SINGLE); if (chatType == MessageUtils.CHAT_SINGLE) { User user = ContactsManager.getInstance().getContactList().get(toChatUsername); setTitle(user.getNick()); showRightView(R.drawable.icon_setting_single, new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(ChatActivity.this, ChatSettingActivity.class).putExtra("userId", toChatUsername)); } }); } chatFragment = new ChatFragment(); chatFragment.setArguments(getIntent().getExtras()); getSupportFragmentManager().beginTransaction().add(R.id.contentFrame, chatFragment).commit(); MyNotification.getInstance().cancel(Integer.parseInt(toChatUsername)); } @Override protected void onDestroy() { super.onDestroy(); activityInstance = null; } @Override protected void onNewIntent(Intent intent) { String username = intent.getStringExtra("userId"); if (toChatUsername.equals(username)) super.onNewIntent(intent); else { finish(); startActivity(intent); } } @Override public void onBackPressed() { chatFragment.onBackPressed(); if (isSingleActivity(this)) { Intent intent = new Intent(this, MainActivity.class); startActivity(intent); } } public boolean isSingleActivity(Context context) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List list = activityManager.getRunningTasks(1); return ((ActivityManager.RunningTaskInfo) list.get(0)).numRunning == 1; } public String getToChatUsername() { return toChatUsername; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/ChatAdapter.java ================================================ package com.htmessage.yichatopen.activity.chat; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.AnimationDrawable; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.TextView; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.main.details.UserDetailsActivity; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.activity.chat.weight.VoicePlayClickListener; import com.htmessage.yichatopen.activity.chat.weight.emoji.SmileUtils; import com.htmessage.yichatopen.utils.ACache; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.DateUtils; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.model.HTMessageBody; import com.htmessage.sdk.model.HTMessageImageBody; import com.htmessage.sdk.model.HTMessageTextBody; import com.htmessage.sdk.model.HTMessageVoiceBody; import com.htmessage.sdk.utils.MessageUtils; import com.htmessage.sdk.model.HTMessage; import com.htmessage.yichatopen.utils.PathUtils; import com.htmessage.yichatopen.utils.ImageUtils; import com.htmessage.yichatopen.activity.ShowBigImageActivity; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * Created by huangfangyi on 2016/11/24. * qq 84543217 */ public class ChatAdapter extends BaseAdapter { private List msgs; private Activity context; private LayoutInflater inflater; private static final int MESSAGE_TEXT_RECEIVED = 0; private static final int MESSAGE_TEXT_SEND = 1; private static final int MESSAGE_IMAGE_RECEIVED = 2; private static final int MESSAGE_IMAGE_SEND = 3; private static final int MESSAGE_VOICE_RECEIVED = 4; private static final int MESSAGE_VOICE_SEND = 5; private String chatTo; private int chatType; private List imageMsgs = new ArrayList<>(); private ChatFragment fragment; public ChatAdapter(List msgs, ChatFragment fragment, String chatTo, int chatType) { this.msgs = msgs; this.context = fragment.getActivity(); this.fragment=fragment; inflater = LayoutInflater.from(context); this.chatTo = chatTo; this.chatType = chatType; imageMsgs.clear(); for (int i = 0; i < msgs.size(); i++) { HTMessage emMessage = msgs.get(i); if (emMessage.getType() == HTMessage.Type.IMAGE) { imageMsgs.add(emMessage); } } } @Override public int getCount() { return msgs.size(); } @Override public HTMessage getItem(int position) { return msgs.get(position); } @Override public long getItemId(int position) { return position; } @Override public int getItemViewType(int position) { HTMessage message = getItem(position); return getItemViewType(message); } @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); for (int i = 0; i < msgs.size(); i++) { HTMessage emMessage = msgs.get(i); if (emMessage.getType() == HTMessage.Type.IMAGE) { if (!imageMsgs.contains(emMessage)) { imageMsgs.add(emMessage); } } } } @Override public int getViewTypeCount() { return 14; } @Override public View getView(int position, View convertView, ViewGroup parent) { HTMessage message = getItem(position); int viewType = getItemViewType(position); if (convertView == null) { convertView = getViewByType(viewType, parent); } ChatViewHolder holder = (ChatViewHolder) convertView.getTag(); if (holder == null) { holder = new ChatViewHolder(); handleViewAndHolder(viewType, convertView, holder, message); } handleData(holder, viewType, message, position); return convertView; } private int getItemViewType(HTMessage htMessage) { HTMessage.Type type = htMessage.getType(); if (type == HTMessage.Type.TEXT) { return htMessage.getDirect() == HTMessage.Direct.RECEIVE ? MESSAGE_TEXT_RECEIVED : MESSAGE_TEXT_SEND; } else if (type == HTMessage.Type.IMAGE) { return htMessage.getDirect() == HTMessage.Direct.RECEIVE ? MESSAGE_IMAGE_RECEIVED : MESSAGE_IMAGE_SEND; } else if (type == HTMessage.Type.VOICE) { return htMessage.getDirect() == HTMessage.Direct.RECEIVE ? MESSAGE_VOICE_RECEIVED : MESSAGE_VOICE_SEND; } return 0; } private View getViewByType(int viewType, ViewGroup parent) { switch (viewType) { case MESSAGE_TEXT_RECEIVED: return inflater.inflate(R.layout.row_received_message, parent, false); case MESSAGE_IMAGE_RECEIVED: return inflater.inflate(R.layout.row_received_picture, parent, false); case MESSAGE_VOICE_RECEIVED: return inflater.inflate(R.layout.row_received_voice, parent, false); case MESSAGE_TEXT_SEND: return inflater.inflate(R.layout.row_sent_message, parent, false); case MESSAGE_IMAGE_SEND: return inflater.inflate(R.layout.row_sent_picture, parent, false); case MESSAGE_VOICE_SEND: return inflater.inflate(R.layout.row_sent_voice, parent, false); default: return inflater.inflate(R.layout.row_sent_message, parent, false); } } private void handleViewAndHolder(int viewType, View convertView, ChatViewHolder holder, final HTMessage htMessage) { holder.reBubble = (RelativeLayout) convertView.findViewById(R.id.bubble); holder.reBubble.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { return false; } }); holder.ivAvatar = (ImageView) convertView.findViewById(R.id.iv_userhead); holder.timeStamp = (TextView) convertView.findViewById(R.id.timestamp); holder.tv_ack_msg = (TextView) convertView.findViewById(R.id.tv_ack_msg); holder.tv_delivered = (TextView) convertView.findViewById(R.id.tv_delivered); if (htMessage.getDirect() == HTMessage.Direct.RECEIVE) { //接收消息,可以显示成员名称 holder.tvNick = (TextView) convertView.findViewById(R.id.tv_userid); } else { holder.progressBar = (ProgressBar) convertView.findViewById(R.id.progress_bar); holder.ivMsgStatus = (ImageView) convertView.findViewById(R.id.msg_status); } if (htMessage.getType() == HTMessage.Type.TEXT) { holder.tvContent = (TextView) convertView.findViewById(R.id.tv_chatcontent); } if (viewType == MESSAGE_IMAGE_SEND || viewType == MESSAGE_IMAGE_RECEIVED) { holder.ivContent = (ImageView) convertView.findViewById(R.id.image); } if (viewType == MESSAGE_VOICE_SEND || viewType == MESSAGE_VOICE_RECEIVED) { holder.tvDuration = (TextView) convertView.findViewById(R.id.tv_length); holder.ivVoice = (ImageView) convertView.findViewById(R.id.iv_voice); if (viewType == MESSAGE_VOICE_RECEIVED) { holder.ivUnread = (ImageView) convertView.findViewById(R.id.iv_unread_voice); } } if (htMessage.getType() == HTMessage.Type.TEXT) { holder.reMain = (RelativeLayout) convertView.findViewById(R.id.re_main); } holder.ivAvatar.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { JSONObject jsonObject = htMessage.getAttributes(); if (jsonObject!=null){ context.startActivity(new Intent(context, UserDetailsActivity.class).putExtra(HTConstant.KEY_USER_INFO,jsonObject.toJSONString())); } } }); convertView.setTag(holder); } private void handleData(ChatViewHolder holder, int viewType, final HTMessage message, int position) { if (message.getDirect() == HTMessage.Direct.SEND && message.getStatus() == HTMessage.Status.FAIL) { holder.ivMsgStatus.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showReSendDialog(message); } }); } HTMessageBody htMessageBody = message.getBody(); if (htMessageBody == null) { return; } if (position == 0) { holder.timeStamp.setText(DateUtils.getTimestampString(new Date(message .getTime()))); holder.timeStamp.setVisibility(View.VISIBLE); } else { // 两条消息时间离得如果稍长,显示时间 if (DateUtils.isCloseEnough(message.getTime(), getItem(position - 1).getTime())) { holder.timeStamp.setVisibility(View.GONE); } else { holder.timeStamp.setText(DateUtils.getTimestampString(new Date( message.getTime()))); holder.timeStamp.setVisibility(View.VISIBLE); } } if (chatType == MessageUtils.CHAT_GROUP && message.getDirect() == HTMessage.Direct.RECEIVE) { holder.tvNick.setVisibility(View.VISIBLE); } else if (chatType == MessageUtils.CHAT_SINGLE && message.getDirect() == HTMessage.Direct.RECEIVE) { holder.tvNick.setVisibility(View.GONE); } String avatar = message.getStringAttribute(HTConstant.JSON_KEY_AVATAR); if (message.getDirect() == HTMessage.Direct.SEND){ if (message.getFrom().equals(HTApp.getInstance().getUsername())){ JSONObject userJson = HTApp.getInstance().getUserJson(); if (userJson.containsKey(HTConstant.JSON_KEY_AVATAR)){ avatar = userJson.getString(HTConstant.JSON_KEY_AVATAR); } } } Glide.with(context).load(avatar).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(holder.ivAvatar); HTMessage.Status status = message.getStatus(); if (message.getDirect() == HTMessage.Direct.SEND) { if (status == HTMessage.Status.CREATE) { holder.progressBar.setVisibility(View.VISIBLE); } else { holder.progressBar.setVisibility(View.GONE); } if (status == HTMessage.Status.FAIL) { holder.ivMsgStatus.setVisibility(View.VISIBLE); } else { holder.ivMsgStatus.setVisibility(View.GONE); } // if (status == HTMessage.Status.SUCCESS){ // holder.tv_delivered.setVisibility(View.VISIBLE); // } else { // holder.tv_delivered.setVisibility(View.INVISIBLE); // } } if (message.getType() == HTMessage.Type.TEXT) { int action = message.getIntAttribute("action", 0); if (action == 3000) { } else if (action == 6001) { holder.timeStamp.setVisibility(View.VISIBLE); holder.reMain.setVisibility(View.GONE); holder.timeStamp.setText(((HTMessageTextBody) htMessageBody).getContent()); } else { holder.reMain.setVisibility(View.VISIBLE); holder.tvContent.setText(SmileUtils.getSmiledText(context, ((HTMessageTextBody) htMessageBody).getContent()), TextView.BufferType.SPANNABLE); } } if (message.getType() == HTMessage.Type.IMAGE) { showImageView(message, holder, true, false); } else if (message.getType() == HTMessage.Type.VOICE) { showVoiceView(message, holder); } } private void showImageView(final HTMessage htMessage, ChatViewHolder holder, boolean isReSize, boolean isLocMsg) { if (!isReSize) { holder.ivContent.setImageResource(R.drawable.ht_location); } else { holder.ivContent.setImageResource(R.drawable.default_image); } String localPath = null; if (!isLocMsg) { HTMessageImageBody htMessageImageBody = (HTMessageImageBody) htMessage.getBody(); localPath = htMessageImageBody.getLocalPath(); } if (!TextUtils.isEmpty(localPath)) { Bitmap bitmap = ACache.get(context.getApplicationContext()).getAsBitmap(htMessage.getMsgId()); if (bitmap == null) { Log.d("bitmap3---->", "null"); if (new File(localPath).exists()) { bitmap = ImageUtils.decodeScaleImage(localPath); if (bitmap != null) { ACache.get(context.getApplicationContext()).put(htMessage.getMsgId(), bitmap); holder.ivContent.setImageBitmap(bitmap); } } else { downLoadImageFromServer(htMessage, holder.ivContent, isReSize, isLocMsg); } } else { holder.ivContent.setImageBitmap(bitmap); } } else { downLoadImageFromServer(htMessage, holder.ivContent, isReSize, isLocMsg); } final String finalLocalPath = localPath; holder.reBubble.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (finalLocalPath == null || !new File(finalLocalPath).exists()) { downLoadBigImageAndOpen(htMessage); } else { context.startActivity(new Intent(context, ShowBigImageActivity.class).putExtra("localPath", finalLocalPath)); } } }); } private void downLoadImageFromServer(final HTMessage htMessage, final ImageView imageView, boolean isReSize, final boolean isLocMsg) { //下载缩略图,显示并且保存至缓存. String remotePath = null; String fileName = null; if (!isLocMsg) { HTMessageImageBody htMessageImageBody = (HTMessageImageBody) htMessage.getBody(); remotePath = htMessageImageBody.getRemotePath(); fileName = htMessageImageBody.getFileName(); } if (!TextUtils.isEmpty(remotePath)) { if (isReSize) { remotePath = remotePath + HTConstant.baseImgUrl_set; } PathUtils pathUtils = new PathUtils(chatTo, context); final String filePath = pathUtils.getImagePath().getAbsolutePath() + "/mini" + fileName; new OkHttpUtils(context).loadFile(remotePath, filePath, new OkHttpUtils.DownloadCallBack() { @Override public void onSuccess() { if (new File(filePath).exists()) { final Bitmap bitmap = ImageUtils.decodeScaleImage(filePath); if (!isLocMsg) { HTMessageImageBody htMessageImageBody = (HTMessageImageBody) htMessage.getBody(); htMessageImageBody.setLocalPath(filePath); htMessage.setBody(htMessageImageBody); } HTClient.getInstance().messageManager().saveMessage(htMessage, false); ACache.get(context.getApplicationContext()).put(htMessage.getMsgId(), bitmap); ((Activity) context).runOnUiThread(new Runnable() { @Override public void run() { imageView.setImageBitmap(bitmap); } }); } } @Override public void onFailure(String message) { } }); } } private void downLoadBigImageAndOpen(final HTMessage htMessage) { final HTMessageImageBody htMessageImageBody = (HTMessageImageBody) htMessage.getBody(); String remotePath = htMessageImageBody.getRemotePath(); String fileName = htMessageImageBody.getFileName(); if (TextUtils.isEmpty(remotePath) || TextUtils.isEmpty(fileName)) { return; } final Dialog progressDialog = HTApp.getInstance().createLoadingDialog(context, context.getString(R.string.loading)); progressDialog.show(); PathUtils pathUtils = new PathUtils(chatTo, context); final String filePath = pathUtils.getImagePath().getAbsolutePath() + "/" + fileName; new OkHttpUtils(context).loadFile(remotePath, filePath, new OkHttpUtils.DownloadCallBack() { @Override public void onSuccess() { (context).runOnUiThread(new Runnable() { @Override public void run() { progressDialog.dismiss(); } }); if (new File(filePath).exists()) { final Bitmap bitmap = ImageUtils.decodeScaleImage(filePath); ACache.get(context.getApplicationContext()).put(htMessage.getMsgId(), bitmap); htMessageImageBody.setLocalPath(filePath); htMessage.setBody(htMessageImageBody); (context).runOnUiThread(new Runnable() { @Override public void run() { notifyDataSetChanged(); } }); HTClient.getInstance().messageManager().saveMessage(htMessage, false); context.startActivity(new Intent(context, ShowBigImageActivity.class).putExtra("localPath", filePath)); } } @Override public void onFailure(String message) { (context).runOnUiThread(new Runnable() { @Override public void run() { progressDialog.dismiss(); } }); } }); } private void showVoiceView(final HTMessage htMessage, final ChatViewHolder holder) { HTMessageVoiceBody htMessageVoiceBody = (HTMessageVoiceBody) htMessage.getBody(); if (htMessageVoiceBody == null) { return; } int len = htMessageVoiceBody.getAudioDuration(); if (len > 0) { holder.tvDuration.setText(len + "\""); holder.tvDuration.setVisibility(View.VISIBLE); } else { holder.tvDuration.setVisibility(View.INVISIBLE); } if (VoicePlayClickListener.playMsgId != null && VoicePlayClickListener.playMsgId.equals(htMessage.getMsgId()) && VoicePlayClickListener.isPlaying) { AnimationDrawable voiceAnimation; if (htMessage.getDirect() == HTMessage.Direct.RECEIVE) { holder.ivVoice.setImageResource(R.anim.voice_from_icon); } else { holder.ivVoice.setImageResource(R.anim.voice_to_icon); } voiceAnimation = (AnimationDrawable) holder.ivVoice.getDrawable(); voiceAnimation.start(); } else { if (htMessage.getDirect() == HTMessage.Direct.RECEIVE) { holder.ivVoice.setImageResource(R.drawable.chatfrom_voice_playing); } else { holder.ivVoice.setImageResource(R.drawable.chatto_voice_playing); } } if (htMessage.getDirect() == HTMessage.Direct.RECEIVE) { if (htMessage.getStatus() == HTMessage.Status.SUCCESS) { holder.ivUnread.setVisibility(View.INVISIBLE); } else { holder.ivUnread.setVisibility(View.VISIBLE); } } holder.reBubble.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new VoicePlayClickListener(htMessage, chatTo, holder.ivVoice, holder.ivUnread, ChatAdapter.this, (context)).onClick(holder.reBubble); } }); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) { holder.reBubble.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { } @Override public void onViewDetachedFromWindow(View v) { if (VoicePlayClickListener.currentPlayListener != null && VoicePlayClickListener.isPlaying) { VoicePlayClickListener.currentPlayListener.stopPlayVoice(); } } }); } } public static class ChatViewHolder { public RelativeLayout reMain; public RelativeLayout reBubble; public ImageView ivAvatar; public TextView tvNick; public TextView timeStamp; public ImageView ivMsgStatus; //文本消息,位置消息,文件消息 public TextView tvContent; //图片消息,视频消息,位置消息,文件消息 public ImageView ivContent; //语音消息 public TextView tvDuration; public ImageView ivUnread; public ImageView ivVoice; //发送消息 public ProgressBar progressBar; //已读显示 public TextView tv_ack_msg; //送达显示 public TextView tv_delivered; } /** * 重新发送消息 * * @param htMessage */ private void showReSendDialog(final HTMessage htMessage) { AlertDialog.Builder buidler = new AlertDialog.Builder(context); View view = View.inflate(context, R.layout.item_diaolog_gridview, null); TextView tv_forward = (TextView) view.findViewById(R.id.tv_forward); TextView textView = (TextView) view.findViewById(R.id.textView); TextView tv_ok = (TextView) view.findViewById(R.id.tv_ok); TextView tv_cancel = (TextView) view.findViewById(R.id.tv_cancel); final ImageView imageView = (ImageView) view.findViewById(R.id.imageView); imageView.setVisibility(View.GONE); tv_forward.setText(R.string.prompt); textView.setText(R.string.resend_text); buidler.setView(view); final AlertDialog dialog = buidler.show(); tv_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); msgs.remove(htMessage); notifyDataSetChanged(); HTClient.getInstance().messageManager().deleteMessage(htMessage.getUsername(), htMessage.getMsgId()); htMessage.setLocalTime(System.currentTimeMillis()); htMessage.setStatus(HTMessage.Status.CREATE); fragment.sendMessage(htMessage); } }); tv_cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/ChatContract.java ================================================ package com.htmessage.yichatopen.activity.chat; import com.htmessage.yichatopen.activity.BasePresenter; import com.htmessage.yichatopen.activity.BaseView; /** * Created by dell on 2017/7/1. */ public interface ChatContract { interface View extends BaseView{ //长按消息体出现的dialog void showItemDialog(); //标题栏 void showTitle(String title); //隐藏输入法 void hideKeyboard(); //显示输入法 void showKeyboard(); //显示录音UI void showSpeakerMode(); //显示输入文字状态 void showInputMode(); //显示更多消息类型 void showExtendMenuItem(); //显示表情UI void showEmojiView(); } interface Presenter extends BasePresenter{ } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/ChatFragment.java ================================================ package com.htmessage.yichatopen.activity.chat; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.provider.MediaStore; import android.support.v4.app.Fragment; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.manager.HTChatManager; import com.htmessage.sdk.model.CmdMessage; import com.htmessage.sdk.model.HTMessage; import com.htmessage.sdk.model.HTMessageImageBody; import com.htmessage.sdk.model.HTMessageVoiceBody; import com.htmessage.sdk.utils.MessageUtils; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.activity.chat.weight.emoji.Emojicon; import com.htmessage.yichatopen.activity.chat.weight.ChatInputView; import com.htmessage.yichatopen.activity.chat.weight.loadmore.PullToLoadMoreListView; import com.htmessage.yichatopen.utils.ACache; import com.htmessage.yichatopen.utils.HTMessageUtils; import com.htmessage.yichatopen.utils.PathUtils; import com.htmessage.yichatopen.activity.chat.weight.ChatExtendMenu; import com.htmessage.yichatopen.widget.HTAlertDialog; import com.htmessage.yichatopen.activity.chat.weight.VoiceRecorderView; import com.htmessage.yichatopen.utils.CommonUtils; import java.io.File; import java.io.IOException; import java.util.Collections; import java.util.List; public class ChatFragment extends Fragment { private static final int REQUEST_CODE_CAMERA = 2; private static final int REQUEST_CODE_LOCAL = 3; /** * params to fragment */ private Bundle fragmentArgs; private int chatType; private String toChatUsername; private File cameraFile; private ListView listView; static final int ITEM_TAKE_PICTURE = 1; static final int ITEM_PICTURE = 2; private int[] itemStrings = {R.string.attach_take_pic, R.string.attach_picture}; private int[] itemdrawables = {R.drawable.chat_takepic_selector, R.drawable.chat_image_selector}; private int[] itemIds = {ITEM_TAKE_PICTURE, ITEM_PICTURE}; private MyItemClickListener extendMenuItemClickListener; private List htMessages; private ChatAdapter adapter; private PullToLoadMoreListView pullToLoadMoreListView; private ChatInputView chatInputView; private MyInputViewLisenter inputViewLisenter; private VoiceRecorderView voiceRecorderView; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_chat, container, false); voiceRecorderView = (VoiceRecorderView) root.findViewById(R.id.voice_recorder); pullToLoadMoreListView = (PullToLoadMoreListView) root.findViewById(R.id.list); pullToLoadMoreListView.setOnRefreshListener(new PullToLoadMoreListView.OnRefreshListener() { @Override public void onPullDownLoadMore() { new Handler().postDelayed(new Runnable() { @Override public void run() { HTMessage message = getLastMessage(); if (message != null) { List htMessages = HTClient.getInstance().messageManager().loadMoreMsgFromDB(toChatUsername, message.getTime(), 20); if (htMessages.size() == 0) { Toast.makeText(getActivity(), R.string.not_more_msg, Toast.LENGTH_SHORT).show(); } else { Collections.reverse(htMessages); htMessages.addAll(0, htMessages); adapter.notifyDataSetChanged(); } } else { Toast.makeText(getActivity(), R.string.not_more_msg, Toast.LENGTH_SHORT).show(); } pullToLoadMoreListView.onRefreshComplete(); } }, 1000); } }); listView = pullToLoadMoreListView.getListView(); extendMenuItemClickListener = new MyItemClickListener(); chatInputView = (ChatInputView) root.findViewById(R.id.inputView); inputViewLisenter = new MyInputViewLisenter(); chatInputView.initView(getActivity(), pullToLoadMoreListView, inputViewLisenter, getExtendMenuItem()); listView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { chatInputView.hideSoftInput(); chatInputView.interceptBackPress(); return false; } }); return root; } private JSONObject extJSON = new JSONObject(); private boolean isHolder = false; private MyBroadcastReciver myBroadcastReciver; @Override public void onActivityCreated(Bundle savedInstanceState) { extJSON.put(HTConstant.JSON_KEY_HXID, HTApp.getInstance().getUserJson().getString(HTConstant.JSON_KEY_HXID)); extJSON.put(HTConstant.JSON_KEY_NICK, HTApp.getInstance().getUserJson().getString(HTConstant.JSON_KEY_NICK)); String avatar = HTApp.getInstance().getUserJson().getString(HTConstant.JSON_KEY_AVATAR); if (!TextUtils.isEmpty(avatar)) { if (!avatar.contains("http:")) { avatar = HTConstant.URL_AVATAR + avatar; } } extJSON.put(HTConstant.JSON_KEY_AVATAR, avatar); fragmentArgs = getArguments(); chatType = fragmentArgs.getInt("chatType", MessageUtils.CHAT_SINGLE); toChatUsername = fragmentArgs.getString("userId"); super.onActivityCreated(savedInstanceState); setUpView(); myBroadcastReciver = new MyBroadcastReciver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(IMAction.ACTION_MESSAGE_WITHDROW); intentFilter.addAction(IMAction.ACTION_MESSAGE_FORWORD); intentFilter.addAction(IMAction.ACTION_NEW_MESSAGE); intentFilter.addAction(IMAction.ACTION_MESSAGE_EMPTY); LocalBroadcastManager.getInstance(getActivity()).registerReceiver(myBroadcastReciver, intentFilter); } private void setUpView() { getAllMessage(); adapter = new ChatAdapter(htMessages, this, toChatUsername, chatType); listView.setAdapter(adapter); listView.setSelection(listView.getCount() - 1); listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { HTMessage htMessage = adapter.getItem(i); if (htMessage != null) { showMsgDialog(htMessage, i); } return true; } }); } private void getAllMessage() { htMessages = HTClient.getInstance().messageManager().getMessageList(toChatUsername); HTClient.getInstance().conversationManager().markAllMessageRead(toChatUsername); } private class MyInputViewLisenter implements ChatInputView.InputViewLisenter { @Override public boolean onPressToSpeakBtnTouch(View v, MotionEvent event) { return voiceRecorderView.onPressToSpeakBtnTouch(v, event, new VoiceRecorderView.EaseVoiceRecorderCallback() { @Override public void onVoiceRecordComplete(String voiceFilePath, int voiceTimeLength) { //Log.d("voiceFilePath--->", voiceFilePath); sendVoiceMessage(voiceFilePath, voiceTimeLength); } }); } @Override public void onBigExpressionClicked(Emojicon emojicon) { } @Override public void onSendButtonClicked(String content) { sendTextMessage(content); } @Override public boolean onEditTextLongClick() { String myCopy = ACache.get(getActivity()).getAsString("myCopy"); if (!TextUtils.isEmpty(myCopy)) { JSONObject jsonObject = JSONObject.parseObject(myCopy); String msgId = jsonObject.getString("msgId"); String imagePath = jsonObject.getString("imagePath"); HTMessage emMessage = getCopyMessage(msgId); if (emMessage == null) { return true; } showCopyContent(jsonObject.getString("copyType"), jsonObject.getString("localPath"), emMessage, imagePath); return true; } return false; } @Override public void onEditTextUp() { new Handler().postDelayed(new Runnable() { @Override public void run() { listView.smoothScrollToPosition(listView.getCount() - 1); } }, 400); } } private HTMessage getCopyMessage(String msgId) { for (HTMessage htMessage : htMessages) { if (htMessage.getMsgId().equals(msgId)) { return htMessage; } } return null; } private HTMessage getLastMessage() { if (htMessages != null && htMessages.size() != 0) { return adapter.getItem(0); } return null; } private ChatExtendMenu getExtendMenuItem() { ChatExtendMenu chatExtendMenu = new ChatExtendMenu(getContext()); //use the menu in base class for (int i = 0; i < itemStrings.length; i++) { chatExtendMenu.registerMenuItem(itemStrings[i], itemdrawables[i], itemIds[i], extendMenuItemClickListener); } chatExtendMenu.init(); return chatExtendMenu; } /** * handle the click event for extend menu */ class MyItemClickListener implements ChatExtendMenu.EaseChatExtendMenuItemClickListener { @Override public void onClick(int itemId, View view) { switch (itemId) { case ITEM_TAKE_PICTURE: selectPicFromCamera(); isHolder = true; break; case ITEM_PICTURE: selectPicFromLocal(); isHolder = true; break; default: break; } } } @Override public void onStop() { if (!isHolder) { ChatActivity.activityInstance = null; } super.onStop(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); isHolder = false; if (resultCode == Activity.RESULT_OK) { if (requestCode == REQUEST_CODE_CAMERA) { // capture new image if (cameraFile != null && cameraFile.exists()) sendImageMessage(cameraFile.getAbsolutePath()); } else if (requestCode == REQUEST_CODE_LOCAL) { // send local image if (data != null) { Uri selectedImage = data.getData(); if (selectedImage != null) { sendPicByUri(selectedImage); } } } } } private void showMsgDialog(final HTMessage message, final int i) { HTAlertDialog fxAlertDialog = new HTAlertDialog(getActivity(), null, new String[]{getActivity().getString(R.string.delete), getActivity().getString(R.string.copy), getActivity().getString(R.string.forward)}); if (message.getDirect() == HTMessage.Direct.SEND) { fxAlertDialog = new HTAlertDialog(getActivity(), null, new String[]{getActivity().getString(R.string.delete), getActivity().getString(R.string.copy), getActivity().getString(R.string.forward), getActivity().getString(R.string.reback)}); } fxAlertDialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { if (position == 0) { //删除 HTClient.getInstance().messageManager().deleteMessage(toChatUsername, message.getMsgId()); htMessages.remove(message); adapter.notifyDataSetChanged(); } else if (position == 1) { //复制 HTMessageUtils.getCopyMsg(getActivity(), message, toChatUsername); } else if (position == 2) {//转发 HTMessageUtils.getForWordMessage(getActivity(), message, toChatUsername, extJSON); } else if (position == 3) {//撤回 long msgTime = message.getTime(); long nowTime = System.currentTimeMillis(); if ((nowTime - msgTime) / (1000 * 60) < 2) { final ProgressDialog progressDialog = new ProgressDialog(getActivity()); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDialog.setMessage(getString(R.string.rebacking)); progressDialog.setCanceledOnTouchOutside(false); progressDialog.show(); CmdMessage cmdMessage = new CmdMessage(); cmdMessage.setTo(toChatUsername); JSONObject jsonObject = new JSONObject(); jsonObject.put("action", 6000); jsonObject.put("msgId", message.getMsgId()); cmdMessage.setBody(jsonObject.toString()); if (chatType == MessageUtils.CHAT_GROUP) { cmdMessage.setChatType(ChatType.groupChat); } HTClient.getInstance().chatManager().sendCmdMessage(cmdMessage, new HTChatManager.HTMessageCallBack() { @Override public void onProgress() { } @Override public void onSuccess() { HTClient.getInstance().messageManager().deleteMessage(toChatUsername, message.getMsgId()); getActivity().runOnUiThread(new Runnable() { @Override public void run() { progressDialog.dismiss(); HTMessage htMessage = HTMessageUtils.creatWithDrowMsg(message); htMessages.set(i, htMessage); adapter.notifyDataSetChanged(); } }); } @Override public void onFailure() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { progressDialog.dismiss(); Toast.makeText(getActivity(), R.string.reback_failed, Toast.LENGTH_SHORT).show(); } }); } }); } else { Toast.makeText(getActivity(), R.string.reback_not_more_than_30, Toast.LENGTH_SHORT).show(); } } } }); } @Override public void onResume() { super.onResume(); HTClient.getInstance().conversationManager().markAllMessageRead(toChatUsername); ChatActivity.activityInstance = (ChatActivity) getActivity(); } public void onBackPressed() { if (!chatInputView.interceptBackPress()) { getActivity().finish(); } } @Override public void onPause() { if (chatInputView != null) { chatInputView.hideSoftInput(); chatInputView.interceptBackPress(); } super.onPause(); } private void sendTextMessage(final String content) { HTMessage htMessage = HTMessage.createTextSendMessage(toChatUsername, content); sendMessage(htMessage); } public void sendMessage(final HTMessage htMessage) { htMessage.setAttributes(extJSON.toJSONString()); if (chatType == MessageUtils.CHAT_GROUP) { htMessage.setChatType(ChatType.groupChat); } HTClient.getInstance().chatManager().sendMessage(htMessage, new HTChatManager.HTMessageCallBack() { @Override public void onProgress() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { htMessages.add(htMessage); adapter.notifyDataSetChanged(); if (htMessages.size() > 0) { listView.setSelection(listView.getCount() - 1); } } }); } @Override public void onSuccess() { htMessage.setStatus(HTMessage.Status.SUCCESS); HTClient.getInstance().messageManager().saveMessage(htMessage, false); getActivity().runOnUiThread(new Runnable() { @Override public void run() { adapter.notifyDataSetChanged(); if (htMessages.size() > 0) { listView.setSelection(listView.getCount() - 1); } } }); } @Override public void onFailure() { getActivity().runOnUiThread(new Runnable() { @Override public void run() { htMessage.setStatus(HTMessage.Status.FAIL); HTClient.getInstance().messageManager().saveMessage(htMessage, false); adapter.notifyDataSetChanged(); if (htMessages.size() > 0) { listView.setSelection(listView.getCount() - 1); } } }); } }); } private void sendVoiceMessage(String filePath, int length) { HTMessage htMessage = HTMessage.createVoiceSendMessage(toChatUsername, filePath, length); sendMessage(htMessage); } /** * 读取图片属性:旋转的角度 * * @param path 图片绝对路径 * @return degree旋转的角度 */ public static int readPictureDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface(path); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); return degree; } return degree; } /** * 旋转图片,使图片保持正确的方向。 * * @param bitmap 原始图片 * @param degrees 原始图片的角度 * @return Bitmap 旋转后的图片 */ public static Bitmap rotateBitmap(Bitmap bitmap, int degrees) { if (degrees == 0 || null == bitmap) { return bitmap; } Matrix matrix = new Matrix(); matrix.setRotate(degrees, bitmap.getWidth() / 2, bitmap.getHeight() / 2); Bitmap bmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); if (null != bitmap) { bitmap.recycle(); } return bmp; } private void sendImageMessage(String imagePath) { Bitmap bmp = BitmapFactory.decodeFile(imagePath); Bitmap bitmap = rotateBitmap(bmp, readPictureDegree(imagePath)); String size = bitmap.getWidth() + "," + bitmap.getHeight(); Log.d("size---->", size); HTMessage htMessage = HTMessage.createImageSendMessage(toChatUsername, imagePath, size); sendMessage(htMessage); } //=================================================================================== /** * send image * * @param selectedImage */ private void sendPicByUri(Uri selectedImage) { String[] filePathColumn = {MediaStore.Images.Media.DATA}; Cursor cursor = getActivity().getContentResolver().query(selectedImage, filePathColumn, null, null, null); if (cursor != null) { cursor.moveToFirst(); int columnIndex = cursor.getColumnIndex(filePathColumn[0]); String picturePath = cursor.getString(columnIndex); cursor.close(); cursor = null; if (picturePath == null || picturePath.equals("null")) { Toast toast = Toast.makeText(getActivity(), R.string.cant_find_pictures, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); return; } sendImageMessage(picturePath); } else { File file = new File(selectedImage.getPath()); if (!file.exists()) { Toast toast = Toast.makeText(getActivity(), R.string.cant_find_pictures, Toast.LENGTH_SHORT); toast.setGravity(Gravity.CENTER, 0, 0); toast.show(); return; } sendImageMessage(file.getAbsolutePath()); } } /** * capture new image */ private void selectPicFromCamera() { if (!CommonUtils.isSdcardExist()) { Toast.makeText(getActivity(), R.string.sd_card_does_not_exist, Toast.LENGTH_SHORT).show(); return; } cameraFile = new File(new PathUtils(toChatUsername, getContext()).getImagePath() + "/" + HTApp.getInstance().getUsername() + System.currentTimeMillis() + ".jpg"); // cameraFile.getParentFile().mkdirs(); startActivityForResult( new Intent(MediaStore.ACTION_IMAGE_CAPTURE).putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cameraFile)), REQUEST_CODE_CAMERA); } /** * select local image */ private void selectPicFromLocal() { Intent intent; if (Build.VERSION.SDK_INT < 19) { intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); } else { intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); } startActivityForResult(intent, REQUEST_CODE_LOCAL); } @Override public void onDestroy() { if (myBroadcastReciver != null) { LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(myBroadcastReciver); } super.onDestroy(); } /** * 复制 * * @param copyType * @param localPath * @param message1 * @param imagePath */ private void showCopyContent(final String copyType, final String localPath, final HTMessage message1, String imagePath) { AlertDialog.Builder buidler = new AlertDialog.Builder(getActivity()); View view = View.inflate(getActivity(), R.layout.item_dialog_gridview, null); TextView tv_forward = (TextView) view.findViewById(R.id.tv_forward); TextView textView = (TextView) view.findViewById(R.id.textView); TextView tv_ok = (TextView) view.findViewById(R.id.tv_ok); TextView tv_cancel = (TextView) view.findViewById(R.id.tv_cancel); final ImageView imageView = (ImageView) view.findViewById(R.id.imageView); imageView.setVisibility(View.GONE); tv_forward.setText(R.string.copy); textView.setText(R.string.really_copy_and_send); buidler.setView(view); if ("image".equals(copyType) && imagePath != null) { imageView.setVisibility(View.VISIBLE); Glide.with(getActivity()).load(imagePath).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView); } final AlertDialog dialog = buidler.show(); tv_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); switch (copyType) { case "text": sendTextMessage(localPath); break; case "voice": HTMessageVoiceBody voiceBody = (HTMessageVoiceBody) message1.getBody(); HTMessage voiceMSg = HTMessage.createVoiceSendMessage(toChatUsername, localPath, voiceBody.getAudioDuration()); sendMessage(voiceMSg); break; case "image": HTMessageImageBody imageBody = (HTMessageImageBody) message1.getBody(); HTMessage message = HTMessage.createImageSendMessage(toChatUsername, localPath, imageBody.getSize()); sendMessage(message); break; } } }); tv_cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); } /** * 撤回消息 * * @param msgId */ private void onMessageWithdrow(String msgId) { for (int i = 0; i < htMessages.size(); i++) { HTMessage htMessage = htMessages.get(i); if (htMessage.getMsgId().equals(msgId)) { HTMessage message = HTMessageUtils.creatWithDrowMsg(htMessage); htMessages.set(i, message); adapter.notifyDataSetChanged(); } } } private class MyBroadcastReciver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(IMAction.ACTION_MESSAGE_WITHDROW)) { String msgId = intent.getStringExtra("msgId"); onMessageWithdrow(msgId); } else if (intent.getAction().equals(IMAction.ACTION_MESSAGE_FORWORD)) { HTMessage message = intent.getParcelableExtra("message"); if (message.getTo().equals(toChatUsername)) { if (!htMessages.contains(message)) { htMessages.add(message); } adapter.notifyDataSetChanged(); if (htMessages.size() > 0) { listView.setSelection(listView.getCount() - 1); } } } else if (intent.getAction().equals(IMAction.ACTION_NEW_MESSAGE)) { HTMessage message = intent.getParcelableExtra("message"); if ((message.getChatType() == ChatType.singleChat) && (message.getFrom().equals(toChatUsername)) || ((message.getChatType() == ChatType.groupChat) && (message.getTo().equals(toChatUsername)))) { if (!htMessages.contains(message)) { htMessages.add(message); } adapter.notifyDataSetChanged(); if (htMessages.size() > 0) { listView.setSelection(listView.getCount() - 1); } HTClient.getInstance().conversationManager().markAllMessageRead(toChatUsername); } } else if (IMAction.ACTION_MESSAGE_EMPTY.equals(intent.getAction())) { htMessages.clear(); adapter.notifyDataSetChanged(); } } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/ChatPresenter.java ================================================ package com.htmessage.yichatopen.activity.chat; /** * Created by dell on 2017/7/1. */ public class ChatPresenter { } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/activity/ChatSettingActivity.java ================================================ package com.htmessage.yichatopen.activity.chat.activity; import android.app.Dialog; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.activity.main.details.UserDetailsActivity; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.sdk.client.HTClient; public class ChatSettingActivity extends BaseActivity implements OnClickListener { // 、置顶 private RelativeLayout rl_switch_chattotop; private RelativeLayout rl_switch_block_groupmsg; private RelativeLayout re_clear; // 状态变化 private ImageView iv_switch_chattotop; private ImageView iv_switch_unchattotop; private ImageView iv_switch_block_groupmsg; private ImageView iv_switch_unblock_groupmsg; private String userId; private Dialog progressDialog; public static ChatSettingActivity instance; private User user; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_chat_setting_single); instance = this; // 获取传过来的userId userId = getIntent().getStringExtra("userId"); user= ContactsManager.getInstance().getContactList().get(userId); // 资料错误则不显示 if (user == null) { finish(); return; } initView(); initData(); } private void initView() { rl_switch_chattotop = (RelativeLayout) findViewById(R.id.rl_switch_chattotop); rl_switch_block_groupmsg = (RelativeLayout) findViewById(R.id.rl_switch_block_groupmsg); re_clear = (RelativeLayout) findViewById(R.id.re_clear); iv_switch_chattotop = (ImageView) findViewById(R.id.iv_switch_chattotop); iv_switch_unchattotop = (ImageView) findViewById(R.id.iv_switch_unchattotop); iv_switch_block_groupmsg = (ImageView) findViewById(R.id.iv_switch_block_groupmsg); iv_switch_unblock_groupmsg = (ImageView) findViewById(R.id.iv_switch_unblock_groupmsg); // // 初始化置顶和免打扰的状态 // if (!blackList.contains(userId)) { // iv_switch_block_groupmsg.setVisibility(View.INVISIBLE); // iv_switch_unblock_groupmsg.setVisibility(View.VISIBLE); // // } else { // iv_switch_block_groupmsg.setVisibility(View.VISIBLE); // iv_switch_unblock_groupmsg.setVisibility(View.INVISIBLE); // } } private void initData() { rl_switch_chattotop.setOnClickListener(this); rl_switch_block_groupmsg.setOnClickListener(this); re_clear.setOnClickListener(this); ImageView ivAvatar= (ImageView) this.findViewById(R.id.iv_avatar); TextView tvNick = (TextView) this.findViewById(R.id.tv_username); tvNick.setText(user.getNick()); String avatarUrl =user.getAvatar(); // String avatarUrl = HTConstant.URL_AVATAR + userJson.getString(HTConstant.JSON_KEY_AVATAR); if(!TextUtils.isEmpty(avatarUrl)){ if (!avatarUrl.contains("http:")){ avatarUrl = HTConstant.URL_AVATAR+avatarUrl; } } Glide.with(this).load( avatarUrl).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(ivAvatar); ivAvatar.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(ChatSettingActivity.this, UserDetailsActivity.class).putExtra(HTConstant.KEY_USER_INFO, user.getUserInfo())); } }); ImageView ivAdd = (ImageView) this.findViewById(R.id.iv_avatar2); ivAdd.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { } }); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.rl_switch_block_groupmsg: // 设置免打扰 progressDialog = HTApp.getInstance().createLoadingDialog(this,"正在设置免打扰..."); progressDialog.show(); if (iv_switch_block_groupmsg.getVisibility() == View.VISIBLE) { new Handler().postDelayed(new Runnable() { public void run() { removeOutBlacklist(userId); progressDialog.dismiss(); } }, 2000); } else { moveToBlacklist(userId); } break; case R.id.re_clear: // 清空聊天记录 progressDialog = HTApp.getInstance().createLoadingDialog(this,"正在清空聊天记录..."); progressDialog.show(); new Handler().postDelayed(new Runnable() { public void run() { HTClient.getInstance().conversationManager().deleteConversationAndMessage(userId); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(new Intent(IMAction.ACTION_MESSAGE_EMPTY).putExtra("id", userId)); progressDialog.dismiss(); } }, 2000); break; case R.id.rl_switch_chattotop: // // 当前状态是已经置顶,点击后取消置顶 // if (iv_switch_chattotop.getVisibility() == View.VISIBLE) { // // iv_switch_chattotop.setVisibility(View.INVISIBLE); // iv_switch_unchattotop.setVisibility(View.VISIBLE); // // if (topMap.containsKey(userId)) { // // topMap.remove(userId); // TopUserDao topUserDao = new TopUserDao( // ChatSettingSingleActivity.this); // // topUserDao.deleteTopUser(userId); // // } // // } else { // // // 当前状态是未置顶点击后置顶 // // iv_switch_chattotop.setVisibility(View.VISIBLE); // iv_switch_unchattotop.setVisibility(View.INVISIBLE); // // if (!topMap.containsKey(userId)) { // TopUser topUser = new TopUser(); // topUser.setTime(System.currentTimeMillis()); // // 1---表示是群组0----个人 // topUser.setType(0); // topUser.setUserName(userId); // Map map = new HashMap(); // map.put(userId, topUser); // topMap.putAll(map); // TopUserDao topUserDao = new TopUserDao( // ChatSettingSingleActivity.this); // topUserDao.saveTopUser(topUser); // // } // // } break; default: break; } } /** * 把user移入到免打扰 */ private void moveToBlacklist(final String username) { // // new Thread(new Runnable() { // public void run() { // try { // // 加入到黑名单 // EMClient.getInstance().contactManager().addUserToBlackList(username, // false); // runOnUiThread(new Runnable() { // public void run() { // progressDialog.dismiss(); // iv_switch_block_groupmsg // .setVisibility(View.VISIBLE); // iv_switch_unblock_groupmsg // .setVisibility(View.INVISIBLE); // // } // }); // } catch (final HyphenateException e) { // runOnUiThread(new Runnable() { // public void run() { // progressDialog.dismiss(); // Toast.makeText(getApplicationContext(), // "设置失败,原因:" + e.toString(), // Toast.LENGTH_SHORT).show(); // } // }); // e.printStackTrace(); // } // } // }).start(); } /** * 移出免打扰 * * @param tobeRemoveUser */ private void removeOutBlacklist(final String tobeRemoveUser) { // try { // // // 移出黑民单 // EMClient.getInstance().contactManager().removeUserFromBlackList(tobeRemoveUser); // iv_switch_block_groupmsg.setVisibility(View.INVISIBLE); // iv_switch_unblock_groupmsg.setVisibility(View.VISIBLE); // } catch (HyphenateException e) { // runOnUiThread(new Runnable() { // public void run() { // // Toast.makeText(getApplicationContext(), "设置失败", // Toast.LENGTH_SHORT).show(); // } // }); // e.printStackTrace(); // } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/activity/ChooseContactActivity.java ================================================ package com.htmessage.yichatopen.activity.chat.activity; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.view.View; import android.widget.AdapterView; import android.widget.Button; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.manager.HTChatManager; import com.htmessage.sdk.model.HTMessage; import com.htmessage.sdk.model.HTMessageImageBody; import com.htmessage.sdk.model.HTMessageVoiceBody; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Map; /** * 项目名称:yichat * 类描述:CheckPeopleActivity 描述: 选择转发人员 * 创建人:songlijie * 创建时间:2017/3/18 17:04 * 邮箱:814326663@qq.com */ public class ChooseContactActivity extends BaseActivity implements AdapterView.OnItemClickListener, View.OnClickListener { private ArrayList users = new ArrayList<>(); private ArrayList friends = new ArrayList<>(); private TextView tv_group_check, tv_title; private ListView list; private ChooseContactAdapter adAdapter; private String forwordType; private String localUrl,obj,exobj; private HTMessage message1; private JSONObject object,extJSON; private String imagePath,msgId,toChatUsername; private Button btn_rtc; private List msgList; @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.activity_check_people); getData(); initView(); initData(); setListener(); } private void initData() { btn_rtc.setVisibility(View.VISIBLE); btn_rtc.setText(R.string.str_send); msgList = HTClient.getInstance().messageManager().getMessageList(toChatUsername); extJSON = JSONObject.parseObject(exobj); message1 = getCopyMessage(msgId); getContacts(); adAdapter = new ChooseContactAdapter(ChooseContactActivity.this, friends); list.setAdapter(adAdapter); } private void getData() { obj = getIntent().getStringExtra("obj"); object = JSONObject.parseObject(obj); imagePath = object.getString("imagePath"); forwordType = object.getString("forwordType"); localUrl = object.getString("localPath"); msgId = object.getString("msgId"); toChatUsername =object.getString("toChatUsername"); exobj =object.getString("exobj"); getContacts(); } private void getContacts() { friends.clear(); // 获取本地好友列表 Map users = ContactsManager.getInstance().getContactList(); Iterator> iterator = users.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); friends.add(entry.getValue()); } // 对list进行排序 Collections.sort(friends, new PinyinComparator() {}); } private void initView() { tv_group_check = (TextView) findViewById(R.id.tv_group_check); tv_title = (TextView) findViewById(R.id.tv_title); list = (ListView) findViewById(R.id.list); btn_rtc = (Button) findViewById(R.id.btn_rtc); } private void setListener() { tv_group_check.setOnClickListener(this); list.setOnItemClickListener(this); btn_rtc.setOnClickListener(this); } @Override public void onItemClick(AdapterView parent, View view, int position, long id) { CheckBox checkBox = (CheckBox) view.findViewById(R.id.checkbox); checkBox.toggle(); ChooseContactAdapter.getIsSelected().put(position, checkBox.isChecked());//将CheckBox的选中状况记录下来 // 调整选定条目 if (checkBox.isChecked() == true) { users.add(adAdapter.getItem(position)); } else { users.remove(adAdapter.getItem(position)); } } public void getContactsInServer() { if (!HTClient.getInstance().isLogined()) { return; } List params = new ArrayList(); new OkHttpUtils(this).post(params, HTConstant.URL_FriendList, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { int code = jsonObject.getIntValue("code"); switch (code) { case 1: JSONArray friends = jsonObject.getJSONArray("user"); if (friends != null || friends.size() != 0) { List users = new ArrayList(); for (int i = 0; i < friends.size(); i++) { JSONObject friend = friends.getJSONObject(i); User user = CommonUtils.Json2User(friend); users.add(user); } ContactsManager.getInstance().saveContactList(users); } break; } } @Override public void onFailure(String errorMsg) { } }); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_rtc: if (users.size() == 0 || users == null) { Toast.makeText(ChooseContactActivity.this, R.string.please_check_contant, Toast.LENGTH_SHORT).show(); return; } showMessageFarWordDialog(users, forwordType, localUrl); break; } } private void showMessageFarWordDialog(final ArrayList users, final String forwordType, final String localUrl) { AlertDialog.Builder buidler = new AlertDialog.Builder(this); View view = View.inflate(this, R.layout.item_dialog_gridview, null); TextView tv_forward = (TextView) view.findViewById(R.id.tv_forward); TextView textView = (TextView) view.findViewById(R.id.textView); ImageView imageView = (ImageView) view.findViewById(R.id.imageView); TextView tv_ok = (TextView) view.findViewById(R.id.tv_ok); TextView tv_cancel = (TextView) view.findViewById(R.id.tv_cancel); textView.setText(R.string.forword_always); if ("image".equals(forwordType) && localUrl != null) { imageView.setVisibility(View.VISIBLE); Glide.with(this).load(imagePath).diskCacheStrategy(DiskCacheStrategy.ALL).into(imageView); } tv_forward.setText(getString(R.string.forword_people).replace("1", String.valueOf(users.size()))); buidler.setView(view); final AlertDialog dialog = buidler.show(); tv_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); for (int i = 0; i < users.size(); i++) { User easeUser = users.get(i); switch (forwordType) { case "text": HTMessage textMessage= HTMessage.createTextSendMessage(easeUser.getUsername(),localUrl); sendMessage(textMessage); break; case "voice": HTMessageVoiceBody voiceBody = (HTMessageVoiceBody) message1.getBody(); HTMessage voiceMSg = HTMessage.createVoiceSendMessage(easeUser.getUsername(), localUrl, voiceBody.getAudioDuration()); sendMessage(voiceMSg); break; case "image": HTMessageImageBody imageBody = (HTMessageImageBody) message1.getBody(); HTMessage message = HTMessage.createImageSendMessage(easeUser.getUsername(), localUrl, imageBody.getSize()); sendMessage(message); break; } } } }); tv_cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); } private void sendMessage(final HTMessage htMessage){ htMessage.setAttributes(extJSON.toJSONString()); htMessage.setChatType(ChatType.singleChat); HTClient.getInstance().chatManager().sendMessage(htMessage, new HTChatManager.HTMessageCallBack() { @Override public void onProgress() { } @Override public void onSuccess() { runOnUiThread(new Runnable() { @Override public void run() { htMessage.setStatus(HTMessage.Status.SUCCESS); HTClient.getInstance().messageManager().saveMessage(htMessage,false); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(new Intent(IMAction.ACTION_MESSAGE_FORWORD).putExtra("message",htMessage)); } }); } @Override public void onFailure() { runOnUiThread(new Runnable() { @Override public void run() { htMessage.setStatus(HTMessage.Status.FAIL); HTClient.getInstance().messageManager().saveMessage(htMessage,false); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(new Intent(IMAction.ACTION_MESSAGE_FORWORD).putExtra("message",htMessage)); } }); } }); finish(); } @Override protected void onResume() { super.onResume(); friends.clear(); getContacts(); if (adAdapter!=null){ adAdapter.notifyDataSetChanged(); } } private HTMessage getCopyMessage(String msgId) { for (HTMessage htMessage : msgList) { if (htMessage.getMsgId().equals(msgId)) { return htMessage; } } return null; } public class PinyinComparator implements Comparator { @SuppressLint("DefaultLocale") @Override public int compare(User o1, User o2) { String py1 = o1.getInitialLetter(); String py2 = o2.getInitialLetter(); if (py1.equals(py2)) { return o1.getNick().compareTo(o2.getNick()); } else { if ("#".equals(py1)) { return 1; } else if ("#".equals(py2)) { return -1; } return py1.compareTo(py2); } } private boolean isEmpty(String str) { return "".equals(str.trim()); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/activity/ChooseContactAdapter.java ================================================ package com.htmessage.yichatopen.activity.chat.activity; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.domain.User; import java.util.ArrayList; import java.util.HashMap; /** * 项目名称:yichat * 类描述:PeopleCheckAdapter 描述: 选择转发人员的适配器 * 创建人:songlijie * 创建时间:2017/3/18 17:27 * 邮箱:814326663@qq.com */ public class ChooseContactAdapter extends BaseAdapter { private Context mContext; private ArrayList beans = new ArrayList<>(); // 用来控制CheckBox的选中状况 private static HashMap isSelected; public ChooseContactAdapter(Context mContext, ArrayList beans) { this.mContext = mContext; this.beans = beans; isSelected = new HashMap(); initDate(); } @Override public int getCount() { return beans.size(); } // 初始化isSelected的数据 private void initDate() { for (int i = 0; i < beans.size(); i++) { getIsSelected().put(i, false); } } @Override public User getItem(int position) { return beans.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null){ convertView = View.inflate(mContext, R.layout.latout_pre_videocall_item,null); holder = new ViewHolder(); holder.iv_game_avatar = (ImageView) convertView.findViewById(R.id.iv_game_avatar); holder.tv_username = (TextView) convertView.findViewById(R.id.tv_username); holder.checkbox = (CheckBox) convertView.findViewById(R.id.checkbox); convertView.setTag(holder); }else{ holder = (ViewHolder) convertView.getTag(); } User gameBean = beans.get(position); holder.tv_username.setText(gameBean.getNick()); Glide.with(mContext).load(gameBean.getAvatar()).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).error(R.drawable.default_avatar).into(holder.iv_game_avatar); // 根据isSelected来设置checkbox的选中状况 holder.checkbox.setChecked(getIsSelected().get(position)); return convertView; } public static class ViewHolder{ public TextView tv_username; public ImageView iv_game_avatar; public CheckBox checkbox; } public static HashMap getIsSelected() { return isSelected; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/ChatExtendMenu.java ================================================ package com.htmessage.yichatopen.activity.chat.weight; import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.GridView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.widget.zxing.activity.DensityUtil; import java.util.ArrayList; import java.util.List; /** * Extend menu when user want send image, voice clip, etc * */ public class ChatExtendMenu extends GridView{ protected Context context; private List itemModels = new ArrayList(); public ChatExtendMenu(Context context, AttributeSet attrs, int defStyle) { this(context, attrs); } public ChatExtendMenu(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public ChatExtendMenu(Context context) { super(context); init(context, null); } private void init(Context context, AttributeSet attrs){ this.context = context; TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ChatExtendMenu); int numColumns = ta.getInt(R.styleable.ChatExtendMenu_numColumns, 4); ta.recycle(); setNumColumns(numColumns); setStretchMode(GridView.STRETCH_COLUMN_WIDTH); setGravity(Gravity.CENTER_VERTICAL); setVerticalSpacing(DensityUtil.dip2px(context, 8)); } /** * init */ public void init(){ setAdapter(new ItemAdapter(context, itemModels)); } /** * register menu item * * @param name * item name * @param drawableRes * background of item * @param itemId * id * @param listener * on click event of item */ public void registerMenuItem(String name, int drawableRes, int itemId, EaseChatExtendMenuItemClickListener listener) { ChatMenuItemModel item = new ChatMenuItemModel(); item.name = name; item.image = drawableRes; item.id = itemId; item.clickListener = listener; itemModels.add(item); } /** * register menu item * * @param nameRes * resource id of itme name * @param drawableRes * background of item * @param itemId * id * @param listener * on click event of item */ public void registerMenuItem(int nameRes, int drawableRes, int itemId, EaseChatExtendMenuItemClickListener listener) { registerMenuItem(context.getString(nameRes), drawableRes, itemId, listener); } private class ItemAdapter extends ArrayAdapter{ private Context context; public ItemAdapter(Context context, List objects) { super(context, 1, objects); this.context = context; } @Override public View getView(final int position, View convertView, ViewGroup parent) { ChatMenuItem menuItem = null; if(convertView == null){ convertView = new ChatMenuItem(context); } menuItem = (ChatMenuItem) convertView; menuItem.setImage(getItem(position).image); menuItem.setText(getItem(position).name); menuItem.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if(getItem(position).clickListener != null){ getItem(position).clickListener.onClick(getItem(position).id, v); } } }); return convertView; } } public interface EaseChatExtendMenuItemClickListener{ void onClick(int itemId, View view); } class ChatMenuItemModel{ String name; int image; int id; EaseChatExtendMenuItemClickListener clickListener; } class ChatMenuItem extends LinearLayout { private ImageView imageView; private TextView textView; public ChatMenuItem(Context context, AttributeSet attrs, int defStyle) { this(context, attrs); } public ChatMenuItem(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } public ChatMenuItem(Context context) { super(context); init(context, null); } private void init(Context context, AttributeSet attrs) { LayoutInflater.from(context).inflate(R.layout.chat_menu_item, this); imageView = (ImageView) findViewById(R.id.image); textView = (TextView) findViewById(R.id.text); } public void setImage(int resid) { imageView.setBackgroundResource(resid); } public void setText(int resid) { textView.setText(resid); } public void setText(String text) { textView.setText(text); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/ChatInputView.java ================================================ package com.htmessage.yichatopen.activity.chat.weight; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Rect; import android.os.Build; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.content.ContextCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; 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 com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.chat.weight.emoji.DefaultEmojiconDatas; import com.htmessage.yichatopen.activity.chat.weight.emoji.EmojiFragment; import com.htmessage.yichatopen.activity.chat.weight.emoji.Emojicon; import com.htmessage.yichatopen.activity.chat.weight.emoji.SmileUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * Created by huangfangyi on 2017/7/4. * qq 84543217 */ public class ChatInputView extends LinearLayout implements View.OnClickListener { private static final String SHARE_PREFERENCE_NAME = "com.yichatsystem.app"; private static final String SHARE_PREFERENCE_TAG = "soft_input_height"; //切换到语音输入按钮 private Button btn_set_mode_voice; //文本输入框 private EditText et_sendmessage; //文本输入框的linearlayou private LinearLayout ll_press_to_input; //语音输入的linearlayout private LinearLayout ll_press_to_speak; //切换到文字输入的按钮 private Button btn_set_mode_keyboard; //按住录音的按钮 private Button tv_recording; //表情按钮正常状态 private Button btn_emoticons_normal; //右边切换到输入状态的按钮 private Button btn_emoticons_checked; //选择更多的消息类型 private Button btn_more; //消息发送和按钮 private Button btn_send; //表情及更多消息类型的父View private LinearLayout ll_more; //表情区域 private TabLayout tl_face_container; private ViewPager emoji_viewpager; //更多消息类型 private TabLayout tablelayout_extend; private ViewPager extend_viewpager; // private EmotionInputDetector emotionInputDetector; //输入框上面的view private View mContentView; private InputMethodManager mInputManager; private SharedPreferences sp; private LinearLayout ll_emoji; private LinearLayout ll_extend; private InputViewLisenter inputViewLisenter; public ChatInputView(Context context) { super(context); init(context); } public ChatInputView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } public ChatInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void showEmotionLayout() { int softInputHeight = getSupportSoftInputHeight(); if (softInputHeight == 0) { softInputHeight = sp.getInt(SHARE_PREFERENCE_TAG, 400); } hideSoftInput(); ll_more.getLayoutParams().height = softInputHeight; ll_more.setVisibility(View.VISIBLE); // inputViewLisenter.onEditTextUp(); } public void init(Context context) { LayoutInflater.from(context).inflate(R.layout.widget_input_view, this); } private ChatExtendMenu chatExtendMenus; public void initView(Activity activity, View aboveView, InputViewLisenter inputViewLisenter, ChatExtendMenu chatExtendMenus) { this.chatExtendMenus = chatExtendMenus; this.inputViewLisenter = inputViewLisenter; mContentView = aboveView; //切换到语音输入按钮 btn_set_mode_voice = (Button) this.findViewById(R.id.btn_set_mode_voice); //文本输入框 et_sendmessage = (EditText) this.findViewById(R.id.et_sendmessage); //文本输入框的linearlayou ll_press_to_input = (LinearLayout) this.findViewById(R.id.ll_press_to_input); //语音输入的linearlayout ll_press_to_speak = (LinearLayout) this.findViewById(R.id.ll_press_to_speak); //切换到文字输入的按钮(左边) btn_set_mode_keyboard = (Button) this.findViewById(R.id.btn_set_mode_keyboard); //按住录音的按钮 tv_recording = (Button) this.findViewById(R.id.tv_recording); //表情按钮正常状态 btn_emoticons_normal = (Button) this.findViewById(R.id.btn_emoticons_normal); //右边切换到输入状态的按钮 btn_emoticons_checked = (Button) this.findViewById(R.id.btn_emoticons_checked); //选择更多的消息类型 btn_more = (Button) this.findViewById(R.id.btn_more); //消息发送和按钮 btn_send = (Button) this.findViewById(R.id.btn_send); //表情及更多消息类型的父View ll_more = (LinearLayout) this.findViewById(R.id.ll_more); //表情区域 ll_emoji = (LinearLayout) this.findViewById(R.id.ll_emoji); tl_face_container = (TabLayout) this.findViewById(R.id.tl_face_container); emoji_viewpager = (ViewPager) this.findViewById(R.id.emoji_viewpager); //更多消息类型 ll_extend = (LinearLayout) this.findViewById(R.id.ll_extend); tablelayout_extend = (TabLayout) this.findViewById(R.id.tablelayout_extend); extend_viewpager = (ViewPager) this.findViewById(R.id.extend_viewpager); //初始化表情区域 initEmojiView(activity); //初始化更多消息 ininExtendView(); //设置按钮监听 setListener(); mInputManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); sp = getContext().getSharedPreferences(SHARE_PREFERENCE_NAME, Context.MODE_PRIVATE); activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); hideSoftInput(); } // public void setAboveView(Activity activity,View upView){ // emotionInputDetector= EmotionInputDetector.with((AppCompatActivity)activity) // .setEmotionView(ll_more) // .bindToContent(upView) // .bindToEditText(et_sendmessage) // .bindToEmotionButton(btn_emoticons_normal) // .build(); // } public boolean interceptBackPress() { // TODO: 15/11/2 change this method's name if (ll_more.isShown()) { hideEmotionLayout(false); if (!btn_emoticons_normal.isShown()) { btn_emoticons_checked.setVisibility(GONE); btn_emoticons_normal.setVisibility(VISIBLE); } return true; } return false; } private void setListener() { btn_set_mode_voice.setOnClickListener(this); et_sendmessage.setOnClickListener(this); btn_set_mode_keyboard.setOnClickListener(this); btn_emoticons_normal.setOnClickListener(this); btn_emoticons_checked.setOnClickListener(this); btn_more.setOnClickListener(this); btn_send.setOnClickListener(this); et_sendmessage.requestFocus(); et_sendmessage.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { if (!btn_emoticons_normal.isShown()) { btn_emoticons_checked.setVisibility(GONE); btn_emoticons_normal.setVisibility(VISIBLE); } if (ll_more.isShown()) { lockContentHeight(); hideEmotionLayout(true); et_sendmessage.postDelayed(new Runnable() { @Override public void run() { unlockContentHeightDelayed(); } }, 200L); inputViewLisenter.onEditTextUp(); return false; } if (!isSoftInputShown()) { inputViewLisenter.onEditTextUp(); } } return false; } }); // et_sendmessage.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { // @Override // public void onGlobalLayout() { // // et_sendmessage.getViewTreeObserver().removeOnGlobalLayoutListener(this); // float imagePositionX = et_sendmessage.getX(); // float imagePositionY = et_sendmessage.getY(); // float imageWidth = et_sendmessage.getWidth(); // float imageHeight = et_sendmessage.getHeight(); // //设置文本大小 // // tvInImage.setMaxWidth((int) imageWidth); // Log.d("et_sendmessage--->",imagePositionX+"-"+imagePositionY+"-"+imageWidth+"-"+imageHeight); // } // }); // // et_sendmessage.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { if (!TextUtils.isEmpty(s.toString())) { btn_more.setVisibility(GONE); btn_send.setVisibility(VISIBLE); } else { btn_send.setVisibility(GONE); btn_more.setVisibility(VISIBLE); } Log.d("onEditTextClicked1", s.toString()); } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { if (!TextUtils.isEmpty(s.toString())) { btn_more.setVisibility(GONE); btn_send.setVisibility(VISIBLE); } else { btn_send.setVisibility(GONE); btn_more.setVisibility(VISIBLE); } Log.d("onEditTextClicked", s.toString()); } }); // // tv_recording.setClickable(true); // View ll_te=this.findViewById(R.id.ll_speak); // ll_te.setClickable(true); // ll_te.setLongClickable(true); tv_recording.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { Log.d("voiceFilePath--->", "onTouch"); if (inputViewLisenter != null) { Log.d("voiceFilePath--->", "inputViewLisenter"); inputViewLisenter.onPressToSpeakBtnTouch(v, event); } return false; } }); et_sendmessage.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { if (inputViewLisenter != null) { return inputViewLisenter.onEditTextLongClick(); } return false; } }); } public void hideSoftInput() { mInputManager.hideSoftInputFromWindow(et_sendmessage.getWindowToken(), 0); } private void hideEmotionLayout(boolean showSoftInput) { if (ll_more.isShown()) { ll_more.setVisibility(View.GONE); if (showSoftInput) { showSoftInput(); } } } private void unlockContentHeightDelayed() { et_sendmessage.postDelayed(new Runnable() { @Override public void run() { ((LayoutParams) mContentView.getLayoutParams()).weight = 1.0F; } }, 200L); } private void showSoftInput() { et_sendmessage.requestFocus(); et_sendmessage.post(new Runnable() { @Override public void run() { mInputManager.showSoftInput(et_sendmessage, 0); // inputViewLisenter.onEditTextUp(); } }); } private void lockContentHeight() { LayoutParams params = (LayoutParams) mContentView.getLayoutParams(); params.height = mContentView.getHeight(); params.weight = 0.0F; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_set_mode_voice: showVoiceRecordingView(); inputViewLisenter.onEditTextUp(); btn_emoticons_checked.setVisibility(GONE); btn_send.setVisibility(GONE); btn_more.setVisibility(VISIBLE); btn_emoticons_normal.setVisibility(VISIBLE); break; // case R.id.et_sendmessage: // // break; case R.id.btn_set_mode_keyboard: showInputView(); inputViewLisenter.onEditTextUp(); if (!TextUtils.isEmpty(et_sendmessage.getText().toString())) { btn_more.setVisibility(GONE); btn_send.setVisibility(VISIBLE); } break; case R.id.btn_emoticons_normal: showInputView(); inputViewLisenter.onEditTextUp(); btn_emoticons_normal.setVisibility(GONE); btn_emoticons_checked.setVisibility(VISIBLE); if (isSoftInputShown()) { lockContentHeight(); showEmotionLayout(); unlockContentHeightDelayed(); } else { setEmojiMode(); showEmotionLayout(); } break; case R.id.btn_emoticons_checked: showInputView(); inputViewLisenter.onEditTextUp(); et_sendmessage.requestFocus(); btn_emoticons_checked.setVisibility(GONE); btn_emoticons_normal.setVisibility(VISIBLE); if (ll_extend.isShown()) { setEmojiMode(); } else { lockContentHeight(); hideEmotionLayout(true); unlockContentHeightDelayed(); } break; case R.id.btn_more: btn_emoticons_normal.setVisibility(VISIBLE); btn_emoticons_checked.setVisibility(GONE); showInputView(); inputViewLisenter.onEditTextUp(); if (ll_more.isShown()) { if (ll_emoji.isShown()) { setExtendMode(); } else { lockContentHeight(); hideEmotionLayout(true); unlockContentHeightDelayed(); } } else { if (isSoftInputShown()) { lockContentHeight(); showEmotionLayout(); unlockContentHeightDelayed(); } else { setExtendMode(); showEmotionLayout(); } } break; case R.id.btn_send: String content = et_sendmessage.getText().toString(); et_sendmessage.setText(""); inputViewLisenter.onSendButtonClicked(content); break; } } private boolean isSoftInputShown() { return getSupportSoftInputHeight() != 0; } private void showVoiceRecordingView() { ll_press_to_input.setVisibility(GONE); ll_press_to_speak.setVisibility(VISIBLE); interceptBackPress(); } private void setEmojiMode() { ll_extend.setVisibility(GONE); ll_emoji.setVisibility(VISIBLE); } private void setExtendMode() { ll_emoji.setVisibility(GONE); ll_extend.setVisibility(VISIBLE); } private void showInputView() { ll_press_to_speak.setVisibility(GONE); ll_press_to_input.setVisibility(VISIBLE); } private void initEmojiView(Context context) { ll_emoji.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_BEGINNING); ll_emoji.setDividerDrawable(ContextCompat.getDrawable(getContext(), R.drawable.divider_horizontal)); final List fragmentList = new ArrayList<>(); fragmentList.add(new EmojiFragment(Arrays.asList(DefaultEmojiconDatas.getData()), 7, 3, new EmojiFragment.OnEmojiListener() { @Override public void onDeleteImageClicked() { editTextDelete(et_sendmessage); } @Override public void onExpressionClicked(Emojicon emojicon) { editTextAddEmoji(emojicon); } })); fragmentList.add(new EmojiFragment(Arrays.asList(DefaultEmojiconDatas.getData()), 7, 3, new EmojiFragment.OnEmojiListener() { @Override public void onDeleteImageClicked() { editTextDelete(et_sendmessage); } @Override public void onExpressionClicked(Emojicon emojicon) { editTextAddEmoji(emojicon); } })); emoji_viewpager.setAdapter(new FragmentPagerAdapter(((AppCompatActivity) context).getSupportFragmentManager()) { @Override public int getCount() { return fragmentList.size(); } @Override public Fragment getItem(int position) { return fragmentList.get(position); } }); tl_face_container.setupWithViewPager(emoji_viewpager); TabLayout.Tab[] tabs = new TabLayout.Tab[fragmentList.size()]; for (int i = 0; i < fragmentList.size(); i++) { tabs[i] = tl_face_container.getTabAt(i); // View view=View.inflate(getContext(),R.layout.wigth_emoji_bottom_item,null); //(ImageView) view.findViewById(R.id.iv_emoji); ImageView imageView = new ImageView(getContext()); imageView.setImageResource(R.drawable.ee_0); tabs[i].setCustomView(imageView); } LinearLayout mLinearLayout = (LinearLayout) tl_face_container.getChildAt(0); // 在所有子控件的中间显示分割线(还可能只显示顶部、尾部和不显示分割线) mLinearLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE | LinearLayout.SHOW_DIVIDER_END); // mLinearLayout.setShowDividers(); // 设置分割线的距离本身(LinearLayout)的内间距 // mLinearLayout.setDividerPadding(20); // 设置分割线的样式 mLinearLayout.setDividerDrawable(ContextCompat.getDrawable(getContext(), R.drawable.divider_vertical)); } private void editTextDelete(EditText editText) { if (!TextUtils.isEmpty(editText.getText())) { KeyEvent event = new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL); editText.dispatchKeyEvent(event); } } private void editTextAddEmoji(Emojicon emojicon) { if (emojicon.getType() != Emojicon.Type.BIG_EXPRESSION) { if (emojicon.getEmojiText() != null) { et_sendmessage.append(SmileUtils.getSmiledText(getContext(), emojicon.getEmojiText())); } } else { if (inputViewLisenter != null) { inputViewLisenter.onBigExpressionClicked(emojicon); } } } private int getSupportSoftInputHeight() { Rect r = new Rect(); ((Activity) getContext()).getWindow().getDecorView().getWindowVisibleDisplayFrame(r); int screenHeight = ((Activity) getContext()).getWindow().getDecorView().getRootView().getHeight(); int softInputHeight = screenHeight - r.bottom; if (Build.VERSION.SDK_INT >= 20) { // When SDK Level >= 20 (Android L), the softInputHeight will contain the height of softButtonsBar (if has) softInputHeight = softInputHeight - getSoftButtonsBarHeight(); } if (softInputHeight < 0) { Log.w("EmotionInputDetector", "Warning: value of softInputHeight is below zero!"); } if (softInputHeight > 0) { sp.edit().putInt(SHARE_PREFERENCE_TAG, softInputHeight).apply(); } return softInputHeight; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) private int getSoftButtonsBarHeight() { DisplayMetrics metrics = new DisplayMetrics(); ((Activity) getContext()).getWindowManager().getDefaultDisplay().getMetrics(metrics); int usableHeight = metrics.heightPixels; ((Activity) getContext()).getWindowManager().getDefaultDisplay().getRealMetrics(metrics); int realHeight = metrics.heightPixels; if (realHeight > usableHeight) { return realHeight - usableHeight; } else { return 0; } } public interface InputViewLisenter { boolean onPressToSpeakBtnTouch(View v, MotionEvent event); void onBigExpressionClicked(Emojicon emojicon); void onSendButtonClicked(String content); boolean onEditTextLongClick(); void onEditTextUp(); } private void ininExtendView() { final List views = new ArrayList<>(); views.add(chatExtendMenus); extend_viewpager.setAdapter(new PagerAdapter() { @Override public int getCount() { return views.size(); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public Object instantiateItem(ViewGroup arg0, int arg1) { ((ViewPager) arg0).addView(views.get(arg1)); return views.get(arg1); } @Override public void destroyItem(ViewGroup arg0, int arg1, Object arg2) { ((ViewPager) arg0).removeView(views.get(arg1)); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/VoicePlayClickListener.java ================================================ package com.htmessage.yichatopen.activity.chat.weight; import android.app.Activity; import android.content.Context; import android.graphics.drawable.AnimationDrawable; import android.media.AudioManager; import android.media.MediaPlayer; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.BaseAdapter; import android.widget.ImageView; import com.htmessage.yichatopen.HTClientHelper; import com.htmessage.yichatopen.R; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.model.HTMessageVoiceBody; import com.htmessage.sdk.model.HTMessage; import com.htmessage.yichatopen.manager.SettingsManager; import com.htmessage.yichatopen.utils.HTMessageUtils; import java.io.File; /** * Created by huangfangyi on 2016/12/5. * qq 84543217 */ public class VoicePlayClickListener implements View.OnClickListener { private static final String TAG = "VoicePlayClickListener"; HTMessage message; ImageView voiceIconView; private AnimationDrawable voiceAnimation = null; MediaPlayer mediaPlayer = null; ImageView iv_read_status; Activity activity; private ChatType chatType; private BaseAdapter adapter; public static boolean isPlaying = false; public static VoicePlayClickListener currentPlayListener = null; public static String playMsgId; String chatTo; public VoicePlayClickListener(HTMessage message, String chatTo, ImageView v, ImageView iv_read_status, BaseAdapter adapter, Activity context) { this.message = message; // voiceBody = (EMVoiceMessageBody) message.getBody(); this.iv_read_status = iv_read_status; this.adapter = adapter; voiceIconView = v; this.activity = context; this.chatType = message.getChatType(); this.chatTo = chatTo; } public void stopPlayVoice() { voiceAnimation.stop(); if (message.getDirect() == HTMessage.Direct.RECEIVE) { voiceIconView.setImageResource(R.drawable.chatfrom_voice_playing); } else { voiceIconView.setImageResource(R.drawable.chatto_voice_playing); } // stop play voice if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); } isPlaying = false; playMsgId = null; adapter.notifyDataSetChanged(); } public void playVoice(String filePath) { if (!(new File(filePath).exists())) { return; } playMsgId = message.getMsgId(); AudioManager audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); mediaPlayer = new MediaPlayer(); if (SettingsManager.getInstance().getSettingMsgSpeaker()) { audioManager.setMode(AudioManager.MODE_NORMAL); audioManager.setSpeakerphoneOn(true); mediaPlayer.setAudioStreamType(AudioManager.STREAM_RING); } else { audioManager.setSpeakerphoneOn(false);// 关闭扬声器 // 把声音设定成Earpiece(听筒)出来,设定为正在通话中 audioManager.setMode(AudioManager.MODE_IN_CALL); mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL); } try { mediaPlayer.setDataSource(filePath); mediaPlayer.prepare(); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mediaPlayer.release(); mediaPlayer = null; stopPlayVoice(); // stop animation } }); isPlaying = true; currentPlayListener = this; mediaPlayer.start(); showAnimation(); // 如果是接收的消息 if (message.getDirect() == HTMessage.Direct.RECEIVE) { // if (!message.isAcked() && chatType == ChatType.Chat) { // // 告知对方已读这条消息 // EMClient.getInstance().chatManager().ackMessageRead(message.getFrom(), message.getMsgId()); // } if (message.getStatus() != HTMessage.Status.SUCCESS && iv_read_status != null && iv_read_status.getVisibility() == View.VISIBLE) { // 隐藏自己未播放这条语音消息的标志 iv_read_status.setVisibility(View.INVISIBLE); HTClient.getInstance().messageManager().updateSuccess(message); // message.setListened(true); // EMClient.getInstance().chatManager().setMessageListened(message); } } } catch (Exception e) { System.out.println(); } } // show the voice playing animation private void showAnimation() { // play voice, and start animation if (message.getDirect() == HTMessage.Direct.RECEIVE) { voiceIconView.setImageResource(R.anim.voice_from_icon); } else { voiceIconView.setImageResource(R.anim.voice_to_icon); } voiceAnimation = (AnimationDrawable) voiceIconView.getDrawable(); voiceAnimation.start(); } @Override public void onClick(View v) { String st = activity.getResources().getString(R.string.Is_download_voice_click_later); if (isPlaying) { if (playMsgId != null && playMsgId.equals(message.getMsgId())) { currentPlayListener.stopPlayVoice(); return; } currentPlayListener.stopPlayVoice(); } HTMessageVoiceBody htMessageVoiceBody = (HTMessageVoiceBody) message.getBody(); String loaclPath = htMessageVoiceBody.getLocalPath(); if (message.getDirect() == HTMessage.Direct.SEND) { // Log.d("getBodyJSON--->",message.getBodyJSON().getString(MessageUtils.LOCAL_PATH)); // for sent msg, we will try to play the voice file directly playVoice(loaclPath); } else { if (!TextUtils.isEmpty(loaclPath)) { if (message.getStatus() == HTMessage.Status.SUCCESS) { File file = new File(loaclPath); if (file.exists() && file.isFile()) playVoice(loaclPath); else Log.e(TAG, "file not exist"); } } else { // if(message.getBodyJSON().containsKey(MessageUtils.REMOTE_PATH)){ // String remotePath=message.getBodyJSON().getString(MessageUtils.REMOTE_PATH); downLoadVoiceFileFromServer(message); //} } // // else if (message.status() == HTMessage.Status.INPROGRESS) { // Toast.makeText(activity, st, Toast.LENGTH_SHORT).show(); // } else if (message.status() == HTMessage.Status.FAIL) { // Toast.makeText(activity, st, Toast.LENGTH_SHORT).show(); // new AsyncTask() { // // @Override // protected Void doInBackground(Void... params) { // EMClient.getInstance().chatManager().downloadAttachment(message); // ImageUtils.getScaledImage() // return null; // } // // @Override // protected void onPostExecute(Void result) { // super.onPostExecute(result); // adapter.notifyDataSetChanged(); // } // // }.execute(); // // } } } private void downLoadVoiceFileFromServer(final HTMessage htMessage) { HTMessageUtils.loadMessageFile(htMessage, chatTo, activity, new HTMessageUtils.CallBack() { @Override public void error() { } @Override public void completed(String localPath) { playVoice(localPath); HTMessageVoiceBody htMessageVoiceBody = (HTMessageVoiceBody) htMessage.getBody(); //JSONObject jsonObject=htMessage.getBodyJSON(); // jsonObject.put(MessageUtils.LOCAL_PATH,localPath); htMessageVoiceBody.setLocalPath(localPath); htMessage.setBody(htMessageVoiceBody); HTClient.getInstance().messageManager().updateSuccess(htMessage); } }); // // final JSONObject jsonObject = htMessage.getBodyJSON(); // if (!jsonObject.containsKey(MessageUtils.REMOTE_PATH) || !jsonObject.containsKey(MessageUtils.FILE_NAME)) { // // return; // } // final ProgressDialog progressDialog = new ProgressDialog(activity); // progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); // progressDialog.setCanceledOnTouchOutside(false); // progressDialog.setMessage("正在加载"); // progressDialog.show(); // HTPathUtils pathUtils = new HTPathUtils(chatTo, activity); // String remotePath = jsonObject.getString(MessageUtils.REMOTE_PATH); // String fileName = jsonObject.getString(MessageUtils.FILE_NAME); // final String filePath = pathUtils.getImagePath().getAbsolutePath() + fileName; // Log.d("filePath22--->", filePath); // FileDownloader.getImpl().create(remotePath) // .setPath(filePath) // .setListener(new FileDownloadListener() { // @Override // protected void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) { // } // // @Override // protected void connected(BaseDownloadTask task, String etag, boolean isContinue, int soFarBytes, int totalBytes) { // } // // @Override // protected void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) { // } // // @Override // protected void blockComplete(BaseDownloadTask task) { // } // // @Override // protected void retry(final BaseDownloadTask task, final Throwable ex, final int retryingTimes, final int soFarBytes) { // } // // @Override // protected void completed(BaseDownloadTask task) { // //下载完成后,更新本地的图片cache,已经消息体的localPath // // jsonObject.put(MessageUtils.IMAGE_LOCAL_PATH,filePath); // ((Activity) context).runOnUiThread(new Runnable() { // @Override // public void run() { // progressDialog.dismiss(); // // } // }); // if (new File(filePath).exists()) { // final Bitmap bitmap = com.fanxin.tigase.utils.ImageUtils.decodeScaleImage(filePath, 240, 240); // ACache.get(context).put(htMessage.getMsgId(), bitmap); // jsonObject.put(MessageUtils.LOCAL_PATH, filePath); // htMessage.setBody(jsonObject.toJSONString()); // ((Activity) context).runOnUiThread(new Runnable() { // @Override // public void run() { // notifyDataSetChanged(); // } // }); // MessageManager.getInstance().saveMessage(chatTo, htMessage, false); // context.startActivity(new Intent(context, ShowBigImageActivity.class).putExtra("localPath",filePath)); // } // // // // } // // @Override // protected void paused(BaseDownloadTask task, int soFarBytes, int totalBytes) { // } // // @Override // protected void error(BaseDownloadTask task, Throwable e) { // ((Activity) context).runOnUiThread(new Runnable() { // @Override // public void run() { // progressDialog.dismiss(); // } // }); // } // // @Override // protected void warn(BaseDownloadTask task) { // } // }).start(); // LinearLayout linearLayout; // linearLayout.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { // @Override // public void onViewAttachedToWindow(View v) { // // } // // @Override // public void onViewDetachedFromWindow(View v) { // // } // }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/VoiceRecorder.java ================================================ package com.htmessage.yichatopen.activity.chat.weight; import android.content.Context; import android.media.MediaRecorder; import android.os.Handler; import android.os.SystemClock; import android.text.format.Time; import android.util.Log; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.utils.PathUtils; import java.io.File; import java.io.IOException; import java.util.Date; public class VoiceRecorder { MediaRecorder recorder; static final String PREFIX = "voice"; static final String EXTENSION = ".amr"; private boolean isRecording = false; private long startTime; private String voiceFilePath = null; private String voiceFileName = null; private File file; private Handler handler; public VoiceRecorder(Handler handler) { this.handler = handler; } /** * start recording to the file */ public String startRecording(Context appContext) { file = null; try { // need to create recorder every time, otherwise, will got exception // from setOutputFile when try to reuse if (recorder != null) { recorder.release(); recorder = null; } recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); recorder.setAudioChannels(1); // MONO recorder.setAudioSamplingRate(8000); // 8000Hz recorder.setAudioEncodingBitRate(64); // seems if change this to // 128, still got same file // size. // one easy way is to use temp file // file = File.createTempFile(PREFIX + userId, EXTENSION, // User.getVoicePath()); voiceFileName = getVoiceFileName("temp"); ; voiceFilePath = new PathUtils(null, HTApp.getInstance().getApplicationContext()).getVoicePath() + voiceFileName; Log.d("voiceFilePath--->",voiceFilePath); file = new File(voiceFilePath); recorder.setOutputFile(file.getAbsolutePath()); recorder.prepare(); isRecording = true; recorder.start(); } catch (IOException e) { // EMLog.e("voice", "prepare() failed"); } new Thread(new Runnable() { @Override public void run() { try { while (isRecording) { android.os.Message msg = new android.os.Message(); msg.what = recorder.getMaxAmplitude() * 13 / 0x7FFF; handler.sendMessage(msg); SystemClock.sleep(100); } } catch (Exception e) { // from the crash report website, found one NPE crash from // one android 4.0.4 htc phone // maybe handler is null for some reason } } }).start(); startTime = new Date().getTime(); return file == null ? null : file.getAbsolutePath(); } /** * stop the recoding * * @return seconds of the voice recorded */ public void discardRecording() { if (recorder != null) { try { recorder.stop(); recorder.release(); recorder = null; if (file != null && file.exists() && !file.isDirectory()) { file.delete(); } } catch (IllegalStateException e) { } catch (RuntimeException e){} isRecording = false; } } public int stopRecoding() { if(recorder != null){ isRecording = false; recorder.stop(); recorder.release(); recorder = null; if(file == null || !file.exists() || !file.isFile()){ return 401; } if (file.length() == 0) { file.delete(); return 401; } int seconds = (int) (new Date().getTime() - startTime) / 1000; // EMLog.d("voice", "voice recording finished. seconds:" + seconds + " file length:" + file.length()); return seconds; } return 0; } protected void finalize() throws Throwable { super.finalize(); if (recorder != null) { recorder.release(); } } private String getVoiceFileName(String uid) { Time now = new Time(); now.setToNow(); return uid + now.toString().substring(0, 15) + EXTENSION; } public boolean isRecording() { return isRecording; } public String getVoiceFilePath() { return voiceFilePath; } public String getVoiceFileName() { return voiceFileName; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/VoiceRecorderView.java ================================================ package com.htmessage.yichatopen.activity.chat.weight; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.CountDownTimer; import android.os.Handler; import android.os.PowerManager; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.utils.CommonUtils; /** * Voice recorder view */ public class VoiceRecorderView extends RelativeLayout { protected Context context; protected LayoutInflater inflater; protected Drawable[] micImages; protected VoiceRecorder voiceRecorder; protected PowerManager.WakeLock wakeLock; protected ImageView micImage; protected TextView recordingHint; private int duration = 60; protected Handler micImageHandler = new Handler() { @Override public void handleMessage(android.os.Message msg) { // change image micImage.setImageDrawable(micImages[msg.what]); } }; public VoiceRecorderView(Context context) { super(context); init(context); } public VoiceRecorderView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public VoiceRecorderView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { this.context = context; LayoutInflater.from(context).inflate(R.layout.widget_voice_recorder, this); micImage = (ImageView) findViewById(R.id.mic_image); recordingHint = (TextView) findViewById(R.id.recording_hint); voiceRecorder = new VoiceRecorder(micImageHandler); // animation resources, used for recording micImages = new Drawable[]{getResources().getDrawable(R.drawable.record_animate_01), getResources().getDrawable(R.drawable.record_animate_02), getResources().getDrawable(R.drawable.record_animate_03), getResources().getDrawable(R.drawable.record_animate_04), getResources().getDrawable(R.drawable.record_animate_05), getResources().getDrawable(R.drawable.record_animate_06), getResources().getDrawable(R.drawable.record_animate_07), getResources().getDrawable(R.drawable.record_animate_08), getResources().getDrawable(R.drawable.record_animate_09), getResources().getDrawable(R.drawable.record_animate_10), getResources().getDrawable(R.drawable.record_animate_11), getResources().getDrawable(R.drawable.record_animate_12), getResources().getDrawable(R.drawable.record_animate_13), getResources().getDrawable(R.drawable.record_animate_14),}; wakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( PowerManager.SCREEN_DIM_WAKE_LOCK, "demo"); } /** * on speak button touched * * @param v * @param event */ MyCountDownTimer myCountDownTimer; public boolean onPressToSpeakBtnTouch(View v, MotionEvent event, EaseVoiceRecorderCallback recorderCallback) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: duration = 60; try { if (VoicePlayClickListener.isPlaying) VoicePlayClickListener.currentPlayListener.stopPlayVoice(); v.setPressed(true); startRecording(); } catch (Exception e) { v.setPressed(false); } myCountDownTimer = new MyCountDownTimer(60000, 1000, v, recorderCallback, event); myCountDownTimer.start(); return true; case MotionEvent.ACTION_MOVE: if (duration > 10) { if (event.getY() < 0) { showReleaseToCancelHint(); } else { showMoveUpToCancelHint(); } } return true; case MotionEvent.ACTION_UP: if (myCountDownTimer != null) { myCountDownTimer.onFinish(); myCountDownTimer.cancel(); myCountDownTimer = null; return true; } v.setPressed(false); if (event.getY() < 0) { discardRecording(); } else { try { int length = stopRecoding(); if (length > 0) { if (recorderCallback != null) { recorderCallback.onVoiceRecordComplete(getVoiceFilePath(), length); } } else if (length == 401) { Toast.makeText(context, R.string.Recording_without_permission, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, R.string.The_recording_time_is_too_short, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); Toast.makeText(context, R.string.send_failure_please, Toast.LENGTH_SHORT).show(); } } return true; default: discardRecording(); return false; } } /** * 继承 CountDownTimer 防范 *

* 重写 父类的方法 onTick() 、 onFinish() */ public class MyCountDownTimer extends CountDownTimer { private EaseVoiceRecorderCallback recorderCallback; private View view; private MotionEvent event; public MyCountDownTimer(long millisInFuture, long countDownInterval, View view, EaseVoiceRecorderCallback recorderCallback, MotionEvent event) { super(millisInFuture, countDownInterval); this.recorderCallback = recorderCallback; this.view = view; this.event = event; } @Override public void onFinish() { duration = 60; Log.d("onFinish--->", "onFinish"); view.setPressed(false); if (event.getY() < 0) { // discard the recorded audio. discardRecording(); } else { // stop recording and send voice file try { int length = stopRecoding(); if (length > 0) { if (recorderCallback != null) { recorderCallback.onVoiceRecordComplete(getVoiceFilePath(), length); } } else if (length == 401) { Toast.makeText(context, R.string.Recording_without_permission, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, R.string.The_recording_time_is_too_short, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); Toast.makeText(context, R.string.send_failure_please, Toast.LENGTH_SHORT).show(); } } } @Override public void onTick(long millisUntilFinished) { // Log.d("millisUntilFinished--->",millisUntilFinished+""); if (millisUntilFinished / 1000 < 11) { duration = (int) (millisUntilFinished / 1000); recordingHint.setText("还可以录音" + millisUntilFinished / 1000 + "秒"); } } } public interface EaseVoiceRecorderCallback { /** * on voice record complete * * @param voiceFilePath 录音完毕后的文件路径 * @param voiceTimeLength 录音时长 */ void onVoiceRecordComplete(String voiceFilePath, int voiceTimeLength); } public void startRecording() { if (!CommonUtils.isSdcardExist()) { Toast.makeText(context, R.string.Send_voice_need_sdcard_support, Toast.LENGTH_SHORT).show(); return; } try { wakeLock.acquire(); this.setVisibility(View.VISIBLE); recordingHint.setText(context.getString(R.string.move_up_to_cancel)); recordingHint.setBackgroundColor(Color.TRANSPARENT); voiceRecorder.startRecording(context); } catch (Exception e) { e.printStackTrace(); if (wakeLock.isHeld()) wakeLock.release(); if (voiceRecorder != null) voiceRecorder.discardRecording(); this.setVisibility(View.INVISIBLE); Toast.makeText(context, R.string.recoding_fail, Toast.LENGTH_SHORT).show(); return; } } public void showReleaseToCancelHint() { recordingHint.setText(context.getString(R.string.release_to_cancel)); recordingHint.setBackgroundResource(R.drawable.recording_text_hint_bg); } public void showMoveUpToCancelHint() { recordingHint.setText(context.getString(R.string.move_up_to_cancel)); recordingHint.setBackgroundColor(Color.TRANSPARENT); } public void discardRecording() { if (wakeLock.isHeld()) wakeLock.release(); try { // stop recording if (voiceRecorder.isRecording()) { voiceRecorder.discardRecording(); this.setVisibility(View.INVISIBLE); } } catch (Exception e) { } } public int stopRecoding() { this.setVisibility(View.INVISIBLE); if (wakeLock.isHeld()) wakeLock.release(); return voiceRecorder.stopRecoding(); } public String getVoiceFilePath() { return voiceRecorder.getVoiceFilePath(); } public String getVoiceFileName() { return voiceRecorder.getVoiceFileName(); } public boolean isRecording() { return voiceRecorder.isRecording(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/emoji/DefaultEmojiconDatas.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.emoji; import com.htmessage.yichatopen.R; public class DefaultEmojiconDatas { private static String[] emojis = new String[]{ SmileUtils.ee_1, SmileUtils.ee_2, SmileUtils.ee_3, SmileUtils.ee_4, SmileUtils.ee_5, SmileUtils.ee_6, SmileUtils.ee_7, SmileUtils.ee_8, SmileUtils.ee_9, SmileUtils.ee_10, SmileUtils.ee_11, SmileUtils.ee_12, SmileUtils.ee_13, SmileUtils.ee_14, SmileUtils.ee_15, SmileUtils.ee_16, SmileUtils.ee_17, SmileUtils.ee_18, SmileUtils.ee_19, SmileUtils.ee_20, SmileUtils.ee_21, SmileUtils.ee_22, SmileUtils.ee_23, SmileUtils.ee_24, SmileUtils.ee_25, SmileUtils.ee_26, SmileUtils.ee_27, SmileUtils.ee_28, SmileUtils.ee_29, SmileUtils.ee_30, SmileUtils.ee_31, SmileUtils.ee_32, SmileUtils.ee_33, SmileUtils.ee_34, SmileUtils.ee_35, }; private static int[] icons = new int[]{ R.drawable.ee_0, R.drawable.ee_1, R.drawable.ee_2, R.drawable.ee_3, R.drawable.ee_4, R.drawable.ee_5, R.drawable.ee_6, R.drawable.ee_7, R.drawable.ee_8, R.drawable.ee_9, R.drawable.ee_10, R.drawable.ee_11, R.drawable.ee_12, R.drawable.ee_13, R.drawable.ee_14, R.drawable.ee_15, R.drawable.ee_16, R.drawable.ee_17, R.drawable.ee_18, R.drawable.ee_19, R.drawable.ee_20, R.drawable.ee_21, R.drawable.ee_22, R.drawable.ee_23, R.drawable.ee_24, R.drawable.ee_25, R.drawable.ee_26, R.drawable.ee_27, R.drawable.ee_28, R.drawable.ee_29, R.drawable.ee_30, R.drawable.ee_31, R.drawable.ee_32, R.drawable.ee_33, R.drawable.ee_34, }; private static final Emojicon[] DATA = createData(); private static Emojicon[] createData(){ Emojicon[] datas = new Emojicon[icons.length]; for(int i = 0; i < icons.length; i++){ datas[i] = new Emojicon(icons[i], emojis[i], Emojicon.Type.NORMAL); } return datas; } public static Emojicon[] getData(){ return DATA; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/emoji/EmojiFragment.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.emoji; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.GridView; import android.widget.ImageButton; import com.htmessage.yichatopen.R; import java.util.ArrayList; import java.util.List; /** * Created by huangfangyi on 2017/7/5. * qq 84543217 */ public class EmojiFragment extends Fragment { private ViewPager viewPager; private TabLayout tableLayout; private List views; private List emojicons; private int emojiconColumns = 7; private int emojiconRows = 3; private OnEmojiListener onEmojiListener; public EmojiFragment(List emojicons, int emojiconColumns, int emojiconRows, OnEmojiListener onEmojiListener) { this.emojicons = emojicons; this.emojiconColumns = emojiconColumns; this.emojiconRows = emojiconRows; this.onEmojiListener=onEmojiListener; } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_emoji, container,false); viewPager = (ViewPager) root.findViewById(R.id.viewpager_emoji); tableLayout = (TabLayout) root.findViewById(R.id.tabLayout_dot); return root; } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final List views= getViews(); viewPager.setAdapter(new PagerAdapter() { @Override public int getCount() { return views.size(); } @Override public boolean isViewFromObject(View arg0, Object arg1) { return arg0 == arg1; } @Override public Object instantiateItem(ViewGroup arg0, int arg1) { ((ViewPager) arg0).addView(views.get(arg1)); return views.get(arg1); } @Override public void destroyItem(ViewGroup arg0, int arg1, Object arg2) { ((ViewPager) arg0).removeView(views.get(arg1)); } }); tableLayout.setupWithViewPager(viewPager); TabLayout.Tab[] tabs = new TabLayout.Tab[views.size()]; for (int i = 0; i < views.size(); i++) { tabs[i] = tableLayout.getTabAt(i); ImageButton imageView=new ImageButton(getContext()); imageView.setBackground(null); imageView.setImageResource(R.drawable.dot_emoji); tabs[i].setCustomView(imageView); } } private List getViews() { int itemSize = emojiconColumns * emojiconRows -1; int totalSize = emojicons.size(); Emojicon.Type emojiType = Emojicon.Type.NORMAL ; if(totalSize!=0){ emojiType=emojicons.get(0).getType(); } if(emojiType == Emojicon.Type.BIG_EXPRESSION){ itemSize = emojiconColumns * emojiconRows; } int pageSize = totalSize % itemSize == 0 ? totalSize/itemSize : totalSize/itemSize + 1; List views = new ArrayList(); for(int i = 0; i < pageSize; i++){ View view = View.inflate(getContext(), R.layout.emoji_gridview, null); GridView gv = (GridView) view.findViewById(R.id.gridview); gv.setNumColumns(emojiconColumns); List list = new ArrayList(); if(i != pageSize -1){ list.addAll(emojicons.subList(i * itemSize, (i+1) * itemSize)); }else{ list.addAll(emojicons.subList(i * itemSize, totalSize)); } if(emojiType != Emojicon.Type.BIG_EXPRESSION){ Emojicon deleteIcon = new Emojicon(); deleteIcon.setEmojiText(SmileUtils.DELETE_KEY); list.add(deleteIcon); } final EmojiconGridAdapter gridAdapter = new EmojiconGridAdapter(getContext(), 1, list, emojiType); gv.setAdapter(gridAdapter); gv.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { Emojicon emojicon = gridAdapter.getItem(position); if(onEmojiListener != null){ String emojiText = emojicon.getEmojiText(); if(emojiText != null && emojiText.equals(SmileUtils.DELETE_KEY)){ onEmojiListener.onDeleteImageClicked(); }else{ onEmojiListener.onExpressionClicked(emojicon); } } } }); views.add(view); } return views; } public interface OnEmojiListener{ void onDeleteImageClicked(); void onExpressionClicked(Emojicon emojicon); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/emoji/Emojicon.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.emoji; public class Emojicon { public Emojicon(){ } /** * constructor * @param icon- resource id of the icon * @param emojiText- text of emoji icon */ public Emojicon(int icon, String emojiText){ this.icon = icon; this.emojiText = emojiText; this.type = Type.NORMAL; } /** * constructor * @param icon - resource id of the icon * @param emojiText - text of emoji icon * @param type - normal or big */ public Emojicon(int icon, String emojiText, Type type){ this.icon = icon; this.emojiText = emojiText; this.type = type; } /** * identity code */ private String identityCode; /** * static icon resource id */ private int icon; /** * dynamic icon resource id */ private int bigIcon; /** * text of emoji, could be null for big icon */ private String emojiText; /** * name of emoji icon */ private String name; /** * normal or big */ private Type type; /** * path of icon */ private String iconPath; /** * path of big icon */ private String bigIconPath; /** * get the resource id of the icon * @return */ public int getIcon() { return icon; } /** * set the resource id of the icon * @param icon */ public void setIcon(int icon) { this.icon = icon; } /** * get the resource id of the big icon * @return */ public int getBigIcon() { return bigIcon; } /** * set the resource id of the big icon * @return */ public void setBigIcon(int dynamicIcon) { this.bigIcon = dynamicIcon; } /** * get text of emoji icon * @return */ public String getEmojiText() { return emojiText; } /** * set text of emoji icon * @param emojiText */ public void setEmojiText(String emojiText) { this.emojiText = emojiText; } /** * get name of emoji icon * @return */ public String getName() { return name; } /** * set name of emoji icon * @param name */ public void setName(String name) { this.name = name; } /** * get type * @return */ public Type getType() { return type; } /** * set type * @param type */ public void setType(Type type) { this.type = type; } /** * get icon path * @return */ public String getIconPath() { return iconPath; } /** * set icon path * @param iconPath */ public void setIconPath(String iconPath) { this.iconPath = iconPath; } /** * get path of big icon * @return */ public String getBigIconPath() { return bigIconPath; } /** * set path of big icon * @param bigIconPath */ public void setBigIconPath(String bigIconPath) { this.bigIconPath = bigIconPath; } /** * get identity code * @return */ public String getIdentityCode() { return identityCode; } /** * set identity code * @param */ public void setIdentityCode(String identityCode) { this.identityCode = identityCode; } public static final String newEmojiText(int codePoint) { if (Character.charCount(codePoint) == 1) { return String.valueOf(codePoint); } else { return new String(Character.toChars(codePoint)); } } public enum Type{ /** * normal icon, can be input one or more in edit view */ NORMAL, /** * big icon, send out directly when your press it */ BIG_EXPRESSION } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/emoji/EmojiconGridAdapter.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.emoji; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.htmessage.yichatopen.R; import java.util.List; public class EmojiconGridAdapter extends ArrayAdapter{ private Emojicon.Type emojiconType; public EmojiconGridAdapter(Context context, int textViewResourceId, List objects, Emojicon.Type emojiconType) { super(context, textViewResourceId, objects); this.emojiconType = emojiconType; } @Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView == null){ if(emojiconType == Emojicon.Type.BIG_EXPRESSION){ convertView = View.inflate(getContext(), R.layout.row_big_expression, null); }else{ convertView = View.inflate(getContext(), R.layout.row_expression, null); } } ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_expression); Emojicon emojicon = getItem(position); //if you want show a name for the icons, you can set text to R.id.tv_name if(SmileUtils.DELETE_KEY.equals(emojicon.getEmojiText())){ imageView.setImageResource(R.drawable.delete_expression); }else{ if(emojicon.getIcon() != 0){ imageView.setImageResource(emojicon.getIcon()); }else if(emojicon.getIconPath() != null){ Glide.with(getContext()).load(emojicon.getIconPath()).placeholder(R.drawable.default_expression).into(imageView); } } return convertView; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/emoji/SmileUtils.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.activity.chat.weight.emoji; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.graphics.drawable.Drawable; import android.net.Uri; import android.text.Spannable; import android.text.Spannable.Factory; import android.text.style.ImageSpan; import com.htmessage.yichatopen.R; public class SmileUtils { public static final String DELETE_KEY = "em_delete_delete_expression"; public static final String ee_1 = "[):]"; public static final String ee_2 = "[:D]"; public static final String ee_3 = "[;)]"; public static final String ee_4 = "[:-o]"; public static final String ee_5 = "[:p]"; public static final String ee_6 = "[(H)]"; public static final String ee_7 = "[:@]"; public static final String ee_8 = "[:s]"; public static final String ee_9 = "[:$]"; public static final String ee_10 = "[:(]"; public static final String ee_11 = "[:-(]"; public static final String ee_12 = "[:|]"; public static final String ee_13 = "[(a)]"; public static final String ee_14 = "[8o|]"; public static final String ee_15 = "[8-|]"; public static final String ee_16 = "[+o(]"; public static final String ee_17 = "[ emoticons = new HashMap(); static { Emojicon[] emojicons = DefaultEmojiconDatas.getData(); for(int i = 0; i < emojicons.length; i++){ addPattern(emojicons[i].getEmojiText(), emojicons[i].getIcon()); } } /** * add text and icon to the map * @param emojiText-- text of emoji * @param icon -- resource id or local path */ public static void addPattern(String emojiText, Object icon){ emoticons.put(Pattern.compile(Pattern.quote(emojiText)), icon); } /** * replace existing spannable with smiles * @param context * @param spannable * @return */ public static boolean addSmiles(Context context, Spannable spannable) { boolean hasChanges = false; for (Entry entry : emoticons.entrySet()) { Matcher matcher = entry.getKey().matcher(spannable); while (matcher.find()) { boolean set = true; for (ImageSpan span : spannable.getSpans(matcher.start(), matcher.end(), ImageSpan.class)) if (spannable.getSpanStart(span) >= matcher.start() && spannable.getSpanEnd(span) <= matcher.end()) spannable.removeSpan(span); else { set = false; break; } if (set) { hasChanges = true; Object value = entry.getValue(); if(value instanceof String && !((String) value).startsWith("http")){ File file = new File((String) value); if(!file.exists() || file.isDirectory()){ return false; } ImageSpan imageSpan=new ImageSpan(context, Uri.fromFile(file)); Drawable drawable=imageSpan.getDrawable(); drawable.setBounds(0,0,25,25); imageSpan=new ImageSpan(drawable); spannable.setSpan(imageSpan, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); }else{ ImageSpan imageSpan=new ImageSpan(context, (Integer)value); Drawable drawable=imageSpan.getDrawable(); int size=context.getResources().getDimensionPixelSize(R.dimen.emoji_size); drawable.setBounds(0,0,size ,size); imageSpan=new ImageSpan(drawable); spannable.setSpan(imageSpan, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(imageSpan, matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } } } return hasChanges; } public static Spannable getSmiledText(Context context, CharSequence text) { Spannable spannable = spannableFactory.newSpannable(text); addSmiles(context, spannable); return spannable; } public static boolean containsKey(String key){ boolean b = false; for (Entry entry : emoticons.entrySet()) { Matcher matcher = entry.getKey().matcher(key); if (matcher.find()) { b = true; break; } } return b; } public static int getSmilesSize(){ return emoticons.size(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/loadmore/DensityUtil.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.loadmore; import android.app.Activity; import android.content.Context; import android.graphics.Rect; import android.os.Handler; import android.text.TextPaint; import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.ViewTreeObserver; import android.view.WindowManager; import android.widget.ScrollView; import android.widget.TextView; import java.lang.reflect.Field; public class DensityUtil { public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } public static float getScreenDensity(Context context) { return context.getResources().getDisplayMetrics().density; } public static int getStatusBarHeight(Context ctx) { Class c = null; Object obj = null; Field field = null; int x = 0, sbar = 0; try { c = Class.forName("com.android.internal.R$dimen"); obj = c.newInstance(); field = c.getField("status_bar_height"); x = Integer.parseInt(field.get(obj).toString()); sbar = ctx.getResources().getDimensionPixelSize(x); } catch (Exception e1) { e1.printStackTrace(); } return sbar; } public static int getScreenHeightWithoutTitlebar(Context ctx) { int[] screenWidthAndHeight = getScreenWidthAndHeight(ctx); return screenWidthAndHeight[1] -getStatusBarHeight(ctx)- dip2px(ctx, 48); } public static int[] getScreenWidthAndHeight(Context ctx) { WindowManager mWm = (WindowManager) ctx .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics dm = new DisplayMetrics(); // 获取屏幕信息 mWm.getDefaultDisplay().getMetrics(dm); int screenWidth = dm.widthPixels; int screenHeigh = dm.heightPixels; return new int[] { screenWidth, screenHeigh }; } public static void addOnSoftKeyBoardVisibleListener(final Activity activity, final ScrollView scrollView) { final View decorView = activity.getWindow().getDecorView(); decorView.getViewTreeObserver().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { Rect rect = new Rect(); decorView.getWindowVisibleDisplayFrame(rect); int displayHight = rect.bottom - rect.top; int hight = decorView.getHeight(); boolean visible = (double) displayHight / hight < 0.8;// 决断键盘是弹�? System.out.println("===监听" + visible); if (visible) { Handler mHandler = new Handler(); mHandler.postDelayed(new Runnable() { @Override public void run() { // scrollView // .fullScroll(ScrollView.FOCUS_DOWN);// ScrollView滚动到底 scrollView.scrollTo(0, DensityUtil.dip2px(activity, 167)); } }, 50); } } }); } public static String getEllipsisedText(TextView textView) { try { String text = textView.getText().toString(); int lines = textView.getLineCount(); int width = textView.getWidth(); int len = text.length(); Log.d("Test", "text-->" + text + "; lines-->" + lines + "; width-->" + width + ";len-->" + len); TextUtils.TruncateAt where = TextUtils.TruncateAt.END; TextPaint paint = textView.getPaint(); StringBuffer result = new StringBuffer(); int spos = 0, cnt, tmp, hasLines = 0; while(hasLines < lines - 1) { cnt = paint.breakText(text, spos, len, true, width, null); if(cnt >= len - spos) { result.append(text.substring(spos)); break; } tmp = text.lastIndexOf('\n', spos + cnt - 1); if(tmp >= 0 && tmp < spos + cnt) { result.append(text.substring(spos, tmp + 1)); spos += tmp + 1; } else { tmp = text.lastIndexOf(' ', spos + cnt - 1); if(tmp >= spos) { result.append(text.substring(spos, tmp + 1)); spos += tmp + 1; } else { result.append(text.substring(spos, cnt)); spos += cnt; } } hasLines++; } if(spos < len) { result.append(TextUtils.ellipsize(text.subSequence(spos, len), paint, (float) width, where)); } return result.toString(); } catch (Exception e) { e.printStackTrace(); } return null; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/loadmore/Pacman.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.loadmore; import android.animation.Animator; import android.animation.ValueAnimator; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; import android.view.animation.LinearInterpolator; import java.util.ArrayList; import java.util.List; /** * Created by caizhiming on 2016/2/5. */ public class Pacman extends ProgressViewController { private float translateX; private int alpha; private float degrees1,degrees2; @Override public void draw(Canvas canvas, Paint paint) { drawPacman(canvas,paint); drawCircle(canvas,paint); } private void drawPacman(Canvas canvas, Paint paint){ float x=getWidth()/2; float y=getHeight()/2; canvas.save(); canvas.translate(x, y); canvas.rotate(degrees1); paint.setAlpha(255); RectF rectF1=new RectF(-x/1.7f,-y/1.7f,x/1.7f,y/1.7f); canvas.drawArc(rectF1, 0, 270, true, paint); canvas.restore(); canvas.save(); canvas.translate(x, y); canvas.rotate(degrees2); paint.setAlpha(255); RectF rectF2=new RectF(-x/1.7f,-y/1.7f,x/1.7f,y/1.7f); canvas.drawArc(rectF2,90,270,true,paint); canvas.restore(); } private void drawCircle(Canvas canvas, Paint paint) { float radius=getWidth()/11; paint.setAlpha(alpha); canvas.drawCircle(translateX, getHeight() / 2, radius, paint); } @Override public List createAnimation() { List animators=new ArrayList<>(); float startT=getWidth()/11; ValueAnimator translationAnim= ValueAnimator.ofFloat(getWidth()-startT,getWidth()/2); translationAnim.setDuration(650); translationAnim.setInterpolator(new LinearInterpolator()); translationAnim.setRepeatCount(-1); translationAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { translateX = (float) animation.getAnimatedValue(); postInvalidate(); } }); translationAnim.start(); ValueAnimator alphaAnim= ValueAnimator.ofInt(255,122); alphaAnim.setDuration(650); alphaAnim.setRepeatCount(-1); alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { alpha = (int) animation.getAnimatedValue(); postInvalidate(); } }); alphaAnim.start(); ValueAnimator rotateAnim1= ValueAnimator.ofFloat(0, 45, 0); rotateAnim1.setDuration(650); rotateAnim1.setRepeatCount(-1); rotateAnim1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { degrees1 = (float) animation.getAnimatedValue(); postInvalidate(); } }); rotateAnim1.start(); ValueAnimator rotateAnim2= ValueAnimator.ofFloat(0,-45,0); rotateAnim2.setDuration(650); rotateAnim2.setRepeatCount(-1); rotateAnim2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { degrees2 = (float) animation.getAnimatedValue(); postInvalidate(); } }); rotateAnim2.start(); animators.add(translationAnim); animators.add(alphaAnim); animators.add(rotateAnim1); animators.add(rotateAnim2); return animators; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/loadmore/ProgressView.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.loadmore; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.Build; import android.util.AttributeSet; import android.view.View; /** * Created by caizhiming on 2016/2/5. */ public class ProgressView extends View { public static final int DEFAULT_SIZE=25;//dp int mStyleColor; Paint mPaint; ProgressViewController mIndicatorController; private boolean mHasAnimation; public ProgressView(Context context) { super(context); init(null, 0); } public ProgressView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public ProgressView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public ProgressView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(attrs, defStyleAttr); } private void init(AttributeSet attrs, int defStyle) { mStyleColor = Color.WHITE; mPaint=new Paint(); mPaint.setColor(mStyleColor); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); applyIndicator(); } public void setStyleColor(int color){ mStyleColor = color; mPaint.setColor(mStyleColor); this.invalidate(); } private void applyIndicator(){ mIndicatorController=new Pacman(); mIndicatorController.setTarget(this); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = measureDimension(dp2px(DEFAULT_SIZE), widthMeasureSpec); int height = measureDimension(dp2px(DEFAULT_SIZE), heightMeasureSpec); setMeasuredDimension(width, height); } private int measureDimension(int defaultSize,int measureSpec){ int result = defaultSize; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else if (specMode == MeasureSpec.AT_MOST) { result = Math.min(defaultSize, specSize); } else { result = defaultSize; } return result; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawIndicator(canvas); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (!mHasAnimation){ mHasAnimation=true; applyAnimation(); } } @Override public void setVisibility(int v) { if (getVisibility() != v) { super.setVisibility(v); if (v == GONE || v == INVISIBLE) { mIndicatorController.setAnimationStatus(ProgressViewController.AnimStatus.END); } else { mIndicatorController.setAnimationStatus(ProgressViewController.AnimStatus.START); } } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mIndicatorController.setAnimationStatus(ProgressViewController.AnimStatus.CANCEL); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mIndicatorController.setAnimationStatus(ProgressViewController.AnimStatus.START); } void drawIndicator(Canvas canvas){ mIndicatorController.draw(canvas, mPaint); } void applyAnimation(){ mIndicatorController.initAnimation(); } private int dp2px(int dpValue) { return (int) getContext().getResources().getDisplayMetrics().density * dpValue; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/loadmore/ProgressViewController.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.loadmore; import android.animation.Animator; import android.graphics.Canvas; import android.graphics.Paint; import android.view.View; import java.util.List; /** * Created by caizhiming on 2016/2/5. */ public abstract class ProgressViewController { private View mTarget; private List mAnimators; public void setTarget(View target){ this.mTarget=target; } public View getTarget(){ return mTarget; } public int getWidth(){ return mTarget.getWidth(); } public int getHeight(){ return mTarget.getHeight(); } public void postInvalidate(){ mTarget.postInvalidate(); } /** * draw indicator * @param canvas * @param paint */ public abstract void draw(Canvas canvas, Paint paint); /** * create animation or animations */ public abstract List createAnimation(); public void initAnimation(){ mAnimators=createAnimation(); } /** * make animation to start or end when target * view was be Visible or Gone or Invisible. * make animation to cancel when target view * be onDetachedFromWindow. * @param animStatus */ public void setAnimationStatus(AnimStatus animStatus){ if (mAnimators==null){ return; } int count=mAnimators.size(); for (int i = 0; i < count; i++) { Animator animator=mAnimators.get(i); boolean isRunning=animator.isRunning(); switch (animStatus){ case START: if (!isRunning){ animator.start(); } break; case END: if (isRunning){ animator.end(); } break; case CANCEL: if (isRunning){ animator.cancel(); } break; } } } public enum AnimStatus{ START,END,CANCEL } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/loadmore/PullToLoadMoreListView.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.loadmore; import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.ListView; /** * Created by caizhiming on 2016/2/4. * 下拉加载更多控件(仿QQ和微信的对话聊天记录页面) */ public class PullToLoadMoreListView extends FrameLayout { public static final String TAG = PullToLoadMoreListView.class.getSimpleName(); private RefreshHeader mRefreshHeader; private ListView mListView; private int mHeight; private DecelerateInterpolator mInterpolator = new DecelerateInterpolator(5); private int mHeaderHeight; private float mStartY; private float mCurY; private boolean mIsRefreshing; private int mLastCount = 0; private float mLastTranslationY = 0f; public static final int REFRESH_STATUS_PULL_REFRESH = 0;//查看更早记录...; public static final int REFRESH_STATUS_RELEASE_REFRESH = 1;//松开开始加载... public static final int REFRESH_STATUS_REFRESHING = 2;// "正在加载..."; public static final int REFRESH_STATUS_REFRESH_FINISH = 3;//"加载完成"; private int mRefreshStatus = REFRESH_STATUS_PULL_REFRESH; public PullToLoadMoreListView(Context context) { this(context, null, 0); } public PullToLoadMoreListView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public PullToLoadMoreListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } public ListView getListView(){ return mListView; } private void init(Context context, AttributeSet attrs, int defStyleAttr) { mHeaderHeight = DensityUtil.dip2px(getContext(), 50); addHeaderView(context); addListView(context, attrs); } private void addHeaderView(Context context){ mRefreshHeader = new RefreshHeader(context); mHeaderHeight = (int) mRefreshHeader.getHeaderHeight(); addView(mRefreshHeader, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, mHeaderHeight)); } private void addListView(Context context, AttributeSet attrs){ mListView = new ListView(context,attrs); addView(mListView, new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(mHeight <= 0){ mHeight = getMeasuredHeight(); Log.d("czm", "Height =" + mHeight); } } private boolean canChildScrollUp() { if (mListView == null) { return false; } return ViewCompat.canScrollVertically(mListView, -1); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mIsRefreshing) { return true; } switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mStartY = ev.getY(); mCurY = mStartY; break; case MotionEvent.ACTION_MOVE: float curY = ev.getY(); float dy = curY - mStartY; Log.d("czm", "dy=" + dy); if (dy > 0 && !canChildScrollUp()) { return true; } break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { if (mIsRefreshing) { return super.onTouchEvent(event); } switch (event.getAction()) { case MotionEvent.ACTION_MOVE: mCurY = event.getY(); float dy = mCurY - mStartY; Log.v("czm","dy====="+dy); if(dy <0) return true; dy = Math.max(0, dy); if (mListView != null) { Log.d("czm", "dy/mHeight=" + (dy / mHeight)); float offsetY = mInterpolator.getInterpolation(dy / mHeight) * dy/3; mListView.setTranslationY(offsetY); mRefreshHeader.getLayoutParams().height = (int) (offsetY+0.5f); mRefreshHeader.requestLayout(); if(mListView.getTranslationY() >= mHeaderHeight){ mRefreshStatus = REFRESH_STATUS_RELEASE_REFRESH; mRefreshHeader.updateRefreshStatus(mRefreshStatus); }else{ mRefreshStatus = REFRESH_STATUS_PULL_REFRESH; mRefreshHeader.updateRefreshStatus(mRefreshStatus); } } return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if(mListView != null){ if(mListView.getTranslationY() >= mHeaderHeight){ mRefreshStatus = REFRESH_STATUS_REFRESHING; mRefreshHeader.updateRefreshStatus(mRefreshStatus); mIsRefreshing = true; upToMiddleAnim(); }else{ upToTopAnim(); } } return true; default: return super.onTouchEvent(event); } } private void upToMiddleAnim(){ float offsetY = mListView.getTranslationY(); final ValueAnimator backToMiddleAnim = ValueAnimator.ofFloat(offsetY, 0); final float pullHeight = offsetY; backToMiddleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float val = (float) animation.getAnimatedValue(); val = mInterpolator.getInterpolation(val / pullHeight) * val; if (mListView != null) { mListView.setTranslationY(val); } mRefreshHeader.getLayoutParams().height = (int) (val + 0.5f); mRefreshHeader.requestLayout(); Log.d("czm", "offsetY=" + mListView.getTranslationY()); if (mListView.getTranslationY() <= mHeaderHeight) { backToMiddleAnim.cancel(); } } }); backToMiddleAnim.setDuration((long) (offsetY * 600 / pullHeight)); backToMiddleAnim.start(); backToMiddleAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { if (mOnRefreshListener != null) { mLastCount = mListView.getAdapter().getCount(); mOnRefreshListener.onPullDownLoadMore(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); } private void upToTopAnim(){ float offsetY = mListView.getTranslationY(); final ValueAnimator backToTopAnim = ValueAnimator.ofFloat(offsetY, 0); final float pullHeight = offsetY; backToTopAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float val = (float) animation.getAnimatedValue(); val = mInterpolator.getInterpolation(val / pullHeight) * val; if (mListView != null) { mListView.setTranslationY(val); } mRefreshHeader.getLayoutParams().height = (int) (val+0.5f); mRefreshHeader.requestLayout(); Log.d("czm", "offsetY=" + mListView.getTranslationY()); } }); backToTopAnim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { mRefreshStatus = REFRESH_STATUS_PULL_REFRESH; mRefreshHeader.updateRefreshStatus(mRefreshStatus); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); backToTopAnim.setDuration((long) (offsetY * 600 / pullHeight)); backToTopAnim.start(); } private void completeBackToTop(){ mListView.setTranslationY(0); int num = mListView.getAdapter().getCount() - mLastCount; Log.v("czm","num="+num); if(num > 0){ mListView.setSelectionFromTop(num, (int) mLastTranslationY); } } public void onRefreshComplete(){ mLastTranslationY = mListView.getTranslationY(); mIsRefreshing = false; mRefreshStatus = REFRESH_STATUS_REFRESH_FINISH; mRefreshHeader.updateRefreshStatus(mRefreshStatus); completeBackToTop(); } private OnRefreshListener mOnRefreshListener; public void setOnRefreshListener(OnRefreshListener listener){ mOnRefreshListener = listener; } public interface OnRefreshListener{ void onPullDownLoadMore(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/chat/weight/loadmore/RefreshHeader.java ================================================ package com.htmessage.yichatopen.activity.chat.weight.loadmore; import android.content.Context; import android.graphics.Color; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; import android.view.animation.Animation; import android.view.animation.RotateAnimation; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.htmessage.yichatopen.R; /** * Created by caizhiming on 2016/2/4. */ public class RefreshHeader extends LinearLayout { private TextView mStatusTextView; private ProgressView mProgressView; private ImageView mStatusImageView; private Animation mRotateUpAnim; private Animation mRotateDownAnim; private float mHeaderHeight = 0; private String[] mTxtStatus = new String[] { "查看更早记录...", "松开开始加载...", "正在加载...", "加载完成" }; private int mStatus = PullToLoadMoreListView.REFRESH_STATUS_PULL_REFRESH; private static final int ROTATE_ANIM_DURATION = 200; public RefreshHeader(Context context) { this(context, null); } public RefreshHeader(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RefreshHeader(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } public float getHeaderHeight() { return this.mHeaderHeight; } private void init(Context context, AttributeSet attrs, int defStyleAttr) { mHeaderHeight = DensityUtil.dip2px(context, 50); //初始化自己 LayoutParams lpRoot = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); this.setOrientation(LinearLayout.HORIZONTAL); this.setGravity(Gravity.CENTER_HORIZONTAL); this.setLayoutParams(lpRoot); this.setBackgroundColor(Color.parseColor("#458B00")); //add progress view mProgressView = new ProgressView(context); LayoutParams lp2 = new LayoutParams( DensityUtil.dip2px(context, 25), DensityUtil.dip2px(context, 25)); lp2.gravity = Gravity.BOTTOM; lp2.bottomMargin = (int) (mHeaderHeight / 7.5); this.addView(mProgressView, lp2); mProgressView.setVisibility(View.GONE); //add arrow ImageView mStatusImageView = new ImageView(context); mStatusImageView.setImageResource(R.drawable.arrow_down); LayoutParams lp = new LayoutParams( DensityUtil.dip2px(context, 20), DensityUtil.dip2px(context, 20)); lp.gravity = Gravity.BOTTOM; lp.bottomMargin = (int) (mHeaderHeight / 5); this.addView(mStatusImageView, lp); //add refresh text status TextView mStatusTextView = new TextView(context); mStatusTextView.setText(mTxtStatus[0]); mStatusTextView.setTextColor(Color.WHITE); lp = new LayoutParams( DensityUtil.dip2px(context, 100), LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.BOTTOM; lp.leftMargin = DensityUtil.dip2px(context, 5); lp.bottomMargin = (int) (mHeaderHeight / 10); mStatusTextView.setGravity(Gravity.CENTER); this.addView(mStatusTextView, lp); mRotateUpAnim = new RotateAnimation(0.0f, -180.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotateUpAnim.setDuration(ROTATE_ANIM_DURATION); mRotateUpAnim.setFillAfter(true); mRotateDownAnim = new RotateAnimation(-180.0f, 0.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); mRotateDownAnim.setDuration(ROTATE_ANIM_DURATION); mRotateDownAnim.setFillAfter(true); } public void updateRefreshStatus(int status) { mStatusTextView.setText(mTxtStatus[status]); if(status == PullToLoadMoreListView.REFRESH_STATUS_RELEASE_REFRESH && mStatus == PullToLoadMoreListView.REFRESH_STATUS_PULL_REFRESH){ mStatusImageView.clearAnimation(); mStatusImageView.startAnimation(mRotateUpAnim); } if(status == PullToLoadMoreListView.REFRESH_STATUS_PULL_REFRESH && mStatus == PullToLoadMoreListView.REFRESH_STATUS_RELEASE_REFRESH){ mStatusImageView.clearAnimation(); mStatusImageView.startAnimation(mRotateDownAnim); } if(status == PullToLoadMoreListView.REFRESH_STATUS_REFRESHING){ mStatusImageView.clearAnimation(); mStatusImageView.setVisibility(View.GONE); mProgressView.setVisibility(View.VISIBLE); } if(status == PullToLoadMoreListView.REFRESH_STATUS_REFRESH_FINISH){ mProgressView.setVisibility(View.GONE); } if(status == PullToLoadMoreListView.REFRESH_STATUS_PULL_REFRESH && mStatus == PullToLoadMoreListView.REFRESH_STATUS_REFRESH_FINISH){ mStatusImageView.setVisibility(View.VISIBLE); mStatusImageView.startAnimation(mRotateDownAnim); } mStatus = status; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/CharacterParserUtil.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015��3��18�� | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; public class CharacterParserUtil { private static int[] pyvalue = new int[]{-20319, -20317, -20304, -20295, -20292, -20283, -20265, -20257, -20242, -20230, -20051, -20036, -20032, -20026, -20002, -19990, -19986, -19982, -19976, -19805, -19784, -19775, -19774, -19763, -19756, -19751, -19746, -19741, -19739, -19728, -19725, -19715, -19540, -19531, -19525, -19515, -19500, -19484, -19479, -19467, -19289, -19288, -19281, -19275, -19270, -19263, -19261, -19249, -19243, -19242, -19238, -19235, -19227, -19224, -19218, -19212, -19038, -19023, -19018, -19006, -19003, -18996, -18977, -18961, -18952, -18783, -18774, -18773, -18763, -18756, -18741, -18735, -18731, -18722, -18710, -18697, -18696, -18526, -18518, -18501, -18490, -18478, -18463, -18448, -18447, -18446, -18239, -18237, -18231, -18220, -18211, -18201, -18184, -18183, -18181, -18012, -17997, -17988, -17970, -17964, -17961, -17950, -17947, -17931, -17928, -17922, -17759, -17752, -17733, -17730, -17721, -17703, -17701, -17697, -17692, -17683, -17676, -17496, -17487, -17482, -17468, -17454, -17433, -17427, -17417, -17202, -17185, -16983, -16970, -16942, -16915, -16733, -16708, -16706, -16689, -16664, -16657, -16647, -16474, -16470, -16465, -16459, -16452, -16448, -16433, -16429, -16427, -16423, -16419, -16412, -16407, -16403, -16401, -16393, -16220, -16216, -16212, -16205, -16202, -16187, -16180, -16171, -16169, -16158, -16155, -15959, -15958, -15944, -15933, -15920, -15915, -15903, -15889, -15878, -15707, -15701, -15681, -15667, -15661, -15659, -15652, -15640, -15631, -15625, -15454, -15448, -15436, -15435, -15419, -15416, -15408, -15394, -15385, -15377, -15375, -15369, -15363, -15362, -15183, -15180, -15165, -15158, -15153, -15150, -15149, -15144, -15143, -15141, -15140, -15139, -15128, -15121, -15119, -15117, -15110, -15109, -14941, -14937, -14933, -14930, -14929, -14928, -14926, -14922, -14921, -14914, -14908, -14902, -14894, -14889, -14882, -14873, -14871, -14857, -14678, -14674, -14670, -14668, -14663, -14654, -14645, -14630, -14594, -14429, -14407, -14399, -14384, -14379, -14368, -14355, -14353, -14345, -14170, -14159, -14151, -14149, -14145, -14140, -14137, -14135, -14125, -14123, -14122, -14112, -14109, -14099, -14097, -14094, -14092, -14090, -14087, -14083, -13917, -13914, -13910, -13907, -13906, -13905, -13896, -13894, -13878, -13870, -13859, -13847, -13831, -13658, -13611, -13601, -13406, -13404, -13400, -13398, -13395, -13391, -13387, -13383, -13367, -13359, -13356, -13343, -13340, -13329, -13326, -13318, -13147, -13138, -13120, -13107, -13096, -13095, -13091, -13076, -13068, -13063, -13060, -12888, -12875, -12871, -12860, -12858, -12852, -12849, -12838, -12831, -12829, -12812, -12802, -12607, -12597, -12594, -12585, -12556, -12359, -12346, -12320, -12300, -12120, -12099, -12089, -12074, -12067, -12058, -12039, -11867, -11861, -11847, -11831, -11798, -11781, -11604, -11589, -11536, -11358, -11340, -11339, -11324, -11303, -11097, -11077, -11067, -11055, -11052, -11045, -11041, -11038, -11024, -11020, -11019, -11018, -11014, -10838, -10832, -10815, -10800, -10790, -10780, -10764, -10587, -10544, -10533, -10519, -10331, -10329, -10328, -10322, -10315, -10309, -10307, -10296, -10281, -10274, -10270, -10262, -10260, -10256, -10254}; public static String[] pystr = new String[]{"a", "ai", "an", "ang", "ao", "ba", "bai", "ban", "bang", "bao", "bei", "ben", "beng", "bi", "bian", "biao", "bie", "bin", "bing", "bo", "bu", "ca", "cai", "can", "cang", "cao", "ce", "ceng", "cha", "chai", "chan", "chang", "chao", "che", "chen", "cheng", "chi", "chong", "chou", "chu", "chuai", "chuan", "chuang", "chui", "chun", "chuo", "ci", "cong", "cou", "cu", "cuan", "cui", "cun", "cuo", "da", "dai", "dan", "dang", "dao", "de", "deng", "di", "dian", "diao", "die", "ding", "diu", "dong", "dou", "du", "duan", "dui", "dun", "duo", "e", "en", "er", "fa", "fan", "fang", "fei", "fen", "feng", "fo", "fou", "fu", "ga", "gai", "gan", "gang", "gao", "ge", "gei", "gen", "geng", "gong", "gou", "gu", "gua", "guai", "guan", "guang", "gui", "gun", "guo", "ha", "hai", "han", "hang", "hao", "he", "hei", "hen", "heng", "hong", "hou", "hu", "hua", "huai", "huan", "huang", "hui", "hun", "huo", "ji", "jia", "jian", "jiang", "jiao", "jie", "jin", "jing", "jiong", "jiu", "ju", "juan", "jue", "jun", "ka", "kai", "kan", "kang", "kao", "ke", "ken", "keng", "kong", "kou", "ku", "kua", "kuai", "kuan", "kuang", "kui", "kun", "kuo", "la", "lai", "lan", "lang", "lao", "le", "lei", "leng", "li", "lia", "lian", "liang", "liao", "lie", "lin", "ling", "liu", "long", "lou", "lu", "lv", "luan", "lue", "lun", "luo", "ma", "mai", "man", "mang", "mao", "me", "mei", "men", "meng", "mi", "mian", "miao", "mie", "min", "ming", "miu", "mo", "mou", "mu", "na", "nai", "nan", "nang", "nao", "ne", "nei", "nen", "neng", "ni", "nian", "niang", "niao", "nie", "nin", "ning", "niu", "nong", "nu", "nv", "nuan", "nue", "nuo", "o", "ou", "pa", "pai", "pan", "pang", "pao", "pei", "pen", "peng", "pi", "pian", "piao", "pie", "pin", "ping", "po", "pu", "qi", "qia", "qian", "qiang", "qiao", "qie", "qin", "qing", "qiong", "qiu", "qu", "quan", "que", "qun", "ran", "rang", "rao", "re", "ren", "reng", "ri", "rong", "rou", "ru", "ruan", "rui", "run", "ruo", "sa", "sai", "san", "sang", "sao", "se", "sen", "seng", "sha", "shai", "shan", "shang", "shao", "she", "shen", "sheng", "shi", "shou", "shu", "shua", "shuai", "shuan", "shuang", "shui", "shun", "shuo", "si", "song", "sou", "su", "suan", "sui", "sun", "suo", "ta", "tai", "tan", "tang", "tao", "te", "teng", "ti", "tian", "tiao", "tie", "ting", "tong", "tou", "tu", "tuan", "tui", "tun", "tuo", "wa", "wai", "wan", "wang", "wei", "wen", "weng", "wo", "wu", "xi", "xia", "xian", "xiang", "xiao", "xie", "xin", "xing", "xiong", "xiu", "xu", "xuan", "xue", "xun", "ya", "yan", "yang", "yao", "ye", "yi", "yin", "ying", "yo", "yong", "you", "yu", "yuan", "yue", "yun", "za", "zai", "zan", "zang", "zao", "ze", "zei", "zen", "zeng", "zha", "zhai", "zhan", "zhang", "zhao", "zhe", "zhen", "zheng", "zhi", "zhong", "zhou", "zhu", "zhua", "zhuai", "zhuan", "zhuang", "zhui", "zhun", "zhuo", "zi", "zong", "zou", "zu", "zuan", "zui", "zun", "zuo"}; private StringBuilder buffer; private String resource; private static CharacterParserUtil characterParser = new CharacterParserUtil(); public static CharacterParserUtil getInstance() { return characterParser; } public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } /** * 汉字转成ASCII码 * * @param chs * @return */ private int getChsAscii(String chs) { int asc = 0; try { byte[] bytes = chs.getBytes("gb2312"); if (bytes == null || bytes.length > 2 || bytes.length <= 0) { throw new RuntimeException("illegal resource string"); } if (bytes.length == 1) { asc = bytes[0]; } if (bytes.length == 2) { int hightByte = 256 + bytes[0]; int lowByte = 256 + bytes[1]; asc = (256 * hightByte + lowByte) - 256 * 256; } } catch (Exception e) { System.out.println("ERROR:ChineseSpelling.class-getChsAscii(String chs)" + e); } return asc; } /** * 单个字进行解析 * * @param str * @return */ public String convert(String str) { String result = null; int ascii = getChsAscii(str); if (ascii > 0 && ascii < 160) { result = String.valueOf((char) ascii); } else { for (int i = (pyvalue.length - 1); i >= 0; i--) { if (pyvalue[i] <= ascii) { result = pystr[i]; break; } } } return result; } /*** * 词组解析 * * @param chs * @return */ public String getSelling(String chs) { String key, value; buffer = new StringBuilder(); for (int i = 0; i < chs.length(); i++) { key = chs.substring(i, i + 1); if (key.getBytes().length >= 2) { value = (String) convert(key); if (value == null) { value = "unknown"; } } else { value = key; } buffer.append(value); } return buffer.toString(); } public String getSpelling() { return this.getSelling(this.getResource()); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/CountryCodeUtil.java ================================================ package com.htmessage.yichatopen.activity.country; import android.content.Context; import com.htmessage.yichatopen.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * 项目名称:htmessage_sdk * 类描述:CountryCodeUtil 描述: * 创建人:songlijie * 创建时间:2017/3/3 19:25 * 邮箱:814326663@qq.com */ public class CountryCodeUtil { /** * 获取国家列表 */ public static List getCountryList(Context context, boolean isCn) { List mAllCountryList = new ArrayList<>(); String[] countryList = context.getResources().getStringArray(R.array.country_code_list_ch); if (!isCn){ countryList = context.getResources().getStringArray(R.array.country_code_list_en); } for (int i = 0, length = countryList.length; i < length; i++) { String[] country = countryList[i].split("\\*"); String countryName = country[0]; String countryNumber = country[1]; String countrySortKey = new CharacterParserUtil().getSelling(countryName); CountrySortModel countrySortModel = new CountrySortModel(countryName, countryNumber, countrySortKey); String sortLetter = new GetCountryNameSort().getSortLetterBySortKey(countrySortKey); if (sortLetter == null) { sortLetter = new GetCountryNameSort().getSortLetterBySortKey(countryName); } countrySortModel.sortLetters = sortLetter; mAllCountryList.add(countrySortModel); } Collections.sort(mAllCountryList, new CountryComparator()); return mAllCountryList; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/CountryComparator.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015��3��18�� | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; import java.util.Comparator; public class CountryComparator implements Comparator { @Override public int compare(CountrySortModel o1, CountrySortModel o2) { if (o1.sortLetters.equals("@") || o2.sortLetters.equals("#")) { return -1; } else if (o1.sortLetters.equals("#") || o2.sortLetters.equals("@")) { return 1; } else { return o1.sortLetters.compareTo(o2.sortLetters); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/CountryModel.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015��3��30�� | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; import android.graphics.drawable.Drawable; public class CountryModel { // 国家名称 public String countryName; // 国家代码 public String countryNumber; public String simpleCountryNumber; // 国家名称缩写 public String countrySortKey; // 国家图标 public Drawable contactPhoto; public CountryModel(String countryName, String countryNumber, String countrySortKey) { super(); this.countryName = countryName; this.countryNumber = countryNumber; this.countrySortKey = countrySortKey; if (countryNumber != null) { this.simpleCountryNumber = countryNumber.replaceAll("\\-|\\s", ""); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/CountrySortAdapter.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015��3��21�� | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.SectionIndexer; import android.widget.TextView; import com.htmessage.yichatopen.R; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** * 国家码选择 */ public class CountrySortAdapter extends BaseAdapter implements SectionIndexer { private List mList; private Context mContext; LayoutInflater mInflater; /*** * 初始化 * * @param mContext * @param list */ public CountrySortAdapter(Context mContext, List list) { this.mContext = mContext; if (list == null) { this.mList = new ArrayList(); } else { this.mList = list; } } @Override public int getCount() { return this.mList.size(); } @Override public Object getItem(int position) { return mList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View view, ViewGroup parent) { ViewHolder viewHolder = null; final CountrySortModel mContent = mList.get(position); if (view == null) { viewHolder = new ViewHolder(); view = LayoutInflater.from(mContext).inflate(R.layout.coogame_country_item, null); viewHolder.country_sortName = (TextView) view.findViewById(R.id.country_catalog); viewHolder.country_name = (TextView) view.findViewById(R.id.country_name); viewHolder.country_number = (TextView) view.findViewById(R.id.country_number); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } // 根据position获取分类的首字母的Char ascii值 int section = getSectionForPosition(position); // 如果当前位置等于该分类首字母的Char的位置 ,则认为是第一次出现 if (position == getPositionForSection(section)) { viewHolder.country_sortName.setVisibility(View.VISIBLE); viewHolder.country_sortName.setText(mContent.sortLetters); } else { viewHolder.country_sortName.setVisibility(View.GONE); } viewHolder.country_name.setText(this.mList.get(position).countryName); viewHolder.country_number.setText(this.mList.get(position).countryNumber); return view; } @Override public int getPositionForSection(int section) { if (section != 42) { for (int i = 0; i < getCount(); i++) { String sortStr = mList.get(i).sortLetters; char firstChar = sortStr.toUpperCase(Locale.CHINESE).charAt(0); if (firstChar == section) { return i; } } } else { return 0; } return -1; } @Override public int getSectionForPosition(int position) { return mList.get(position).sortLetters.charAt(0); } @Override public Object[] getSections() { return null; } /** * 当ListView数据发生变化时,调用此方法来更新ListView * * @param list */ public void updateListView(List list) { if (list == null) { this.mList = new ArrayList(); } else { this.mList = list; } notifyDataSetChanged(); } public static class ViewHolder { // 国家码简拼所属的字母范围 public TextView country_sortName; // 国家名 public TextView country_name; // 代码 public TextView country_number; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/CountrySortModel.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015��3��21�� | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; public class CountrySortModel extends CountryModel { // 显示数据拼音的首字母 public String sortLetters; public CountrySortToken sortToken = new CountrySortToken(); public CountrySortModel(String name, String number, String countrySortKey) { super(name, number, countrySortKey); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/CountrySortToken.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015��3��21�� | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; public class CountrySortToken { // 简拼 public String simpleSpell = ""; // 全拼 public String wholeSpell = ""; // 中文全名 public String chName = ""; } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/GetCountryNameSort.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015��3��20�� | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; import java.util.ArrayList; import java.util.List; import java.util.Locale; /** * 取姓名首字母及模糊匹配查询 *

*

* 类详细描述 *

* * @author duanbokan */ public class GetCountryNameSort { CharacterParserUtil characterParser = CharacterParserUtil.getInstance(); String chReg = "[\\u4E00-\\u9FA5]+";// 中文字符串匹配 /*** * 将名字转化为拼音并获得首字母 * * @param name * @return */ public String getSortLetter(String name) { String letter = "#"; if (name == null) { return letter; } // 汉字转换成拼音 String pinyin = characterParser.getSelling(name); String sortString = pinyin.substring(0, 1).toUpperCase(Locale.CHINESE); // 正则表达式,判断首字母是否是英文字母 if (sortString.matches("[A-Z]")) { letter = sortString.toUpperCase(Locale.CHINESE); } return letter; } /*** * 取首字母 * * @param sortKey * @return */ public String getSortLetterBySortKey(String sortKey) { if (sortKey == null || "".equals(sortKey.trim())) { return null; } String letter = "#"; // 汉字转换成拼音 String sortString = sortKey.trim().substring(0, 1).toUpperCase(Locale.CHINESE); // 正则表达式,判断首字母是否是英文字母 if (sortString.matches("[A-Z]")) { letter = sortString.toUpperCase(Locale.CHINESE); } return letter; } /*** * 根据输入内容进行查询 * * @param str 输入内容 * @param list 需要查询的List * @return 查询结果 */ public List search(String str, List list) { List filterList = new ArrayList();// 过滤后的list // if (str.matches("^([0-9]|[/+])*$")) {// 正则表达式 匹配号码 if (str.matches("^([0-9]|[/+]).*")) {// 正则表达式 匹配以数字或者加号开头的字符串(包括了带空格及-分割的号码) String simpleStr = str.replaceAll("\\-|\\s", ""); for (CountrySortModel contact : list) { if (contact.countryName != null && contact.countryName != null) { if (contact.simpleCountryNumber.contains(simpleStr) || contact.countryName.contains(str)) { if (!filterList.contains(contact)) { filterList.add(contact); } } } } } else { for (CountrySortModel contact : list) { if (contact.countryNumber != null && contact.countryName != null) { // 姓名全匹配,姓名首字母简拼匹配,姓名全字母匹配 if (contact.countryName.toLowerCase(Locale.CHINESE).contains( str.toLowerCase(Locale.CHINESE)) || contact.countrySortKey.toLowerCase(Locale.CHINESE).replace(" ", "") .contains(str.toLowerCase(Locale.CHINESE)) || contact.sortToken.simpleSpell.toLowerCase(Locale.CHINESE).contains( str.toLowerCase(Locale.CHINESE)) || contact.sortToken.wholeSpell.toLowerCase(Locale.CHINESE).contains( str.toLowerCase(Locale.CHINESE))) { if (!filterList.contains(contact)) { filterList.add(contact); } } } } } return filterList; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/country/SideBar.java ================================================ /* * Copyright 2010 Beijing Xinwei, Inc. All rights reserved. * * History: * ------------------------------------------------------------------------------ * Date | Who | What * 2015年3月18日 | duanbokan | create the file */ package com.htmessage.yichatopen.activity.country; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.TextView; import com.htmessage.yichatopen.R; /** * 类简要描述 */ public class SideBar extends View { // 字母变化监听事件 private OnTouchingLetterChangedListener onTouchingLetterChangedListener; // 字母数组 public static String[] b = {"*", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#"}; // 选中 private int choose = -1; private Paint paint = new Paint(); // 点击后提示当前选中字母 private TextView mTextDialog; public void setTextView(TextView mTextDialog) { this.mTextDialog = mTextDialog; } public SideBar(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public SideBar(Context context, AttributeSet attrs) { super(context, attrs); } public SideBar(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 获取屏幕高度和宽度 int height = getHeight(); int width = getWidth(); // 设置字母高度 float letterHeight = (height * 1f) / b.length; for (int i = 0; i < b.length; i++) { paint.setColor(Color.rgb(23, 122, 126)); paint.setTypeface(Typeface.DEFAULT_BOLD); // 抗锯齿 paint.setAntiAlias(true); paint.setTextSize(20); if (i == choose) { paint.setColor(Color.BLUE); // 设置为加粗字体 paint.setFakeBoldText(true); } // x坐标等于中间-字符串宽度的一半. float xPos = width / 2 - paint.measureText(b[i]) / 2; float yPos = letterHeight * i + letterHeight; canvas.drawText(b[i], xPos, yPos, paint); // 重置画笔 paint.reset(); } } @Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction(); final float touch_y = event.getY(); final int oldChoose = choose; final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; // 点击y坐标所占总高度的比例*b数组的长度就等于点击b中的个数. final int c = (int) (touch_y / getHeight() * b.length); switch (action) { case MotionEvent.ACTION_UP: setBackgroundColor(Color.TRANSPARENT); choose = -1; invalidate(); if (mTextDialog != null) { mTextDialog.setVisibility(View.INVISIBLE); } break; default: setBackgroundResource(R.color.holo_green_light); if (oldChoose != c) { if (c >= 0 && c < b.length) { if (listener != null) { listener.onTouchingLetterChanged(b[c]); } if (mTextDialog != null) { mTextDialog.setText(b[c]); mTextDialog.setVisibility(View.VISIBLE); } choose = c; invalidate(); } } break; } return true; } /** * 向外公开的方法 * * @param onTouchingLetterChangedListener */ public void setOnTouchingLetterChangedListener( OnTouchingLetterChangedListener onTouchingLetterChangedListener) { this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; } /** * 接口 * * @author coder */ public interface OnTouchingLetterChangedListener { public void onTouchingLetterChanged(String s); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/login/LoginActivity.java ================================================ package com.htmessage.yichatopen.activity.login; import android.annotation.TargetApi; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.runtimepermissions.PermissionsManager; import com.htmessage.yichatopen.runtimepermissions.PermissionsResultAction; /** * Login screen */ public class LoginActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); hideBackView(); setTitle(R.string.login_by_mobile); requestPermissions(); LoginFragment loginFragment = (LoginFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (loginFragment == null) { // Create the fragment loginFragment =new LoginFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame, loginFragment); transaction.commit(); } LoginPresenter presenter=new LoginPresenter(loginFragment); } @TargetApi(23) private void requestPermissions() { PermissionsManager.getInstance().requestAllManifestPermissionsIfNecessary(this, new PermissionsResultAction() { @Override public void onGranted() { } @Override public void onDenied(String permission) { } }); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/login/LoginContract.java ================================================ package com.htmessage.yichatopen.activity.login; import android.app.Activity; import android.content.Context; import android.widget.TextView; import com.htmessage.yichatopen.activity.BasePresenter; import com.htmessage.yichatopen.activity.BaseView; /** * Created by huangfangyi on 2017/6/21. * qq 84543217 */ public interface LoginContract { interface View extends BaseView { void showDialog(); void cancelDialog(); String getUsername(); String getPassword(); void setButtonEnable(); void setButtonDisabel(); void showToast(int toastMsg); String getCountryName(); String getCountryCode(); Activity getBaseActivity(); Context getBaseContext(); } interface Presenter extends BasePresenter { void requestServer(String username, String password); void chooseCuntry(Context context, TextView tvCountryName, TextView tvCountryCode); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/login/LoginFragment.java ================================================ package com.htmessage.yichatopen.activity.login; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.main.password.PasswordResetActivity; import com.htmessage.yichatopen.activity.register.RegisterActivity; import com.htmessage.yichatopen.utils.Validator; /** * Created by dell on 2017/6/21. */ public class LoginFragment extends Fragment implements LoginContract.View, View.OnClickListener { private LoginContract.Presenter mPresenter; private EditText et_usertel, et_password; private TextView tv_find_password, tv_country, tv_country_code; private Button btn_login, btn_qtlogin; private RelativeLayout rl_country; private Dialog dialog; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); dialog = HTApp.getInstance().createLoadingDialog(getActivity(), getString(R.string.logining)); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_login, container, false); et_usertel = (EditText) root.findViewById(R.id.et_usertel); et_password = (EditText) root.findViewById(R.id.et_password); btn_login = (Button) root.findViewById(R.id.btn_login); tv_country = (TextView) root.findViewById(R.id.tv_country); tv_country_code = (TextView) root.findViewById(R.id.tv_country_code); tv_find_password = (TextView) root.findViewById(R.id.tv_find_password); rl_country = (RelativeLayout) root.findViewById(R.id.rl_country); btn_qtlogin = (Button) root.findViewById(R.id.btn_qtlogin); setLisenter(); return root; } private void setLisenter() { //输入监听 TextChange textChange = new TextChange(); et_usertel.addTextChangedListener(textChange); et_password.addTextChangedListener(textChange); //登陆按钮监听 btn_login.setOnClickListener(this); //选取国家监听 rl_country.setOnClickListener(this); //跳转注册监听 btn_qtlogin.setOnClickListener(this); tv_find_password.setOnClickListener(this); } @Override public void setPresenter(LoginContract.Presenter presenter) { mPresenter = presenter; } @Override public void showDialog() { if (dialog != null) dialog.show(); } @Override public void cancelDialog() { if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } } @Override public String getUsername() { return et_usertel.getText().toString().trim(); } @Override public String getPassword() { return et_password.getText().toString().trim(); } @Override public void setButtonEnable() { btn_login.setEnabled(true); } @Override public void setButtonDisabel() { btn_login.setEnabled(true); } @Override public void showToast(int toastMsg) { Toast.makeText(getActivity(), toastMsg, Toast.LENGTH_SHORT).show(); } @Override public String getCountryName() { return tv_country.getText().toString().trim(); } @Override public String getCountryCode() { return tv_country_code.getText().toString().trim(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public Context getBaseContext() { return getContext(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_login: if (TextUtils.isEmpty(getUsername())) { showToast( R.string.tel_is_not_allow_null ); return; } //如果是中国地区,进行一个手机号验证 if (getCountryName().equals(getString(R.string.china)) && getCountryCode().equals(getString(R.string.country_code))) { if (!Validator.isMobile(getUsername())) { showToast( R.string.please_input_true_mobile ); return; } } if (TextUtils.isEmpty(getPassword())) { showToast( R.string.pwd_is_not_allow_null); return; } mPresenter.requestServer(getUsername(), getPassword()); break; case R.id.rl_country: mPresenter.chooseCuntry(getContext(), tv_country, tv_country_code); break; case R.id.btn_qtlogin: startActivity(new Intent(getActivity(), RegisterActivity.class)); break; case R.id.tv_find_password: startActivity(new Intent(getActivity(), PasswordResetActivity.class).putExtra("isReset", false)); break; } } // EditText监听器 private class TextChange implements TextWatcher { @Override public void afterTextChanged(Editable arg0) { } @Override public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { } @Override public void onTextChanged(CharSequence cs, int start, int before, int count) { boolean sign1 = et_usertel.getText().length() > 0; boolean sign2 = et_password.getText().length() > 0; if (sign1 & sign2) { setButtonEnable(); } else { setButtonDisabel(); } } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/login/LoginPresenter.java ================================================ package com.htmessage.yichatopen.activity.login; import android.content.Context; import android.content.Intent; import android.widget.TextView; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.htmessage.sdk.client.HTClient; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.main.MainActivity; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import com.htmessage.yichatopen.utils.UpdateLastLoginTimeUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by huangfangyi on 2017/6/21. * qq 84543217 */ public class LoginPresenter implements LoginContract.Presenter { private LoginContract.View loginView; public LoginPresenter(LoginContract.View loginView){ this.loginView=loginView; this.loginView.setPresenter(this); } @Override public void requestServer(String username, String password) { loginView.showDialog(); List params = new ArrayList<>(); params.add(new Param("usertel", username)); params.add(new Param("password", password)); new OkHttpUtils(loginView.getBaseActivity() ).post(params, HTConstant.URL_LOGIN, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { int code = jsonObject.getInteger("code"); switch(code){ case 1: JSONObject userJson = jsonObject.getJSONObject("user"); loginIm(userJson); break; case -1: loginView.cancelDialog(); loginView.showToast(R.string.Account_does_not_exist); break; case -2: loginView.cancelDialog(); loginView.showToast(R.string.Incorrect_password); break; case -3: loginView.cancelDialog(); loginView.showToast(R.string.Account_has_been_disabled); break; default: loginView.cancelDialog(); loginView.showToast(R.string.Server_busy); break; } } @Override public void onFailure(String errorMsg) { loginView.cancelDialog(); } }); } @Override public void chooseCuntry(Context context, TextView tvCountryName, TextView tvCountryCode) { CommonUtils.showPup(context,tvCountryName,tvCountryCode); } private void loginIm(final JSONObject userJson) { HTClient.getInstance().login(userJson.getString(HTConstant.JSON_KEY_HXID), userJson.getString(HTConstant.JSON_KEY_PASSWORD), new HTClient.HTCallBack() { @Override public void onSuccess() { if (userJson == null) { return; } JSONArray friends = userJson.getJSONArray("friend"); if (userJson.containsKey("friend")) { userJson.remove("friend"); } HTApp.getInstance().setUserJson(userJson); Map userlist = new HashMap(); if (friends != null) { for (int i = 0; i < friends.size(); i++) { JSONObject friend = friends.getJSONObject(i); User user = CommonUtils.Json2User(friend); userlist.put(user.getUsername(), user); } List users = new ArrayList(userlist.values()); ContactsManager.getInstance().saveContactList(users); } //上传最近登录时间 UpdateLastLoginTimeUtils.sendLocalTimeToService(loginView.getBaseContext()); loginView.getBaseActivity(). runOnUiThread(new Runnable() { @Override public void run() { loginView.cancelDialog(); loginView.showToast(R.string.login_success); Intent intent = new Intent(loginView.getBaseContext(), MainActivity.class); loginView.getBaseActivity().startActivity(intent); loginView.getBaseActivity().finish(); } }); } @Override public void onError() { loginView.getBaseActivity().runOnUiThread(new Runnable() { @Override public void run() { loginView.cancelDialog(); loginView.showToast(R.string.Login_failed); } }); } }); } @Override public void start() { } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/MainActivity.java ================================================ package com.htmessage.yichatopen.activity.main; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.view.menu.MenuBuilder; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import com.htmessage.sdk.client.HTClient; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.addfriends.add.pre.AddFriendsPreActivity; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.activity.ScanCaptureActivity; import com.htmessage.yichatopen.activity.login.LoginActivity; import com.htmessage.yichatopen.activity.main.contacts.FragmentContacts; import com.htmessage.yichatopen.activity.main.conversation.ConversationFragment; import com.htmessage.yichatopen.activity.main.find.FragmentFind; import com.htmessage.yichatopen.activity.main.profile.FragmentProfile; import com.htmessage.yichatopen.runtimepermissions.PermissionsManager; import com.htmessage.yichatopen.runtimepermissions.PermissionsResultAction; import java.lang.reflect.Method; /** * Created by huangfangyi on 2017/6/24. * qq 84543217 */ public class MainActivity extends BaseActivity implements MainView { private TabLayout mTablayout; private ViewPager mViewPager; private String[] mTitles; private int[] drawabls = new int[]{R.drawable.tab_chat_bg, R.drawable.tab_contact_list_bg, R.drawable.tab_find_bg, R.drawable.tab_profile_bg}; private TabLayout.Tab[] tabs; private Fragment[] fragments; //新消息角标 private TextView unreadLabel; // 新好友申请消息角标 public TextView unreadInvitionLable; // user logged into another device public boolean isConflict = false; private android.app.AlertDialog.Builder exceptionBuilder; private boolean isConflictDialogShow; private MainPrestener mainPrestener; @Override protected void onCreate(Bundle arg0) { if (arg0 != null && arg0.getBoolean("isConflict", false)) { finish(); startActivity(new Intent(this, LoginActivity.class)); return; } super.onCreate(arg0); setContentView(R.layout.activity_base_main); mainPrestener = new MainPrestener(this); mTitles = new String[]{getString(R.string.app_chat), getString(R.string.address_book), getString(R.string.bottom_find), getString(R.string.me)}; Toolbar toolbar = (Toolbar) this.findViewById(R.id.toolbar); setSupportActionBar(toolbar); toolbar.setTitle(getString(R.string.app_name)); toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.item_search: Toast.makeText(getApplicationContext(), R.string.wait_do, Toast.LENGTH_SHORT).show(); break; case R.id.item_add_friend: startActivity(new Intent(MainActivity.this, AddFriendsPreActivity.class)); break; case R.id.item_add_group: Toast.makeText(getApplicationContext(), R.string.open_notice, Toast.LENGTH_SHORT).show(); break; case R.id.item_scan: startActivity(new Intent(MainActivity.this, ScanCaptureActivity.class)); break; } return true; } }); if (getIntent().getBooleanExtra(IMAction.ACTION_CONFLICT, false) && !isConflictDialogShow) { showConflicDialog(); } initViews(); requestPermissions(); mainPrestener.checkVersion(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override protected boolean onPrepareOptionsPanel(View view, Menu menu) { //解决menu item设置的图标不显示 if (menu != null) { if (menu.getClass() == MenuBuilder.class) { try { Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE); m.setAccessible(true); m.invoke(menu, true); } catch (Exception e) { // Out.print(getClass().getSimpleName() + "onMenuOpened...unable to set icons for overflow menu" + e); } } } return super.onPrepareOptionsPanel(view, menu); } private void initViews() { mTablayout = (TabLayout) findViewById(R.id.tabLayout); mViewPager = (ViewPager) findViewById(R.id.viewPager); fragments = new Fragment[]{new ConversationFragment(), new FragmentContacts(), new FragmentFind(), new FragmentProfile()}; mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) { @Override public Fragment getItem(int position) { return fragments[position]; } @Override public int getCount() { return fragments.length; } }); mTablayout.setupWithViewPager(mViewPager); tabs = new TabLayout.Tab[mTitles.length]; for (int i = 0; i < mTitles.length; i++) { tabs[i] = mTablayout.getTabAt(i); tabs[i].setCustomView(getBottomView(i, drawabls[i])); } } @Override protected void onStart() { super.onStart(); mainPrestener.start(); } @Override protected void onDestroy() { super.onDestroy(); if (exceptionBuilder != null) { exceptionBuilder.create().dismiss(); exceptionBuilder = null; isConflictDialogShow = false; } } private View getBottomView(final int index, int drawableRes) { View view = getLayoutInflater().inflate(R.layout.widget_main_button, null); Button button = (Button) view.findViewById(R.id.button); button.setCompoundDrawablesWithIntrinsicBounds(0, drawableRes, 0, 0); // button.setCompoundDrawables(null, dr, null, null); button.setText(mTitles[index]); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (tabs[index] != null) { tabs[index].select(); } } }); if (isCompatible(Build.VERSION_CODES.LOLLIPOP)) { button.setStateListAnimator(null); } TextView textView = (TextView) view.findViewById(R.id.unread_msg_number); if (index == 0) { unreadLabel = textView; } else if (index == 1) { unreadInvitionLable = textView; } return view; } @Override public void setPresenter(MainPrestener presenter) { mainPrestener = presenter; } @Override public Activity getBaseActivity() { return this; } @Override public void showConflicDialog() { isConflictDialogShow = true; //HTClientHelper.getInstance().logout(null); String st = getResources().getString(R.string.Logoff_notification); if (!MainActivity.this.isFinishing()) { // clear up global variables try { if (exceptionBuilder == null) exceptionBuilder = new android.app.AlertDialog.Builder(MainActivity.this); exceptionBuilder.setTitle(st); exceptionBuilder.setMessage(R.string.connect_conflict); exceptionBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); exceptionBuilder = null; isConflictDialogShow = false; HTApp.getInstance().setUserJson(null); finish(); Intent intent = new Intent(MainActivity.this, LoginActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } }); exceptionBuilder.setCancelable(false); exceptionBuilder.show(); isConflict = true; } catch (Exception e) { Log.e(TAG, "---------color conflictBuilder error" + e.getMessage()); } } } @Override public void showUpdateDialog(String message, final String url, final String isForce) { String title = getString(R.string.has_update); AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); View dialogView = View.inflate(MainActivity.this, R.layout.layout_alert_dialog_delete, null); TextView tv_delete_people = (TextView) dialogView.findViewById(R.id.tv_delete_people); View view_line_dialog = dialogView.findViewById(R.id.view_line_dialog); TextView tv_delete_title = (TextView) dialogView.findViewById(R.id.tv_delete_title); TextView tv_cancle = (TextView) dialogView.findViewById(R.id.tv_cancle); TextView tv_ok = (TextView) dialogView.findViewById(R.id.tv_ok); tv_delete_title.setText(title); tv_delete_people.setText(message); tv_cancle.setText(R.string.update_later); tv_ok.setText(R.string.update_now); builder.setView(dialogView); final AlertDialog dialog = builder.show(); if (isForce.equals("1")) { view_line_dialog.setVisibility(View.GONE); tv_cancle.setVisibility(View.GONE); tv_ok.setText(R.string.update_has); dialog.setCancelable(false);//点击屏幕外不取消 返回键也没用 dialog.setCanceledOnTouchOutside(false); //点击屏幕外取消,返回键有用 } else { dialog.setCancelable(true);//点击屏幕外取消 返回键也没用 dialog.setCanceledOnTouchOutside(true); //点击屏幕外取消,返回键有用 } tv_cancle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); if (isForce.equals("1")) { logout(); } } }); tv_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!isForce.equals("1")) { dialog.dismiss(); } Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); Uri uri = Uri.parse(url); intent.setData(uri); MainActivity.this.startActivity(intent); } }); } @Override public void onUnReadMsgs(int count) { if (unreadLabel == null) { return; } if (count > 0) { unreadLabel.setText(String.valueOf(count)); unreadLabel.setVisibility(View.VISIBLE); } else { unreadLabel.setVisibility(View.INVISIBLE); } } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (intent.getBooleanExtra(IMAction.ACTION_CONFLICT, false) && !isConflictDialogShow) { showConflicDialog(); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { moveTaskToBack(false); return true; } return super.onKeyDown(keyCode, event); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { PermissionsManager.getInstance().notifyPermissionsChange(permissions, grantResults); } private void logout() { HTClient.getInstance().logout(new HTClient.HTCallBack() { @Override public void onSuccess() { HTApp.getInstance().setUserJson(null); HTApp.getInstance().finishActivities(); finish(); } @Override public void onError() { HTApp.getInstance().setUserJson(null); HTApp.getInstance().finishActivities(); finish(); } }); } @TargetApi(23) private void requestPermissions() { PermissionsManager.getInstance().requestAllManifestPermissionsIfNecessary(this, new PermissionsResultAction() { @Override public void onGranted() { // Toast.makeText(MainActivity.this, "All permissions have been granted", Toast.LENGTH_SHORT).show(); } @Override public void onDenied(String permission) { //Toast.makeText(MainActivity.this, "Permission " + permission + " has been denied", Toast.LENGTH_SHORT).show(); } }); } @Override public void showInvitionCount(int count) { if (unreadInvitionLable == null){ return; } if (count > 0) { // unreadInvitionLable.setText(String.valueOf(count)); unreadInvitionLable.setVisibility(View.VISIBLE); } else { unreadInvitionLable.setVisibility(View.INVISIBLE); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/MainBasePrester.java ================================================ package com.htmessage.yichatopen.activity.main; import com.htmessage.yichatopen.activity.BasePresenter; /** * Created by huangfangyi on 2017/6/27. * qq 84543217 */ public interface MainBasePrester extends BasePresenter { //检查更新 void checkVersion(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/MainPrestener.java ================================================ package com.htmessage.yichatopen.activity.main; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.PowerManager; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import java.util.ArrayList; import java.util.List; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; /** * Created by huangfangyi on 2017/6/25. * qq 84543217 */ public class MainPrestener implements MainBasePrester { private MainView mainView; private Context context; public MainPrestener(MainView _mainView){ this.mainView=_mainView; mainView.setPresenter(this); context=mainView.getBaseContext(); } @Override public void start() { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { String packageName = context.getPackageName(); PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); if (!pm.isIgnoringBatteryOptimizations(packageName)) { Intent intent = new Intent(); intent.setAction(android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setFlags(FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.parse("package:" + packageName)); context.startActivity(intent); } } } catch (RuntimeException e) { e.printStackTrace(); } } /** * 获取VersionCode * * @return 当前应用的VersionCode */ public String getVersionCode() { try { PackageManager manager = context.getPackageManager(); PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0); String version = String.valueOf(info.versionCode); return version; } catch (Exception e) { e.printStackTrace(); return null; } } @Override public void checkVersion() { final String version = getVersionCode(); List params = new ArrayList<>(); params.add(new Param("system", "0")); new OkHttpUtils(context).post(params, HTConstant.URL_CHECK_UPDATE, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { if (jsonObject != null) { String serviceVersion = jsonObject.getString("newVersion"); String url = jsonObject.getString("url"); String info = jsonObject.getString("info"); String statue = jsonObject.getString("statue"); if (!version.equals(serviceVersion) && (Integer.valueOf(version) < Integer.valueOf(serviceVersion))) { mainView.showUpdateDialog(info, url, statue); } } } @Override public void onFailure(String errorMsg) { } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/MainView.java ================================================ package com.htmessage.yichatopen.activity.main; import com.htmessage.yichatopen.activity.BaseView; import com.htmessage.yichatopen.activity.main.contacts.FragmentContacts; import com.htmessage.yichatopen.activity.main.conversation.ConversationFragment; /** * Created by huangfangyi on 2017/6/25. * qq 84543217 */ public interface MainView extends BaseView,ConversationFragment.NewMeesageListener,FragmentContacts.ContactsListener { void showConflicDialog(); void showUpdateDialog( String message,String url,String isForce); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/contacts/BaseContactsPresenter.java ================================================ package com.htmessage.yichatopen.activity.main.contacts; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.activity.BasePresenter; import java.util.List; /** * Created by huangfangyi on 2017/6/28. * qq 84543217 */ public interface BaseContactsPresenter extends BasePresenter{ List getContactsListInDb(); void deleteContacts(String userId); void moveUserToBlack(String userId); List sortList( List users); void refreshContactsInServer(); int getInvitionCount(); int getContactsCount(); void clearInvitionCount(); void refreshContactsInLocal(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/contacts/ContactsAdapter.java ================================================ package com.htmessage.yichatopen.activity.main.contacts; import android.content.Context; import android.util.SparseIntArray; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.SectionIndexer; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.domain.User; import java.util.ArrayList; import java.util.List; /** * Created by huangfangyi on 2017/6/28. * qq 84543217 */ public class ContactsAdapter extends BaseAdapter implements SectionIndexer { private Context context; private List data; public ContactsAdapter(Context context, List data) { this.context = context; this.data = data; } @Override public int getCount() { return data.size(); } @Override public User getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = View.inflate(context, R.layout.item_contact_list, null); } ViewHolder holder = (ViewHolder) convertView.getTag(); if (holder == null) { holder = new ViewHolder(); holder.tvHeader = (TextView) convertView.findViewById(R.id.header); holder.nameTextview = (TextView) convertView.findViewById(R.id.tv_name); holder.iv_avatar = (ImageView) convertView.findViewById(R.id.iv_avatar); convertView.setTag(holder); } User user = data.get(position); holder.tvHeader.setVisibility(View.VISIBLE); String nick = user.getNick(); String avatar = user.getAvatar(); String headerLetter = user.getInitialLetter(); String preHeaderLetter = getPreHeaderLeeter(position); if (headerLetter.equals(preHeaderLetter)) { holder.tvHeader.setVisibility(View.GONE); } else { holder.tvHeader.setVisibility(View.VISIBLE); holder.tvHeader.setText(headerLetter); } holder.nameTextview.setText(nick); Glide.with(context).load(avatar).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).error(R.drawable.default_avatar).into(holder.iv_avatar); return convertView; } List list; private SparseIntArray positionOfSection; private SparseIntArray sectionOfPosition; @Override public Object[] getSections() { positionOfSection = new SparseIntArray(); sectionOfPosition = new SparseIntArray(); int count = getCount(); list = new ArrayList(); list.add(context.getString(R.string.search_header)); positionOfSection.put(0, 0); sectionOfPosition.put(0, 0); for (int i = 1; i < count; i++) { String letter = getItem(i).getInitialLetter(); System.err.println("contactadapter getsection getHeader:" + letter + " name:" + getItem(i).getUsername()); int section = list.size() - 1; if (list.get(section) != null && !list.get(section).equals(letter)) { list.add(letter); section++; positionOfSection.put(section, i); } sectionOfPosition.put(i, section); } return list.toArray(new String[list.size()]); } public int getPositionForSection(int section) { return positionOfSection.get(section)+1; } public int getSectionForPosition(int position) { return sectionOfPosition.get(position); } private class ViewHolder { ImageView iv_avatar; TextView nameTextview, tvHeader; } private String getPreHeaderLeeter(int position) { if (position > 0) { User user = getItem(position - 1); return user.getInitialLetter(); } return null; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/contacts/ContactsPresenter.java ================================================ package com.htmessage.yichatopen.activity.main.contacts; import android.app.AlertDialog; import android.app.ProgressDialog; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.manager.HTChatManager; import com.htmessage.sdk.model.CmdMessage; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.domain.InviteMessgeDao; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.domain.UserDao; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.UUID; /** * Created by huangfangyi on 2017/6/28. * qq 84543217 */ public class ContactsPresenter implements BaseContactsPresenter { private ContactsView contactsView; private List contacts; private InviteMessgeDao inviteMessgeDao; public ContactsPresenter (ContactsView view){ contactsView=view; contactsView.setPresenter(this); contacts=new ArrayList<>(ContactsManager.getInstance().getContactList().values()); inviteMessgeDao=new InviteMessgeDao(contactsView.getBaseContext()); } @Override public List getContactsListInDb() { contacts= sortList(new ArrayList(ContactsManager.getInstance().getContactList().values())); return contacts; } //删除好友 @Override public void deleteContacts(String userId) { Map users = ContactsManager.getInstance().getContactList(); User user = users.get(userId); users.remove(user.getUsername()); contacts.remove(user); deleteContact(user); } //移入黑名单 @Override public void moveUserToBlack(final String userId) { View diaglogView = View.inflate(contactsView.getBaseContext(), R.layout.layout_alert_dialog_delete, null); TextView tv_delete_title = (TextView) diaglogView.findViewById(R.id.tv_delete_title); TextView tv_delete_people = (TextView) diaglogView.findViewById(R.id.tv_delete_people); TextView tv_cancle = (TextView) diaglogView.findViewById(R.id.tv_cancle); TextView tv_ok = (TextView) diaglogView.findViewById(R.id.tv_ok); tv_delete_title.setText(R.string.prompt); tv_delete_people.setText(R.string.Into_the_blacklist); AlertDialog.Builder builder = new AlertDialog.Builder(contactsView.getBaseActivity()); builder.setView(diaglogView); final AlertDialog dialog = builder.show(); tv_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); showMoveToBlackDialog(userId); } }); tv_cancle.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dialog.dismiss(); } }); } //移除用户 public void deleteContactOnBroast(String id) { Map users = ContactsManager.getInstance().getContactList(); User user = users.get(id); users.remove(id); InviteMessgeDao dao = new InviteMessgeDao(contactsView.getBaseActivity()); dao.deleteMessage(id); UserDao userDao = new UserDao(contactsView.getBaseActivity()); userDao.deleteContact(id); contacts.remove(user); HTClient.getInstance().conversationManager().deleteConversationAndMessage(id); } /** * 删除用户 * * @param user */ private void deleteContact(final User user) { final ProgressDialog pd = new ProgressDialog(contactsView.getBaseActivity()); pd.setMessage(contactsView.getBaseContext().getResources().getString(R.string.deleting)); pd.setCanceledOnTouchOutside(false); pd.show(); List params = new ArrayList<>(); params.add(new Param(HTConstant.JSON_KEY_HXID, user.getUsername())); new OkHttpUtils(contactsView.getBaseActivity()).post(params, HTConstant.URL_DELETE_FRIEND, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { pd.dismiss(); int code = jsonObject.getIntValue("code"); switch (code) { case 1: InviteMessgeDao dao = new InviteMessgeDao(contactsView.getBaseActivity()); dao.deleteMessage(user.getUsername()); UserDao userDao = new UserDao(contactsView.getBaseActivity()); userDao.deleteContact(user.getUsername()); HTClient.getInstance().conversationManager().deleteConversationAndMessage(user.getUsername()); sendDeleteCmd(user); Toast.makeText(contactsView.getBaseActivity(), R.string.delete_sucess, Toast.LENGTH_SHORT).show(); break; default: Toast.makeText(contactsView.getBaseActivity(), R.string.Delete_failed, Toast.LENGTH_SHORT).show(); break; } } @Override public void onFailure(String errorMsg) { pd.dismiss(); Toast.makeText(contactsView.getBaseActivity(), R.string.Delete_failed, Toast.LENGTH_SHORT).show(); } }); } private void sendDeleteCmd(User user) { JSONObject userJson = HTApp.getInstance().getUserJson(); CmdMessage customMessage = new CmdMessage(); JSONObject jsonObject = new JSONObject(); jsonObject.put("action", 1003); JSONObject data = new JSONObject(); data.put("userId", userJson.getString("userId")); data.put("nick", userJson.getString("nick")); data.put("avatar", userJson.getString("avatar")); data.put("role", userJson.getString(HTConstant.JSON_KEY_ROLE)); data.put("teamId", userJson.getString("teamId")); jsonObject.put("data", data); customMessage.setMsgId(UUID.randomUUID().toString()); customMessage.setFrom(HTApp.getInstance().getUsername()); customMessage.setTime(System.currentTimeMillis()); customMessage.setTo(user.getUsername()); customMessage.setBody(jsonObject.toJSONString()); customMessage.setChatType(ChatType.singleChat); HTClient.getInstance().chatManager().sendCmdMessage(customMessage, new HTChatManager.HTMessageCallBack() { @Override public void onProgress() { } @Override public void onSuccess() { } @Override public void onFailure() { } }); } @Override public List sortList(List users) { PinyinComparator comparator = new PinyinComparator(); Collections.sort(users, comparator); return users; } public class PinyinComparator implements Comparator { @Override public int compare(User o1, User o2) { String py1 = o1.getInitialLetter(); String py2 = o2.getInitialLetter(); if (py1.equals(py2)) { return o1.getNick().compareTo(o2.getNick()); } else { if ("#".equals(py1)) { return 1; } else if ("#".equals(py2)) { return -1; } return py1.compareTo(py2); } } } @Override public void refreshContactsInServer() { List params = new ArrayList(); new OkHttpUtils(contactsView.getBaseContext()).post(params, HTConstant.URL_FriendList, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { int code = jsonObject.getIntValue("code"); switch (code) { case 1: JSONArray friends = jsonObject.getJSONArray("user"); if (friends != null || friends.size() != 0) { List users = new ArrayList(); for (int i = 0; i < friends.size(); i++) { JSONObject friend = friends.getJSONObject(i); User user = CommonUtils.Json2User(friend); users.add(user); } ContactsManager.getInstance().saveContactList(users); contacts.clear(); contacts.addAll(sortList(users)); contactsView.refresh(); } break; } } @Override public void onFailure(String errorMsg) { } }); } @Override public int getInvitionCount() { int unreadAddressCountTotal = 0; unreadAddressCountTotal = inviteMessgeDao.getUnreadMessagesCount(); return unreadAddressCountTotal; } @Override public int getContactsCount() { return contacts.size(); } @Override public void clearInvitionCount() { inviteMessgeDao.saveUnreadMessageCount(0); contactsView.showInvitionCount(0); } @Override public void refreshContactsInLocal() { contacts.clear(); contacts.addAll(sortList(new ArrayList(ContactsManager.getInstance().getContactList().values()))); contactsView.refresh(); } @Override public void start() { } /** * 移入黑名单 * @param userId */ private void showMoveToBlackDialog(final String userId) { Map users = ContactsManager.getInstance().getContactList(); final User user = users.get(userId); users.remove(userId); getContactsListInDb().remove(user); final ProgressDialog dialog = new ProgressDialog(contactsView.getBaseContext()); dialog.setMessage(contactsView.getBaseContext().getString(R.string.Is_moved_into_blacklist)); dialog.show(); List params = new ArrayList<>(); params.add(new Param(HTConstant.JSON_KEY_HXID,userId)); new OkHttpUtils(contactsView.getBaseContext()).post(params, HTConstant.URL_ADD_BLACKLIST, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { dialog.dismiss(); int code = jsonObject.getIntValue("code"); switch (code){ case 1: InviteMessgeDao dao = new InviteMessgeDao(contactsView.getBaseActivity()); dao.deleteMessage(userId); UserDao userDao = new UserDao(contactsView.getBaseActivity()); userDao.deleteContact(userId); HTClient.getInstance().conversationManager().deleteConversationAndMessage(userId); Toast.makeText(contactsView.getBaseContext(), R.string.Move_into_blacklist_success, Toast.LENGTH_SHORT).show(); break; default: Toast.makeText(contactsView.getBaseContext(), R.string.Move_into_blacklist_failure, Toast.LENGTH_SHORT).show(); break; } } @Override public void onFailure(String errorMsg) { dialog.dismiss(); Toast.makeText(contactsView.getBaseContext(), R.string.Move_into_blacklist_failure, Toast.LENGTH_SHORT).show(); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/contacts/ContactsView.java ================================================ package com.htmessage.yichatopen.activity.main.contacts; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.activity.BaseView; /** * Created by huangfangyi on 2017/6/28. * qq 84543217 */ public interface ContactsView extends BaseView { void showItemDialog(User user); void showSiderBar(); void showInvitionCount(int count); void showContactsCount(int count); void refresh(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/contacts/FragmentContacts.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.activity.main.contacts; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.content.LocalBroadcastManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.addfriends.newfriend.NewFriendsActivity; import com.htmessage.yichatopen.activity.main.details.UserDetailsActivity; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.widget.HTAlertDialog; /** * contact list */ public class FragmentContacts extends Fragment implements View.OnClickListener, ContactsView { private ContactsAdapter adapter; private ListView listView; private Sidebar sidebar; private TextView tv_unread; private TextView tv_total; private ContactsPresenter contactsPresenter; public interface ContactsListener { void showInvitionCount(int count); } private ContactsListener contactsListener; @Override public void onAttach(Context context) { super.onAttach(context); contactsPresenter = new ContactsPresenter(this); if (context instanceof ContactsListener) { contactsListener = (ContactsListener) context; } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_contactlist, container, false); listView = (ListView) root.findViewById(R.id.list); View footerView = LayoutInflater.from(getActivity()).inflate(R.layout.item_contact_list_footer, null); View headView = LayoutInflater.from(getActivity()).inflate(R.layout.item_contact_list_header, null); listView.addHeaderView(headView); listView.addFooterView(footerView); sidebar = (Sidebar) root.findViewById(R.id.sidebar); tv_unread = (TextView) headView.findViewById(R.id.tv_unread); tv_total = (TextView) footerView.findViewById(R.id.tv_total); showSiderBar(); headView.findViewById(R.id.re_newfriends).setOnClickListener(this); headView.findViewById(R.id.re_chatroom).setOnClickListener(this); headView.findViewById(R.id.re_tag).setOnClickListener(this); headView.findViewById(R.id.re_public).setOnClickListener(this); return root; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); adapter = new ContactsAdapter(getActivity(), contactsPresenter.getContactsListInDb()); listView.setAdapter(adapter); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { if (position != 0 && position != adapter.getCount() + 1) { User user = adapter.getItem(position - 1); startActivity(new Intent(getActivity(), UserDetailsActivity.class) .putExtra(HTConstant.KEY_USER_INFO, user.getUserInfo())); } } }); listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (position != 0 && position != adapter.getCount() + 1) { User user = adapter.getItem(position - 1); showItemDialog(user); } return true; } }); contactsPresenter.refreshContactsInServer(); registerBroadReciever(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.re_newfriends: startActivityForResult(new Intent(getActivity(), NewFriendsActivity.class),10086); contactsPresenter.clearInvitionCount(); break; case R.id.re_chatroom: Toast.makeText(getActivity(), R.string.open_notice, Toast.LENGTH_SHORT).show(); break; case R.id.re_tag: break; case R.id.re_public: break; } } @Override public void setPresenter(ContactsPresenter presenter) { } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void showItemDialog(final User user) { HTAlertDialog fxAlertDialog = new HTAlertDialog(getContext(), null, new String[]{getResources().getString(R.string.delete)}); fxAlertDialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { switch (position) { case 0: contactsPresenter.deleteContacts(user.getUsername()); break; case 1: contactsPresenter.moveUserToBlack(user.getUsername()); break; } refresh(); } }); } @Override public void showSiderBar() { sidebar.setVisibility(View.VISIBLE); sidebar.setListView(listView); } @Override public void showInvitionCount(int count) { if (count != 0) { tv_unread.setVisibility(View.VISIBLE); // tv_unread.setText( count+""); } else { tv_unread.setVisibility(View.GONE); } contactsListener.showInvitionCount(count); } @Override public void showContactsCount(int count) { tv_total.setText(count + getString(R.string.more_people)); } @Override public void refresh() { adapter.notifyDataSetChanged(); showContactsCount(contactsPresenter.getContactsCount()); } private class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (IMAction.ACTION_CONTACT_CHANAGED.equals(action)) { contactsPresenter.refreshContactsInLocal(); } else if (IMAction.ACTION_INVITE_MESSAGE.equals(action)) { showInvitionCount(1); } else if (IMAction.CMD_DELETE_FRIEND.equals(action)) { String userId = intent.getStringExtra(HTConstant.JSON_KEY_HXID); contactsPresenter.deleteContactOnBroast(userId); refresh(); } } } private MyBroadcastReceiver myBroadcastReceiver; private void registerBroadReciever() { myBroadcastReceiver = new MyBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(IMAction.ACTION_CONTACT_CHANAGED); intentFilter.addAction(IMAction.ACTION_INVITE_MESSAGE); intentFilter.addAction(IMAction.CMD_DELETE_FRIEND); LocalBroadcastManager.getInstance(getContext()).registerReceiver(myBroadcastReceiver, intentFilter); } @Override public void onDestroy() { try { LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(myBroadcastReceiver); } catch (Exception e) { e.printStackTrace(); } super.onDestroy(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK){ showInvitionCount(0); } super.onActivityResult(requestCode, resultCode, data); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/contacts/Sidebar.java ================================================ /** * Copyright (C) 2013-2014 EaseMob Technologies. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.activity.main.contacts; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Align; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.HeaderViewListAdapter; import android.widget.ListView; import android.widget.TextView; import com.htmessage.yichatopen.R; public class Sidebar extends View{ private Paint paint; private TextView header; private float height; private ListView mListView; private Context context; public void setListView(ListView listView){ mListView = listView; } public Sidebar(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; init(); } private String[] sections = new String[]{"↑","☆","A","B","C","D","E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z","#"}; private void init(){ paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.DKGRAY); paint.setTextAlign(Align.CENTER); paint.setTextSize(getResources().getDimensionPixelSize(R.dimen.sider_bar_textsize)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float center = getWidth() / 2; height = getHeight() / sections.length; for (int i = sections.length - 1; i > -1; i--) { canvas.drawText(sections[i], center, height * (i+1), paint); } } private int sectionForPoint(float y) { int index = (int) (y / height); if(index < 0) { index = 0; } if(index > sections.length - 1){ index = sections.length - 1; } return index; } private void setHeaderTextAndscroll(MotionEvent event){ if (mListView == null) { //check the mListView to avoid NPE. but the mListView shouldn't be null //need to check the call stack later return; } String headerString = sections[sectionForPoint(event.getY())]; header.setText(headerString); HeaderViewListAdapter ha = (HeaderViewListAdapter) mListView.getAdapter(); ContactsAdapter adapter = (ContactsAdapter) ha.getWrappedAdapter(); // ContactAdapter adapter = (ContactAdapter) mListView.getAdapter(); String[] adapterSections = (String[]) adapter.getSections(); try { for (int i = adapterSections.length - 1; i > -1; i--) { if(adapterSections[i].equals(headerString)){ mListView.setSelection(adapter.getPositionForSection(i)); break; } } } catch (Exception e) { Log.e("setHeaderTextAndscroll", e.getMessage()); } } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN:{ if(header == null){ header = (TextView) ((View)getParent()).findViewById(R.id.floating_header); } setHeaderTextAndscroll(event); header.setVisibility(View.VISIBLE); setBackgroundResource(R.drawable.sidebar_background_pressed); return true; } case MotionEvent.ACTION_MOVE:{ setHeaderTextAndscroll(event); return true; } case MotionEvent.ACTION_UP: header.setVisibility(View.INVISIBLE); setBackgroundColor(Color.TRANSPARENT); return true; case MotionEvent.ACTION_CANCEL: header.setVisibility(View.INVISIBLE); setBackgroundColor(Color.TRANSPARENT); return true; } return super.onTouchEvent(event); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/conversation/BaseConversationPresenter.java ================================================ package com.htmessage.yichatopen.activity.main.conversation; import com.htmessage.sdk.model.HTConversation; import com.htmessage.sdk.model.HTMessage; import com.htmessage.yichatopen.activity.BasePresenter; import java.util.List; /** * Created by huangfangyi on 2017/6/27. * qq 84543217 */ public interface BaseConversationPresenter extends BasePresenter { List getAllConversations(); void deleteConversation(HTConversation conversation); void setTopConversation(HTConversation conversation); void cancelTopConversation(HTConversation conversation); void refreshConversations(); void onNewMsgReceived(HTMessage htmessage); int getUnreadMsgCount(); void markAllMessageRead(HTConversation conversation); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/conversation/ConversationAdapter.java ================================================ package com.htmessage.yichatopen.activity.main.conversation; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.activity.chat.weight.emoji.SmileUtils; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.utils.DateUtils; import com.htmessage.sdk.model.HTConversation; import com.htmessage.sdk.model.HTMessage; import com.htmessage.sdk.model.HTMessageTextBody; import java.util.Date; import java.util.List; public class ConversationAdapter extends BaseAdapter { private List htConversations; private Context context; public ConversationAdapter(Context context, List htConversations) { this.context = context; this.htConversations = htConversations; } @Override public int getCount() { return htConversations.size(); } @Override public HTConversation getItem(int position) { return htConversations.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = LayoutInflater.from(context).inflate(R.layout.item_conversation_single, parent, false); } ViewHolder holder = (ViewHolder) convertView.getTag(); if (holder == null) { holder = new ViewHolder(); holder.tv_name = (TextView) convertView.findViewById(R.id.name); holder.tv_content = (TextView) convertView.findViewById(R.id.message); holder.tv_time = (TextView) convertView.findViewById(R.id.time); holder.tv_unread = (TextView) convertView.findViewById(R.id.unread_msg_number); holder.ivAvatar = (ImageView) convertView.findViewById(R.id.avatar); holder.tv_group_tag=(TextView) convertView.findViewById(R.id.tv_group_tag); holder.re_main=(RelativeLayout) convertView.findViewById(R.id.re_main); convertView.setTag(holder); } HTConversation htConversation = getItem(position); HTMessage htMessage = null; List messages = htConversation.getAllMessages(); if (messages.size() > 0) { htMessage = htConversation.getLastMessage(); } String userId = htConversation.getUserId(); User user = ContactsManager.getInstance().getContactList().get(userId); holder.tv_group_tag.setVisibility(View.INVISIBLE); if (user != null) { holder.tv_name.setText(user.getNick()); Glide.with(context).load(user.getAvatar()).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(holder.ivAvatar); } else { holder.tv_name.setText(userId); Glide.with(context).load(R.drawable.default_avatar).into(holder.ivAvatar); } if (htConversation.getUnReadCount() > 0) { // show unread message count holder.tv_unread.setText(String.valueOf(htConversation.getUnReadCount())); holder.tv_unread.setVisibility(View.VISIBLE); } else { holder.tv_unread.setVisibility(View.INVISIBLE); } if (htMessage != null) { holder.tv_content.setText(SmileUtils.getSmiledText(context, getContent(htMessage)), TextView.BufferType.SPANNABLE); holder.tv_time.setText(DateUtils.getTimestampString(new Date(htMessage.getTime()))); }else{ holder.tv_content.setText(""); holder.tv_time.setText(DateUtils.getTimestampString(new Date(htConversation.getTime()))); } if(htConversation.getTopTimestamp()!=0){ holder.re_main.setBackgroundResource(R.drawable.list_item_bg_gray); }else{ holder.re_main.setBackgroundResource(R.drawable.list_item_bg_white); } return convertView; } private static class ViewHolder { /** * 和谁的聊天记录 */ TextView tv_name; /** * 消息未读数 */ TextView tv_unread; /** * 最后一条消息的内容 */ TextView tv_content; /** * 最后一条消息的时间 */ TextView tv_time; ImageView ivAvatar; //群组的标识 TextView tv_group_tag; RelativeLayout re_main; } protected final static String[] msgs = { "[图片消息]", "[语音消息]"}; private String getContent(HTMessage message) { String notifyText = ""; if (message.getType() == null) { return ""; } switch (message.getType()) { case TEXT: HTMessageTextBody textBody = (HTMessageTextBody) message.getBody(); String content = textBody.getContent(); if (content != null) { notifyText += content; } else { notifyText += msgs[0]; } break; case IMAGE: notifyText += msgs[0]; break; case VOICE: notifyText += msgs[1]; break; } return notifyText; } // /** // * 根据消息内容和消息类型获取消息内容提示 // * // * @param message // * @param context // * @return // */ // private String getMessageDigest(HTMessage message, Context context) { // String digest = ""; // switch (message.getType()) { // case LOCATION: // 位置消息 // if (message.getDirect() == HTMessage.Direct.RECEIVE) { // // digest = getStrng(context, R.string.location_recv); // digest = String.format(digest, message.getFrom()); // return digest; // } else { // // digest = EasyUtils.getAppResourceString(context, // // "location_prefix"); // digest = getStrng(context, R.string.location_prefix); // } // break; // case IMAGE: // 图片消息 // // digest = getStrng(context, R.string.picture); // // break; // case VOICE:// 语音消息 // digest = getStrng(context, R.string.voice); // break; // case VIDEO: // 视频消息 // digest = getStrng(context, R.string.video); // break; // case TXT: // 文本消息 // if (!message.getBooleanAttribute( // Constant.MESSAGE_ATTR_IS_VOICE_CALL, false)) { // TextMessageBody txtBody = (TextMessageBody) message.getBody(); // digest = txtBody.getMessage(); // } else { // TextMessageBody txtBody = (TextMessageBody) message.getBody(); // digest = getStrng(context, R.string.voice_call) // + txtBody.getMessage(); // } // break; // case FILE: // 普通文件消息 // digest = getStrng(context, R.string.file); // break; // default: // System.err.println("error, unknow type"); // return ""; // } // // return digest; // } String getStrng(Context context, int resId) { return context.getResources().getString(resId); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/conversation/ConversationFragment.java ================================================ package com.htmessage.yichatopen.activity.main.conversation; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.content.LocalBroadcastManager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.model.HTConversation; import com.htmessage.sdk.utils.MessageUtils; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.activity.chat.ChatActivity; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.widget.HTAlertDialog; public class ConversationFragment extends Fragment implements ConversationView { private ListView listView; private ConversationAdapter adapter; public RelativeLayout errorItem; public TextView errorText; public NewMeesageListener mListener; private ConversationPresenter conPresenter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_home, container, false); errorItem = (RelativeLayout) root.findViewById(R.id.rl_error_item); errorText = (TextView) errorItem.findViewById(R.id.tv_connect_errormsg); listView = (ListView) root.findViewById(R.id.list); return root; } @Override public void setPresenter(ConversationPresenter presenter) { // conPresenter = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void showItemDialog(final HTConversation htConversation) { String topTitle = getString(R.string.stick_conversation); if (htConversation.getTopTimestamp() != 0) { //已经置顶的会话,显示取消置顶 topTitle = getString(R.string.cancle_stick_conversation); } HTAlertDialog fxAlertDialog = new HTAlertDialog(getActivity(), null, new String[]{getString(R.string.delete), topTitle}); fxAlertDialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { if (position == 0) { conPresenter.deleteConversation(htConversation); } else if (position == 1) { if (htConversation.getTopTimestamp() != 0) {//如果是置顶过的 就取消置顶 conPresenter.cancelTopConversation(htConversation); } else { //如果是没有置顶的就置顶 conPresenter.setTopConversation(htConversation); } } } }); } @Override public void refresh() { conPresenter.refreshConversations(); adapter.notifyDataSetChanged(); onUnreadMsgChange(); } public interface NewMeesageListener { //返回多少条未读消息 void onUnReadMsgs(int count); } @Override public void onAttach(Context context) { super.onAttach(context); conPresenter = new ConversationPresenter(this); if (context instanceof NewMeesageListener) { mListener = ((NewMeesageListener) context); onUnreadMsgChange(); } } private void onUnreadMsgChange() { mListener.onUnReadMsgs(conPresenter.getUnreadMsgCount()); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); adapter = new ConversationAdapter(getActivity(), conPresenter.getAllConversations()); // 设置adapter listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { HTConversation htConversation = adapter.getItem(position); conPresenter.markAllMessageRead(htConversation); Intent intent = new Intent(getActivity(), ChatActivity.class).putExtra("userId", htConversation.getUserId()); if (htConversation.getChatType() == ChatType.groupChat) { intent.putExtra("chatType", MessageUtils.CHAT_GROUP); } startActivity(intent); } }); listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView adapterView, View view, int i, long l) { HTConversation htConversation = adapter.getItem(i); showItemDialog(htConversation); return true; } }); registerConnectionBroadCast(); } private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (IMAction.ACTION_CONNECTION_CHANAGED.equals(intent.getAction())) { boolean isConnected = intent.getBooleanExtra("state", true); if (isConnected) { errorItem.setVisibility(View.GONE); } else { errorItem.setVisibility(View.VISIBLE); if (CommonUtils.isNetWorkConnected(getContext())) { errorText.setText(R.string.can_not_connect_chat_server_connection); } else { errorText.setText(R.string.the_current_network); } } } else if (IMAction.ACTION_NEW_MESSAGE.equals(intent.getAction()) || IMAction.ACTION_MESSAGE_WITHDROW.equals(intent.getAction()) || IMAction.ACTION_REMOVED_FROM_GROUP.equals(intent.getAction()) || IMAction.CMD_DELETE_FRIEND.equals(intent.getAction())) { // 收到新消息,收到撤回消息,收到群相关消息-被提出群聊 refresh(); } } }; private void registerConnectionBroadCast() { IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(IMAction.ACTION_CONNECTION_CHANAGED); intentFilter.addAction(IMAction.ACTION_NEW_MESSAGE); intentFilter.addAction(IMAction.ACTION_MESSAGE_WITHDROW); intentFilter.addAction(IMAction.CMD_DELETE_FRIEND); LocalBroadcastManager.getInstance(getActivity()).registerReceiver(broadcastReceiver, intentFilter); } @Override public void onDestroy() { LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(broadcastReceiver); super.onDestroy(); } @Override public void onResume() { super.onResume(); refresh(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/conversation/ConversationPresenter.java ================================================ package com.htmessage.yichatopen.activity.main.conversation; import android.content.Context; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.model.HTConversation; import com.htmessage.sdk.model.HTMessage; import java.util.List; /** * Created by huangfangyi on 2017/6/27. * qq 84543217 */ public class ConversationPresenter implements BaseConversationPresenter { private ConversationView conversationView; private Context context; private List allConversations; public ConversationPresenter(ConversationView view) { conversationView = view; conversationView.setPresenter(this); context = conversationView.getBaseContext(); allConversations = HTClient.getInstance().conversationManager().getAllConversations(); } @Override public void start() { } @Override public List getAllConversations() { return allConversations; } @Override public void deleteConversation(HTConversation htConversation) { allConversations.remove(htConversation); HTClient.getInstance().messageManager().deleteUserMessage(htConversation.getUserId(), true); conversationView.refresh(); } @Override public void setTopConversation(HTConversation htConversation) { HTClient.getInstance().conversationManager().setConversationTop(htConversation,System.currentTimeMillis()); conversationView.refresh(); } @Override public void cancelTopConversation(HTConversation htConversation) { HTClient.getInstance().conversationManager().setConversationTop(htConversation,0); conversationView.refresh(); } @Override public void refreshConversations() { allConversations.clear(); allConversations.addAll(HTClient.getInstance().conversationManager().getAllConversations()); } @Override public void onNewMsgReceived(HTMessage htMessage) { } @Override public int getUnreadMsgCount() { int unreadMsgCountTotal = 0; if (allConversations.size() != 0 && allConversations != null) { for (int i = 0; i < allConversations.size(); i++) { unreadMsgCountTotal += allConversations.get(i).getUnReadCount(); } } return unreadMsgCountTotal; } @Override public void markAllMessageRead(HTConversation conversation) { HTClient.getInstance().conversationManager().markAllMessageRead(conversation.getUserId()); conversationView.refresh(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/conversation/ConversationView.java ================================================ package com.htmessage.yichatopen.activity.main.conversation; import com.htmessage.sdk.model.HTConversation; import com.htmessage.yichatopen.activity.BaseView; /** * Created by huangfangyi on 2017/6/27. * qq 84543217 */ public interface ConversationView extends BaseView{ void showItemDialog(HTConversation htConversation); void refresh(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/details/UserDetailesFragment.java ================================================ package com.htmessage.yichatopen.activity.main.details; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.addfriends.add.end.AddFriendsFinalActivity; import com.htmessage.yichatopen.activity.chat.ChatActivity; /** * 项目名称:yichat0504 * 类描述:UserDetailesFragment 描述: * 创建人:songlijie * 创建时间:2017/7/10 11:52 * 邮箱:814326663@qq.com */ public class UserDetailesFragment extends Fragment implements UserDetailsView ,View.OnClickListener{ private UserDetailsPrester prester; private Button btnMsg,btnAdd; private ImageView iv_avatar,iv_sex; private TextView tv_name,tv_yichat_number,tv_mobile,tv_region,tv_mixin; private RelativeLayout rl_region,rl_yichat_id,re_mobile; private Dialog dialog; private JSONObject userJson; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); dialog = HTApp.getInstance().createLoadingDialog(getBaseActivity(),getString(R.string.now_refresh_msg)); dialog.setCancelable(true); dialog.setCanceledOnTouchOutside(true); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View infoView = inflater.inflate(R.layout.activity_userinfo, container, false); initView(infoView); getData(); setLstenter(); return infoView; } private void getData() { if (!TextUtils.isEmpty(getFxid())){ prester.refreshInfo(getFxid(),true); return; } if (getUserJson() == null){ getBaseActivity().finish(); return; } showUi(getUserJson()); if (prester.isFriend(getUserJson().getString(HTConstant.JSON_KEY_HXID))){ prester.refreshInfo(getUserJson().getString(HTConstant.JSON_KEY_HXID),false); }else{ prester.refreshInfo(getUserJson().getString(HTConstant.JSON_KEY_HXID),true); } } private void setLstenter() { btnAdd.setOnClickListener(this); btnMsg.setOnClickListener(this); } private void initView(View infoView) { btnMsg = (Button) infoView.findViewById(R.id.btn_msg); btnAdd= (Button) infoView.findViewById(R.id.btn_add); iv_avatar = (ImageView) infoView.findViewById(R.id.iv_avatar); iv_sex = (ImageView) infoView.findViewById(R.id.iv_sex); tv_name = (TextView) infoView.findViewById(R.id.tv_name); tv_yichat_number = (TextView) infoView.findViewById(R.id.tv_yichat_number); tv_mobile = (TextView) infoView.findViewById(R.id.tv_mobile); tv_region = (TextView) infoView.findViewById(R.id.tv_region); tv_mixin = (TextView) infoView.findViewById(R.id.tv_mixin); rl_region = (RelativeLayout) infoView.findViewById(R.id.rl_region); rl_yichat_id = (RelativeLayout) infoView.findViewById(R.id.rl_yichat_id); re_mobile = (RelativeLayout) infoView.findViewById(R.id.re_mobile); } @Override public void onRefreshSuccess(String msg) { Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show(); } @Override public void onRefreshFailed(String error) { Toast.makeText(getBaseContext(), error, Toast.LENGTH_SHORT).show(); } @Override public String getFxid() { return getBaseActivity().getIntent().getStringExtra(HTConstant.JSON_KEY_HXID); } @Override public JSONObject getUserJson() { String extra = getBaseActivity().getIntent().getStringExtra(HTConstant.KEY_USER_INFO); JSONObject object = JSONObject.parseObject(extra); return object; } @Override public void showDialog() { if (dialog!=null){ dialog.show(); } } @Override public void hintDialog() { if (dialog!=null){ dialog.dismiss(); } } @Override public void showUi(JSONObject object) { this.userJson = object; String hxid = object.getString(HTConstant.JSON_KEY_HXID); String yichatId = object.getString(HTConstant.JSON_KEY_FXID); String Tel = object.getString(HTConstant.JSON_KEY_TEL); String province = object.getString(HTConstant.JSON_KEY_PROVINCE); String city = object.getString(HTConstant.JSON_KEY_CITY); String sign = object.getString(HTConstant.JSON_KEY_SIGN); if (!TextUtils.isEmpty(yichatId)){ tv_mixin.setText(getString(R.string.app_id)+yichatId); }else{ tv_mixin.setText(getString(R.string.app_id)+getString(R.string.not_set)); } if (!TextUtils.isEmpty(sign)) { tv_yichat_number.setText(sign); } else { tv_yichat_number.setText(R.string.not_set); } if (!TextUtils.isEmpty(Tel)){ tv_mobile.setText(Tel); }else{ tv_mobile.setText(R.string.not_set); } if (!TextUtils.isEmpty(province) && !TextUtils.isEmpty(city)){ tv_region.setText(province+" "+city); if (province.equals(city)) { tv_region.setText(city); } }else{ tv_region.setText(R.string.not_set); } String nick = object.getString(HTConstant.JSON_KEY_NICK); String avatarUrl =object.getString(HTConstant.JSON_KEY_AVATAR); if(!TextUtils.isEmpty(avatarUrl)){ if (!avatarUrl.contains("http:")){ avatarUrl = HTConstant.URL_AVATAR+avatarUrl; } } Glide.with(this).load(avatarUrl).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(iv_avatar); tv_name.setText(nick); String sex = object.getString(HTConstant.JSON_KEY_SEX); if (!TextUtils.isEmpty(sex)){ if ("1".equals(sex) || sex.equals(getString(R.string.male))) { iv_sex.setImageResource(R.drawable.icon_male); } else if ("0".equals(sex) || sex.equals(getString(R.string.female))){ iv_sex.setImageResource(R.drawable.icon_female); } }else{ iv_sex.setImageResource(R.drawable.icon_male); } //资料是自己 if (prester.isMe(hxid)) { btnMsg.setVisibility(View.GONE); btnAdd.setVisibility(View.GONE); return; } //不是好友的 if (!prester.isFriend(hxid)) { btnMsg.setVisibility(View.GONE); btnAdd.setVisibility(View.VISIBLE); return; } } @Override public void setPresenter(UserDetailsPrester presenter) { this.prester = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_add: startActivity(new Intent(getBaseActivity(), AddFriendsFinalActivity.class).putExtra(HTConstant.KEY_USER_INFO, userJson.toJSONString())); break; case R.id.btn_msg: startActivity(new Intent(getBaseActivity(), ChatActivity.class).putExtra("userId",userJson.getString(HTConstant.JSON_KEY_HXID))); break; } } @Override public void onDestroy() { prester.onDestory(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/details/UserDetailsActivity.java ================================================ package com.htmessage.yichatopen.activity.main.details; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.R; /** * Created by huangfangyi on 2016/7/6.\ * QQ:84543217 */ public class UserDetailsActivity extends BaseActivity { /** * * 用户详情页接收两种传值 * 1:用户完整资料的JSON字符串-userInfo-这种情况如果是好友进行刷新 * 2:只传用户的hxid,这种情况直接从网络取数据显示-如果是好友,刷新资料 * */ @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.activity_base); setTitle(R.string.Detailed_information); UserDetailesFragment fragment = (UserDetailesFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (fragment == null){ fragment = new UserDetailesFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame,fragment); transaction.commit(); } new UserDetailsPrester(fragment); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/details/UserDetailsBasePrester.java ================================================ package com.htmessage.yichatopen.activity.main.details; import com.htmessage.yichatopen.activity.BasePresenter; /** * 项目名称:yichat0504 * 类描述:UserDetailsBasePrester 描述: * 创建人:songlijie * 创建时间:2017/7/10 11:35 * 邮箱:814326663@qq.com */ public interface UserDetailsBasePrester extends BasePresenter { void onDestory(); void refreshInfo(String userId, boolean backTask); boolean isMe(String userId); boolean isFriend(String userId); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/details/UserDetailsPrester.java ================================================ package com.htmessage.yichatopen.activity.main.details; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.domain.UserDao; import com.htmessage.yichatopen.manager.ContactsManager; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import java.util.ArrayList; import java.util.List; /** * 项目名称:yichat0504 * 类描述:UserDetailsPrester 描述: * 创建人:songlijie * 创建时间:2017/7/10 11:40 * 邮箱:814326663@qq.com */ public class UserDetailsPrester implements UserDetailsBasePrester { private UserDetailsView detailsView; public UserDetailsPrester(UserDetailsView detailsView) { this.detailsView = detailsView; this.detailsView.setPresenter(this); } @Override public void onDestory() { detailsView = null; } @Override public void refreshInfo(final String userId, final boolean backTask) { if (!backTask){ detailsView.showDialog(); } List parms = new ArrayList<>(); parms.add(new Param("userId", userId)); new OkHttpUtils(detailsView.getBaseContext()).post(parms, HTConstant.URL_Get_UserInfo, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { int code = jsonObject.getInteger("code"); if (!backTask){ detailsView.hintDialog(); } switch (code){ case 1: JSONObject json = jsonObject.getJSONObject("user"); //刷新ui if (isFriend(userId)) { User user = CommonUtils.Json2User(json); UserDao dao = new UserDao(detailsView.getBaseContext()); dao.saveContact(user); ContactsManager.getInstance().getContactList().put(user.getUsername(), user); } detailsView.showUi(json); break; default: detailsView.hintDialog(); break; } } @Override public void onFailure(String errorMsg) { detailsView.onRefreshFailed(errorMsg); if (!backTask){ detailsView.hintDialog(); } } }); } @Override public boolean isMe(String userId) { return HTApp.getInstance().getUsername().equals(userId); } @Override public boolean isFriend(String userId) { return ContactsManager.getInstance().getContactList().containsKey(userId); } @Override public void start() { } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/details/UserDetailsView.java ================================================ package com.htmessage.yichatopen.activity.main.details; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.activity.BaseView; /** * 项目名称:yichat0504 * 类描述:UserDetailsView 描述: * 创建人:songlijie * 创建时间:2017/7/10 11:45 * 邮箱:814326663@qq.com */ public interface UserDetailsView extends BaseView { void onRefreshSuccess(String msg); void onRefreshFailed(String error); String getFxid(); JSONObject getUserJson(); void showDialog(); void hintDialog(); void showUi(JSONObject object); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/find/FragmentFind.java ================================================ package com.htmessage.yichatopen.activity.main.find; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.RelativeLayout; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.ScanCaptureActivity; import com.htmessage.yichatopen.activity.main.find.recentlypeople.PeopleRecentlyActivity; public class FragmentFind extends Fragment implements View.OnClickListener { private RelativeLayout rl_friends, re_qrcode, rl_near; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_find, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { if (savedInstanceState != null && savedInstanceState.getBoolean("isConflict", false)) return; super.onActivityCreated(savedInstanceState); initView(); initData(); setListener(); } private void setListener() { rl_friends.setOnClickListener(this); re_qrcode.setOnClickListener(this); rl_near.setOnClickListener(this); } private void initData() { } private void initView() { rl_friends = (RelativeLayout) getView().findViewById(R.id.rl_friends); re_qrcode = (RelativeLayout) getView().findViewById(R.id.re_qrcode); rl_near = (RelativeLayout) getView().findViewById(R.id.rl_near); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.rl_friends://志工圈 break; case R.id.re_qrcode: //扫一扫 startActivity(new Intent(getActivity(), ScanCaptureActivity.class)); break; case R.id.rl_near://最近在线 startActivity(new Intent(getActivity(), PeopleRecentlyActivity.class)); break; } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/find/recentlypeople/PeopleRecentlyActivity.java ================================================ package com.htmessage.yichatopen.activity.main.find.recentlypeople; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; /** * 项目名称:yichat0504 * 类描述:PeopleRecentlyActivity 描述: * 创建人:songlijie * 创建时间:2017/7/5 16:53 * 邮箱:814326663@qq.com */ public class PeopleRecentlyActivity extends BaseActivity { @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.activity_base); setTitle(R.string.people_time); PeopleRecentlyFragment peopleRecentlyFragment = (PeopleRecentlyFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (peopleRecentlyFragment == null) { // Create the fragment peopleRecentlyFragment = new PeopleRecentlyFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame, peopleRecentlyFragment); transaction.commit(); } PeopleRecentlyPrestener prestener = new PeopleRecentlyPrestener(peopleRecentlyFragment); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/find/recentlypeople/PeopleRecentlyAdapter.java ================================================ package com.htmessage.yichatopen.activity.main.find.recentlypeople; import android.content.Context; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.utils.DateUtils; import java.util.ArrayList; import java.util.List; /** * 项目名称:yichat0504 * 类描述:PeopleRecentlyAdapter 描述: * 创建人:songlijie * 创建时间:2017/7/5 17:17 * 邮箱:814326663@qq.com */ public class PeopleRecentlyAdapter extends BaseAdapter { private Context context; private List peoples = new ArrayList<>(); public PeopleRecentlyAdapter(Context context, List peoples) { this.context = context; this.peoples = peoples; } @Override public int getCount() { return peoples.size(); } @Override public JSONObject getItem(int position) { return peoples.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = View.inflate(parent.getContext(), R.layout.item_people_recently, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } JSONObject jsonObject = peoples.get(position); String nick = jsonObject.getString(HTConstant.JSON_KEY_NICK); if (TextUtils.isEmpty(nick)) { nick = jsonObject.getString(HTConstant.JSON_KEY_HXID); } String recentlytime = jsonObject.getString("recentlyTime"); String serviceTime = jsonObject.getString("serviceTime"); long l = Long.valueOf(recentlytime) * 1000; long l2 = Long.valueOf(serviceTime) * 1000; String difference = DateUtils.getTimeDifference(parent.getContext(),l, l2); String avatar = null; if (jsonObject.containsKey(HTConstant.JSON_KEY_AVATAR)) { avatar = jsonObject.getString(HTConstant.JSON_KEY_AVATAR); if (!TextUtils.isEmpty(avatar)) { if (!avatar.contains("http")) { avatar = HTConstant.baseOssUrl + avatar; } } } String sex = jsonObject.getString(HTConstant.JSON_KEY_SEX); if (!TextUtils.isEmpty(sex)) { if ("1".equals(sex) || sex.equals(parent.getContext().getString(R.string.male))) { holder.iv_sex.setImageResource(R.drawable.icon_male); } else if ("0".equals(sex) || sex.equals(parent.getContext().getString(R.string.female))) { holder.iv_sex.setImageResource(R.drawable.icon_female); } } else { holder.iv_sex.setImageResource(R.drawable.icon_male); } String sign = jsonObject.getString(HTConstant.JSON_KEY_SIGN); if (!TextUtils.isEmpty(sign)) { holder.ll_sign.setVisibility(View.VISIBLE); holder.tv_sign.setText(sign); } else { holder.ll_sign.setVisibility(View.GONE); } holder.tv_name.setText(nick); holder.tv_time.setText(difference);//DateUtils.getStringTime(Long.valueOf(recentlytime) * 1000) Glide.with(parent.getContext()).load(avatar).placeholder(R.drawable.default_avatar).error(R.drawable.default_avatar).diskCacheStrategy(DiskCacheStrategy.ALL).into(holder.iv_avatar); return convertView; } private class ViewHolder { private TextView tv_name, tv_time, tv_sign; private ImageView iv_avatar, iv_sex; private LinearLayout ll_sign; public ViewHolder(View view) { tv_name = (TextView) view.findViewById(R.id.tv_name); tv_time = (TextView) view.findViewById(R.id.tv_time); tv_sign = (TextView) view.findViewById(R.id.tv_sign); iv_avatar = (ImageView) view.findViewById(R.id.iv_avatar); iv_sex = (ImageView) view.findViewById(R.id.iv_sex); ll_sign = (LinearLayout) view.findViewById(R.id.ll_sign); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/find/recentlypeople/PeopleRecentlyBasePrestener.java ================================================ package com.htmessage.yichatopen.activity.main.find.recentlypeople; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.activity.BasePresenter; /** * 项目名称:yichat0504 * 类描述:PeopleRecentlyBasePrestener 描述: * 创建人:songlijie * 创建时间:2017/7/5 16:28 * 邮箱:814326663@qq.com */ public interface PeopleRecentlyBasePrestener extends BasePresenter { void requestData(int page,int pageSize,boolean loadMore); void onListClickListener(JSONObject object); void onDestory(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/find/recentlypeople/PeopleRecentlyFragment.java ================================================ package com.htmessage.yichatopen.activity.main.find.recentlypeople; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.widget.swipyrefresh.SwipyRefreshLayout; import java.util.ArrayList; import java.util.List; /** * 项目名称:yichat0504 * 类描述:PeopleRecentlyFragment 描述: * 创建人:songlijie * 创建时间:2017/7/5 16:54 * 邮箱:814326663@qq.com */ public class PeopleRecentlyFragment extends Fragment implements PeopleRecentlyView,AdapterView.OnItemClickListener ,SwipyRefreshLayout.OnRefreshListener{ private PeopleRecentlyPrestener prestener; private Dialog diallog; private SwipyRefreshLayout swipyrefresh; private ListView listview_people_recently; private List peoples = new ArrayList<>(); private PeopleRecentlyAdapter adapter; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); diallog = HTApp.getInstance().createLoadingDialog(getBaseContext(),getString(R.string.loading)); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View timeview = inflater.inflate(R.layout.activity_people_recently, container, false); initView(timeview); initData(); setListener(); return timeview; } private void setListener() { listview_people_recently.setOnItemClickListener(this); swipyrefresh.setOnRefreshListener(this); } private void initData() { adapter = new PeopleRecentlyAdapter(getBaseContext(), peoples); listview_people_recently.setAdapter(adapter); onRefresh(1); } private void initView(View timeview) { swipyrefresh = (SwipyRefreshLayout)timeview.findViewById(R.id.swipyrefresh); listview_people_recently = (ListView) timeview.findViewById(R.id.listview_people_recently); } @Override public void showLoadingDialog() { if (diallog!=null){ diallog.show(); } } @Override public void hideLoadingDialog() { if (diallog!=null){ diallog.dismiss(); } swipyrefresh.setRefreshing(false); } @Override public void onRequestSuccess(List peopleList) { swipyrefresh.setRefreshing(false); peoples.addAll(peopleList); adapter.notifyDataSetChanged(); } @Override public void onRequestFailed(String errorMsg) { swipyrefresh.setRefreshing(false); Toast.makeText(getBaseContext(), errorMsg, Toast.LENGTH_SHORT).show(); } @Override public void setPresenter(PeopleRecentlyPrestener presenter) { this.prestener = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void onItemClick(AdapterView parent, View view, int position, long id) { JSONObject item = adapter.getItem(position); prestener.onListClickListener(item); } @Override public void onRefresh(int index) { peoples.clear(); index = 1; prestener.requestData(index,20,false); } @Override public void onLoad(int index) { index++; prestener.requestData(index,20,true); } @Override public void onDestroy() { prestener.onDestory(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/find/recentlypeople/PeopleRecentlyPrestener.java ================================================ package com.htmessage.yichatopen.activity.main.find.recentlypeople; import android.content.Intent; import android.util.Log; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.main.details.UserDetailsActivity; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import java.util.ArrayList; import java.util.List; /** * 项目名称:yichat0504 * 类描述:PeopleRecentlyPrestener 描述: * 创建人:songlijie * 创建时间:2017/7/5 16:34 * 邮箱:814326663@qq.com */ public class PeopleRecentlyPrestener implements PeopleRecentlyBasePrestener { private PeopleRecentlyView timeView; public PeopleRecentlyPrestener(PeopleRecentlyView timeView) { this.timeView = timeView; this.timeView.setPresenter(this); } @Override public void requestData(int page, int pageSize, final boolean loadMore) { timeView.showLoadingDialog(); List params = new ArrayList<>(); params.add(new Param("currentPage",String.valueOf(page))); params.add(new Param("pageSize",String.valueOf(pageSize))); new OkHttpUtils(timeView.getBaseContext()).post(params, HTConstant.URL_GET_RECENTLY_PEOPLE, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { timeView.hideLoadingDialog(); int code = jsonObject.getIntValue("code"); switch (code){ case 1: List peoples = new ArrayList(); JSONArray data = jsonObject.getJSONArray("data"); if (data!=null && data.size()!=0){ for (int i = 0; i { void showLoadingDialog(); void hideLoadingDialog(); void onRequestSuccess(List peoples); void onRequestFailed(String errorMsg); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/password/PasswordBasePrester.java ================================================ package com.htmessage.yichatopen.activity.main.password; import com.htmessage.yichatopen.activity.BasePresenter; /** * 项目名称:HTOpen * 类描述:PasswordBasePrester 描述: * 创建人:songlijie * 创建时间:2017/7/7 14:59 * 邮箱:814326663@qq.com */ public interface PasswordBasePrester extends BasePresenter { void sendSMSCode(String mobile,String countryName,String countryCode); void resetPassword(String cacheCode,String smsCode,String password,String confimPwd,String mobile); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/password/PasswordPrester.java ================================================ package com.htmessage.yichatopen.activity.main.password; import android.app.ProgressDialog; import android.content.Intent; import android.text.TextUtils; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.htmessage.sdk.client.HTClient; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.activity.login.LoginActivity; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import com.htmessage.yichatopen.utils.Validator; import java.util.ArrayList; import java.util.List; /** * 项目名称:HTOpen * 类描述:PasswordPrester 描述: * 创建人:songlijie * 创建时间:2017/7/7 15:07 * 邮箱:814326663@qq.com */ public class PasswordPrester implements PasswordBasePrester { private String TAG = PasswordPrester.class.getSimpleName(); private PasswordView passwordView; public PasswordPrester(PasswordView passwordView) { this.passwordView = passwordView; this.passwordView.setPresenter(this); } @Override public void sendSMSCode(String mobile, String countryName, String countryCode) { if (TextUtils.isEmpty(mobile)) { Toast.makeText(passwordView.getBaseContext(), R.string.mobile_not_be_null, Toast.LENGTH_SHORT).show(); return; } if (countryName.equals(passwordView.getBaseContext().getString(R.string.china)) && countryCode.equals(passwordView.getBaseContext().getString(R.string.country_code))){ if (!Validator.isMobile(mobile)) { Toast.makeText(passwordView.getBaseContext(), R.string.please_input_true_mobile, Toast.LENGTH_SHORT).show(); return; } passwordView.startTimeDown(); }else{ passwordView.onSendSMSCodeSuccess("1234"); } } @Override public void resetPassword(String cacheCode, String smsCode, String password, String confimPwd, String mobile) { if (TextUtils.isEmpty(mobile)) { Toast.makeText(passwordView.getBaseContext(), R.string.mobile_not_be_null, Toast.LENGTH_SHORT).show(); return; } // if (TextUtils.isEmpty(smsCode) || TextUtils.isEmpty(cacheCode)) { // Toast.makeText(passwordView.getBaseContext(), R.string.please_input_code, Toast.LENGTH_SHORT).show(); // return; // } if (TextUtils.isEmpty(password) || TextUtils.isEmpty(confimPwd)) { Toast.makeText(passwordView.getBaseContext(), R.string.new_password_cannot_be_empty, Toast.LENGTH_SHORT).show(); return; } // if (!cacheCode.equals(smsCode)){ // Toast.makeText(passwordView.getBaseContext(), R.string.code_is_wrong, Toast.LENGTH_SHORT).show(); // return; // } if (!password.equals(confimPwd)) { Toast.makeText(passwordView.getBaseContext(), R.string.Two_input_password, Toast.LENGTH_SHORT).show(); return; } final ProgressDialog progressDialog = new ProgressDialog(passwordView.getBaseContext()); progressDialog.setMessage(passwordView.getBaseContext().getString(R.string.are_reset_password)); progressDialog.setCanceledOnTouchOutside(false); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDialog.show(); List params = new ArrayList(); params.add(new Param("newPassword", password)); params.add(new Param("tel", mobile)); new OkHttpUtils(passwordView.getBaseContext()).post(params, HTConstant.URL_RESETPASSWORD, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { progressDialog.dismiss(); int code = jsonObject.getIntValue("code"); switch(code){ case 1: passwordView.clearCacheCode(); passwordView.onResetSuccess(passwordView.getBaseContext().getString(R.string.password_reset_success)); logOut(passwordView.getIsReset()); break; default: passwordView.onResetFailed(passwordView.getBaseContext().getString(R.string.password_reset_failed)); break; } } @Override public void onFailure(String errorMsg) { progressDialog.dismiss(); passwordView.onResetFailed(passwordView.getBaseContext().getString(R.string.password_reset_failed)); } }); } private void logOut(boolean isReset) { if (isReset){ HTClient.getInstance().logout(new HTClient.HTCallBack() { @Override public void onSuccess() { passwordView.getBaseActivity().runOnUiThread(new Runnable() { public void run() { // show login scree HTApp.getInstance().setUserJson(null); HTApp.getInstance().finishActivities(); passwordView.getBaseActivity().startActivity(new Intent(passwordView.getBaseActivity(), LoginActivity.class)); passwordView.getBaseActivity().finish(); } }); } @Override public void onError() { passwordView.getBaseActivity().runOnUiThread(new Runnable() { @Override public void run() { passwordView.onLogOutFailed(passwordView.getBaseContext().getString(R.string.logout_failed)); } }); } }); }else{ passwordView.getBaseActivity().finish(); } } @Override public void start() { } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/password/PasswordResetActivity.java ================================================ package com.htmessage.yichatopen.activity.main.password; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; /** * Created by huangfangyi on 2016/10/7. * qq 84543217 */ public class PasswordResetActivity extends BaseActivity{ @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.activity_base); getData(); initView(); } private void initView() { PasswordResetFragment fragment = (PasswordResetFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (fragment == null){ fragment = new PasswordResetFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame, fragment); transaction.commit(); } PasswordPrester prester = new PasswordPrester(fragment); } private void getData() { boolean isReset = getIntent().getBooleanExtra("isReset", false); if (isReset){ setTitle(R.string.resetPassword); }else{ setTitle(R.string.find_pwd); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/password/PasswordResetFragment.java ================================================ package com.htmessage.yichatopen.activity.main.password; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.utils.ACache; import com.htmessage.yichatopen.utils.CommonUtils; /** * 项目名称:HTOpen * 类描述:PasswordResetFragment 描述: * 创建人:songlijie * 创建时间:2017/7/7 15:33 * 邮箱:814326663@qq.com */ public class PasswordResetFragment extends Fragment implements PasswordView ,OnClickListener{ private Button btn_ok, btn_code; private EditText et_code,et_usertel,et_password,et_password_confire; private String mobile; private TextView tv_title; private ACache aCache; private TextView tv_country,tv_country_code; private RelativeLayout rl_country,rl_smscode; private PasswordPrester prester; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View pswView = inflater.inflate(R.layout.activity_psw_reset, container, false); getData(); initView(pswView); initData(); setListener(); return pswView; } private void setListener() { tv_country.setOnClickListener(this); tv_country_code.setOnClickListener(this); rl_country.setOnClickListener(this); btn_ok.setOnClickListener(this); btn_code.setOnClickListener(this); } private void initData() { rl_smscode.setVisibility(View.GONE); if (getIsReset()) { tv_title.setText(R.string.resetPassword); btn_ok.setText(R.string.resetPassword); mobile = HTApp.getInstance().getUserJson().getString(HTConstant.JSON_KEY_TEL); et_usertel.setText(mobile); et_usertel.setEnabled(false); et_usertel.clearFocus(); } else { tv_title.setText(R.string.find_pwd); btn_ok.setText(R.string.find_pwd); et_usertel.setHint(R.string.input_bind_mobile); et_usertel.setEnabled(true); et_usertel.requestFocus(); } } private void initView(View pswView) { //获取国家code tv_country = (TextView) pswView.findViewById(R.id.tv_country); tv_country_code = (TextView) pswView.findViewById(R.id.tv_country_code); rl_country = (RelativeLayout) pswView.findViewById(R.id.rl_country); rl_smscode = (RelativeLayout) pswView.findViewById(R.id.rl_smscode); et_usertel = (EditText) pswView.findViewById(R.id.et_usertel); et_password = (EditText) pswView.findViewById(R.id.et_password); et_password_confire = (EditText) pswView.findViewById(R.id.et_password_confire); et_code = (EditText) pswView.findViewById(R.id.et_code); btn_ok = (Button) pswView.findViewById(R.id.btn_ok); btn_code = (Button) pswView.findViewById(R.id.btn_code); tv_title= (TextView) pswView.findViewById(R.id.tv_title); } private void getData() { aCache = ACache.get(getActivity()); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.rl_country: CommonUtils.showPup(getBaseActivity(),tv_country,tv_country_code); break; case R.id.btn_code: prester.sendSMSCode(getMobile(),getCountryName(),getCountryCode()); break; case R.id.btn_ok: prester.resetPassword(getCacheCode(),getSMSCode(),getPwd(),getConfimPwd(),getMobile()); break; } } @Override public String getCountryName() { return tv_country.getText().toString().trim(); } @Override public String getCountryCode() { return tv_country_code.getText().toString().trim(); } @Override public String getCacheCode() { return aCache.getAsString("code"); } @Override public String getSMSCode() { return et_code.getText().toString().trim(); } @Override public boolean getIsReset() { return getActivity().getIntent().getBooleanExtra("isReset", false); } @Override public String getMobile() { return et_usertel.getText().toString().trim(); } @Override public String getPwd() { return et_password.getText().toString().trim(); } @Override public String getConfimPwd() { return et_password_confire.getText().toString().trim(); } @Override public void clearCacheCode() { aCache.put("code",""); } @Override public void onSendSMSCodeSuccess(String msg) { if ("1234".equals(msg)){ et_code.setText(msg); } aCache.put("code",msg); Toast.makeText(getBaseContext(), R.string.code_is_send, Toast.LENGTH_SHORT).show(); } @Override public void onSendSMSCodeFailed(String error) { Toast.makeText(getBaseContext(), error, Toast.LENGTH_SHORT).show(); } @Override public void onResetSuccess(String msg) { Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show(); } @Override public void onResetFailed(String error) { Toast.makeText(getBaseContext(), error, Toast.LENGTH_SHORT).show(); } @Override public void startTimeDown() { } @Override public void finishTimeDown() { } @Override public void onLogOutFailed(String msg) { Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show(); } @Override public void setPresenter(PasswordPrester presenter) { this.prester = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/password/PasswordView.java ================================================ package com.htmessage.yichatopen.activity.main.password; import com.htmessage.yichatopen.activity.BaseView; /** * 项目名称:HTOpen * 类描述:PasswordView 描述: * 创建人:songlijie * 创建时间:2017/7/7 15:08 * 邮箱:814326663@qq.com */ public interface PasswordView extends BaseView{ String getCountryName(); String getCountryCode(); String getCacheCode(); String getSMSCode(); boolean getIsReset(); String getMobile(); String getPwd(); String getConfimPwd(); void clearCacheCode(); void onSendSMSCodeSuccess(String msg); void onSendSMSCodeFailed(String error); void onResetSuccess(String msg); void onResetFailed(String error); void startTimeDown(); void finishTimeDown(); void onLogOutFailed(String msg); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/FragmentProfile.java ================================================ package com.htmessage.yichatopen.activity.main.profile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.main.qrcode.QrCodeActivity; import com.htmessage.yichatopen.activity.main.profile.info.profile.ProfileActivity; import com.htmessage.yichatopen.activity.SettingsActivity; public class FragmentProfile extends Fragment implements View.OnClickListener { private InfoChangedListener listener; private ImageView ivAvatar; private TextView tvNick; private TextView tvFxid; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_profile, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getData(); setListener(); initView(HTApp.getInstance().getUserJson()); } private void getData() { IntentFilter intent = new IntentFilter(IMAction.ACTION_UPDATE_INFO); listener = new InfoChangedListener(); LocalBroadcastManager.getInstance(getActivity()).registerReceiver(listener, intent); } private void initView(JSONObject jsonObject) { ivAvatar = (ImageView) getView().findViewById(R.id.iv_avatar); tvNick = (TextView) getView().findViewById(R.id.tv_name); tvFxid = (TextView) getView().findViewById(R.id.tv_fxid); String avatarUrl = jsonObject.getString(HTConstant.JSON_KEY_AVATAR); Glide.with(getActivity()).load(avatarUrl).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(ivAvatar); tvNick.setText(jsonObject.getString(HTConstant.JSON_KEY_NICK)); String fxid = jsonObject.getString(HTConstant.JSON_KEY_FXID); if (!TextUtils.isEmpty(fxid)) { tvFxid.setText(getString(R.string.app_id) + fxid); } else { tvFxid.setText(getString(R.string.app_id) + getString(R.string.not_set)); } } private void setListener() { getView().findViewById(R.id.re_myinfo).setOnClickListener(this); getView().findViewById(R.id.re_setting).setOnClickListener(this); getView().findViewById(R.id.re_xiangce).setOnClickListener(this); getView().findViewById(R.id.rl_qrcode).setOnClickListener(this); getView().findViewById(R.id.re_pro_test).setOnClickListener(this); getView().findViewById(R.id.re_github).setOnClickListener(this); getView().findViewById(R.id.re_oschina).setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.re_myinfo: startActivity(new Intent(getActivity(), ProfileActivity.class)); break; case R.id.re_setting: startActivity(new Intent(getActivity(), SettingsActivity.class)); break; case R.id.re_xiangce: break; case R.id.rl_qrcode: //我的二维码 startActivity(new Intent(getActivity(), QrCodeActivity.class)); break; case R.id.re_pro_test://pro版本 toStartBrowser(HTConstant.YICHATPROURL); break; case R.id.re_github: toStartBrowser(HTConstant.GITHUBURL); break; case R.id.re_oschina: toStartBrowser(HTConstant.OSCHINAURL); break; } } private void toStartBrowser(String url) { Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); Uri uri = Uri.parse(url); intent.setData(uri); intent.setClassName("com.android.browser", "com.android.browser.BrowserActivity"); startActivity(intent); } private class InfoChangedListener extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (IMAction.ACTION_UPDATE_INFO.equals(intent.getAction())) { String type = intent.getStringExtra(HTConstant.KEY_CHANGE_TYPE); if (HTConstant.JSON_KEY_AVATAR.equals(type)) { String avatar = intent.getStringExtra(HTConstant.JSON_KEY_AVATAR); Glide.with(getActivity()).load(avatar).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(ivAvatar); } else if (HTConstant.JSON_KEY_FXID.equals(type)) { String fxid = intent.getStringExtra(HTConstant.JSON_KEY_FXID); tvFxid.setText(getString(R.string.app_id) + fxid); } else if (HTConstant.JSON_KEY_NICK.equals(type)) { String nick = intent.getStringExtra(HTConstant.JSON_KEY_NICK); tvNick.setText(nick); } } } } @Override public void onDestroy() { super.onDestroy(); if (listener != null) { LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(listener); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/profile/ProfileActivity.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.profile; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.R; public class ProfileActivity extends BaseActivity { private ProfilePrester prester; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); setTitle(R.string.title_user_profile); ProfileFragment fragment = (ProfileFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (fragment == null){ fragment = new ProfileFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame, fragment); transaction.commit(); } prester = new ProfilePrester(fragment); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { prester.result(requestCode,resultCode,data); super.onActivityResult(requestCode, resultCode, data); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/profile/ProfileBasePrester.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.profile; import android.content.Intent; import com.htmessage.yichatopen.activity.BasePresenter; /** * 项目名称:HTOpen * 类描述:ProfileBasePrester 描述: * 创建人:songlijie * 创建时间:2017/7/7 10:50 * 邮箱:814326663@qq.com */ public interface ProfileBasePrester extends BasePresenter { void resgisteRecivier(); void showPhotoDialog(); void showSexDialog(); void result(int requestCode, int resultCode, Intent intent); void onDestory(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/profile/ProfileFragment.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.profile; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.main.qrcode.QrCodeActivity; import com.htmessage.yichatopen.activity.main.region.RegionActivity; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.activity.main.profile.info.update.ProfileUpdateActivity; /** * 项目名称:HTOpen * 类描述:ProfileFragment 描述: * 创建人:songlijie * 创建时间:2017/7/7 11:47 * 邮箱:814326663@qq.com */ public class ProfileFragment extends Fragment implements ProfileView, View.OnClickListener{ private RelativeLayout re_avatar,re_name,re_fxid,re_sex,re_region,re_sign,re_qrcode; private ImageView iv_avatar; private TextView tv_name; private TextView tv_fxid; private TextView tv_sex; private TextView tv_sign, tv_region; private String sex; private ProfilePrester prester; private JSONObject userJson; private static final int UPDATE_REGION = 7; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_profile_info, container, false); initView(view); initData(); setListener(); return view; } private void setListener() { //设置监听 re_avatar.setOnClickListener(this); re_name.setOnClickListener(this); re_fxid.setOnClickListener(this); re_sex.setOnClickListener(this); re_region.setOnClickListener(this); re_sign.setOnClickListener(this); re_qrcode.setOnClickListener(this); } private void initData() { prester.resgisteRecivier(); userJson = getUserJson(); String nick = userJson.getString(HTConstant.JSON_KEY_NICK); String fxid = userJson.getString(HTConstant.JSON_KEY_FXID); sex = userJson.getString(HTConstant.JSON_KEY_SEX); String sign = userJson.getString(HTConstant.JSON_KEY_SIGN); String province = userJson.getString(HTConstant.JSON_KEY_PROVINCE); String city = userJson.getString(HTConstant.JSON_KEY_CITY); String avatarUrl = userJson.getString(HTConstant.JSON_KEY_AVATAR); if (!TextUtils.isEmpty(avatarUrl)) { if (!avatarUrl.contains("http:")) { avatarUrl = HTConstant.URL_AVATAR + avatarUrl; } } Glide.with(getBaseContext()).load(avatarUrl).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).into(iv_avatar); tv_name.setText(nick); if (TextUtils.isEmpty(fxid)) { tv_fxid.setText(R.string.not_set); } else { tv_fxid.setText(fxid); } if (!TextUtils.isEmpty(sex)) { switch (sex) { case "1": case "男": tv_sex.setText(R.string.male); break; case "0": case "女": tv_sex.setText(R.string.female); break; default: tv_sex.setText(R.string.not_set); break; } } else { tv_sex.setText(R.string.not_set); } if (TextUtils.isEmpty(sign)) { tv_sign.setText(R.string.not_input); } else { tv_sign.setText(sign); } if (!TextUtils.isEmpty(province) && !TextUtils.isEmpty(city)) { tv_region.setText(province + " " + city); } else { tv_region.setText(R.string.not_set); } } private void initView(View view) { iv_avatar = (ImageView) view.findViewById(R.id.iv_avatar); tv_name = (TextView) view.findViewById(R.id.tv_name); tv_fxid = (TextView) view.findViewById(R.id.tv_fxid); tv_sex = (TextView) view.findViewById(R.id.tv_sex); tv_sign = (TextView) view.findViewById(R.id.tv_sign); tv_region = (TextView) view.findViewById(R.id.tv_region); re_avatar = (RelativeLayout) view.findViewById(R.id.re_avatar); re_name= (RelativeLayout) view.findViewById(R.id.re_name); re_fxid = (RelativeLayout) view.findViewById(R.id.re_fxid); re_sex = (RelativeLayout) view.findViewById(R.id.re_sex); re_region= (RelativeLayout) view.findViewById(R.id.re_region); re_sign = (RelativeLayout) view.findViewById(R.id.re_sign); re_qrcode = (RelativeLayout) view.findViewById(R.id.re_qrcode); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.re_avatar: prester.showPhotoDialog(); break; case R.id.re_name: startActivity(new Intent(getActivity(), ProfileUpdateActivity.class) .putExtra("type", ProfileUpdateActivity.TYPE_NICK) .putExtra("default", userJson.getString(HTConstant.JSON_KEY_NICK))); break; case R.id.re_fxid: if (TextUtils.isEmpty(userJson.getString(HTConstant.JSON_KEY_FXID))) { startActivity(new Intent(getActivity(), ProfileUpdateActivity.class) .putExtra("type", ProfileUpdateActivity.TYPE_FXID)); } break; case R.id.re_sex: prester.showSexDialog(); break; case R.id.re_region: getBaseActivity().startActivityForResult(new Intent(getBaseActivity(), RegionActivity.class), UPDATE_REGION); break; case R.id.re_sign: startActivity(new Intent(getActivity(), ProfileUpdateActivity.class) .putExtra("type", ProfileUpdateActivity.TYPE_SIGN) .putExtra("default", userJson.getString(HTConstant.JSON_KEY_SIGN))); break; case R.id.re_qrcode: startActivity(new Intent(getActivity(), QrCodeActivity.class)); break; } } @Override public void onNickUpdate(String nick, boolean isHang) { tv_name.setText(nick); } @Override public void onSexUpdate(int sex, boolean isHang) { tv_sex.setText(sex); } @Override public void onSignUpdate(String sign, boolean isHang) { tv_sign.setText(sign); } @Override public void onAvatarUpdate(String avatar, boolean isHang) { Glide.with(getBaseContext()).load(avatar).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.default_avatar).error(R.drawable.default_avatar).into(iv_avatar); } @Override public void onRegionUpdate(String region, boolean isHang) { tv_region.setText(region); } @Override public void onFxidUpdate(String fxid, boolean isHang) { tv_fxid.setText(fxid); } @Override public void onUpdateSuccess(String msg) { Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show(); } @Override public void onUpdateFailed(String error) { Toast.makeText(getBaseContext(), error, Toast.LENGTH_SHORT).show(); } @Override public JSONObject getUserJson() { return HTApp.getInstance().getUserJson(); } @Override public void setPresenter(ProfilePrester presenter) { this.prester = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void onDestroy() { prester.onDestory(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/profile/ProfilePrester.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.profile; import android.app.Activity; import android.app.Dialog; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.provider.MediaStore; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import android.util.Log; import com.alibaba.fastjson.JSONObject; import com.alibaba.sdk.android.oss.ClientException; import com.alibaba.sdk.android.oss.ServiceException; import com.alibaba.sdk.android.oss.model.PutObjectRequest; import com.alibaba.sdk.android.oss.model.PutObjectResult; import com.htmessage.sdk.utils.UploadFileUtils; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import com.htmessage.yichatopen.widget.HTAlertDialog; import com.soundcloud.android.crop.Crop; import java.io.File; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; /** * 项目名称:HTOpen * 类描述:ProfilePrester 描述: * 创建人:songlijie * 创建时间:2017/7/7 10:49 * 邮箱:814326663@qq.com */ public class ProfilePrester implements ProfileBasePrester { private static final int PHOTO_REQUEST_TAKEPHOTO = 1;// 拍照 private static final int PHOTO_REQUEST_GALLERY = 2;// 从相册中选择 private static final int PHOTO_REQUEST_CUT = 3;// 结果 private static final int UPDATE_REGION = 7; private String imageName; private String dirPath; private JSONObject userJson; private ProfileView profileView; private InfoChangedListener listener; public ProfilePrester(ProfileView profileView) { this.profileView = profileView; this.profileView.setPresenter(this); dirPath = HTApp.getInstance().getDirFilePath(); userJson = profileView.getUserJson(); } @Override public void resgisteRecivier() { IntentFilter intent = new IntentFilter(IMAction.ACTION_UPDATE_INFO); listener = new InfoChangedListener(); LocalBroadcastManager.getInstance(profileView.getBaseContext()).registerReceiver(listener, intent); } private void updateInfo(final String key, final String value) { if (TextUtils.isEmpty(key) && TextUtils.isEmpty(value)){ return; } final Dialog progressDialog = HTApp.getInstance().createLoadingDialog(profileView.getBaseContext(), profileView.getBaseContext().getString(R.string.are_uploading)); progressDialog.show(); List params = new ArrayList(); params.add(new Param(key, value)); params.add(new Param("userId", HTApp.getInstance().getUsername())); new OkHttpUtils(profileView.getBaseContext()).post(params, HTConstant.URL_UPDATE, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { progressDialog.dismiss(); int code = jsonObject.getIntValue("code"); if (code == 1) { //update ui if (key.equals(HTConstant.JSON_KEY_SEX)) { switch (value) { case "1": profileView.onSexUpdate(R.string.male,true); break; case "0": profileView.onSexUpdate(R.string.female,true); break; } } userJson.put(key, value); HTApp.getInstance().setUserJson(userJson); profileView.onUpdateSuccess(profileView.getBaseContext().getString(R.string.update_success)); } else { profileView.onUpdateFailed(profileView.getBaseContext().getString(R.string.upload_failed)+code); } } @Override public void onFailure(String errorMsg) { profileView.onUpdateFailed(errorMsg); progressDialog.dismiss(); } }); } private void updateRegion(final String province,final String city) { if (TextUtils.isEmpty(province) && TextUtils.isEmpty(city)){ return; } final Dialog progressDialog = HTApp.getInstance().createLoadingDialog(profileView.getBaseContext(), profileView.getBaseContext().getString(R.string.are_uploading)); progressDialog.show(); final List params = new ArrayList(); params.add(new Param(HTConstant.JSON_KEY_PROVINCE, province)); params.add(new Param(HTConstant.JSON_KEY_CITY, city)); params.add(new Param("userId", HTApp.getInstance().getUsername())); new OkHttpUtils(profileView.getBaseContext()).post(params, HTConstant.URL_UPDATE, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { progressDialog.dismiss(); int code = jsonObject.getIntValue("code"); if (code == 1) { //update ui userJson.put(HTConstant.JSON_KEY_PROVINCE, province); userJson.put(HTConstant.JSON_KEY_CITY, city); HTApp.getInstance().setUserJson(userJson); profileView.onRegionUpdate(province +" "+city,true); profileView.onUpdateSuccess(profileView.getBaseContext().getString(R.string.update_success)); } else { profileView.onUpdateFailed(profileView.getBaseContext().getString(R.string.upload_failed) + code); } } @Override public void onFailure(String errorMsg) { profileView.onUpdateFailed(errorMsg); progressDialog.dismiss(); } }); } private void updateAvatar(final String key,final String value){ if (TextUtils.isEmpty(key) && TextUtils.isEmpty(value)){ return; } final Dialog progressDialog = HTApp.getInstance().createLoadingDialog(profileView.getBaseContext(), profileView.getBaseContext().getString(R.string.are_uploading)); progressDialog.show(); final String fileName = value.substring(value.lastIndexOf("/") + 1); new UploadFileUtils(profileView.getBaseContext(), fileName, value).asyncUploadFile(new UploadFileUtils.a() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { } @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { final String url = HTConstant.baseOssUrl + fileName; profileView.getBaseActivity().runOnUiThread(new Runnable() { @Override public void run() { UpLoadAvator(value, key, url, progressDialog); } }); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { profileView.getBaseActivity().runOnUiThread(new Runnable() { @Override public void run() { progressDialog.dismiss(); } }); } }); } private void UpLoadAvator(final String filePath, final String key, final String value, final Dialog progressDialog) { List params = new ArrayList(); params.add(new Param(key, value)); new OkHttpUtils(profileView.getBaseContext()).post(params, HTConstant.URL_UPDATE, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { progressDialog.dismiss(); int code = jsonObject.getIntValue("code"); switch (code) { case 1: if (key.equals(HTConstant.JSON_KEY_AVATAR)) { userJson.put(HTConstant.JSON_KEY_AVATAR, value); HTApp.getInstance().setUserJson(userJson); profileView.onAvatarUpdate(filePath,true); LocalBroadcastManager.getInstance(profileView.getBaseContext()).sendBroadcast(new Intent(IMAction.ACTION_UPDATE_INFO).putExtra(HTConstant.JSON_KEY_AVATAR, value).putExtra(HTConstant.KEY_CHANGE_TYPE, HTConstant.JSON_KEY_AVATAR)); } profileView.onUpdateSuccess(profileView.getBaseContext().getString(R.string.update_success)); break; default: String info = jsonObject.getString("info"); profileView.onUpdateFailed(info); break; } } @Override public void onFailure(String errorMsg) { profileView.onUpdateFailed(errorMsg); progressDialog.dismiss(); } }); } @Override public void showPhotoDialog() { HTAlertDialog fxAlertDialog = new HTAlertDialog(profileView.getBaseContext(), null, new String[]{profileView.getBaseContext().getString(R.string.attach_take_pic), profileView.getBaseContext().getString(R.string.image_manager)}); fxAlertDialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { switch (position) { case 0: imageName = getNowTime() + ".png"; Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 指定调用相机拍照后照片的储存路径 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(dirPath, imageName))); profileView.getBaseActivity().startActivityForResult(intent, PHOTO_REQUEST_TAKEPHOTO); break; case 1: imageName = getNowTime() + ".png"; Crop.pickImage(profileView.getBaseActivity(), PHOTO_REQUEST_GALLERY); break; } } }); } @Override public void showSexDialog() { String title = profileView.getBaseContext().getString(R.string.sex); HTAlertDialog fxAlertDialog = new HTAlertDialog(profileView.getBaseContext(), title, new String[]{profileView.getBaseContext().getString(R.string.male), profileView.getBaseContext().getString(R.string.female)}); fxAlertDialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { switch (position) { case 0: updateInfo(HTConstant.JSON_KEY_SEX, "1"); break; case 1: updateInfo(HTConstant.JSON_KEY_SEX, "0"); break; } } }); } @Override public void result(int requestCode, int resultCode, Intent intent) { if (resultCode == Activity.RESULT_OK) { switch (requestCode) { case PHOTO_REQUEST_TAKEPHOTO: beginCrop(Uri.fromFile(new File(dirPath, imageName))); break; case PHOTO_REQUEST_GALLERY: if (intent != null) beginCrop(intent.getData()); break; case PHOTO_REQUEST_CUT: Uri output = Crop.getOutput(intent); updateAvatar(HTConstant.JSON_KEY_AVATAR, output.getPath()); break; case UPDATE_REGION: Log.d("slj","-----更新地区:"+UPDATE_REGION); if (intent != null) { String province = intent.getStringExtra("province"); String city = intent.getStringExtra("city"); updateRegion(province, city); } break; } } } private void unRegisterReciver(){ if (listener != null){ LocalBroadcastManager.getInstance(profileView.getBaseContext()).unregisterReceiver(listener); } } @Override public void onDestory() { unRegisterReciver(); profileView = null; } @Override public void start() { } private class InfoChangedListener extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (IMAction.ACTION_UPDATE_INFO.equals(intent.getAction())) { String type = intent.getStringExtra(HTConstant.KEY_CHANGE_TYPE); if (HTConstant.JSON_KEY_SIGN.equals(type)) { String sign = intent.getStringExtra(HTConstant.JSON_KEY_SIGN); profileView.onSignUpdate(sign,true); } else if (HTConstant.JSON_KEY_FXID.equals(type)) { String fxid = intent.getStringExtra(HTConstant.JSON_KEY_FXID); profileView.onFxidUpdate(fxid,true); } else if (HTConstant.JSON_KEY_NICK.equals(type)) { String nick = intent.getStringExtra(HTConstant.JSON_KEY_NICK); profileView.onNickUpdate(nick,true); } } } } private void beginCrop(Uri source) { Uri destination = Uri.fromFile(new File(dirPath, imageName)); Crop.of(source, destination).asSquare().start(profileView.getBaseActivity(), PHOTO_REQUEST_CUT); } private String getNowTime() { Date date = new Date(System.currentTimeMillis()); SimpleDateFormat dateFormat = new SimpleDateFormat("MMddHHmmssSS"); return dateFormat.format(date); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/profile/ProfileView.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.profile; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.activity.BaseView; /** * 项目名称:HTOpen * 类描述:ProfileView 描述: * 创建人:songlijie * 创建时间:2017/7/7 11:00 * 邮箱:814326663@qq.com */ public interface ProfileView extends BaseView{ void onNickUpdate(String nick,boolean isHang); void onSexUpdate(int sex,boolean isHang); void onSignUpdate(String sign,boolean isHang); void onAvatarUpdate(String avatar,boolean isHang); void onRegionUpdate(String region,boolean isHang); void onFxidUpdate(String fxid,boolean isHang); void onUpdateSuccess(String msg); void onUpdateFailed(String error); JSONObject getUserJson(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/update/ProfileUpdateActivity.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.update; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import android.view.View; import com.htmessage.yichatopen.activity.BaseActivity; import com.htmessage.yichatopen.R; /** * Created by huangfangyi on 2016/7/3.\ * QQ:84543217 */ public class ProfileUpdateActivity extends BaseActivity { public static final int TYPE_NICK = 0; public static final int TYPE_FXID = 1; public static final int TYPE_SIGN = 2; private UpdateProfilePrestener prestener; private String title; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); int type = getIntent().getIntExtra("type", 0); switch (type){ case 0: title = getString(R.string.change_nick); break; case 1: title = getString(R.string.change_mixin); break; case 2: title = getString(R.string.change_personalized_signature); break; } setTitle(title); ProfileUpdateFragment fragment = (ProfileUpdateFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (fragment == null){ fragment = new ProfileUpdateFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame,fragment); transaction.commit(); } prestener = new UpdateProfilePrestener(fragment); showRightTextView(R.string.ok, new View.OnClickListener() { @Override public void onClick(View v) { prestener.update(); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/update/ProfileUpdateFragment.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.update; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.htmessage.yichatopen.R; /** * 项目名称:HTOpen * 类描述:ProfileUpdateFragment 描述: * 创建人:songlijie * 创建时间:2017/7/7 16:20 * 邮箱:814326663@qq.com */ public class ProfileUpdateFragment extends Fragment implements UpdateProfileView,View.OnClickListener{ private String defaultStr; private TextView saveTV; private EditText infoET; private TextView titleTV; private int type; private UpdateProfilePrestener prestener; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View updateView = inflater.inflate(R.layout.activity_update_info, container, false); getData(); initView(updateView); initData(); setListener(); return updateView; } private void setListener() { saveTV.setOnClickListener(this); } private void initData() { String title = prestener.getTitle(type); titleTV.setText(title); if (defaultStr != null) { infoET.setText(defaultStr); infoET.setSelection(infoET.getText().length()); } } private void initView(View updateView) { titleTV = (TextView) updateView.findViewById(R.id.tv_title); saveTV = (TextView) updateView.findViewById(R.id.tv_save); infoET = (EditText) updateView.findViewById(R.id.et_info); } private void getData() { defaultStr = getDefultString(); type = getType(); } @Override public String getDefultString() { return getBaseActivity().getIntent().getStringExtra("default"); } @Override public int getType() { return getBaseActivity().getIntent().getIntExtra("type", 0); } @Override public String getInputString() { return infoET.getText().toString().trim(); } @Override public void onUpdateSuccess(String msg) { Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show(); getBaseActivity().finish(); } @Override public void onUpdateFailed(String msg) { Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show(); } @Override public void setPresenter(UpdateProfilePrestener presenter) { this.prestener = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.tv_save: prestener.updateInfo(prestener.getKey(type),getInputString(),getDefultString()); break; } } @Override public void onDestroy() { prestener.onDestory(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/update/UpdateProfileBasePrester.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.update; import com.htmessage.yichatopen.activity.BasePresenter; /** * 项目名称:HTOpen * 类描述:UpdateProfileBasePrester 描述: * 创建人:songlijie * 创建时间:2017/7/7 13:32 * 邮箱:814326663@qq.com */ public interface UpdateProfileBasePrester extends BasePresenter { void update(); void updateInfo(String key,String value,String defaultStr); String getTitle(int type); String getKey(int type); void onDestory(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/update/UpdateProfilePrestener.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.update; import android.app.Dialog; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; import android.text.TextUtils; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.IMAction; import com.htmessage.yichatopen.utils.OkHttpUtils; import com.htmessage.yichatopen.utils.Param; import com.htmessage.yichatopen.utils.Validator; import java.util.ArrayList; import java.util.List; /** * 项目名称:HTOpen * 类描述:UpdateProfilePrestener 描述: * 创建人:songlijie * 创建时间:2017/7/7 13:38 * 邮箱:814326663@qq.com */ public class UpdateProfilePrestener implements UpdateProfileBasePrester { private UpdateProfileView updateProfileView; private static final int TYPE_NICK = 0; private static final int TYPE_FXID = 1; private static final int TYPE_SIGN = 2; public UpdateProfilePrestener(UpdateProfileView updateProfileView) { this.updateProfileView = updateProfileView; this.updateProfileView.setPresenter(this); } @Override public void update() { updateInfo(getKey(updateProfileView.getType()),updateProfileView.getInputString(),updateProfileView.getDefultString()); } @Override public void updateInfo(final String key, final String value , String defaultStr) { if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value) || ((defaultStr != null) && value.equals(defaultStr))) { return; } if(HTConstant.JSON_KEY_FXID.equals(key)){ if(Validator.isChinese(value)){ updateProfileView.onUpdateFailed(updateProfileView.getBaseActivity().getString(R.string.mixin_can_not_has_chinese)); return; } } if (value.length() > 30) { updateProfileView.onUpdateFailed(updateProfileView.getBaseActivity().getString(R.string.string_not_30)); return; } final Dialog progressDialog = HTApp.getInstance().createLoadingDialog(updateProfileView.getBaseActivity(),updateProfileView.getBaseActivity().getString(R.string.are_uploading)); progressDialog.show(); //本地用户资料 final JSONObject userJson = HTApp.getInstance().getUserJson(); List params = new ArrayList(); params.add(new Param(key, value)); new OkHttpUtils(updateProfileView.getBaseActivity()).post(params, HTConstant.URL_UPDATE, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { progressDialog.dismiss(); int code = jsonObject.getIntValue("code"); if (code == 1) { userJson.put(key, value); HTApp.getInstance().setUserJson(userJson); LocalBroadcastManager.getInstance(updateProfileView.getBaseActivity()).sendBroadcast(new Intent(IMAction.ACTION_UPDATE_INFO).putExtra(HTConstant.KEY_CHANGE_TYPE,key).putExtra(key,value)); updateProfileView.onUpdateSuccess(updateProfileView.getBaseActivity().getString(R.string.update_success)); } else { updateProfileView.onUpdateFailed(updateProfileView.getBaseActivity().getString(R.string.upload_failed)+code); } } @Override public void onFailure(String errorMsg) { updateProfileView.onUpdateFailed(errorMsg); progressDialog.dismiss(); } }); } @Override public String getTitle(int type) { String title = ""; switch (type) { case TYPE_NICK: title = updateProfileView.getBaseActivity().getString(R.string.change_nick); break; case TYPE_FXID: title = updateProfileView.getBaseActivity().getString(R.string.change_mixin); break; case TYPE_SIGN: title = updateProfileView.getBaseActivity().getString(R.string.change_personalized_signature); break; } return title; } @Override public String getKey(int type) { String key = ""; switch (type) { case TYPE_NICK: key = HTConstant.JSON_KEY_NICK; break; case TYPE_FXID: key = HTConstant.JSON_KEY_FXID; break; case TYPE_SIGN: key = HTConstant.JSON_KEY_SIGN; break; } return key; } @Override public void onDestory() { updateProfileView = null; } @Override public void start() { } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/profile/info/update/UpdateProfileView.java ================================================ package com.htmessage.yichatopen.activity.main.profile.info.update; import com.htmessage.yichatopen.activity.BaseView; /** * 项目名称:HTOpen * 类描述:UpdateProfileView 描述: * 创建人:songlijie * 创建时间:2017/7/7 13:41 * 邮箱:814326663@qq.com */ public interface UpdateProfileView extends BaseView{ String getDefultString(); int getType(); String getInputString(); void onUpdateSuccess(String msg); void onUpdateFailed(String msg); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/qrcode/QrCodeActivity.java ================================================ package com.htmessage.yichatopen.activity.main.qrcode; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; public class QrCodeActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); setTitle(R.string.me_qrcode); QrCodeFragment fragment = (QrCodeFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (fragment == null){ fragment = new QrCodeFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame,fragment); transaction.commit(); } new QrCodePrester(fragment); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/qrcode/QrCodeBasePrester.java ================================================ package com.htmessage.yichatopen.activity.main.qrcode; import com.htmessage.yichatopen.activity.BasePresenter; /** * 项目名称:yichat0504 * 类描述:QrCodeBasePrester 描述: * 创建人:songlijie * 创建时间:2017/7/10 10:36 * 邮箱:814326663@qq.com */ public interface QrCodeBasePrester extends BasePresenter { void onDestory(); void CreateQrCode(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/qrcode/QrCodeFragment.java ================================================ package com.htmessage.yichatopen.activity.main.qrcode; import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.Toast; import com.htmessage.yichatopen.R; /** * 项目名称:yichat0504 * 类描述:QrCodeFragment 描述: * 创建人:songlijie * 创建时间:2017/7/10 10:52 * 邮箱:814326663@qq.com */ public class QrCodeFragment extends Fragment implements QrCodeView { private ImageView imageView; private QrCodePrester prester; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.activity_qrcode_generate, container, false); initView(view); getData(); return view; } private void getData() { prester.CreateQrCode(); } private void initView(View view) { imageView = (ImageView) view.findViewById(R.id.code_image); } @Override public void showQrCode(Bitmap bitmap) { imageView.setImageBitmap(bitmap); } @Override public void showError(String error) { Toast.makeText(getBaseContext(), error, Toast.LENGTH_SHORT).show(); } @Override public void setPresenter(QrCodePrester presenter) { this.prester = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } @Override public void onDestroy() { prester.onDestory(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/qrcode/QrCodePrester.java ================================================ package com.htmessage.yichatopen.activity.main.qrcode; import android.graphics.Bitmap; import android.graphics.Color; import android.util.Log; import com.alibaba.fastjson.JSONObject; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTConstant; /** * 项目名称:yichat0504 * 类描述:QrCodePrester 描述: * 创建人:songlijie * 创建时间:2017/7/10 10:44 * 邮箱:814326663@qq.com */ public class QrCodePrester implements QrCodeBasePrester { private String TAG = QrCodePrester.class.getSimpleName(); private QrCodeView codeView; public QrCodePrester(QrCodeView codeView) { this.codeView = codeView; this.codeView.setPresenter(this); } @Override public void onDestory() { codeView = null; } @Override public void CreateQrCode() { JSONObject userJson = HTApp.getInstance().getUserJson(); String key = "userInfo:"; JSONObject object =new JSONObject(); object.put(HTConstant.JSON_KEY_HXID,HTApp.getInstance().getUsername()); object.put(HTConstant.JSON_KEY_NICK,userJson.getString(HTConstant.JSON_KEY_NICK)); object.put(HTConstant.JSON_KEY_TEL,userJson.getString(HTConstant.JSON_KEY_TEL)); object.put(HTConstant.JSON_KEY_FXID,userJson.getString(HTConstant.JSON_KEY_FXID)); object.put(HTConstant.JSON_KEY_SEX,userJson.getString(HTConstant.JSON_KEY_SEX)); object.put(HTConstant.JSON_KEY_AVATAR,userJson.getString(HTConstant.JSON_KEY_AVATAR)); object.put(HTConstant.JSON_KEY_PROVINCE,userJson.getString(HTConstant.JSON_KEY_PROVINCE)); object.put(HTConstant.JSON_KEY_CITY,userJson.getString(HTConstant.JSON_KEY_CITY)); object.put(HTConstant.JSON_KEY_SIGN,userJson.getString(HTConstant.JSON_KEY_SIGN)); try { Bitmap bitmap = generateQRCode(key + object.toJSONString()); codeView.showQrCode(bitmap); } catch (WriterException e) { codeView.showError(e.getMessage()); e.printStackTrace(); } } @Override public void start() { } private Bitmap bitMatrix2Bitmap(BitMatrix matrix) { int w = matrix.getWidth(); int h = matrix.getHeight(); int[] rawData = new int[w * h]; for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int color = Color.WHITE; if (matrix.get(i, j)) { color = Color.BLACK; } rawData[i + (j * w)] = color; } } Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565); bitmap.setPixels(rawData, 0, w, 0, 0, w, h); return bitmap; } private Bitmap generateQRCode(String content) throws WriterException { Log.d(TAG,"二维码:"+content); QRCodeWriter writer = new QRCodeWriter(); BitMatrix matrix = writer.encode(content, BarcodeFormat.QR_CODE, 500, 500); return bitMatrix2Bitmap(matrix); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/qrcode/QrCodeView.java ================================================ package com.htmessage.yichatopen.activity.main.qrcode; import android.graphics.Bitmap; import com.htmessage.yichatopen.activity.BaseView; /** * 项目名称:yichat0504 * 类描述:QrCodeView 描述: * 创建人:songlijie * 创建时间:2017/7/10 10:44 * 邮箱:814326663@qq.com */ public interface QrCodeView extends BaseView { void showQrCode(Bitmap bitmap); void showError(String error); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/region/RegionActivity.java ================================================ package com.htmessage.yichatopen.activity.main.region; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; public class RegionActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); setTitle(R.string.region); RegionFragment fragment = (RegionFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (fragment == null){ fragment = new RegionFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame,fragment); transaction.commit(); } new RegionPresenter(fragment); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/region/RegionBasePrestener.java ================================================ package com.htmessage.yichatopen.activity.main.region; import com.htmessage.yichatopen.activity.BasePresenter; import java.util.List; /** * 项目名称:yichat0504 * 类描述:RegionBasePrestener 描述: * 创建人:songlijie * 创建时间:2017/7/10 13:53 * 邮箱:814326663@qq.com */ public interface RegionBasePrestener extends BasePresenter { List getProvinceList(); List getCityList(int position); void onDestory(); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/region/RegionFragment.java ================================================ package com.htmessage.yichatopen.activity.main.region; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import com.htmessage.yichatopen.R; import java.util.ArrayList; import java.util.List; /** * 项目名称:yichat0504 * 类描述:RegionFragment 描述: * 创建人:songlijie * 创建时间:2017/7/10 14:21 * 邮箱:814326663@qq.com */ public class RegionFragment extends Fragment implements RegionView{ private ListView list_province; private ListView list_city; // 一个城市的数据列表 private List citys = new ArrayList(); private RegionAdapter cAdapter; private RegionAdapter pAdapter; private String province; private RegionPresenter presenter; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View regionView = inflater.inflate(R.layout.activity_region, container, false); initView(regionView); initData(); setListener(); return regionView; } private void setListener() { list_province.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { presenter.onItemClickListener(position,1); } }); list_city.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { presenter.onItemClickListener(position,2); } }); } private void initData() { pAdapter = new RegionAdapter(getBaseContext(), presenter.getProvinceList()); cAdapter = new RegionAdapter(getBaseContext(), citys); list_province.setAdapter(pAdapter); list_city.setAdapter(cAdapter); } private void initView(View regionView) { list_province = (ListView) regionView.findViewById(R.id.list_province); list_city = (ListView) regionView.findViewById(R.id.list_city); } @Override public void showCityList(List cityList) { cAdapter.setData(cityList); cAdapter.notifyDataSetChanged(); } @Override public void showProvince(String province) { this.province = province; } @Override public void showCity(String city) { Intent intent=new Intent(); intent.putExtra("city", city); intent.putExtra("province", province); getBaseActivity().setResult(Activity.RESULT_OK,intent); getBaseActivity().finish(); } @Override public void setPresenter(RegionPresenter presenter) { this.presenter = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public Activity getBaseActivity() { return getActivity(); } private class RegionAdapter extends BaseAdapter { private Context context; private List data; private LayoutInflater inflater; public RegionAdapter(Context _context, List data) { this.context = _context; this.data = data; inflater = LayoutInflater.from(context); } public void setData(List _data) { data = _data; } @Override public int getCount() { return data.size(); } @Override public String getItem(int position) { return data.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(R.layout.item_region, parent, false); } ViewHolder holder = (ViewHolder) convertView.getTag(); if (holder == null) { holder = new ViewHolder(); holder.tv_name = (TextView) convertView .findViewById(R.id.tv_name); convertView.setTag(holder); } String regionName = getItem(position); holder.tv_name.setText(regionName); return convertView; } } static class ViewHolder { TextView tv_name; } @Override public void onDestroy() { presenter.onDestory(); super.onDestroy(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/region/RegionPresenter.java ================================================ package com.htmessage.yichatopen.activity.main.region; import com.htmessage.yichatopen.R; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * 项目名称:yichat0504 * 类描述:RegionPresenter 描述: * 创建人:songlijie * 创建时间:2017/7/10 14:04 * 邮箱:814326663@qq.com */ public class RegionPresenter implements RegionBasePrestener { // 一个省份的数据列表 private List provinces = new ArrayList(); // 一个城市的数据列表 private List citys = new ArrayList(); // 所有省份下的城市列表 private final int[] ARRAY_CITY = new int[] { R.array.beijin_province_item, R.array.heibei_province_item, R.array.shandong_province_item, R.array.shanghai_province_item, R.array.guangdong_province_item, R.array.anhui_province_item, R.array.fujian_province_item, R.array.gansu_province_item, R.array.guangxi_province_item, R.array.guizhou_province_item, R.array.hainan_province_item, R.array.henan_province_item, R.array.heilongjiang_province_item, R.array.hubei_province_item, R.array.hunan_province_item, R.array.jilin_province_item, R.array.jiangsu_province_item, R.array.jiangxi_province_item, R.array.liaoning_province_item, R.array.neimenggu_province_item, R.array.ningxia_province_item, R.array.qinghai_province_item, R.array.shanxi1_province_item, R.array.shanxi2_province_item, R.array.sichuan_province_item, R.array.tianjin_province_item, R.array.xizang_province_item, R.array.xinjiang_province_item, R.array.yunnan_province_item, R.array.zhejiang_province_item, R.array.chongqing_province_item, R.array.taiwan_province_item, R.array.hongkong_province_item, R.array.aomen_province_item }; private RegionView regionView; public RegionPresenter(RegionView regionView) { this.regionView = regionView; this.regionView.setPresenter(this); } @Override public List getProvinceList() { provinces = Arrays.asList(regionView.getBaseActivity().getResources().getStringArray(R.array.province_item)); return provinces; } @Override public List getCityList(int position) { citys = Arrays.asList(regionView.getBaseActivity().getResources().getStringArray(ARRAY_CITY[position])); return citys; } @Override public void onDestory() { regionView = null; } @Override public void start() { } public void onItemClickListener(int position,int type){ if (type ==1){ String province = provinces.get(position); regionView.showProvince(province); regionView.showCityList(getCityList(position)); }else{ String city = citys.get(position); regionView.showCity(city); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/main/region/RegionView.java ================================================ package com.htmessage.yichatopen.activity.main.region; import com.htmessage.yichatopen.activity.BaseView; import java.util.List; /** * 项目名称:yichat0504 * 类描述:RegionView 描述: * 创建人:songlijie * 创建时间:2017/7/10 14:05 * 邮箱:814326663@qq.com */ public interface RegionView extends BaseView { void showCityList(List cityList); void showProvince(String province); void showCity(String city); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/register/RegisterActivity.java ================================================ package com.htmessage.yichatopen.activity.register; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.BaseActivity; /** * 注册页 */ public class RegisterActivity extends BaseActivity { private RegisterPresenter presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); setTitle(R.string.register); RegisterFragment registerFragment = (RegisterFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); if (registerFragment == null) { // Create the fragment registerFragment =new RegisterFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.contentFrame, registerFragment); transaction.commit(); } presenter=new RegisterPresenter(registerFragment); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { presenter.result(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/register/RegisterContract.java ================================================ package com.htmessage.yichatopen.activity.register; import android.app.Activity; import android.content.Intent; import com.htmessage.yichatopen.activity.BasePresenter; import com.htmessage.yichatopen.activity.BaseView; /** * Created by huangfangyi on 2017/6/23. * qq 84543217 */ public interface RegisterContract { public int PHOTO_REQUEST_TAKEPHOTO = 1;// 拍照 public int PHOTO_REQUEST_GALLERY = 2;// 从相册中选择 public int PHOTO_REQUEST_CUT = 3;// 结果 public interface View extends BaseView{ void showAvatar(String imagePath); void showDialog(); void cancelDialog(); void showPassword(); void hidePassword(); void enableButton(); void disableButton(); void showToast(int msgRes); String getOriginImagePath(); Activity getBaseActivity(); } public interface Presenter extends BasePresenter{ void registerInServer(String nickName,String mobile,String password); void result(int requsetCode, int resultCode, Intent intent); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/activity/register/RegisterFragment.java ================================================ package com.htmessage.yichatopen.activity.register; import android.Manifest; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.Html; import android.text.Selection; import android.text.Spannable; import android.text.TextUtils; import android.text.TextWatcher; import android.text.method.HideReturnsTransformationMethod; import android.text.method.PasswordTransformationMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.utils.CommonUtils; import com.htmessage.yichatopen.utils.Validator; import com.htmessage.yichatopen.widget.HTAlertDialog; import com.soundcloud.android.crop.Crop; import java.io.File; /** * Created by huangfangyi on 2017/6/23. * qq 84543217 */ public class RegisterFragment extends Fragment implements View.OnClickListener, RegisterContract.View { private EditText et_usernick, et_usertel, et_password; private Button btn_register; private ImageView iv_hide, iv_show, iv_photo; private TextView tv_xieyi, tv_country, tv_country_code; private RelativeLayout rl_country; private RegisterContract.Presenter mPresenter; //选取的原始图片 private String imagePathOrigin = null; private Dialog dialog; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); dialog = HTApp.getInstance().createLoadingDialog(getActivity(), getString(R.string.Is_the_registered)); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_register, container, false); et_usernick = (EditText) root.findViewById(R.id.et_usernick); et_usertel = (EditText) root.findViewById(R.id.et_usertel); et_password = (EditText) root.findViewById(R.id.et_password); //获取国家code tv_country = (TextView) root.findViewById(R.id.tv_country); tv_country_code = (TextView) root.findViewById(R.id.tv_country_code); rl_country = (RelativeLayout) root.findViewById(R.id.rl_country); btn_register = (Button) root.findViewById(R.id.btn_register); tv_xieyi = (TextView) root.findViewById(R.id.tv_xieyi); iv_hide = (ImageView) root.findViewById(R.id.iv_hide); iv_show = (ImageView) root.findViewById(R.id.iv_show); iv_photo = (ImageView) root.findViewById(R.id.iv_photo); initView(); setLisenter(); return root; } private void initView() { String xieyi = "" + getString(R.string.press_top) + " " + "\"" + getString(R.string.register) + "\"" + " " + getString(R.string.btn_means_agree) + "" + "" + "" + getString(R.string.Secret_agreement) + "" + ""; tv_xieyi.setText(Html.fromHtml(xieyi)); } private void setLisenter() { // 监听多个输入框 TextChange textChange = new TextChange(); et_usernick.addTextChangedListener(textChange); et_usertel.addTextChangedListener(textChange); et_password.addTextChangedListener(textChange); tv_country.setOnClickListener(this); tv_country_code.setOnClickListener(this); rl_country.setOnClickListener(this); iv_hide.setOnClickListener(this); iv_show.setOnClickListener(this); iv_photo.setOnClickListener(this); btn_register.setOnClickListener(this); } @Override public void setPresenter(RegisterContract.Presenter presenter) { mPresenter = presenter; } @Override public Context getBaseContext() { return getContext(); } @Override public void showAvatar(String imagePath) { Glide.with(getActivity()).load(imagePath).diskCacheStrategy(DiskCacheStrategy.ALL).error(R.drawable.default_image).into(iv_photo); } @Override public void showDialog() { if (dialog != null) dialog.show(); } @Override public void cancelDialog() { if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } } @Override public void showPassword() { iv_show.setVisibility(View.GONE); iv_hide.setVisibility(View.VISIBLE); et_password .setTransformationMethod(PasswordTransformationMethod .getInstance()); } // 切换后将密码EditText光标置于末尾 private void editTextEnd() { CharSequence charSequence = et_password.getText(); if (charSequence instanceof Spannable) { Spannable spanText = (Spannable) charSequence; Selection.setSelection(spanText, charSequence.length()); } } @Override public void hidePassword() { iv_hide.setVisibility(View.GONE); iv_show.setVisibility(View.VISIBLE); et_password .setTransformationMethod(HideReturnsTransformationMethod .getInstance()); } @Override public void enableButton() { btn_register.setEnabled(true); } @Override public void disableButton() { btn_register.setEnabled(false); } @Override public void showToast(int msgRes) { Toast.makeText(getActivity(), msgRes, Toast.LENGTH_SHORT).show(); } @Override public String getOriginImagePath() { return imagePathOrigin; } @Override public Activity getBaseActivity() { return getActivity(); } // EditText监听器 class TextChange implements TextWatcher { @Override public void afterTextChanged(Editable arg0) { } @Override public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { } @Override public void onTextChanged(CharSequence cs, int start, int before, int count) { boolean sign1 = et_usernick.getText().length() > 0; boolean sign2 = et_usertel.getText().length() > 0; boolean sign3 = et_password.getText().length() > 0; if (sign1 & sign2 & sign3) { enableButton(); } else { disableButton(); } } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.iv_hide: hidePassword(); editTextEnd(); break; case R.id.iv_show: showPassword(); editTextEnd(); break; case R.id.btn_register: String usernick = et_usernick.getText().toString().trim(); String password = et_password.getText().toString().trim(); String usertel = et_usertel.getText().toString().trim(); String country = tv_country.getText().toString().trim(); String countryCode = tv_country_code.getText().toString().trim(); if (TextUtils.isEmpty(usertel)) { showToast(R.string.mobile_not_be_null); return; } if (country.equals(getString(R.string.china)) && countryCode.equals(getString(R.string.country_code))) { if (!Validator.isMobile(usertel)) { showToast(R.string.please_input_true_mobile); return; } } if (TextUtils.isEmpty(password)) { showToast(R.string.pwd_is_not_allow_null); return; } mPresenter.registerInServer(usernick, usertel,password); break; case R.id.rl_country: CommonUtils.showPup(getContext(), tv_country, tv_country_code); break; case R.id.iv_photo: showCamera(); break; } } private String getAvatarName() { return HTApp.getInstance().getDirFilePath() + "org_" + System.currentTimeMillis() + ".png"; } // 拍照部分 private void showCamera() { HTAlertDialog fxAlertDialog = new HTAlertDialog(getActivity(), null, new String[]{getString(R.string.attach_take_pic), getString(R.string.image_manager)}); fxAlertDialog.init(new HTAlertDialog.OnItemClickListner() { @Override public void onClick(int position) { imagePathOrigin = getAvatarName(); switch (position) { case 0: if(!checkPermission(Manifest.permission.CAMERA)){ return; } Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 指定调用相机拍照后照片的储存路径 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(imagePathOrigin))); getActivity().startActivityForResult(intent, RegisterContract.PHOTO_REQUEST_TAKEPHOTO); break; case 1: if( !checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) ){ return; } Crop.pickImage(getActivity(), RegisterContract.PHOTO_REQUEST_GALLERY); break; } } }); } private boolean checkPermission(String permissionName) { PackageManager pm = getActivity().getPackageManager(); boolean permission = (PackageManager.PERMISSION_GRANTED == pm.checkPermission(permissionName, getActivity().getPackageName())); if (permission) { return true; }else { showToast(R.string.no_permission_camera); requestPermissions(new String[]{permissionName}, REQUEST_CODE); return false; } } private static final int REQUEST_CODE=100; @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(requestCode==REQUEST_CODE){ for(int i=0;i params = new ArrayList(); params.add(new Param("usertel", usertel)); params.add(new Param("password", password)); params.add(new Param("usernick", usernick)); if (!TextUtils.isEmpty(imageName)) { params.add(new Param("avatar", imageName)); } new OkHttpUtils(registerView.getBaseActivity()).post(params, HTConstant.URL_REGISTER, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { int status = jsonObject.getInteger("code"); switch (status) { case 1: JSONObject user = jsonObject.getJSONObject("user"); if (user != null) { HTClient.getInstance().register(user.getString(HTConstant.JSON_KEY_HXID), user.getString(HTConstant.JSON_KEY_PASSWORD), new HTClient.HTCallBack() { @Override public void onSuccess() { registerView.getBaseActivity().runOnUiThread(new Runnable() { @Override public void run() { registerView.cancelDialog(); registerView.showToast(R.string.Registered_successfully); registerView.getBaseActivity().finish(); } }); } @Override public void onError() { registerView.getBaseActivity().runOnUiThread(new Runnable() { @Override public void run() { registerView.cancelDialog(); registerView.showToast(R.string.Registration_failed); } }); } }); } break; case -1: registerView.cancelDialog(); registerView.showToast(R.string.mobile_is_register); break; case -2: registerView.cancelDialog(); registerView.showToast(R.string.Incorrect_phone_number_format); break; default: registerView.cancelDialog(); registerView.showToast(R.string.Server_busy); break; } } @Override public void onFailure(String errorMsg) { registerView.cancelDialog(); registerView.showToast(R.string.Server_busy); } }); } private void uploadAvatar(final String usernick, final String password, final String usertel, String filePath) { final String fileName = filePath.substring(filePath.lastIndexOf("/") + 1); new UploadFileUtils(registerView.getBaseActivity(), fileName, filePath).asyncUploadFile(new UploadFileUtils.a() { @Override public void onProgress(PutObjectRequest request, long currentSize, long totalSize) { } @Override public void onSuccess(PutObjectRequest request, PutObjectResult result) { final String url = HTConstant.baseOssUrl + fileName; registerView.getBaseActivity(). runOnUiThread(new Runnable() { @Override public void run() { register(usernick, password, usertel, url); } }); } @Override public void onFailure(PutObjectRequest request, ClientException clientExcepion, ServiceException serviceException) { registerView.getBaseActivity().runOnUiThread(new Runnable() { @Override public void run() { registerView.cancelDialog(); } }); } }); } @Override public void result(int requestCode, int resultCode, Intent intent) { if (resultCode == Activity.RESULT_OK) { switch (requestCode) { case RegisterContract.PHOTO_REQUEST_GALLERY: if (intent != null) beginCrop(intent.getData()); break; case RegisterContract.PHOTO_REQUEST_TAKEPHOTO: beginCrop(Uri.fromFile(new File(registerView.getOriginImagePath()))); break; case RegisterContract.PHOTO_REQUEST_CUT: Uri output = Crop.getOutput(intent); Log.d("output---->",output.getPath()); registerView.showAvatar(output.getPath()); break; } } else { //裁剪失败 if(requestCode==RegisterContract.PHOTO_REQUEST_CUT){ cropImagePath=null; registerView.showAvatar(null); } } } private void beginCrop(Uri inputUri) { cropImagePath= HTApp.getInstance().getDirFilePath() + "crop_" + System.currentTimeMillis() + ".png"; Uri outputUri = Uri.fromFile(new File(cropImagePath)); Crop.of(inputUri, outputUri).asSquare().start(registerView.getBaseActivity(), RegisterContract.PHOTO_REQUEST_CUT); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/domain/InviteMessage.java ================================================ package com.htmessage.yichatopen.domain; public class InviteMessage { private String from; private long time; private String reason; private Status status; private int id; public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public String getReason() { return reason; } public void setReason(String reason) { this.reason = reason; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } public int getId() { return id; } public void setId(int id) { this.id = id; } public enum Status { BEINVITEED, BEREFUSED, BEAGREED, AGREED, REFUSED } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/domain/InviteMessgeDao.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.domain; import android.content.ContentValues; import android.content.Context; import com.htmessage.yichatopen.manager.DBManager; import java.util.List; public class InviteMessgeDao { public static final String TABLE_NAME = "new_friends_msgs"; public static final String COLUMN_NAME_ID = "id"; public static final String COLUMN_NAME_FROM = "username"; public static final String COLUMN_NAME_GROUP_ID = "groupid"; public static final String COLUMN_NAME_GROUP_Name = "groupname"; public static final String COLUMN_NAME_TIME = "time"; public static final String COLUMN_NAME_REASON = "reason"; public static final String COLUMN_NAME_STATUS = "status"; public static final String COLUMN_NAME_ISINVITEFROMME = "isInviteFromMe"; public static final String COLUMN_NAME_GROUPINVITER = "groupinviter"; public static final String COLUMN_NAME_UNREAD_MSG_COUNT = "unreadMsgCount"; public InviteMessgeDao(Context context){ } /** * save message * @param message * @return return cursor of the message */ public Integer saveMessage(InviteMessage message){ return DBManager.getInstance().saveMessage(message); } /** * update message * @param msgId * @param values */ public void updateMessage(int msgId,ContentValues values){ DBManager.getInstance().updateMessage(msgId, values); } /** * get messges * @return */ public List getMessagesList(){ return DBManager.getInstance().getMessagesList(); } public void deleteMessage(String from){ DBManager.getInstance().deleteMessage(from); } public int getUnreadMessagesCount(){ return DBManager.getInstance().getUnreadNotifyCount(); } public void saveUnreadMessageCount(int count){ DBManager.getInstance().setUnreadNotifyCount(count); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/domain/User.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.domain; import android.annotation.SuppressLint; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.utils.CommonUtils; @SuppressLint("ParcelCreator") public class User implements Parcelable { /** * initial letter for nickname */ protected String initialLetter; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } /** * avatar of the user */ protected String avatar; private String username; protected String userInfo; private String nick; public String getNick() { return nick; } public void setNick(String nick) { this.nick = nick; } public User(String username){ this.username = username; } public String getInitialLetter() { if(initialLetter == null){ CommonUtils.setUserInitialLetter(this); } return initialLetter; } public void setInitialLetter(String initialLetter) { this.initialLetter = initialLetter; } public String getAvatar() { if(!TextUtils.isEmpty(avatar)){ if (!avatar.contains("http")){ avatar = HTConstant.URL_AVATAR+avatar; } } return avatar; } public void setAvatar(String avatar) { this.avatar = avatar; } public String getUserInfo(){ return userInfo; } public void setUserInfo(String userInfo){ this.userInfo=userInfo; } @Override public int hashCode() { return 17 * getUsername().hashCode(); } @Override public boolean equals(Object o) { if (o == null || !(o instanceof User)) { return false; } return getUsername().equals(((User) o).getUsername()); } @Override public String toString() { return nick == null ? username : nick; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/domain/UserDao.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.domain; import android.content.Context; import com.htmessage.yichatopen.manager.DBManager; import java.util.List; import java.util.Map; public class UserDao { public static final String TABLE_NAME = "uers"; public static final String COLUMN_NAME_ID = "username"; public static final String COLUMN_NAME_NICK = "nick"; public static final String COLUMN_NAME_AVATAR = "avatar"; public static final String COLUMN_NAME_INFO = "userInfo"; public static final String PREF_TABLE_NAME = "pref"; public static final String COLUMN_NAME_DISABLED_GROUPS = "disabled_groups"; public static final String COLUMN_NAME_DISABLED_IDS = "disabled_ids"; public static final String ROBOT_TABLE_NAME = "robots"; public static final String ROBOT_COLUMN_NAME_ID = "username"; public static final String ROBOT_COLUMN_NAME_NICK = "nick"; public static final String ROBOT_COLUMN_NAME_AVATAR = "avatar"; public UserDao(Context context) { } /** * save contact list * * @param contactList */ public void saveContactList(List contactList) { DBManager.getInstance().saveContactList(contactList); } /** * get contact list * * @return */ public Map getContactList() { return DBManager.getInstance().getContactList(); } /** * delete a contact * @param username */ public void deleteContact(String username){ DBManager.getInstance().deleteContact(username); } /** * save a contact * @param user */ public void saveContact(User user){ DBManager.getInstance().saveContact(user); } public void setDisabledGroups(List groups){ DBManager.getInstance().setDisabledGroups(groups); } public List getDisabledGroups(){ return DBManager.getInstance().getDisabledGroups(); } public void setDisabledIds(List ids){ DBManager.getInstance().setDisabledIds(ids); } public List getDisabledIds(){ return DBManager.getInstance().getDisabledIds(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/ContactsManager.java ================================================ package com.htmessage.yichatopen.manager; import android.content.Context; import android.util.Log; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.domain.UserDao; import java.util.List; import java.util.Map; /** * Created by huangfangyi on 2016/12/8. * qq 84543217 */ public class ContactsManager { private static ContactsManager contactsManager; private Context context; private UserDao userDao; private Map contacts=null; synchronized public static void init(Context context) { if (contactsManager == null) { contactsManager = new ContactsManager(context); } } public ContactsManager(Context context) { this.context = context; userDao= new UserDao(context); initContacts(); } public static ContactsManager getInstance(){ if(contactsManager==null){ throw new RuntimeException("please init this first!"); } return contactsManager; } public boolean saveContactList(List contactList) { Log.d("saveContactList----->",contactList.size()+""); contacts.clear(); for (User user:contactList){ contacts.put(user.getUsername(),user); } userDao.saveContactList(contactList); return true; } public Map getContactList() { if(contacts==null){ contacts=userDao.getContactList(); } return contacts; } public void saveContact(User user){ contacts.put(user.getUsername(),user); userDao.saveContact(user); } private void initContacts(){ contacts=userDao.getContactList(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/DBManager.java ================================================ package com.htmessage.yichatopen.manager; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.domain.InviteMessage; import com.htmessage.yichatopen.domain.InviteMessage.Status; import com.htmessage.yichatopen.domain.InviteMessgeDao; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.domain.UserDao; import com.htmessage.yichatopen.utils.CommonUtils; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; public class DBManager { static private DBManager dbMgr = new DBManager(); private DbOpenHelper dbHelper; private DBManager() { dbHelper = DbOpenHelper.getInstance(HTApp.getInstance().getApplicationContext()); } public static synchronized DBManager getInstance() { if (dbMgr == null) { dbMgr = new DBManager(); } return dbMgr; } /** * save contact list * * @param contactList */ synchronized public void saveContactList(List contactList) { Log.d("saveContactList3----->", contactList.size() + ""); SQLiteDatabase db = dbHelper.getWritableDatabase(); if (db.isOpen()) { db.delete(UserDao.TABLE_NAME, null, null); for (User user : contactList) { ContentValues values = new ContentValues(); values.put(UserDao.COLUMN_NAME_ID, user.getUsername()); if (user.getNick() != null) values.put(UserDao.COLUMN_NAME_NICK, user.getNick()); if (user.getAvatar() != null) values.put(UserDao.COLUMN_NAME_AVATAR, user.getAvatar()); if (user.getUserInfo() != null) values.put(UserDao.COLUMN_NAME_INFO, user.getUserInfo()); db.replace(UserDao.TABLE_NAME, null, values); } } } /** * get contact list * * @return */ synchronized public Map getContactList() { SQLiteDatabase db = dbHelper.getReadableDatabase(); Map users = new Hashtable(); if (db.isOpen()) { Cursor cursor = db.rawQuery("select * from " + UserDao.TABLE_NAME /* + " desc" */, null); while (cursor.moveToNext()) { String username = cursor.getString(cursor.getColumnIndex(UserDao.COLUMN_NAME_ID)); String nick = cursor.getString(cursor.getColumnIndex(UserDao.COLUMN_NAME_NICK)); String avatar = cursor.getString(cursor.getColumnIndex(UserDao.COLUMN_NAME_AVATAR)); String userInfo = cursor.getString(cursor.getColumnIndex(UserDao.COLUMN_NAME_INFO)); User user = new User(username); user.setNick(nick); user.setAvatar(avatar); user.setUserInfo(userInfo); CommonUtils.setUserInitialLetter(user); if ("123456789".contains(user.getInitialLetter())) { user.setInitialLetter("#"); } users.put(username, user); } cursor.close(); } return users; } /** * delete a contact * * @param username */ synchronized public void deleteContact(String username) { SQLiteDatabase db = dbHelper.getWritableDatabase(); if (db.isOpen()) { db.delete(UserDao.TABLE_NAME, UserDao.COLUMN_NAME_ID + " = ?", new String[]{username}); } } /** * save a contact * * @param user */ synchronized public void saveContact(User user) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(UserDao.COLUMN_NAME_ID, user.getUsername()); if (user.getNick() != null) values.put(UserDao.COLUMN_NAME_NICK, user.getNick()); if (user.getAvatar() != null) values.put(UserDao.COLUMN_NAME_AVATAR, user.getAvatar()); if (user.getUserInfo() != null) values.put(UserDao.COLUMN_NAME_INFO, user.getUserInfo()); if (db.isOpen()) { db.replace(UserDao.TABLE_NAME, null, values); } } public void setDisabledGroups(List groups) { setList(UserDao.COLUMN_NAME_DISABLED_GROUPS, groups); } public List getDisabledGroups() { return getList(UserDao.COLUMN_NAME_DISABLED_GROUPS); } public void setDisabledIds(List ids) { setList(UserDao.COLUMN_NAME_DISABLED_IDS, ids); } public List getDisabledIds() { return getList(UserDao.COLUMN_NAME_DISABLED_IDS); } synchronized private void setList(String column, List strList) { StringBuilder strBuilder = new StringBuilder(); for (String hxid : strList) { strBuilder.append(hxid).append("$"); } SQLiteDatabase db = dbHelper.getWritableDatabase(); if (db.isOpen()) { ContentValues values = new ContentValues(); values.put(column, strBuilder.toString()); db.update(UserDao.PREF_TABLE_NAME, values, null, null); } } synchronized private List getList(String column) { SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.rawQuery("select " + column + " from " + UserDao.PREF_TABLE_NAME, null); if (!cursor.moveToFirst()) { cursor.close(); return null; } String strVal = cursor.getString(0); if (strVal == null || strVal.equals("")) { return null; } cursor.close(); String[] array = strVal.split("$"); if (array != null && array.length > 0) { List list = new ArrayList(); for (String str : array) { list.add(str); } return list; } return null; } /** * save a message * * @param message * @return return cursor of the message */ public synchronized Integer saveMessage(InviteMessage message) { SQLiteDatabase db = dbHelper.getWritableDatabase(); int id = -1; if (db.isOpen()) { ContentValues values = new ContentValues(); values.put(InviteMessgeDao.COLUMN_NAME_FROM, message.getFrom()); values.put(InviteMessgeDao.COLUMN_NAME_REASON, message.getReason()); values.put(InviteMessgeDao.COLUMN_NAME_TIME, message.getTime()); values.put(InviteMessgeDao.COLUMN_NAME_STATUS, message.getStatus().ordinal()); db.insert(InviteMessgeDao.TABLE_NAME, null, values); Cursor cursor = db.rawQuery("select last_insert_rowid() from " + InviteMessgeDao.TABLE_NAME, null); if (cursor.moveToFirst()) { id = cursor.getInt(0); } cursor.close(); } return id; } /** * update message * * @param msgId * @param values */ synchronized public void updateMessage(int msgId, ContentValues values) { SQLiteDatabase db = dbHelper.getWritableDatabase(); if (db.isOpen()) { db.update(InviteMessgeDao.TABLE_NAME, values, InviteMessgeDao.COLUMN_NAME_ID + " = ?", new String[]{String.valueOf(msgId)}); } } /** * get messges * * @return */ synchronized public List getMessagesList() { SQLiteDatabase db = dbHelper.getReadableDatabase(); List msgs = new ArrayList(); if (db.isOpen()) { Cursor cursor = db.rawQuery("select * from " + InviteMessgeDao.TABLE_NAME + " desc", null); while (cursor.moveToNext()) { InviteMessage msg = new InviteMessage(); int id = cursor.getInt(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_ID)); String from = cursor.getString(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_FROM)); String groupid = cursor.getString(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_GROUP_ID)); String groupname = cursor.getString(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_GROUP_Name)); String reason = cursor.getString(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_REASON)); long time = cursor.getLong(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_TIME)); int status = cursor.getInt(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_STATUS)); String groupInviter = cursor.getString(cursor.getColumnIndex(InviteMessgeDao.COLUMN_NAME_GROUPINVITER)); msg.setId(id); msg.setFrom(from); msg.setReason(reason); msg.setTime(time); if (status == Status.BEINVITEED.ordinal()) msg.setStatus(Status.BEINVITEED); else if (status == Status.BEAGREED.ordinal()) msg.setStatus(Status.BEAGREED); else if (status == Status.BEREFUSED.ordinal()) msg.setStatus(Status.BEREFUSED); else if (status == Status.AGREED.ordinal()) msg.setStatus(Status.AGREED); else if (status == Status.REFUSED.ordinal()) msg.setStatus(Status.REFUSED); msgs.add(msg); } cursor.close(); } return msgs; } /** * delete invitation message * * @param from */ synchronized public void deleteMessage(String from) { SQLiteDatabase db = dbHelper.getWritableDatabase(); if (db.isOpen()) { db.delete(InviteMessgeDao.TABLE_NAME, InviteMessgeDao.COLUMN_NAME_FROM + " = ?", new String[]{from}); } } public synchronized int getUnreadNotifyCount() { int count = 0; SQLiteDatabase db = dbHelper.getReadableDatabase(); if (db.isOpen()) { Cursor cursor = db.rawQuery("select " + InviteMessgeDao.COLUMN_NAME_UNREAD_MSG_COUNT + " from " + InviteMessgeDao.TABLE_NAME, null); if (cursor.moveToFirst()) { count = cursor.getInt(0); } cursor.close(); } return count; } public synchronized void setUnreadNotifyCount(int count) { SQLiteDatabase db = dbHelper.getWritableDatabase(); if (db.isOpen()) { ContentValues values = new ContentValues(); values.put(InviteMessgeDao.COLUMN_NAME_UNREAD_MSG_COUNT, count); db.update(InviteMessgeDao.TABLE_NAME, values, null, null); } } synchronized public void closeDB() { if (dbHelper != null) { dbHelper.closeDB(); } dbMgr = null; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/DbOpenHelper.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.manager; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.domain.InviteMessgeDao; import com.htmessage.yichatopen.domain.UserDao; public class DbOpenHelper extends SQLiteOpenHelper { private static final int DATABASE_VERSION = 1; private static DbOpenHelper instance; private static final String USERNAME_TABLE_CREATE = "CREATE TABLE " + UserDao.TABLE_NAME + " (" + UserDao.COLUMN_NAME_NICK + " TEXT, " + UserDao.COLUMN_NAME_AVATAR + " TEXT, " + UserDao.COLUMN_NAME_INFO+ " TEXT, " + UserDao.COLUMN_NAME_ID + " TEXT PRIMARY KEY);"; private static final String INIVTE_MESSAGE_TABLE_CREATE = "CREATE TABLE " + InviteMessgeDao.TABLE_NAME + " (" + InviteMessgeDao.COLUMN_NAME_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + InviteMessgeDao.COLUMN_NAME_FROM + " TEXT, " + InviteMessgeDao.COLUMN_NAME_GROUP_ID + " TEXT, " + InviteMessgeDao.COLUMN_NAME_GROUP_Name + " TEXT, " + InviteMessgeDao.COLUMN_NAME_REASON + " TEXT, " + InviteMessgeDao.COLUMN_NAME_STATUS + " INTEGER, " + InviteMessgeDao.COLUMN_NAME_ISINVITEFROMME + " INTEGER, " + InviteMessgeDao.COLUMN_NAME_UNREAD_MSG_COUNT + " INTEGER, " + InviteMessgeDao.COLUMN_NAME_TIME + " TEXT, " + InviteMessgeDao.COLUMN_NAME_GROUPINVITER + " TEXT); "; private DbOpenHelper(Context context) { super(context, getUserDatabaseName(), null, DATABASE_VERSION); Log.d("SDKDbOpenHelper----->",getUserDatabaseName()); } public static DbOpenHelper getInstance(Context context) { if (instance == null) { instance = new DbOpenHelper(context.getApplicationContext()); } return instance; } private static String getUserDatabaseName() { return HTApp.getInstance().getUsername()+ "_app.db"; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(USERNAME_TABLE_CREATE); db.execSQL(INIVTE_MESSAGE_TABLE_CREATE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } public void closeDB() { if (instance != null) { try { SQLiteDatabase db = instance.getWritableDatabase(); db.close(); } catch (Exception e) { e.printStackTrace(); } instance = null; } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/LocalUserManager.java ================================================ /** * Copyright (C) 2013-2014 EaseMob Technologies. All rights reserved. *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.manager; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; public class LocalUserManager { /** * 保存Preference的name */ public static final String PREFERENCE_NAME = "userInfo"; private static SharedPreferences mSharedPreferences; private static LocalUserManager mPreferencemManager; private static SharedPreferences.Editor editor; private String SHARED_KEY_USER_INFO = "shared_key_user_info"; private static Context context; private LocalUserManager(Context cxt) { this.context=cxt; mSharedPreferences = cxt.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); editor = mSharedPreferences.edit(); } public static synchronized void init(Context cxt) { if (mPreferencemManager == null) { mPreferencemManager = new LocalUserManager(cxt); } } /** * 单例模式,获取instance实例 * * @param * @return */ public synchronized static LocalUserManager getInstance() { Log.d("CurrentUserManager---->",context.getPackageName()); if (mPreferencemManager == null) { throw new RuntimeException("please init first!"); } return mPreferencemManager; } public void setUserJson(JSONObject userJson) { String userInfo = ""; if (userJson != null) { try { userInfo = userJson.toJSONString(); } catch (JSONException e) { } } editor.putString(SHARED_KEY_USER_INFO, userInfo); editor.commit(); } public JSONObject getUserJson() { JSONObject userJson =null; String userStr = mSharedPreferences.getString(SHARED_KEY_USER_INFO, null); if (userStr != null) { userJson = JSONObject.parseObject(userStr); } return userJson; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/Manager.java ================================================ package com.htmessage.yichatopen.manager; import android.content.Context; /** * Created by huangfangyi on 2016/12/8. * qq 84543217 */ public class Manager { public static void initManagerList(Context context){ NotifierManager.init(context); ContactsManager.init(context); SettingsManager.init(context); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/MyNotification.java ================================================ package com.htmessage.yichatopen.manager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.support.annotation.NonNull; import android.support.v4.app.NotificationCompat; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.domain.User; import com.htmessage.yichatopen.activity.chat.ChatActivity; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.model.HTConversation; import com.htmessage.sdk.model.HTMessage; import com.htmessage.sdk.model.HTMessageTextBody; /** * Created by huangfangyi on 2016/12/19. * qq 84543217 */ public class MyNotification { private static MyNotification myNotification; private static NotificationManager manager = null; private Context mContext; private Notification.Builder nBuilder; private NotificationCompat.Builder builder; private boolean isOpenOutGoing = false;//默认不打开常驻 打开滑动删除 private static Notification notification; public MyNotification(Context context) { this.mContext = context; manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); //为了版本兼容 选择V4包下的NotificationCompat进行构造 builder = new NotificationCompat.Builder(mContext); nBuilder = new Notification.Builder(mContext); setOutGoingAndAutoCancle(isOpenOutGoing);// 默认不打开常驻 } public static void init(Context context) { if (myNotification == null) { myNotification = new MyNotification(context); } } public static MyNotification getInstance() { if (myNotification == null) { throw new RuntimeException("please init first~!"); } return myNotification; } /** * 设置常驻和滑动删除是否打开 * * @param flag false 打开滑动删除,不设置常驻 true 设置常驻,不设置滑动删除 */ public void setOutGoingAndAutoCancle(@NonNull boolean flag) { if (builder != null) { builder.setAutoCancel(!flag); builder.setOngoing(flag); } if (nBuilder != null) { nBuilder.setAutoCancel(!flag); nBuilder.setOngoing(flag); } } public void onNewMessage(HTMessage htMessage) { String userId = htMessage.getUsername(); String userNick = userId; Intent intent = new Intent(); intent.setClass(mContext, ChatActivity.class); intent.putExtra("userId", userId); if (htMessage.getChatType() == ChatType.singleChat) { User user = ContactsManager.getInstance().getContactList().get(userId); if (user != null) { userNick = user.getNick(); } } // 如果当前Activity启动在前台,则不开启新的Activity。 intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(mContext, Integer.valueOf(userId), intent, PendingIntent.FLAG_UPDATE_CURRENT); builder .setContentIntent(pendingIntent) .setContentTitle(userNick) // .setTicker("发来一个新消息") .setContentText(getContent(htMessage)) .setWhen(System.currentTimeMillis()) .setSmallIcon(mContext.getApplicationInfo().icon); notification = builder.build(); notification.flags = Notification.FLAG_ONGOING_EVENT; notification.flags = Notification.FLAG_AUTO_CANCEL; manager.notify(Integer.valueOf(userId), notification);//发送通知 } public void cancel(int id) { manager.cancel(id); } protected final static String[] msgs = { "发来一张图片", "发来一段语音"}; private String getContent(HTMessage message) { HTConversation htConversation = HTClient.getInstance().conversationManager().getConversation(message.getFrom()); String notifyText = ""; if (htConversation != null) { if (htConversation.getUnReadCount() > 0) { notifyText = mContext.getString(R.string.zhongkuohao) + htConversation.getUnReadCount() + mContext.getString(R.string.zhongkuohao_msg); } } switch (message.getType()) { case TEXT: HTMessageTextBody htMessageTextBody = (HTMessageTextBody) message.getBody(); String content = htMessageTextBody.getContent(); if (content != null) { notifyText += content; } else { notifyText += msgs[0]; } break; case IMAGE: notifyText += msgs[0]; break; case VOICE: notifyText += msgs[1]; break; } return notifyText; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/Notifier.java ================================================ /************************************************************ * * Hyphenate CONFIDENTIAL * __________________ * Copyright (C) 2016 Hyphenate Inc. All rights reserved. *

* NOTICE: All information contained herein is, and remains * the property of Hyphenate Inc. * Dissemination of this information or reproduction of this material * is strictly forbidden unless prior written permission is obtained * from Hyphenate Inc. */ package com.htmessage.yichatopen.manager; import android.app.ActivityManager; import android.app.NotificationManager; import android.content.Context; import android.media.AudioManager; import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.os.Vibrator; import java.util.HashSet; import java.util.List; import java.util.Locale; /** * new message notifier class *

* this class is subject to be inherited and implement the relative APIs */ public class Notifier { private final static String TAG = "notify"; Ringtone ringtone = null; protected final static String[] msg_eng = {"sent a message", "sent a picture", "sent a voice", "sent location message", "sent a video", "sent a file", "%1 contacts sent %2 messages" }; protected final static String[] msg_ch = {"发来一条消息", "发来一张图片", "发来一段语音", "发来位置信息", "发来一个视频", "发来一个文件", "%1个联系人发来%2条消息" }; protected static int notifyID = 0525; // start notification id protected static int foregroundNotifyID = 0555; protected NotificationManager notificationManager = null; protected HashSet fromUsers = new HashSet(); protected int notificationNum = 0; protected Context appContext; protected String packageName; protected String[] msgs; protected long lastNotifiyTime; protected AudioManager audioManager; protected Vibrator vibrator; public Notifier() { } /** * this function can be override * * @param context * @return */ public Notifier init(Context context) { appContext = context; notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); packageName = appContext.getApplicationInfo().packageName; if (Locale.getDefault().getLanguage().equals("zh")) { msgs = msg_ch; } else { msgs = msg_eng; } audioManager = (AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE); vibrator = (Vibrator) appContext.getSystemService(Context.VIBRATOR_SERVICE); return this; } /** * this function can be override */ public void reset() { resetNotificationCount(); cancelNotificaton(); } void resetNotificationCount() { notificationNum = 0; fromUsers.clear(); } void cancelNotificaton() { if (notificationManager != null) notificationManager.cancel(notifyID); } public static boolean isAppRunningForeground(Context context) { ActivityManager var1 = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List var2 = var1.getRunningTasks(1); return context.getPackageName().equalsIgnoreCase(((ActivityManager.RunningTaskInfo) var2.get(0)).baseActivity.getPackageName()); } // // // /** // * vibrate and play tone // */ // public void vibrateAndPlayTone( String string) { // // // if (System.currentTimeMillis() - lastNotifiyTime < 1000) { // // received new messages within 2 seconds, skip play ringtone // return; // } // // try { // lastNotifiyTime = System.currentTimeMillis(); // // // check if in silent mode // if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) { // return; // } // //// long[] pattern = new long[] { 0, 180, 80, 120 }; //// vibrator.vibrate(pattern, -1); // if (ringtone == null) { // Uri notificationUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); // // ringtone = RingtoneManager.getRingtone(appContext, notificationUri); // if (ringtone == null) { // return; // } // } // // if (!ringtone.isPlaying()) { // String vendor = Build.MANUFACTURER; // // ringtone.play(); // // for samsung S3, we meet a bug that the phone will // // continue ringtone without stop // // so add below special handler to stop it after 3s if // // needed // if (vendor != null && vendor.toLowerCase().contains("samsung")) { // Thread ctlThread = new Thread() { // public void run() { // try { // Thread.sleep(3000); // if (ringtone.isPlaying()) { // ringtone.stop(); // } // } catch (Exception e) { // } // } // }; // ctlThread.run(); // } // } // // } catch (Exception e) { // e.printStackTrace(); // } // } /** * vibrate and play tone */ public void vibrateAndPlayTone(String string) { if (System.currentTimeMillis() - lastNotifiyTime < 1000) { //时间间隔小于1秒钟的返回 // received new messages within 2 seconds, skip play ringtone return; } lastNotifiyTime=System.currentTimeMillis(); playSoundAndVibrator(); } /** * 播放声音 */ private void playSound() { try { lastNotifiyTime = System.currentTimeMillis(); // check if in silent mode if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) { return; } if (ringtone == null) { Uri notificationUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); ringtone = RingtoneManager.getRingtone(appContext, notificationUri); if (ringtone == null) { return; } } if (!ringtone.isPlaying()) { String vendor = Build.MANUFACTURER; ringtone.play(); // for samsung S3, we meet a bug that the phone will // continue ringtone without stop // so add below special handler to stop it after 3s if // needed if (vendor != null && vendor.toLowerCase().contains("samsung")) { Thread ctlThread = new Thread() { public void run() { try { Thread.sleep(3000); if (ringtone.isPlaying()) { ringtone.stop(); } } catch (Exception e) { } } }; ctlThread.run(); } } } catch (Exception e) { e.printStackTrace(); } } /** * 声音和震动 */ private void playSoundAndVibrator() { try { lastNotifiyTime = System.currentTimeMillis(); // check if in silent mode if (audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) { return; } if (vibrator == null) { vibrator = (Vibrator) appContext.getSystemService(Context.VIBRATOR_SERVICE); } if (ringtone == null) { Uri notificationUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); ringtone = RingtoneManager.getRingtone(appContext, notificationUri); if (ringtone == null) { return; } } /** * 四个参数就是——停止 开启 停止 开启 * -1不重复,非-1为从pattern的指定下标开始重复 */ long[] pattern = new long[]{0, 180, 80, 120}; vibrator.vibrate(pattern, -1); /** * 播放声音 */ if (!ringtone.isPlaying()) { String vendor = Build.MANUFACTURER; ringtone.play(); // for samsung S3, we meet a bug that the phone will // continue ringtone without stop // so add below special handler to stop it after 3s if // needed if (vendor != null && vendor.toLowerCase().contains("samsung")) { Thread ctlThread = new Thread() { public void run() { try { Thread.sleep(3000); if (ringtone.isPlaying()) { ringtone.stop(); } } catch (Exception e) { } } }; ctlThread.run(); } } } catch (Exception e) { e.printStackTrace(); } } /** * 震动 */ private void playVibrator() { lastNotifiyTime = System.currentTimeMillis(); if (vibrator == null) { vibrator = (Vibrator) appContext.getSystemService(Context.VIBRATOR_SERVICE); } /** * 四个参数就是——停止 开启 停止 开启 * -1不重复,非-1为从pattern的指定下标开始重复 */ long[] pattern = new long[]{0, 180, 80, 120}; vibrator.vibrate(pattern, -1); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/NotifierManager.java ================================================ package com.htmessage.yichatopen.manager; import android.content.Context; /** * Created by huangfangyi on 2016/10/12. * qq 84543217 */ public class NotifierManager { private static NotifierManager notifierManager = null; /** * application context */ private Context appContext; /** * get notifierManager of EaseUI * * @return */ public static NotifierManager getInstance() { if (notifierManager == null) { throw new RuntimeException("NotifierManager please init first!"); } return notifierManager; } public static synchronized void init(Context context) { if (notifierManager == null) { notifierManager = new NotifierManager(context); } } public NotifierManager(Context context) { appContext = context; initNotifier(); } /** * the notifier */ private Notifier notifier = null; private void initNotifier() { notifier = createNotifier(); notifier.init(appContext); } protected Notifier createNotifier() { return new Notifier(); } public Notifier getNotifier() { return notifier; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/PreferenceManager.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. *

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.manager; import android.content.Context; import android.content.SharedPreferences; public class PreferenceManager { /** * name of preference */ public static final String PREFERENCE_NAME = "saveInfo"; private static SharedPreferences mSharedPreferences; private static PreferenceManager mPreferencemManager; private static SharedPreferences.Editor editor; private String SHARED_KEY_SETTING_NOTIFICATION = "shared_key_setting_notification"; private String SHARED_KEY_SETTING_SOUND = "shared_key_setting_sound"; private String SHARED_KEY_SETTING_VIBRATE = "shared_key_setting_vibrate"; private String SHARED_KEY_SETTING_SPEAKER = "shared_key_setting_speaker"; private static String SHARED_KEY_SETTING_CHATROOM_OWNER_LEAVE = "shared_key_setting_chatroom_owner_leave"; private static String SHARED_KEY_SETTING_DELETE_MESSAGES_WHEN_EXIT_GROUP = "shared_key_setting_delete_messages_when_exit_group"; private static String SHARED_KEY_SETTING_AUTO_ACCEPT_GROUP_INVITATION = "shared_key_setting_auto_accept_group_invitation"; private static String SHARED_KEY_SETTING_ADAPTIVE_VIDEO_ENCODE = "shared_key_setting_adaptive_video_encode"; private static String SHARED_KEY_SETTING_GROUPS_SYNCED = "SHARED_KEY_SETTING_GROUPS_SYNCED"; private static String SHARED_KEY_SETTING_CONTACT_SYNCED = "SHARED_KEY_SETTING_CONTACT_SYNCED"; private static String SHARED_KEY_SETTING_BALCKLIST_SYNCED = "SHARED_KEY_SETTING_BALCKLIST_SYNCED"; private static String SHARED_KEY_CURRENTUSER_USERNAME = "SHARED_KEY_CURRENTUSER_USERNAME"; private static String SHARED_KEY_CURRENTUSER_NICK = "SHARED_KEY_CURRENTUSER_NICK"; private static String SHARED_KEY_CURRENTUSER_AVATAR = "SHARED_KEY_CURRENTUSER_AVATAR"; private PreferenceManager(Context cxt) { mSharedPreferences = cxt.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); editor = mSharedPreferences.edit(); } public static synchronized void init(Context cxt) { if (mPreferencemManager == null) { mPreferencemManager = new PreferenceManager(cxt); } } /** * get instance of PreferenceManager * * @param * @return */ public synchronized static PreferenceManager getInstance() { if (mPreferencemManager == null) { throw new RuntimeException("please init first!"); } return mPreferencemManager; } public void setSettingMsgNotification(boolean paramBoolean) { editor.putBoolean(SHARED_KEY_SETTING_NOTIFICATION, paramBoolean); editor.commit(); } private static final String KEY_GROUP = "KEY_GROUP"; public void setActivityCount(String groupId, int count) { editor.putInt(KEY_GROUP + groupId, count); editor.commit(); } public int getActivityCount(String groupId) { return mSharedPreferences.getInt(KEY_GROUP + groupId, 0); } public boolean getSettingMsgNotification() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_NOTIFICATION, true); } public void setSettingMsgSound(boolean paramBoolean) { editor.putBoolean(SHARED_KEY_SETTING_SOUND, paramBoolean); editor.commit(); } public boolean getSettingMsgSound() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_SOUND, true); } public void setSettingMsgVibrate(boolean paramBoolean) { editor.putBoolean(SHARED_KEY_SETTING_VIBRATE, paramBoolean); editor.commit(); } public boolean getSettingMsgVibrate() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_VIBRATE, true); } public void setSettingMsgSpeaker(boolean paramBoolean) { editor.putBoolean(SHARED_KEY_SETTING_SPEAKER, paramBoolean); editor.commit(); } public boolean getSettingMsgSpeaker() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_SPEAKER, true); } public void setSettingAllowChatroomOwnerLeave(boolean value) { editor.putBoolean(SHARED_KEY_SETTING_CHATROOM_OWNER_LEAVE, value); editor.commit(); } public boolean getSettingAllowChatroomOwnerLeave() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_CHATROOM_OWNER_LEAVE, true); } public void setDeleteMessagesAsExitGroup(boolean value) { editor.putBoolean(SHARED_KEY_SETTING_DELETE_MESSAGES_WHEN_EXIT_GROUP, value); editor.commit(); } public boolean isDeleteMessagesAsExitGroup() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_DELETE_MESSAGES_WHEN_EXIT_GROUP, true); } public void setAutoAcceptGroupInvitation(boolean value) { editor.putBoolean(SHARED_KEY_SETTING_AUTO_ACCEPT_GROUP_INVITATION, value); editor.commit(); } public boolean isAutoAcceptGroupInvitation() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_AUTO_ACCEPT_GROUP_INVITATION, true); } public void setAdaptiveVideoEncode(boolean value) { editor.putBoolean(SHARED_KEY_SETTING_ADAPTIVE_VIDEO_ENCODE, value); editor.commit(); } public boolean isAdaptiveVideoEncode() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_ADAPTIVE_VIDEO_ENCODE, false); } public void setGroupsSynced(boolean synced) { editor.putBoolean(SHARED_KEY_SETTING_GROUPS_SYNCED, synced); editor.commit(); } public boolean isGroupsSynced() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_GROUPS_SYNCED, false); } public void setContactSynced(boolean synced) { editor.putBoolean(SHARED_KEY_SETTING_CONTACT_SYNCED, synced); editor.commit(); } public boolean isContactSynced() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_CONTACT_SYNCED, false); } public void setBlacklistSynced(boolean synced) { editor.putBoolean(SHARED_KEY_SETTING_BALCKLIST_SYNCED, synced); editor.commit(); } public boolean isBacklistSynced() { return mSharedPreferences.getBoolean(SHARED_KEY_SETTING_BALCKLIST_SYNCED, false); } public void setCurrentUserNick(String nick) { editor.putString(SHARED_KEY_CURRENTUSER_NICK, nick); editor.commit(); } public void setCurrentUserAvatar(String avatar) { editor.putString(SHARED_KEY_CURRENTUSER_AVATAR, avatar); editor.commit(); } public String getCurrentUserNick() { return mSharedPreferences.getString(SHARED_KEY_CURRENTUSER_NICK, null); } public String getCurrentUserAvatar() { return mSharedPreferences.getString(SHARED_KEY_CURRENTUSER_AVATAR, null); } public void setCurrentUserName(String username) { editor.putString(SHARED_KEY_CURRENTUSER_USERNAME, username); editor.commit(); } public String getCurrentUsername() { return mSharedPreferences.getString(SHARED_KEY_CURRENTUSER_USERNAME, null); } public void removeCurrentUserInfo() { editor.remove(SHARED_KEY_CURRENTUSER_NICK); editor.remove(SHARED_KEY_CURRENTUSER_AVATAR); editor.commit(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/manager/SettingsManager.java ================================================ package com.htmessage.yichatopen.manager; import android.content.Context; import java.util.HashMap; import java.util.Map; public class SettingsManager { protected Context context = null; protected Map valueCache = new HashMap(); private static SettingsManager settingManager; public SettingsManager(Context ctx){ context = ctx; PreferenceManager.init(context); } public synchronized static void init(Context context){ if(settingManager==null){ settingManager=new SettingsManager(context); } } public static SettingsManager getInstance(){ if(settingManager==null){ throw new RuntimeException("settingManager please init first"); } return settingManager; } public void setSettingMsgNotification(boolean paramBoolean) { PreferenceManager.getInstance().setSettingMsgNotification(paramBoolean); valueCache.put(Key.VibrateAndPlayToneOn, paramBoolean); } public boolean getSettingMsgNotification() { Object val = valueCache.get(Key.VibrateAndPlayToneOn); if(val == null){ val = PreferenceManager.getInstance().getSettingMsgNotification(); valueCache.put(Key.VibrateAndPlayToneOn, val); } return (Boolean) (val != null?val:true); } public void setSettingMsgSound(boolean paramBoolean) { PreferenceManager.getInstance().setSettingMsgSound(paramBoolean); valueCache.put(Key.PlayToneOn, paramBoolean); } public boolean getSettingMsgSound() { Object val = valueCache.get(Key.PlayToneOn); if(val == null){ val = PreferenceManager.getInstance().getSettingMsgSound(); valueCache.put(Key.PlayToneOn, val); } return (Boolean) (val != null?val:true); } public void setSettingMsgVibrate(boolean paramBoolean) { PreferenceManager.getInstance().setSettingMsgVibrate(paramBoolean); valueCache.put(Key.VibrateOn, paramBoolean); } public boolean getSettingMsgVibrate() { Object val = valueCache.get(Key.VibrateOn); if(val == null){ val = PreferenceManager.getInstance().getSettingMsgVibrate(); valueCache.put(Key.VibrateOn, val); } return (Boolean) (val != null?val:true); } public void setSettingMsgSpeaker(boolean paramBoolean) { PreferenceManager.getInstance().setSettingMsgSpeaker(paramBoolean); valueCache.put(Key.SpakerOn, paramBoolean); } public boolean getSettingMsgSpeaker() { Object val = valueCache.get(Key.SpakerOn); if(val == null){ val = PreferenceManager.getInstance().getSettingMsgSpeaker(); valueCache.put(Key.SpakerOn, val); } return (Boolean) (val != null?val:true); } public void allowChatroomOwnerLeave(boolean value){ PreferenceManager.getInstance().setSettingAllowChatroomOwnerLeave(value); } public boolean isChatroomOwnerLeaveAllowed(){ return PreferenceManager.getInstance().getSettingAllowChatroomOwnerLeave(); } public void setDeleteMessagesAsExitGroup(boolean value) { PreferenceManager.getInstance().setDeleteMessagesAsExitGroup(value); } public boolean isDeleteMessagesAsExitGroup() { return PreferenceManager.getInstance().isDeleteMessagesAsExitGroup(); } public void setAutoAcceptGroupInvitation(boolean value) { PreferenceManager.getInstance().setAutoAcceptGroupInvitation(value); } public boolean isAutoAcceptGroupInvitation() { return PreferenceManager.getInstance().isAutoAcceptGroupInvitation(); } public void setAdaptiveVideoEncode(boolean value) { PreferenceManager.getInstance().setAdaptiveVideoEncode(value); } public boolean isAdaptiveVideoEncode() { return PreferenceManager.getInstance().isAdaptiveVideoEncode(); } enum Key{ VibrateAndPlayToneOn, VibrateOn, PlayToneOn, SpakerOn, DisabledGroups, DisabledIds } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/runtimepermissions/Permissions.java ================================================ /** * Copyright 2015 Anthony Restaino Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.htmessage.yichatopen.runtimepermissions; /** * Enum class to handle the different states * of permissions since the PackageManager only * has a granted and denied state. */ enum Permissions { GRANTED, DENIED, NOT_FOUND } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/runtimepermissions/PermissionsManager.java ================================================ /** * Copyright 2015 Anthony Restaino Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.htmessage.yichatopen.runtimepermissions; import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; import android.util.Log; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; /** * A class to help you manage your permissions simply. */ public class PermissionsManager { private static final String TAG = PermissionsManager.class.getSimpleName(); private final Set mPendingRequests = new HashSet(1); private final Set mPermissions = new HashSet(1); private final List> mPendingActions = new ArrayList>(1); private static PermissionsManager mInstance = null; public static PermissionsManager getInstance() { if (mInstance == null) { mInstance = new PermissionsManager(); } return mInstance; } private PermissionsManager() { initializePermissionsMap(); } /** * This method uses reflection to read all the permissions in the Manifest class. * This is necessary because some permissions do not exist on older versions of Android, * since they do not exist, they will be denied when you check whether you have permission * which is problematic since a new permission is often added where there was no previous * permission required. We initialize a Set of available permissions and check the set * when checking if we have permission since we want to know when we are denied a permission * because it doesn't exist yet. */ private synchronized void initializePermissionsMap() { Field[] fields = Manifest.permission.class.getFields(); for (Field field : fields) { String name = null; try { name = (String) field.get(""); } catch (IllegalAccessException e) { Log.e(TAG, "Could not access field", e); } mPermissions.add(name); } } /** * This method retrieves all the permissions declared in the application's manifest. * It returns a non null array of permisions that can be declared. * * @param activity the Activity necessary to check what permissions we have. * @return a non null array of permissions that are declared in the application manifest. */ @NonNull private synchronized String[] getManifestPermissions(@NonNull final Activity activity) { PackageInfo packageInfo = null; List list = new ArrayList(1); try { Log.d(TAG, activity.getPackageName()); packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "A problem occurred when retrieving permissions", e); } if (packageInfo != null) { String[] permissions = packageInfo.requestedPermissions; if (permissions != null) { for (String perm : permissions) { Log.d(TAG, "Manifest contained permission: " + perm); list.add(perm); } } } return list.toArray(new String[list.size()]); } /** * This method adds the {@link PermissionsResultAction} to the current list * of pending actions that will be completed when the permissions are * received. The list of permissions passed to this method are registered * in the PermissionsResultAction object so that it will be notified of changes * made to these permissions. * * @param permissions the required permissions for the action to be executed. * @param action the action to add to the current list of pending actions. */ private synchronized void addPendingAction(@NonNull String[] permissions, @Nullable PermissionsResultAction action) { if (action == null) { return; } action.registerPermissions(permissions); mPendingActions.add(new WeakReference(action)); } /** * This method removes a pending action from the list of pending actions. * It is used for cases where the permission has already been granted, so * you immediately wish to remove the pending action from the queue and * execute the action. * * @param action the action to remove */ private synchronized void removePendingAction(@Nullable PermissionsResultAction action) { for (Iterator> iterator = mPendingActions.iterator(); iterator.hasNext(); ) { WeakReference weakRef = iterator.next(); if (weakRef.get() == action || weakRef.get() == null) { iterator.remove(); } } } /** * This static method can be used to check whether or not you have a specific permission. * It is basically a less verbose method of using {@link ActivityCompat#checkSelfPermission(Context, String)} * and will simply return a boolean whether or not you have the permission. If you pass * in a null Context object, it will return false as otherwise it cannot check the permission. * However, the Activity parameter is nullable so that you can pass in a reference that you * are not always sure will be valid or not (e.g. getActivity() from Fragment). * * @param context the Context necessary to check the permission * @param permission the permission to check * @return true if you have been granted the permission, false otherwise */ @SuppressWarnings("unused") public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) { return context != null && (ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission)); } /** * This static method can be used to check whether or not you have several specific permissions. * It is simpler than checking using {@link ActivityCompat#checkSelfPermission(Context, String)} * for each permission and will simply return a boolean whether or not you have all the permissions. * If you pass in a null Context object, it will return false as otherwise it cannot check the * permission. However, the Activity parameter is nullable so that you can pass in a reference * that you are not always sure will be valid or not (e.g. getActivity() from Fragment). * * @param context the Context necessary to check the permission * @param permissions the permissions to check * @return true if you have been granted all the permissions, false otherwise */ @SuppressWarnings("unused") public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) { if (context == null) { return false; } boolean hasAllPermissions = true; for (String perm : permissions) { hasAllPermissions &= hasPermission(context, perm); } return hasAllPermissions; } /** * This method will request all the permissions declared in your application manifest * for the specified {@link PermissionsResultAction}. The purpose of this method is to enable * all permissions to be requested at one shot. The PermissionsResultAction is used to notify * you of the user allowing or denying each permission. The Activity and PermissionsResultAction * parameters are both annotated Nullable, but this method will not work if the Activity * is null. It is only annotated Nullable as a courtesy to prevent crashes in the case * that you call this from a Fragment where {@link Fragment#getActivity()} could yield * null. Additionally, you will not receive any notification of permissions being granted * if you provide a null PermissionsResultAction. * * @param activity the Activity necessary to request and check permissions. * @param action the PermissionsResultAction used to notify you of permissions being accepted. */ @SuppressWarnings("unused") public synchronized void requestAllManifestPermissionsIfNecessary(final @Nullable Activity activity, final @Nullable PermissionsResultAction action) { if (activity == null) { return; } String[] perms = getManifestPermissions(activity); requestPermissionsIfNecessaryForResult(activity, perms, action); } /** * This method should be used to execute a {@link PermissionsResultAction} for the array * of permissions passed to this method. This method will request the permissions if * they need to be requested (i.e. we don't have permission yet) and will add the * PermissionsResultAction to the queue to be notified of permissions being granted or * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately. * The Activity variable is nullable, but if it is null, the method will fail to execute. * This is only nullable as a courtesy for Fragments where getActivity() may yeild null * if the Fragment is not currently added to its parent Activity. * * @param activity the activity necessary to request the permissions. * @param permissions the list of permissions to request for the {@link PermissionsResultAction}. * @param action the PermissionsResultAction to notify when the permissions are granted or denied. */ @SuppressWarnings("unused") public synchronized void requestPermissionsIfNecessaryForResult(@Nullable Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) { if (activity == null) { return; } addPendingAction(permissions, action); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { doPermissionWorkBeforeAndroidM(activity, permissions, action); } else { List permList = getPermissionsListToRequest(activity, permissions, action); if (permList.isEmpty()) { //if there is no permission to request, there is no reason to keep the action int the list removePendingAction(action); } else { String[] permsToRequest = permList.toArray(new String[permList.size()]); mPendingRequests.addAll(permList); ActivityCompat.requestPermissions(activity, permsToRequest, 1); } } } /** * This method should be used to execute a {@link PermissionsResultAction} for the array * of permissions passed to this method. This method will request the permissions if * they need to be requested (i.e. we don't have permission yet) and will add the * PermissionsResultAction to the queue to be notified of permissions being granted or * denied. In the case of pre-Android Marshmallow, permissions will be granted immediately. * The Fragment variable is used, but if {@link Fragment#getActivity()} returns null, this method * will fail to work as the activity reference is necessary to check for permissions. * * @param fragment the fragment necessary to request the permissions. * @param permissions the list of permissions to request for the {@link PermissionsResultAction}. * @param action the PermissionsResultAction to notify when the permissions are granted or denied. */ @SuppressWarnings("unused") public synchronized void requestPermissionsIfNecessaryForResult(@NonNull Fragment fragment, @NonNull String[] permissions, @Nullable PermissionsResultAction action) { Activity activity = fragment.getActivity(); if (activity == null) { return; } addPendingAction(permissions, action); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { doPermissionWorkBeforeAndroidM(activity, permissions, action); } else { List permList = getPermissionsListToRequest(activity, permissions, action); if (permList.isEmpty()) { //if there is no permission to request, there is no reason to keep the action int the list removePendingAction(action); } else { String[] permsToRequest = permList.toArray(new String[permList.size()]); mPendingRequests.addAll(permList); fragment.requestPermissions(permsToRequest, 1); } } } /** * This method notifies the PermissionsManager that the permissions have change. If you are making * the permissions requests using an Activity, then this method should be called from the * Activity callback onRequestPermissionsResult() with the variables passed to that method. If * you are passing a Fragment to make the permissions request, then you should call this in * the {@link Fragment#onRequestPermissionsResult(int, String[], int[])} method. * It will notify all the pending PermissionsResultAction objects currently * in the queue, and will remove the permissions request from the list of pending requests. * * @param permissions the permissions that have changed. * @param results the values for each permission. */ @SuppressWarnings("unused") public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) { int size = permissions.length; if (results.length < size) { size = results.length; } Iterator> iterator = mPendingActions.iterator(); while (iterator.hasNext()) { PermissionsResultAction action = iterator.next().get(); for (int n = 0; n < size; n++) { if (action == null || action.onResult(permissions[n], results[n])) { iterator.remove(); break; } } } for (int n = 0; n < size; n++) { mPendingRequests.remove(permissions[n]); } } /** * When request permissions on devices before Android M (Android 6.0, API Level 23) * Do the granted or denied work directly according to the permission status * * @param activity the activity to check permissions * @param permissions the permissions names * @param action the callback work object, containing what we what to do after * permission check */ private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) { for (String perm : permissions) { if (action != null) { if (!mPermissions.contains(perm)) { action.onResult(perm, Permissions.NOT_FOUND); } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) { action.onResult(perm, Permissions.DENIED); } else { action.onResult(perm, Permissions.GRANTED); } } } } /** * Filter the permissions list: * If a permission is not granted, add it to the result list * if a permission is granted, do the granted work, do not add it to the result list * * @param activity the activity to check permissions * @param permissions all the permissions names * @param action the callback work object, containing what we what to do after * permission check * @return a list of permissions names that are not granted yet */ @NonNull private List getPermissionsListToRequest(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) { List permList = new ArrayList(permissions.length); for (String perm : permissions) { if (!mPermissions.contains(perm)) { if (action != null) { action.onResult(perm, Permissions.NOT_FOUND); } } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) { if (!mPendingRequests.contains(perm)) { permList.add(perm); } } else { if (action != null) { action.onResult(perm, Permissions.GRANTED); } } } return permList; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/runtimepermissions/PermissionsResultAction.java ================================================ /** * Copyright 2015 Anthony Restaino Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.htmessage.yichatopen.runtimepermissions; import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.support.annotation.CallSuper; import android.support.annotation.NonNull; import android.util.Log; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * This abstract class should be used to create an if/else action that the PermissionsManager * can execute when the permissions you request are granted or denied. Simple use involves * creating an anonymous instance of it and passing that instance to the * requestPermissionsIfNecessaryForResult method. The result will be sent back to you as * either onGranted (all permissions have been granted), or onDenied (a required permission * has been denied). Ideally you put your functionality in the onGranted method and notify * the user what won't work in the onDenied method. */ public abstract class PermissionsResultAction { private static final String TAG = PermissionsResultAction.class.getSimpleName(); private final Set mPermissions = new HashSet(1); private Looper mLooper = Looper.getMainLooper(); /** * Default Constructor */ public PermissionsResultAction() {} /** * Alternate Constructor. Pass the looper you wish the PermissionsResultAction * callbacks to be executed on if it is not the current Looper. For instance, * if you are making a permissions request from a background thread but wish the * callback to be on the UI thread, use this constructor to specify the UI Looper. * * @param looper the looper that the callbacks will be called using. */ @SuppressWarnings("unused") public PermissionsResultAction(@NonNull Looper looper) {mLooper = looper;} /** * This method is called when ALL permissions that have been * requested have been granted by the user. In this method * you should put all your permissions sensitive code that can * only be executed with the required permissions. */ public abstract void onGranted(); /** * This method is called when a permission has been denied by * the user. It provides you with the permission that was denied * and will be executed on the Looper you pass to the constructor * of this class, or the Looper that this object was created on. * * @param permission the permission that was denied. */ public abstract void onDenied(String permission); /** * This method is used to determine if a permission not * being present on the current Android platform should * affect whether the PermissionsResultAction should continue * listening for events. By default, it returns true and will * simply ignore the permission that did not exist. Usually this will * work fine since most new permissions are introduced to * restrict what was previously allowed without permission. * If that is not the case for your particular permission you * request, override this method and return false to result in the * Action being denied. * * @param permission the permission that doesn't exist on this * Android version * @return return true if the PermissionsResultAction should * ignore the lack of the permission and proceed with exection * or false if the PermissionsResultAction should treat the * absence of the permission on the API level as a denial. */ @SuppressWarnings({"WeakerAccess", "SameReturnValue"}) public synchronized boolean shouldIgnorePermissionNotFound(String permission) { Log.d(TAG, "Permission not found: " + permission); return true; } @SuppressWarnings("WeakerAccess") @CallSuper protected synchronized final boolean onResult(final @NonNull String permission, int result) { if (result == PackageManager.PERMISSION_GRANTED) { return onResult(permission, Permissions.GRANTED); } else { return onResult(permission, Permissions.DENIED); } } /** * This method is called when a particular permission has changed. * This method will be called for all permissions, so this method determines * if the permission affects the state or not and whether it can proceed with * calling onGranted or if onDenied should be called. * * @param permission the permission that changed. * @param result the result for that permission. * @return this method returns true if its primary action has been completed * and it should be removed from the data structure holding a reference to it. */ @SuppressWarnings("WeakerAccess") @CallSuper protected synchronized final boolean onResult(final @NonNull String permission, Permissions result) { mPermissions.remove(permission); if (result == Permissions.GRANTED) { if (mPermissions.isEmpty()) { new Handler(mLooper).post(new Runnable() { @Override public void run() { onGranted(); } }); return true; } } else if (result == Permissions.DENIED) { new Handler(mLooper).post(new Runnable() { @Override public void run() { onDenied(permission); } }); return true; } else if (result == Permissions.NOT_FOUND) { if (shouldIgnorePermissionNotFound(permission)) { if (mPermissions.isEmpty()) { new Handler(mLooper).post(new Runnable() { @Override public void run() { onGranted(); } }); return true; } } else { new Handler(mLooper).post(new Runnable() { @Override public void run() { onDenied(permission); } }); return true; } } return false; } /** * This method registers the PermissionsResultAction object for the specified permissions * so that it will know which permissions to look for changes to. The PermissionsResultAction * will then know to look out for changes to these permissions. * * @param perms the permissions to listen for */ @SuppressWarnings("WeakerAccess") @CallSuper protected synchronized final void registerPermissions(@NonNull String[] perms) { Collections.addAll(mPermissions, perms); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/ACache.java ================================================ package com.htmessage.yichatopen.utils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.PixelFormat; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.RandomAccessFile; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; public class ACache { public static final int TIME_HOUR = 60 * 60; public static final int TIME_DAY = TIME_HOUR * 24; private static final int MAX_SIZE = 1000 * 1000 * 50; // 50 mb private static final int MAX_COUNT = Integer.MAX_VALUE; // 不限制存放数据的数量 private static Map mInstanceMap = new HashMap(); private ACacheManager mCache; public static ACache get(Context ctx) { return get(ctx, "ACache"); } public static ACache get(Context ctx, String cacheName) { File f = new File(ctx.getCacheDir(), cacheName); return get(f, MAX_SIZE, MAX_COUNT); } public static ACache get(File cacheDir) { return get(cacheDir, MAX_SIZE, MAX_COUNT); } public static ACache get(Context ctx, long max_zise, int max_count) { File f = new File(ctx.getCacheDir(), "ACache"); return get(f, max_zise, max_count); } public static ACache get(File cacheDir, long max_zise, int max_count) { ACache manager = mInstanceMap.get(cacheDir.getAbsoluteFile() + myPid()); if (manager == null) { manager = new ACache(cacheDir, max_zise, max_count); mInstanceMap.put(cacheDir.getAbsolutePath() + myPid(), manager); } return manager; } private static String myPid() { return "_" + android.os.Process.myPid(); } private ACache(File cacheDir, long max_size, int max_count) { if (!cacheDir.exists() && !cacheDir.mkdirs()) { throw new RuntimeException("can't make dirs in " + cacheDir.getAbsolutePath()); } mCache = new ACacheManager(cacheDir, max_size, max_count); } class xFileOutputStream extends FileOutputStream { File file; public xFileOutputStream(File file) throws FileNotFoundException { super(file); this.file = file; } public void close() throws IOException { super.close(); mCache.put(file); } } // ======================================= // ============ String数据 读写 ============== // ======================================= /** * 保存 String数据 到 缓存中 * * @param key 保存的key * @param value 保存的String数据 */ public void put(String key, String value) { File file = mCache.newFile(key); BufferedWriter out = null; try { out = new BufferedWriter(new FileWriter(file), 1024); out.write(value); } catch (IOException e) { e.printStackTrace(); } finally { if (out != null) { try { out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } mCache.put(file); } } /** * 保存 String数据 到 缓存中 * * @param key 保存的key * @param value 保存的String数据 * @param saveTime 保存的时间,单位:秒 */ public void put(String key, String value, int saveTime) { put(key, Utils.newStringWithDateInfo(saveTime, value)); } /** * 读取 String数据 * * @param key * @return String 数据 */ public String getAsString(String key) { File file = mCache.get(key); if (!file.exists()) return null; boolean removeFile = false; BufferedReader in = null; try { in = new BufferedReader(new FileReader(file)); String readString = ""; String currentLine; while ((currentLine = in.readLine()) != null) { readString += currentLine; } if (!Utils.isDue(readString)) { return Utils.clearDateInfo(readString); } else { removeFile = true; return null; } } catch (IOException e) { e.printStackTrace(); return null; } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if (removeFile) remove(key); } } // ======================================= // ============= JSONObject 数据 读写 ============== // ======================================= /** * 保存 JSONObject数据 到 缓存中 * * @param key 保存的key * @param value 保存的JSON数据 */ public void put(String key, JSONObject value) { put(key, value.toJSONString()); } /** * 保存 JSONObject数据 到 缓存中 * * @param key 保存的key * @param value 保存的JSONObject数据 * @param saveTime 保存的时间,单位:秒 */ public void put(String key, JSONObject value, int saveTime) { put(key, value.toString(), saveTime); } /** * 读取JSONObject数据 * * @param key * @return JSONObject数据 */ public JSONObject getAsJSONObject(String key) { String JSONString = getAsString(key); try { JSONObject obj = JSONObject.parseObject(JSONString); return obj; } catch (Exception e) { e.printStackTrace(); return null; } } // ======================================= // ============ JSONArray 数据 读写 ============= // ======================================= /** * 保存 JSONArray数据 到 缓存中 * * @param key 保存的key * @param value 保存的JSONArray数据 */ public void put(String key, JSONArray value) { put(key, value.toJSONString()); } /** * 保存 JSONArray数据 到 缓存中 * * @param key 保存的key * @param value 保存的JSONArray数据 * @param saveTime 保存的时间,单位:秒 */ public void put(String key, JSONArray value, int saveTime) { put(key, value.toJSONString(), saveTime); } /** * 读取JSONArray数据 * * @param key * @return JSONArray数据 */ public JSONArray getAsJSONArray(String key) { String JSONString = getAsString(key); try { JSONArray obj = JSONArray.parseArray(JSONString); return obj; } catch (Exception e) { e.printStackTrace(); return null; } } // ======================================= // ============== byte 数据 读写 ============= // ======================================= /** * 保存 byte数据 到 缓存中 * * @param key 保存的key * @param value 保存的数据 */ public void put(String key, byte[] value) { File file = mCache.newFile(key); FileOutputStream out = null; try { out = new FileOutputStream(file); out.write(value); } catch (Exception e) { e.printStackTrace(); } finally { if (out != null) { try { out.flush(); out.close(); } catch (IOException e) { e.printStackTrace(); } } mCache.put(file); } } /** * Cache for a stream * * @param key the file name. * @return OutputStream stream for writing data. * @throws FileNotFoundException if the file can not be created. */ public OutputStream put(String key) throws FileNotFoundException { return new xFileOutputStream(mCache.newFile(key)); } /** * @param key the file name. * @return (InputStream or null) stream previously saved in cache. * @throws FileNotFoundException if the file can not be opened */ public InputStream get(String key) throws FileNotFoundException { File file = mCache.get(key); if (!file.exists()) return null; return new FileInputStream(file); } /** * 保存 byte数据 到 缓存中 * * @param key 保存的key * @param value 保存的数据 * @param saveTime 保存的时间,单位:秒 */ public void put(String key, byte[] value, int saveTime) { put(key, Utils.newByteArrayWithDateInfo(saveTime, value)); } /** * 获取 byte 数据 * * @param key * @return byte 数据 */ public byte[] getAsBinary(String key) { RandomAccessFile RAFile = null; boolean removeFile = false; try { File file = mCache.get(key); if (!file.exists()) return null; RAFile = new RandomAccessFile(file, "r"); byte[] byteArray = new byte[(int) RAFile.length()]; RAFile.read(byteArray); if (!Utils.isDue(byteArray)) { return Utils.clearDateInfo(byteArray); } else { removeFile = true; return null; } } catch (Exception e) { e.printStackTrace(); return null; } finally { if (RAFile != null) { try { RAFile.close(); } catch (IOException e) { e.printStackTrace(); } } if (removeFile) remove(key); } } // ======================================= // ============= 序列化 数据 读写 =============== // ======================================= /** * 保存 Serializable数据 到 缓存中 * * @param key 保存的key * @param value 保存的value */ public void put(String key, Serializable value) { put(key, value, -1); } /** * 保存 Serializable数据到 缓存中 * * @param key 保存的key * @param value 保存的value * @param saveTime 保存的时间,单位:秒 */ public void put(String key, Serializable value, int saveTime) { ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(value); byte[] data = baos.toByteArray(); if (saveTime != -1) { put(key, data, saveTime); } else { put(key, data); } } catch (Exception e) { e.printStackTrace(); } finally { try { oos.close(); } catch (IOException e) { } } } /** * 保存 Serializable数据到 缓存中 * * @param key 保存的key * @param value 保存的value * @param saveTime 保存的时间,单位:秒 */ public void put(String key, Object value) { ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(value); byte[] data = baos.toByteArray(); put(key, data); } catch (Exception e) { e.printStackTrace(); } finally { try { oos.close(); } catch (IOException e) { } } } /** * 读取 Serializable数据 * * @param key * @return Serializable 数据 */ public Object getAsObject(String key) { byte[] data = getAsBinary(key); if (data != null) { ByteArrayInputStream bais = null; ObjectInputStream ois = null; try { bais = new ByteArrayInputStream(data); ois = new ObjectInputStream(bais); Object reObject = ois.readObject(); return reObject; } catch (Exception e) { e.printStackTrace(); return null; } finally { try { if (bais != null) bais.close(); } catch (IOException e) { e.printStackTrace(); } try { if (ois != null) ois.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } // ======================================= // ============== bitmap 数据 读写 ============= // ======================================= /** * 保存 bitmap 到 缓存中 * * @param key 保存的key * @param value 保存的bitmap数据 */ public void put(String key, Bitmap value) { put(key, Utils.Bitmap2Bytes(value)); } /** * 保存 bitmap 到 缓存中 * * @param key 保存的key * @param value 保存的 bitmap 数据 * @param saveTime 保存的时间,单位:秒 */ public void put(String key, Bitmap value, int saveTime) { put(key, Utils.Bitmap2Bytes(value), saveTime); } /** * 读取 bitmap 数据 * * @param key * @return bitmap 数据 */ public Bitmap getAsBitmap(String key) { if (getAsBinary(key) == null) { return null; } return Utils.Bytes2Bimap(getAsBinary(key)); } // ======================================= // ============= drawable 数据 读写 ============= // ======================================= /** * 保存 drawable 到 缓存中 * * @param key 保存的key * @param value 保存的drawable数据 */ public void put(String key, Drawable value) { put(key, Utils.drawable2Bitmap(value)); } /** * 保存 drawable 到 缓存中 * * @param key 保存的key * @param value 保存的 drawable 数据 * @param saveTime 保存的时间,单位:秒 */ public void put(String key, Drawable value, int saveTime) { put(key, Utils.drawable2Bitmap(value), saveTime); } /** * 读取 Drawable 数据 * * @param key * @return Drawable 数据 */ public Drawable getAsDrawable(String key) { if (getAsBinary(key) == null) { return null; } return Utils.bitmap2Drawable(Utils.Bytes2Bimap(getAsBinary(key))); } /** * 获取缓存文件 * * @param key * @return value 缓存的文件 */ public File file(String key) { File f = mCache.newFile(key); if (f.exists()) return f; return null; } /** * 移除某个key * * @param key * @return 是否移除成功 */ public boolean remove(String key) { return mCache.remove(key); } /** * 清除所有数据 */ public void clear() { mCache.clear(); } /** * @author 杨福海(michael) www.yangfuhai.com * @version 1.0 * @title 缓存管理器 */ public class ACacheManager { private final AtomicLong cacheSize; private final AtomicInteger cacheCount; private final long sizeLimit; private final int countLimit; private final Map lastUsageDates = Collections.synchronizedMap(new HashMap()); protected File cacheDir; private ACacheManager(File cacheDir, long sizeLimit, int countLimit) { this.cacheDir = cacheDir; this.sizeLimit = sizeLimit; this.countLimit = countLimit; cacheSize = new AtomicLong(); cacheCount = new AtomicInteger(); calculateCacheSizeAndCacheCount(); } /** * 计算 cacheSize和cacheCount */ private void calculateCacheSizeAndCacheCount() { new Thread(new Runnable() { @Override public void run() { int size = 0; int count = 0; File[] cachedFiles = cacheDir.listFiles(); if (cachedFiles != null) { for (File cachedFile : cachedFiles) { size += calculateSize(cachedFile); count += 1; lastUsageDates.put(cachedFile, cachedFile.lastModified()); } cacheSize.set(size); cacheCount.set(count); } } }).start(); } private void put(File file) { int curCacheCount = cacheCount.get(); while (curCacheCount + 1 > countLimit) { long freedSize = removeNext(); cacheSize.addAndGet(-freedSize); curCacheCount = cacheCount.addAndGet(-1); } cacheCount.addAndGet(1); long valueSize = calculateSize(file); long curCacheSize = cacheSize.get(); while (curCacheSize + valueSize > sizeLimit) { long freedSize = removeNext(); curCacheSize = cacheSize.addAndGet(-freedSize); } cacheSize.addAndGet(valueSize); Long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); lastUsageDates.put(file, currentTime); } private File get(String key) { File file = newFile(key); Long currentTime = System.currentTimeMillis(); file.setLastModified(currentTime); lastUsageDates.put(file, currentTime); return file; } private File newFile(String key) { return new File(cacheDir, key.hashCode() + ""); } private boolean remove(String key) { File image = get(key); return image.delete(); } private void clear() { lastUsageDates.clear(); cacheSize.set(0); File[] files = cacheDir.listFiles(); if (files != null) { for (File f : files) { f.delete(); } } } /** * 移除旧的文件 * * @return */ private long removeNext() { if (lastUsageDates.isEmpty()) { return 0; } Long oldestUsage = null; File mostLongUsedFile = null; Set> entries = lastUsageDates.entrySet(); synchronized (lastUsageDates) { for (Entry entry : entries) { if (mostLongUsedFile == null) { mostLongUsedFile = entry.getKey(); oldestUsage = entry.getValue(); } else { Long lastValueUsage = entry.getValue(); if (lastValueUsage < oldestUsage) { oldestUsage = lastValueUsage; mostLongUsedFile = entry.getKey(); } } } } long fileSize = calculateSize(mostLongUsedFile); if (mostLongUsedFile.delete()) { lastUsageDates.remove(mostLongUsedFile); } return fileSize; } private long calculateSize(File file) { return file.length(); } } /** * @author 杨福海(michael) www.yangfuhai.com * @version 1.0 * @title 时间计算工具类 */ private static class Utils { /** * 判断缓存的String数据是否到期 * * @param str * @return true:到期了 false:还没有到期 */ private static boolean isDue(String str) { return isDue(str.getBytes()); } /** * 判断缓存的byte数据是否到期 * * @param data * @return true:到期了 false:还没有到期 */ private static boolean isDue(byte[] data) { String[] strs = getDateInfoFromDate(data); if (strs != null && strs.length == 2) { String saveTimeStr = strs[0]; while (saveTimeStr.startsWith("0")) { saveTimeStr = saveTimeStr.substring(1, saveTimeStr.length()); } long saveTime = Long.valueOf(saveTimeStr); long deleteAfter = Long.valueOf(strs[1]); if (System.currentTimeMillis() > saveTime + deleteAfter * 1000) { return true; } } return false; } private static String newStringWithDateInfo(int second, String strInfo) { return createDateInfo(second) + strInfo; } private static byte[] newByteArrayWithDateInfo(int second, byte[] data2) { byte[] data1 = createDateInfo(second).getBytes(); byte[] retdata = new byte[data1.length + data2.length]; System.arraycopy(data1, 0, retdata, 0, data1.length); System.arraycopy(data2, 0, retdata, data1.length, data2.length); return retdata; } private static String clearDateInfo(String strInfo) { if (strInfo != null && hasDateInfo(strInfo.getBytes())) { strInfo = strInfo.substring(strInfo.indexOf(mSeparator) + 1, strInfo.length()); } return strInfo; } private static byte[] clearDateInfo(byte[] data) { if (hasDateInfo(data)) { return copyOfRange(data, indexOf(data, mSeparator) + 1, data.length); } return data; } private static boolean hasDateInfo(byte[] data) { return data != null && data.length > 15 && data[13] == '-' && indexOf(data, mSeparator) > 14; } private static String[] getDateInfoFromDate(byte[] data) { if (hasDateInfo(data)) { String saveDate = new String(copyOfRange(data, 0, 13)); String deleteAfter = new String(copyOfRange(data, 14, indexOf(data, mSeparator))); return new String[]{saveDate, deleteAfter}; } return null; } private static int indexOf(byte[] data, char c) { for (int i = 0; i < data.length; i++) { if (data[i] == c) { return i; } } return -1; } private static byte[] copyOfRange(byte[] original, int from, int to) { int newLength = to - from; if (newLength < 0) throw new IllegalArgumentException(from + " > " + to); byte[] copy = new byte[newLength]; System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength)); return copy; } private static final char mSeparator = ' '; private static String createDateInfo(int second) { String currentTime = System.currentTimeMillis() + ""; while (currentTime.length() < 13) { currentTime = "0" + currentTime; } return currentTime + "-" + second + mSeparator; } /* * Bitmap → byte[] */ private static byte[] Bitmap2Bytes(Bitmap bm) { if (bm == null) { return null; } ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray(); } /* * byte[] → Bitmap */ private static Bitmap Bytes2Bimap(byte[] b) { if (b.length == 0) { return null; } return BitmapFactory.decodeByteArray(b, 0, b.length); } /* * Drawable → Bitmap */ private static Bitmap drawable2Bitmap(Drawable drawable) { if (drawable == null) { return null; } // 取 drawable 的长宽 int w = drawable.getIntrinsicWidth(); int h = drawable.getIntrinsicHeight(); // 取 drawable 的颜色格式 Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; // 建立对应 bitmap Bitmap bitmap = Bitmap.createBitmap(w, h, config); // 建立对应 bitmap 的画布 Canvas canvas = new Canvas(bitmap); drawable.setBounds(0, 0, w, h); // 把 drawable 内容画到画布中 drawable.draw(canvas); return bitmap; } /* * Bitmap → Drawable */ @SuppressWarnings("deprecation") private static Drawable bitmap2Drawable(Bitmap bm) { if (bm == null) { return null; } BitmapDrawable bd = new BitmapDrawable(bm); bd.setTargetDensity(bm.getDensity()); return new BitmapDrawable(bm); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/CommonUtils.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.utils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.graphics.drawable.ColorDrawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.DisplayMetrics; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.PopupWindow; import android.widget.TextView; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.country.CountryCodeUtil; import com.htmessage.yichatopen.activity.country.CountryComparator; import com.htmessage.yichatopen.activity.country.CountrySortAdapter; import com.htmessage.yichatopen.activity.country.CountrySortModel; import com.htmessage.yichatopen.activity.country.GetCountryNameSort; import com.htmessage.yichatopen.activity.country.SideBar; import com.htmessage.yichatopen.HTConstant; import com.htmessage.yichatopen.domain.User; import com.github.promeg.pinyinhelper.Pinyin; public class CommonUtils { private static final String TAG = "CommonUtils"; public static User Json2User(JSONObject userJson) { User user = new User(userJson.getString(HTConstant.JSON_KEY_HXID)); user.setNick(userJson.getString(HTConstant.JSON_KEY_NICK)); user.setAvatar(userJson.getString(HTConstant.JSON_KEY_AVATAR)); user.setUserInfo(userJson.toJSONString()); CommonUtils.setUserInitialLetter(user); return user; } public static boolean isNetWorkConnected(Context context) { if (context != null) { ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); if (mNetworkInfo != null) { return mNetworkInfo.isAvailable() && mNetworkInfo.isConnected(); } } return false; } /** * check if sdcard exist * * @return */ public static boolean isSdcardExist() { if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) return true; else return false; } static String getString(Context context, int resId){ return context.getResources().getString(resId); } /** * get top activity * @param context * @return */ public static String getTopActivity(Context context) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List runningTaskInfos = manager.getRunningTasks(1); if (runningTaskInfos != null) return runningTaskInfos.get(0).topActivity.getClassName(); else return ""; } /** * set initial letter of according user's nickname( username if no nickname) * * @param * @param user */ public static void setUserInitialLetter(User user) { final String DefaultLetter = "#"; String letter = DefaultLetter; if ( !TextUtils.isEmpty(user.getNick()) ) { letter = Pinyin.toPinyin(user.getNick().toCharArray()[0]); user.setInitialLetter(letter.toUpperCase().substring(0,1)); if ("123456789".contains(user.getInitialLetter())) { user.setInitialLetter("#"); } return; } if (letter == DefaultLetter && !TextUtils.isEmpty(user.getUsername())) { letter = Pinyin.toPinyin(user.getUsername().toCharArray()[0]); } user.setInitialLetter(letter.substring(0,1)); if ("123456789".contains(user.getInitialLetter())) { user.setInitialLetter("#"); } } /** * 获取国家代码 * * @param context 上下文对象 * @param country 显示国家的textview * @param countryCode 显示国家代码的textview */ public static void showPup(final Context context, final TextView country, final TextView countryCode) { final boolean cn = context.getResources().getConfiguration().locale.getCountry().equals("CN"); final List countryList = CountryCodeUtil.getCountryList(context, cn); //获得pup的view View view = LayoutInflater.from(context).inflate(R.layout.layout_pup, null, false); final ImageView iv_back = (ImageView) view.findViewById(R.id.iv_back); final TextView tv_title = (TextView) view.findViewById(R.id.tv_title); tv_title.setText(R.string.country); final EditText country_et_search = (EditText) view.findViewById(R.id.country_et_search); final ImageView country_iv_cleartext = (ImageView) view.findViewById(R.id.country_iv_cleartext); final ListView ll_country = (ListView) view.findViewById(R.id.country_lv_list); final TextView country_dialog = (TextView) view.findViewById(R.id.country_dialog); final SideBar country_sidebar = (SideBar) view.findViewById(R.id.country_sidebar); country_sidebar.setTextView(country_dialog); final CountrySortAdapter adapter = new CountrySortAdapter(context, countryList); ll_country.setAdapter(adapter); DisplayMetrics dm = new DisplayMetrics(); //取得窗口属性 ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm); //窗口的宽度 int screenWidth = dm.widthPixels; //窗口高度 int screenHeight = dm.heightPixels / 2; //设置window的宽高 1 window的布局 2、window的宽 3、window的高 4、window是否获取焦点 // final PopupWindow window = new PopupWindow(view, WindowManager.LayoutParams.MATCH_PARENT, screenHeight, true); final PopupWindow window = new PopupWindow(view, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, true); //设置window背景色 window.setBackgroundDrawable(new ColorDrawable(0x00000000)); //设置可以获取焦点,否则弹出菜单中的EditText是无法获取输入的 window.setFocusable(true); //设置键盘不遮盖 window.setSoftInputMode(PopupWindow.INPUT_METHOD_NEEDED); window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); //设置window动画 // window.setAnimationStyle(R.style.custom_pup_style); //设置window在底部显示 window.showAtLocation(view, Gravity.BOTTOM, 0, 0); country_iv_cleartext.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { country_et_search.setText(""); Collections.sort(countryList, new CountryComparator()); adapter.updateListView(countryList); } }); country_et_search.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { String searchContent = country_et_search.getText().toString(); if (searchContent.equals("")) { country_iv_cleartext.setVisibility(View.INVISIBLE); } else { country_iv_cleartext.setVisibility(View.VISIBLE); } if (searchContent.length() > 0) { // 按照输入内容进行匹配 ArrayList fileterList = (ArrayList) new GetCountryNameSort() .search(searchContent, countryList); adapter.updateListView(fileterList); } else { adapter.updateListView(countryList); } ll_country.setSelection(0); } }); // 右侧sideBar监听 country_sidebar.setOnTouchingLetterChangedListener(new SideBar.OnTouchingLetterChangedListener() { @Override public void onTouchingLetterChanged(String s) { // 该字母首次出现的位置 int position = adapter.getPositionForSection(s.charAt(0)); if (position != -1) { ll_country.setSelection(position); } } }); ll_country.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int i, long l) { window.dismiss(); String countryName = null; String countryNumber = null; String searchContent = country_et_search.getText().toString(); if (searchContent.length() > 0) { // 按照输入内容进行匹配 ArrayList fileterList = (ArrayList) new GetCountryNameSort() .search(searchContent, countryList); //获取国家名字及代码 countryName = fileterList.get(i).countryName; countryNumber = fileterList.get(i).countryNumber; } else { //获取国家名字及代码 countryName = countryList.get(i).countryName; countryNumber = countryList.get(i).countryNumber; } country.setText(countryName); countryCode.setText(countryNumber); Log.e(TAG, "countryName: + " + countryName + "countryNumber: " + countryNumber); } }); iv_back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { window.dismiss(); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/DateUtils.java ================================================ package com.htmessage.yichatopen.utils; import android.content.Context; import android.util.Log; import com.htmessage.yichatopen.R; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Locale; /** * Created by huangfangyi on 2016/12/24. * qq 84543217 */ public class DateUtils { private static final long INTERVAL_IN_MILLISECONDS = 30000L; public DateUtils() { } /** * 计算时间差 * * @param starTime * 开始时间 * @param endTime * 结束时间 * @param type * 返回类型 ==1----天,时,分。 ==2----时 * @return 返回时间差 */ public static String getTimeDifference(Context context, long starTime, long endTime) { String timeString = ""; try { Date parse = new Date(starTime); Date parse1 = new Date(endTime); long diff = parse1.getTime() - parse.getTime(); long day = diff / (24 * 60 * 60 * 1000); long hour = (diff / (60 * 60 * 1000) - day * 24); long min = ((diff / (60 * 1000)) - day * 24 * 60 - hour * 60); long s = (diff / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60); long ms = (diff - day * 24 * 60 * 60 * 1000 - hour * 60 * 60 * 1000 - min * 60 * 1000 - s * 1000); long hour1 = diff / (60 * 60 * 1000); long min1 = ((diff / (60 * 1000)) - hour1 * 60); if (day == 0){ if (hour1 == 0){ if (0<=min1 && min1<=5){ timeString = context.getString(R.string.just); }else if (min1>5 && min1<=15){ timeString = context.getString(R.string.just_15); }else if (min1>15 && min1<=30){ timeString = context.getString(R.string.just_30); }else{ timeString = context.getString(R.string.just_1hour); } }else{ timeString = hour1+context.getString(R.string.An_hour_ago); } }else{ timeString = day+context.getString(R.string.Days_ago); } // timeString = hour1 + "小时" + min1 + "分"; Log.d("slj",day + "天" + hour + "小时" + min + "分" + s + "秒"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return timeString; } public static String getTimestampString(Date var0) { String var1 = null; String var2 = Locale.getDefault().getLanguage(); boolean var3 = var2.startsWith("zh"); long var4 = var0.getTime(); if(isSameDay(var4)) { if(var3) { var1 = "aa hh:mm"; } else { var1 = "hh:mm aa"; } } else if(isYesterday(var4)) { if(!var3) { return "Yesterday " + (new SimpleDateFormat("hh:mm aa", Locale.ENGLISH)).format(var0); } var1 = "昨天aa hh:mm"; } else if(var3) { var1 = "M月d日aa hh:mm"; } else { var1 = "MMM dd hh:mm aa"; } return var3?(new SimpleDateFormat(var1, Locale.CHINESE)).format(var0):(new SimpleDateFormat(var1, Locale.ENGLISH)).format(var0); } public static boolean isCloseEnough(long var0, long var2) { long var4 = var0 - var2; if(var4 < 0L) { var4 = -var4; } return var4 < 30000L; } private static boolean isSameDay(long var0) { TimeInfo var2 = getTodayStartAndEndTime(); return var0 > var2.getStartTime() && var0 < var2.getEndTime(); } private static boolean isYesterday(long var0) { TimeInfo var2 = getYesterdayStartAndEndTime(); return var0 > var2.getStartTime() && var0 < var2.getEndTime(); } public static Date StringToDate(String var0, String var1) { SimpleDateFormat var2 = new SimpleDateFormat(var1); Date var3 = null; try { var3 = var2.parse(var0); } catch (ParseException var5) { var5.printStackTrace(); } return var3; } public static String toTime(int var0) { var0 /= 1000; int var1 = var0 / 60; boolean var2 = false; if(var1 >= 60) { int var4 = var1 / 60; var1 %= 60; } int var3 = var0 % 60; return String.format("%02d:%02d", new Object[]{Integer.valueOf(var1), Integer.valueOf(var3)}); } public static String toTimeBySecond(int var0) { int var1 = var0 / 60; boolean var2 = false; if(var1 >= 60) { int var4 = var1 / 60; var1 %= 60; } int var3 = var0 % 60; return String.format("%02d:%02d", new Object[]{Integer.valueOf(var1), Integer.valueOf(var3)}); } public static TimeInfo getYesterdayStartAndEndTime() { Calendar var0 = Calendar.getInstance(); var0.add(Calendar.DAY_OF_MONTH, -1);//5 var0.set(Calendar.HOUR_OF_DAY, 0);//11 var0.set(Calendar.MINUTE, 0);//12 var0.set(Calendar.SECOND, 0);//13 var0.set(Calendar.MILLISECOND, 0);//Calendar.MILLISECOND Date var1 = var0.getTime(); long var2 = var1.getTime(); Calendar var4 = Calendar.getInstance(); var4.add(Calendar.DAY_OF_MONTH, -1);//5 var4.set(Calendar.HOUR_OF_DAY, 23);//11 var4.set(Calendar.MINUTE, 59);//12 var4.set(Calendar.SECOND, 59);//13 var4.set(Calendar.MILLISECOND, 999);//Calendar.MILLISECOND Date var5 = var4.getTime(); long var6 = var5.getTime(); TimeInfo var8 = new TimeInfo(); var8.setStartTime(var2); var8.setEndTime(var6); return var8; } public static TimeInfo getTodayStartAndEndTime() { Calendar var0 = Calendar.getInstance(); var0.set(Calendar.HOUR_OF_DAY, 0); var0.set(Calendar.MINUTE, 0); var0.set(Calendar.SECOND, 0); var0.set(Calendar.MILLISECOND, 0); Date var1 = var0.getTime(); long var2 = var1.getTime(); new SimpleDateFormat("yyyy-MM-dd HH:mm:ss S"); Calendar var5 = Calendar.getInstance(); var5.set(Calendar.HOUR_OF_DAY, 23); var5.set(Calendar.MINUTE, 59); var5.set(Calendar.SECOND, 59); var5.set(Calendar.MILLISECOND, 999); Date var6 = var5.getTime(); long var7 = var6.getTime(); TimeInfo var9 = new TimeInfo(); var9.setStartTime(var2); var9.setEndTime(var7); return var9; } public static TimeInfo getBeforeYesterdayStartAndEndTime() { Calendar var0 = Calendar.getInstance(); var0.add(Calendar.DAY_OF_MONTH, -2); var0.set(Calendar.HOUR_OF_DAY, 0); var0.set(Calendar.MINUTE, 0); var0.set(Calendar.SECOND, 0); var0.set(Calendar.MILLISECOND, 0); Date var1 = var0.getTime(); long var2 = var1.getTime(); Calendar var4 = Calendar.getInstance(); var4.add(Calendar.DAY_OF_MONTH, -2); var4.set(Calendar.HOUR_OF_DAY, 23); var4.set(Calendar.MINUTE, 59); var4.set(Calendar.SECOND, 59); var4.set(Calendar.MILLISECOND, 999); Date var5 = var4.getTime(); long var6 = var5.getTime(); TimeInfo var8 = new TimeInfo(); var8.setStartTime(var2); var8.setEndTime(var6); return var8; } public static TimeInfo getCurrentMonthStartAndEndTime() { Calendar var0 = Calendar.getInstance(); var0.set(Calendar.DATE, 1); var0.set(Calendar.HOUR_OF_DAY, 0); var0.set(Calendar.MINUTE, 0); var0.set(Calendar.SECOND, 0); var0.set(Calendar.MILLISECOND, 0); Date var1 = var0.getTime(); long var2 = var1.getTime(); Calendar var4 = Calendar.getInstance(); Date var5 = var4.getTime(); long var6 = var5.getTime(); TimeInfo var8 = new TimeInfo(); var8.setStartTime(var2); var8.setEndTime(var6); return var8; } public static TimeInfo getLastMonthStartAndEndTime() { Calendar var0 = Calendar.getInstance(); var0.add(Calendar.MONTH, -1); var0.set(Calendar.DATE, 1); var0.set(Calendar.HOUR_OF_DAY, 0); var0.set(Calendar.MINUTE, 0); var0.set(Calendar.SECOND, 0); var0.set(Calendar.MILLISECOND, 0); Date var1 = var0.getTime(); long var2 = var1.getTime(); Calendar var4 = Calendar.getInstance(); var4.add(Calendar.MONTH, -1); var4.set(Calendar.DATE, 1); var4.set(Calendar.HOUR_OF_DAY, 23); var4.set(Calendar.MINUTE, 59); var4.set(Calendar.SECOND, 59); var4.set(Calendar.MILLISECOND, 999); var4.roll(Calendar.DATE, -1); Date var5 = var4.getTime(); long var6 = var5.getTime(); TimeInfo var8 = new TimeInfo(); var8.setStartTime(var2); var8.setEndTime(var6); return var8; } public static String getStringTime(long time){ Date date = new Date(time); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String startTime = sdf.format(date); return startTime; } public static String getTimestampStr() { return Long.toString(System.currentTimeMillis()); } public static class TimeInfo { private long startTime; private long endTime; public TimeInfo() { } public long getStartTime() { return this.startTime; } public void setStartTime(long var1) { this.startTime = var1; } public long getEndTime() { return this.endTime; } public void setEndTime(long var1) { this.endTime = var1; } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/HTMessageUtils.java ================================================ package com.htmessage.yichatopen.utils; import android.app.Activity; import android.app.ProgressDialog; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; import com.alibaba.fastjson.JSONObject; import com.htmessage.sdk.ChatType; import com.htmessage.sdk.client.HTClient; import com.htmessage.sdk.model.HTMessage; import com.htmessage.sdk.model.HTMessageImageBody; import com.htmessage.sdk.model.HTMessageTextBody; import com.htmessage.sdk.model.HTMessageVoiceBody; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.HTClientHelper; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.activity.chat.activity.ChooseContactActivity; import com.htmessage.yichatopen.HTConstant; import java.io.File; /** * Created by huangfangyi on 2017/7/8. * qq 84543217 */ public class HTMessageUtils { public static HTMessage creatWithDrowMsg(HTMessage htMessage) { String text = null; JSONObject jsonObject = htMessage.getAttributes(); String userId = jsonObject.getString(HTConstant.JSON_KEY_HXID); String nick = jsonObject.getString(HTConstant.JSON_KEY_NICK); if (HTApp.getInstance().getUsername().equals(userId)) { text = HTApp.getContext().getString(R.string.revoke_content); } else { text = String.format(HTApp.getContext().getString(R.string.revoke_content_someone), nick); } jsonObject.put("action", 6001); HTMessage message1 = HTMessage.createTextSendMessage(htMessage.getUsername(), text); message1.setMsgId(htMessage.getMsgId()); message1.setChatType(htMessage.getChatType()); message1.setDirect(htMessage.getDirect()); message1.setAttributes(jsonObject.toJSONString()); message1.setTime(htMessage.getTime()); message1.setLocalTime(htMessage.getLocalTime()); message1.setFrom(htMessage.getFrom()); message1.setTo(htMessage.getTo()); message1.setStatus(htMessage.getStatus()); message1.setExt(htMessage.getExt()); HTClient.getInstance().messageManager().updateMessageInDB(message1); return message1; } /** * 获取copy的信息 * * @param context * @param message */ public static void getCopyMsg(Activity context, HTMessage message, String toChatUserName) { String copyType = ""; String fileName = ""; String localUrl = ""; String remotePath = ""; HTMessage.Type type = message.getType(); if (type == HTMessage.Type.IMAGE) { copyType = "image"; HTMessageImageBody body = (HTMessageImageBody) message.getBody(); fileName = body.getFileName(); remotePath = body.getRemotePath(); localUrl = body.getLocalPath(); } else if (type == HTMessage.Type.VOICE) { HTMessageVoiceBody body = (HTMessageVoiceBody) message.getBody(); copyType = "voice"; remotePath = body.getRemotePath(); fileName = body.getFileName(); localUrl = body.getLocalPath(); } else if (type == HTMessage.Type.TEXT) { copyType = "text"; localUrl = ((HTMessageTextBody) message.getBody()).getContent(); } String msgId = message.getMsgId(); if (!TextUtils.isEmpty(localUrl)) { switch (copyType) { case "text": showCopySendDialog(context, copyType, localUrl, message, null); break; default: getFilePath(context, copyType, message, msgId, fileName, remotePath, toChatUserName, null); break; } } else { switch (copyType) { case "text": showCopySendDialog(context, copyType, localUrl, message, null); break; default: getFilePath(context, copyType, message, msgId, fileName, remotePath, toChatUserName, null); break; } } } /** * 下载copy文件并复制 * * @param context * @param copyType * @param message * @param msgId * @param fileName * @param remotePath */ public static void getFilePath(final Activity context, final String copyType, final HTMessage message, String msgId, final String fileName, final String remotePath) { String fileType = fileName.substring(fileName.lastIndexOf(".")); if (!TextUtils.isEmpty(msgId) && !TextUtils.isEmpty(fileType) && !TextUtils.isEmpty(remotePath)) { // final File file = new File(HTApp.getInstance().getDirFilePath() + msgId + fileType); final File file = new File(HTApp.getInstance().getDirFilePath() + fileName); if (file.exists()) { switch (copyType) { case "image": showCopySendDialog(context, copyType, file.getAbsolutePath(), message, file.getAbsolutePath()); break; case "voice": showCopySendDialog(context, copyType, file.getAbsolutePath(), message, null); break; } return; } final ProgressDialog dialog = new ProgressDialog(context); dialog.setMessage(context.getString(R.string.copying)); dialog.setCanceledOnTouchOutside(false); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); final String path = HTApp.getInstance().getDirFilePath() + fileName; dialog.show(); new OkHttpUtils(context).loadFile(remotePath, path, new OkHttpUtils.DownloadCallBack() { @Override public void onSuccess() { if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } File file1 = new File(path); switch (copyType) { case "image": showCopySendDialog(context, copyType, file1.getAbsolutePath(), message, file1.getAbsolutePath()); break; case "voice": showCopySendDialog(context, copyType, file1.getAbsolutePath(), message, null); break; } ((Activity) context).runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(context, R.string.copy_success, Toast.LENGTH_SHORT).show(); } }); } @Override public void onFailure(String message) { if (dialog != null && dialog.isShowing()) { dialog.dismiss(); } ((Activity) context).runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(context, R.string.copy_failed, Toast.LENGTH_SHORT).show(); } }); } }); } } /** * 复制并转发 * * @param copyType * @param localPath * @param message1 */ public static void showCopySendDialog(Activity context, final String copyType, String localPath, final HTMessage message1, String imagePath) { if (message1.getType() == HTMessage.Type.TEXT) { ClipboardManager cm = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); // 将文本内容放到系统剪贴板里。 cm.setText(localPath); ACache.get(context.getApplicationContext()).remove("myCopy"); } else { JSONObject jsonObject = new JSONObject(); jsonObject.put("copyType", copyType); jsonObject.put("localPath", localPath); jsonObject.put("msgId", message1.getMsgId()); jsonObject.put("imagePath", imagePath); ACache.get(context.getApplicationContext()).put("myCopy", jsonObject.toJSONString()); } } /** * 获取copy的信息 * * @param context * @param message */ public static void getForWordMessage(Activity context, HTMessage message, final String toChatUsername, final JSONObject userJson) { String copyType = ""; String fileName = ""; String localUrl = ""; String remotePath = ""; HTMessage.Type type = message.getType(); if (type == HTMessage.Type.IMAGE) { copyType = "image"; HTMessageImageBody body = (HTMessageImageBody) message.getBody(); fileName = body.getFileName(); remotePath = body.getRemotePath(); localUrl = body.getLocalPath(); } else if (type == HTMessage.Type.VOICE) { HTMessageVoiceBody body = (HTMessageVoiceBody) message.getBody(); copyType = "voice"; remotePath = body.getRemotePath(); fileName = body.getFileName(); localUrl = body.getLocalPath(); } else if (type == HTMessage.Type.TEXT) { copyType = "text"; localUrl = ((HTMessageTextBody) message.getBody()).getContent(); } String msgId = message.getMsgId(); if (!TextUtils.isEmpty(localUrl)) { switch (copyType) { case "text": showForwordDialog(context, copyType, localUrl, message, null, toChatUsername, userJson); break; default: getFilePath(context, copyType, message, msgId, fileName, remotePath, toChatUsername, userJson); break; } } else { switch (copyType) { case "text": showForwordDialog(context, copyType, localUrl, message, null, toChatUsername, userJson); break; default: getFilePath(context, copyType, message, msgId, fileName, remotePath, toChatUsername, userJson); break; } } } /** * 下载copy文件并复制 * * @param context * @param copyType * @param message * @param msgId * @param fileName * @param remotePath */ public static void getFilePath(final Activity context, final String copyType, final HTMessage message, String msgId, final String fileName, final String remotePath, final String toChatUsername, final JSONObject userJson) { String fileType = fileName.substring(fileName.lastIndexOf(".")); String filePath = null; if (!TextUtils.isEmpty(msgId) && !TextUtils.isEmpty(fileType) && !TextUtils.isEmpty(remotePath)) { PathUtils pathUtils = new PathUtils(toChatUsername, context); if (message.getType() == HTMessage.Type.VOICE) { filePath = pathUtils.getVoicePath().getAbsolutePath() + "/" + fileName; } else if (message.getType() == HTMessage.Type.IMAGE) { filePath = pathUtils.getImagePath().getAbsolutePath() + "/" + fileName; } File file = new File(filePath); if (file.exists()) { switch (copyType) { case "image": if (userJson != null) { showForwordDialog(context, copyType, file.getAbsolutePath(), message, file.getAbsolutePath(), toChatUsername, userJson); } else { showCopySendDialog(context, copyType, file.getAbsolutePath(), message, file.getAbsolutePath()); } break; case "voice": if (userJson != null) { showForwordDialog(context, copyType, file.getAbsolutePath(), message, null, toChatUsername, userJson); } else { showCopySendDialog(context, copyType, file.getAbsolutePath(), message, null); } break; } return; } loadMessageFile(message, toChatUsername, context, new CallBack() { @Override public void error() { if (userJson == null) { Toast.makeText(context, R.string.copy_failed, Toast.LENGTH_SHORT).show(); } } @Override public void completed(String localPath) { switch (copyType) { case "image": if (userJson != null) { showForwordDialog(context, copyType, localPath, message, localPath, toChatUsername, userJson); } else { showCopySendDialog(context, copyType,localPath, message, localPath); } break; case "voice": if (userJson != null) { showForwordDialog(context, copyType, localPath, message, null, toChatUsername, userJson); } else { Log.d("slj","===复制:"+userJson); showCopySendDialog(context, copyType, localPath, message, null); } break; } if (userJson == null) { Toast.makeText(context, R.string.copy_success, Toast.LENGTH_SHORT).show(); } } }); } // if (!TextUtils.isEmpty(msgId) && !TextUtils.isEmpty(fileType) && !TextUtils.isEmpty(remotePath)) { // final File file = new File(HTApp.getInstance().getDirFilePath() + fileName); // if (file.exists()) { // switch (copyType) { // case "image": // showForwordDialog(context, copyType, file.getAbsolutePath(), message, file.getAbsolutePath(),toChatUsername,userJson); // break; // case "voice": // showForwordDialog(context, copyType, file.getAbsolutePath(), message, null,toChatUsername,userJson); // break; // } // return; // } // final ProgressDialog dialog = new ProgressDialog(context); // dialog.setMessage(context.getString(R.string.forword_get)); // dialog.setCanceledOnTouchOutside(false); // dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); //// final String path = HTApp.getInstance().getDirFilePath() + msgId + fileType; // final String path = HTApp.getInstance().getDirFilePath() + fileName; // dialog.show(); // new OkHttpUtils(context).loadFile(remotePath, path, new OkHttpUtils.DownloadCallBack() { // @Override // public void onSuccess() { // if (dialog != null && dialog.isShowing()) { // dialog.dismiss(); // } // File file1 = new File(path); // switch (copyType) { // case "image": // showForwordDialog(context, copyType, file1.getAbsolutePath(), message, file1.getAbsolutePath(),toChatUsername,userJson); // break; // case "voice": // showForwordDialog(context, copyType, file1.getAbsolutePath(), message, null,toChatUsername,userJson); // break; // } // } // // @Override // public void onFailure(String message) { // if (dialog != null && dialog.isShowing()) { // dialog.dismiss(); // } // } // }); // } } /** * 转发 * * @param forwordType * @param localPath * @param message1 * @param imagePath * @param toChatUsername * @param extJSON */ private static void showForwordDialog(Activity context, final String forwordType, final String localPath, final HTMessage message1, String imagePath, String toChatUsername, JSONObject extJSON) { JSONObject jsonObject = new JSONObject(); jsonObject.put("forwordType", forwordType); jsonObject.put("localPath", localPath); jsonObject.put("msgId", message1.getMsgId()); jsonObject.put("imagePath", imagePath); jsonObject.put("toChatUsername", toChatUsername); jsonObject.put("exobj", extJSON.toJSONString()); Intent intent = new Intent(context, ChooseContactActivity.class); intent.putExtra("obj", jsonObject.toJSONString()); context.startActivity(intent); } public static void loadMessageFile(HTMessage htMessage, String chatTo, final Activity context, final CallBack callBack) { final ProgressDialog progressDialog = new ProgressDialog(context); progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); progressDialog.setCanceledOnTouchOutside(false); progressDialog.setMessage(context.getString(R.string.loading)); progressDialog.show(); PathUtils pathUtils = new PathUtils(chatTo, context); String remotePath = ""; String fileName = ""; String filePath = null; if (htMessage.getType() == HTMessage.Type.VOICE) { HTMessageVoiceBody htMessageVoiceBody = (HTMessageVoiceBody) htMessage.getBody(); remotePath = htMessageVoiceBody.getRemotePath(); fileName = htMessageVoiceBody.getFileName(); filePath = pathUtils.getVoicePath().getAbsolutePath() + "/" + fileName; } else if (htMessage.getType() == HTMessage.Type.IMAGE) { HTMessageImageBody htMessageImageBody = (HTMessageImageBody) htMessage.getBody(); remotePath = htMessageImageBody.getRemotePath(); fileName = htMessageImageBody.getFileName(); filePath = pathUtils.getImagePath().getAbsolutePath() + "/" + fileName; } final String finalFilePath = filePath; new OkHttpUtils(context).loadFile(remotePath, filePath, new OkHttpUtils.DownloadCallBack() { @Override public void onSuccess() { context.runOnUiThread(new Runnable() { @Override public void run() { progressDialog.dismiss(); callBack.completed(finalFilePath); } }); } @Override public void onFailure(String message) { context.runOnUiThread(new Runnable() { @Override public void run() { callBack.error(); progressDialog.dismiss(); } }); } }); } public interface CallBack { void error(); void completed(String localPath); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/ImageUtils.java ================================================ package com.htmessage.yichatopen.utils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.media.ExifInterface; import android.media.ThumbnailUtils; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * Created by huangfangyi on 2016/12/4. * qq 84543217 */ public class ImageUtils { public ImageUtils() { } public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) { return getRoundedCornerBitmap(bitmap, 6); } public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, float roundPx) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } public static Bitmap getVideoThumbnail(String videoPath, int width, int height, int kind) { Bitmap bitmap = null; // 获取视频的缩略图 bitmap = ThumbnailUtils.createVideoThumbnail(videoPath, kind); System.out.println("w" + bitmap.getWidth()); System.out.println("h" + bitmap.getHeight()); bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height, ThumbnailUtils.OPTIONS_RECYCLE_INPUT); return bitmap; } public static Bitmap decodeScaleImage(String var0) { BitmapFactory.Options var3 = getBitmapOptions(var0); int var4 = calculateInSampleSize(var3, 420, 420); var3.inSampleSize = var4; var3.inJustDecodeBounds = false; Bitmap var5 = BitmapFactory.decodeFile(var0, var3); int var6 = readPictureDegree(var0); Bitmap var7 = null; if (var5 != null && var6 != 0) { var7 = rotaingImageView(var6, var5); var5.recycle(); var5 = null; return var7; } else { return var5; } } public static String getScaledImage(Context var0, String var1) { File var2 = new File(var1); if (!var2.exists()) { return var1; } else { long var3 = var2.length(); if (var3 <= 102400L) { return var1; } else { Bitmap var5 = decodeScaleImage(var1); try { File var6 = File.createTempFile("image", ".jpg", var0.getFilesDir()); FileOutputStream var7 = new FileOutputStream(var6); var5.compress(Bitmap.CompressFormat.JPEG, 70, var7); var7.close(); return var6.getAbsolutePath(); } catch (Exception var8) { var8.printStackTrace(); return var1; } } } } public static String getScaledImage(Context var0, String var1, int var2) { File var3 = new File(var1); if (var3.exists()) { long var4 = var3.length(); if (var4 > 102400L) { Bitmap var6 = decodeScaleImage(var1); try { File var7 = new File(var0.getExternalCacheDir(), "eaemobTemp" + var2 + ".jpg"); FileOutputStream var8 = new FileOutputStream(var7); var6.compress(Bitmap.CompressFormat.JPEG, 60, var8); var8.close(); return var7.getAbsolutePath(); } catch (Exception var9) { var9.printStackTrace(); } } } return var1; } public static Bitmap rotaingImageView(int var0, Bitmap var1) { Matrix var2 = new Matrix(); var2.postRotate((float) var0); Bitmap var3 = Bitmap.createBitmap(var1, 0, 0, var1.getWidth(), var1.getHeight(), var2, true); return var3; } /* 获取缩略图 * * @param path * @param targetWidth * @return */ public static String getThumbnailImage(String path, int targetWidth, int targetHight) { Bitmap scaleImage = decodeScaleImage(path); try { File file = File.createTempFile("image", ".jpg"); FileOutputStream fileOutputStream = new FileOutputStream(file); scaleImage.compress(Bitmap.CompressFormat.JPEG, 60, fileOutputStream); fileOutputStream.close(); return file.getAbsolutePath(); } catch (Exception e) { e.printStackTrace(); return path; } } /** * 图片解析 * * @param context * @param resId * @param targetWidth * @param targetHeight * @return */ public static Bitmap decodeScaleImage(Context context, int resId, int targetWidth, int targetHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(context.getResources(), resId, options); options.inSampleSize = calculateInSampleSize(options, targetWidth, targetHeight); options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId, options); return bitmap; } /** * 计算样本大小 * * @param options * @param targetWidth * @param targetHeight * @return */ public static int calculateInSampleSize(BitmapFactory.Options options, int targetWidth, int targetHeight) { int height = options.outHeight; int width = options.outWidth; int scale = 1; if (height > targetHeight || width > targetWidth) { int heightScale = Math.round((float) height / (float) targetHeight); int widthScale = Math.round((float) width / (float) targetWidth); scale = heightScale > widthScale ? heightScale : widthScale; } return scale; } /** * 获取BitmapFactory.Options * * @param pathName * @return */ public static BitmapFactory.Options getBitmapOptions(String pathName) { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; BitmapFactory.decodeFile(pathName, opts); return opts; } /** * 获取图片角度 * * @param filename * @return */ public static int readPictureDegree(String filename) { short degree = 0; try { ExifInterface exifInterface = new ExifInterface(filename); int anInt = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); switch (anInt) { case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; case ExifInterface.ORIENTATION_FLIP_VERTICAL: case ExifInterface.ORIENTATION_TRANSPOSE: case ExifInterface.ORIENTATION_TRANSVERSE: default: break; case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; } } catch (IOException e) { e.printStackTrace(); } return degree; } /** * 旋转ImageView * * @param degree * @param source * @return */ public static Bitmap rotatingImageView(int degree, Bitmap source) { Matrix matrix = new Matrix(); matrix.postRotate((float) degree); return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true); } /** * 设置水印图片在左上角 * * @param src * @param watermark * @param paddingLeft * @param paddingTop * @return */ public static Bitmap createWaterMaskLeftTop(Context context, Bitmap src, Bitmap watermark, int paddingLeft, int paddingTop) { return createWaterMaskBitmap(src, watermark, dp2px(context, paddingLeft), dp2px(context, paddingTop)); } /** * 创建水印图片 * @param src * @param watermark * @param paddingLeft * @param paddingTop * @return */ private static Bitmap createWaterMaskBitmap(Bitmap src, Bitmap watermark, int paddingLeft, int paddingTop) { if (src == null) { return null; } int width = src.getWidth(); int height = src.getHeight(); //创建一个bitmap Bitmap newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图 //将该图片作为画布 Canvas canvas = new Canvas(newb); //在画布 0,0坐标上开始绘制原始图片 canvas.drawBitmap(src, 0, 0, null); //在画布上绘制水印图片 canvas.drawBitmap(watermark, paddingLeft, paddingTop, null); // 保存 canvas.save(Canvas.ALL_SAVE_FLAG); // 存储 canvas.restore(); return newb; } /** * 设置水印图片在右下角 * * @param src * @param watermark * @param paddingRight * @param paddingBottom * @return */ public static Bitmap createWaterMaskRightBottom(Context context, Bitmap src, Bitmap watermark, int paddingRight, int paddingBottom) { return createWaterMaskBitmap(src, watermark, src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight), src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom)); } /** * 设置水印图片到右上角 * * @param src * @param watermark * @param paddingRight * @param paddingTop * @return */ public static Bitmap createWaterMaskRightTop(Context context, Bitmap src, Bitmap watermark, int paddingRight, int paddingTop) { return createWaterMaskBitmap(src, watermark, src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight), dp2px(context, paddingTop)); } /** * 设置水印图片到左下角 * * @param src * @param watermark * @param paddingLeft * @param paddingBottom * @return */ public static Bitmap createWaterMaskLeftBottom(Context context, Bitmap src, Bitmap watermark, int paddingLeft, int paddingBottom) { return createWaterMaskBitmap(src, watermark, dp2px(context, paddingLeft), src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom)); } /** * 设置水印图片到中间 * * @param src * @param watermark * @return */ public static Bitmap createWaterMaskCenter(Bitmap src, Bitmap watermark) { return createWaterMaskBitmap(src, watermark, (src.getWidth() - watermark.getWidth()) / 2, (src.getHeight() - watermark.getHeight()) / 2); } /** * 给图片添加文字到左上角 * * @param context * @param bitmap * @param text * @return */ public static Bitmap drawTextToLeftTop(Context context, Bitmap bitmap, String text, int size, int color, int paddingLeft, int paddingTop) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(color); paint.setTextSize(dp2px(context, size)); Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds); return drawTextToBitmap(context, bitmap, text, paint, bounds, dp2px(context, paddingLeft), dp2px(context, paddingTop) + bounds.height()); } /** * 绘制文字到右下角 * * @param context * @param bitmap * @param text * @param size * @param color * @param paddingRight * @param paddingBottom * @return */ public static Bitmap drawTextToRightBottom(Context context, Bitmap bitmap, String text, int size, int color, int paddingRight, int paddingBottom) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(color); paint.setTextSize(dp2px(context, size)); Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds); return drawTextToBitmap(context, bitmap, text, paint, bounds, bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight), bitmap.getHeight() - dp2px(context, paddingBottom)); } /** * 绘制文字到右上方 * * @param context * @param bitmap * @param text * @param size * @param color * @param paddingRight * @param paddingTop * @return */ public static Bitmap drawTextToRightTop(Context context, Bitmap bitmap, String text, int size, int color, int paddingRight, int paddingTop) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(color); paint.setTextSize(dp2px(context, size)); Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds); return drawTextToBitmap(context, bitmap, text, paint, bounds, bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight), dp2px(context, paddingTop) + bounds.height()); } /** * 绘制文字到左下方 * * @param context * @param bitmap * @param text * @param size * @param color * @param paddingLeft * @param paddingBottom * @return */ public static Bitmap drawTextToLeftBottom(Context context, Bitmap bitmap, String text, int size, int color, int paddingLeft, int paddingBottom) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(color); paint.setTextSize(dp2px(context, size)); Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds); return drawTextToBitmap(context, bitmap, text, paint, bounds, dp2px(context, paddingLeft), bitmap.getHeight() - dp2px(context, paddingBottom)); } /** * 绘制文字到中间 * * @param context * @param bitmap * @param text * @param size * @param color * @return */ public static Bitmap drawTextToCenter(Context context, Bitmap bitmap, String text, int size, int color) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(color); paint.setTextSize(dp2px(context, size)); Rect bounds = new Rect(); paint.getTextBounds(text, 0, text.length(), bounds); return drawTextToBitmap(context, bitmap, text, paint, bounds, (bitmap.getWidth() - bounds.width()) / 2, (bitmap.getHeight() + bounds.height()) / 2); } /** * 图片上绘制文字 * @param context * @param bitmap * @param text * @param paint * @param bounds * @param paddingLeft * @param paddingTop * @return */ private static Bitmap drawTextToBitmap(Context context, Bitmap bitmap, String text, Paint paint, Rect bounds, int paddingLeft, int paddingTop) { android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig(); paint.setDither(true); // 获取跟清晰的图像采样 paint.setFilterBitmap(true);// 过滤一些 if (bitmapConfig == null) { bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888; } bitmap = bitmap.copy(bitmapConfig, true); Canvas canvas = new Canvas(bitmap); canvas.drawText(text, paddingLeft, paddingTop, paint); return bitmap; } /** * 缩放图片 * * @param src * @param w * @param h * @return */ public static Bitmap scaleWithWH(Bitmap src, double w, double h) { if (w == 0 || h == 0 || src == null) { return src; } else { // 记录src的宽高 int width = src.getWidth(); int height = src.getHeight(); // 创建一个matrix容器 Matrix matrix = new Matrix(); // 计算缩放比例 float scaleWidth = (float) (w / width); float scaleHeight = (float) (h / height); // 开始缩放 matrix.postScale(scaleWidth, scaleHeight); // 创建缩放后的图片 return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true); } } /** * dip转pix * * @param context * @param dp * @return */ public static int dp2px(Context context, float dp) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5f); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/OkHttpUtils.java ================================================ package com.htmessage.yichatopen.utils; import android.content.Context; import android.os.Handler; import android.os.Message; import android.util.Log; import android.widget.Toast; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTApp; import com.htmessage.yichatopen.R; import com.htmessage.yichatopen.HTConstant; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.FileNameMap; import java.net.URLConnection; import java.util.List; import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.Callback; import okhttp3.FormBody; import okhttp3.Headers; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; /** * Created by huangfangyi on 2016/10/27. * qq 84543217 */ public class OkHttpUtils { private Context context; private OkHttpClient okHttpClient; private static final int RESULT_ERROR = 1000; private static final int RESULT_SUCESS = 2000; private HttpCallBack httpCallBack; private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); int reusltCode = msg.what; switch (reusltCode) { case RESULT_ERROR: httpCallBack.onFailure((String) msg.obj); // Toast.makeText(context, "服务器端无响应", Toast.LENGTH_SHORT).show(); Log.d("result----->", (String) msg.obj); break; case RESULT_SUCESS: String result = (String) msg.obj; Log.d("result----->", result); try { JSONObject jsonObject = JSONObject.parseObject(result); httpCallBack.onResponse(jsonObject); } catch (JSONException e) { httpCallBack.onFailure((String) msg.obj); // Toast.makeText(context, "响应数据解析错误", Toast.LENGTH_SHORT).show(); } break; } } }; public OkHttpUtils(Context context) { this.context = context; okHttpClient = new OkHttpClient.Builder() .connectTimeout(10000L, TimeUnit.MILLISECONDS) .readTimeout(10000L, TimeUnit.MILLISECONDS) .build(); } //纯粹键值对post请求 public void post(List params, String url, HttpCallBack httpCallBack) { Log.d("url----->>", url); this.httpCallBack = httpCallBack; FormBody.Builder bodyBulder = new FormBody.Builder(); JSONObject userJson = HTApp.getInstance().getUserJson(); if (userJson!=null){ String session = userJson.getString(HTConstant.JSON_KEY_SESSION); if (session!=null){ bodyBulder.add("session",session); } Log.d("session----->>", session); } for (Param param : params) { bodyBulder.add(param.getKey(), param.getValue()); Log.d("param.getKey()----->>", param.getKey()); Log.d("param.getValue()----->>", param.getValue()); } RequestBody requestBody = bodyBulder.build(); Request request = new Request.Builder() .url(url) .post(requestBody) .build(); startRequest(request); } //键值对+文件 post请求 public void post(List params, List files, String url, HttpCallBack httpCallBack) { Log.d("url----->>", url); this.httpCallBack = httpCallBack; MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); for (Param param : params) { builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\""), RequestBody.create(MediaType.parse(guessMimeType(param.getKey())), param.getValue())); Log.d("param.getKey()----->>", param.getKey()); Log.d("param.getValue()----->>", param.getValue()); } for (File file : files) { if (file != null && file.exists()) { //TODO-本项目固化文件的键名为“file” builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"" + "file" + "\"; filename=\"" + file.getName()+ "\""), RequestBody.create(MediaType.parse(guessMimeType(file.getName())), file)); Log.d("file.getName()----->>", file.getName()); } } RequestBody requestBody = builder.build(); Request request = new Request.Builder() .url(url) .post(requestBody) .build(); startRequest(request); } private void startRequest(Request request) { if (CommonUtils.isNetWorkConnected(context)){ okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Message message = handler.obtainMessage(); message.what = RESULT_ERROR; message.obj = e.getMessage().toString(); message.sendToTarget(); } @Override public void onResponse(Call call, Response response) throws IOException { Message message = handler.obtainMessage(); message.what = RESULT_SUCESS; message.obj = response.body().string(); message.sendToTarget(); } }); }else{ Toast.makeText(context, R.string.the_current_network, Toast.LENGTH_SHORT).show(); } } public interface HttpCallBack { void onResponse(JSONObject jsonObject); void onFailure(String errorMsg); } /** * 下载不带进度 */ public interface DownloadCallBack { void onSuccess(); void onFailure(String message); } private String guessMimeType(String path) { FileNameMap fileNameMap = URLConnection.getFileNameMap(); String contentTypeFor = fileNameMap.getContentTypeFor(path); if (contentTypeFor == null) { contentTypeFor = "application/octet-stream"; } return contentTypeFor; } public void loadFile(String url, final String savePath, final DownloadCallBack callBack){ Request request = new Request.Builder() //下载地址 .url(url) .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { callBack.onFailure(e.getMessage()); } @Override public void onResponse(Call call, Response response) throws IOException { int len; byte[] buf = new byte[2048]; InputStream inputStream = response.body().byteStream(); //可以在这里自定义路径 File file1 = new File(savePath); FileOutputStream fileOutputStream = new FileOutputStream(file1); while ((len = inputStream.read(buf)) != -1) { fileOutputStream.write(buf, 0, len); } fileOutputStream.flush(); fileOutputStream.close(); inputStream.close(); callBack.onSuccess(); } }); } /** * 下载的带进度的callback */ public interface ProgressDownloadCallBack { void onSuccess(); void onProgress(int progress); void onFailure(); } /** * 下载文件带进度 * * @param url 下载地址 * @param savePath 保存地址 * @param callBack ProgressDownloadCallBack */ public void loadFileHasProgress(String url, final String savePath, final ProgressDownloadCallBack callBack) { Request request = new Request.Builder() //下载地址 .url(url) .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { callBack.onFailure(); } @Override public void onResponse(Call call, Response response) throws IOException { int len; byte[] buf = new byte[2048]; InputStream inputStream = response.body().byteStream(); long requestLength = response.body().contentLength(); long total = 0; //可以在这里自定义路径 File file1 = new File(savePath); FileOutputStream fileOutputStream = new FileOutputStream(file1); while ((len = inputStream.read(buf)) != -1) { total += len; // publishing the progress.... if (requestLength > 0) // only if total length is known callBack.onProgress((int) (total * 100 / requestLength)); fileOutputStream.write(buf, 0, len); } fileOutputStream.flush(); fileOutputStream.close(); inputStream.close(); callBack.onSuccess(); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/Param.java ================================================ package com.htmessage.yichatopen.utils; /** * Created by ustc on 2016/6/30. */ public class Param { private String key; private String value; public Param(String key, String value) { this.key = key; this.value = value; } public String getKey() { return key; } public String getValue() { return value; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/PathUtils.java ================================================ package com.htmessage.yichatopen.utils; import android.content.Context; import android.os.Environment; import android.util.Log; import com.htmessage.yichatopen.HTApp; import com.htmessage.sdk.model.HTMessage; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; /** * Created by huangfangyi on 2016/12/4. * qq 84543217 */ public class PathUtils { private static PathUtils instance; private static File storageDir = null; private File voicePath = null; private File imagePath = null; private File videoPath = null; private File filePath; private static String pathPrefix; public PathUtils(String chatTo, Context context) { String username= HTApp.getInstance().getUsername(); String packageName = context.getPackageName(); pathPrefix = "/Android/data/" + packageName + "/"; this.voicePath = generateVoicePath(username,chatTo,context); if(!this.voicePath.exists()) { this.voicePath.mkdirs(); } this.imagePath = generateImagePath(username, chatTo, context); if(!this.imagePath.exists()) { this.imagePath.mkdirs(); } this.videoPath = generateVideoPath(username, chatTo, context); if(!this.videoPath.exists()) { this.videoPath.mkdirs(); } this.filePath = generateFiePath(username, chatTo, context); if(!this.filePath.exists()) { this.filePath.mkdirs(); } } public File getImagePath() { return this.imagePath; } public File getVoicePath() { return this.voicePath; } public File getFilePath() { return this.filePath; } public File getVideoPath() { return this.videoPath; } private static File getStorageDir(Context context) { if(storageDir == null) { File file = Environment.getExternalStorageDirectory(); if(file.exists()) { return file; } storageDir = context.getFilesDir(); } return storageDir; } private static File generateImagePath(String username, String chatTo, Context context) { String filePath = null; if(username == null) { filePath = pathPrefix + chatTo + "/image/"; } else { filePath = pathPrefix + username + "/" + chatTo + "/image/"; } return new File(getStorageDir(context), filePath); } private static File generateVoicePath(String username, String chatTo, Context context) { String filePath = null; if(username == null) { filePath = pathPrefix + chatTo + "/voice/"; } else { filePath = pathPrefix + username + "/" + chatTo + "/voice/"; } return new File(getStorageDir(context), filePath); } private static File generateFiePath(String username, String chatTo, Context context) { String filePath = null; if(username == null) { filePath = pathPrefix + chatTo + "/file/"; } else { filePath = pathPrefix + username + "/" + chatTo + "/file/"; } return new File(getStorageDir(context), filePath); } private static File generateVideoPath(String username, String chatTo, Context context) { String filePath = null; if(username == null) { filePath = pathPrefix + chatTo + "/video/"; } else { filePath = pathPrefix + username + "/" + chatTo + "/video/"; } return new File(getStorageDir(context), filePath); } public void saveSendFileInDisk(final String filePath, final HTMessage.Type type, final NewFilePathCallBack newFilePathCallBack){ final String fileName=filePath.substring(filePath.lastIndexOf("/")+1); new Thread(new Runnable() { @Override public void run() { String finalPath=null; if(type== HTMessage.Type.IMAGE){ finalPath=getImagePath().getAbsolutePath()+fileName; }else if(type== HTMessage.Type.VOICE){ finalPath=getVoicePath().getAbsolutePath()+fileName; } else if(type== HTMessage.Type.VIDEO){ finalPath=getVideoPath().getAbsolutePath()+fileName; } else if(type== HTMessage.Type.FILE){ finalPath=getFilePath().getAbsolutePath()+fileName; } Log.d("filePath--->",filePath); Log.d("finalPath--->",finalPath); copyFile(filePath,finalPath,newFilePathCallBack); } }).start(); } interface NewFilePathCallBack{ void onSuccess(String filePath); } /** * 复制单个文件 * @param oldPath String 原文件路径 如:c:/fqf.txt * @param newPath String 复制后路径 如:f:/fqf.txt * @return boolean */ public void copyFile(String oldPath, String newPath,NewFilePathCallBack newFilePathCallBack) { try { int bytesum = 0; int byteread = 0; File oldfile = new File(oldPath); if (oldfile.exists()) { //文件存在时 InputStream inStream = new FileInputStream(oldPath); //读入原文件 FileOutputStream fs = new FileOutputStream(newPath); byte[] buffer = new byte[1444]; int length; while ( (byteread = inStream.read(buffer)) != -1) { bytesum += byteread; //字节数 文件大小 System.out.println(bytesum); fs.write(buffer, 0, byteread); } inStream.close(); } newFilePathCallBack.onSuccess(newPath); } catch (Exception e) { System.out.println("复制单个文件操作出错"); e.printStackTrace(); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/UpdateLastLoginTimeUtils.java ================================================ package com.htmessage.yichatopen.utils; import android.content.Context; import android.util.Log; import com.alibaba.fastjson.JSONObject; import com.htmessage.yichatopen.HTConstant; import java.util.ArrayList; import java.util.List; /** * 项目名称:yichat0504 * 类描述:UpdateLocalLoginTimeUtils 描述: * 创建人:songlijie * 创建时间:2017/7/5 17:48 * 邮箱:814326663@qq.com */ public class UpdateLastLoginTimeUtils { private static String TAG = UpdateLastLoginTimeUtils.class.getSimpleName(); public static void sendLocalTimeToService(Context context){ List params = new ArrayList<>(); new OkHttpUtils(context).post(params, HTConstant.URL_SEND_LOCAL_LOGIN_TIME, new OkHttpUtils.HttpCallBack() { @Override public void onResponse(JSONObject jsonObject) { int code = jsonObject.getIntValue("code"); switch (code){ case 1: Log.d(TAG,"上传本地成功!"); break; default: Log.d(TAG,"上传本地失败!"); break; } } @Override public void onFailure(String errorMsg) { Log.d(TAG,"上传本地失败!"+errorMsg); } }); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/utils/Validator.java ================================================ package com.htmessage.yichatopen.utils; import java.util.regex.Pattern; /** * 校验器:利用正则表达式校验邮箱、手机号等 * * @author liujiduo * */ public class Validator { /** * 正则表达式:验证用户名 */ public static final String REGEX_USERNAME = "^[a-zA-Z]\\w{5,17}$"; /** * 正则表达式:验证密码 */ public static final String REGEX_PASSWORD = "^[a-zA-Z0-9]{6,16}$"; /** * 正则表达式:验证手机号 .matches("^[1][3578]\\d{9}") */ // public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";// public static final String REGEX_MOBILE = "^(13[0-9]|15[012356789]|17[03678]|18[0-9]|14[57])[0-9]{8}$"; /** * 正则表达式:验证邮箱 */ public static final String REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; /** * 正则表达式:验证汉字 */ public static final String REGEX_CHINESE = "^[\u4e00-\u9fa5],{0,}$"; /** * 正则表达式:验证身份证 */ public static final String REGEX_ID_CARD = "(^\\d{18}$)|(^\\d{15}$)"; /** * 正则表达式:验证URL */ public static final String REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?"; /** * 正则表达式:验证IP地址 */ public static final String REGEX_IP_ADDR = "(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)"; /** * 校验用户名 * * @param username * @return 校验通过返回true,否则返回false */ public static boolean isUsername(String username) { return Pattern.matches(REGEX_USERNAME, username); } /** * 校验密码 * * @param password * @return 校验通过返回true,否则返回false */ public static boolean isPassword(String password) { return Pattern.matches(REGEX_PASSWORD, password); } /** * 校验手机号 * * @param mobile * @return 校验通过返回true,否则返回false */ public static boolean isMobile(String mobile) { return Pattern.matches(REGEX_MOBILE, mobile); } /** * 校验邮箱 * * @param email * @return 校验通过返回true,否则返回false */ public static boolean isEmail(String email) { return Pattern.matches(REGEX_EMAIL, email); } /** * 校验汉字 * * @param chinese * @return 校验通过返回true,否则返回false */ public static boolean isChinese(String chinese) { return Pattern.matches(REGEX_CHINESE, chinese); } /** * 校验身份证 * * @param idCard * @return 校验通过返回true,否则返回false */ public static boolean isIDCard(String idCard) { return Pattern.matches(REGEX_ID_CARD, idCard); } /** * 校验URL * * @param url * @return 校验通过返回true,否则返回false */ public static boolean isUrl(String url) { return Pattern.matches(REGEX_URL, url); } /** * 校验IP地址 * * @param ipAddr * @return */ public static boolean isIPAddr(String ipAddr) { return Pattern.matches(REGEX_IP_ADDR, ipAddr); } public static void main(String[] args) { String username = "fdsdfsdj"; System.out.println(Validator.isUsername(username)); System.out.println(Validator.isChinese(username)); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/HTAlertDialog.java ================================================ package com.htmessage.yichatopen.widget; import android.app.AlertDialog; import android.content.Context; import android.view.View; import android.view.Window; import android.widget.LinearLayout; import android.widget.TextView; import com.htmessage.yichatopen.R; /** * Created by huangfangyi on 2016/7/3.\ * QQ:84543217 */ public class HTAlertDialog { private Context context; private String title; private String[] items; public HTAlertDialog(Context context, String title, String[] items) { this.context = context; this.title = title; this.items = items; } public void init(final OnItemClickListner onItemClickListner) { final AlertDialog dlg = new AlertDialog.Builder(context).create(); dlg.show(); Window window = dlg.getWindow(); window.setContentView(R.layout.dialog_alert); if (title != null) { window.findViewById(R.id.ll_title).setVisibility(View.VISIBLE); TextView tv_title = (TextView) window.findViewById(R.id.tv_title); tv_title.setText(title); } //默认是2个item--最多支持5个,如需增加,请修改布局文件 LinearLayout[] linearLayouts=new LinearLayout[]{ (LinearLayout) window.findViewById(R.id.ll_content1), (LinearLayout) window.findViewById(R.id.ll_content2), (LinearLayout) window.findViewById(R.id.ll_content3), (LinearLayout) window.findViewById(R.id.ll_content4), (LinearLayout) window.findViewById(R.id.ll_content5) }; TextView[] textViews=new TextView[]{ (TextView) window.findViewById(R.id.tv_content1), (TextView) window.findViewById(R.id.tv_content2), (TextView) window.findViewById(R.id.tv_content3), (TextView) window.findViewById(R.id.tv_content4), (TextView) window.findViewById(R.id.tv_content5), }; for(int i=0;i= VERSION_CODES.JELLY_BEAN) { SDK16.postOnAnimation(view, runnable); } else { view.postDelayed(runnable, SIXTY_FPS_INTERVAL); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/photoview/IPhotoView.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /******************************************************************************* * Copyright 2011, 2012 Chris Banes. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.htmessage.yichatopen.widget.photoview; import android.graphics.RectF; import android.view.View; import android.widget.ImageView; public interface IPhotoView { /** * Returns true if the PhotoView is set to allow zooming of Photos. * * @return true if the PhotoView allows zooming. */ boolean canZoom(); /** * Gets the Display Rectangle of the currently displayed Drawable. The * Rectangle is relative to this View and includes all scaling and * translations. * * @return - RectF of Displayed Drawable */ RectF getDisplayRect(); /** * @return The current minimum scale level. What this value represents depends on the current {@link ImageView.ScaleType}. */ float getMinScale(); /** * @return The current middle scale level. What this value represents depends on the current {@link ImageView.ScaleType}. */ float getMidScale(); /** * @return The current maximum scale level. What this value represents depends on the current {@link ImageView.ScaleType}. */ float getMaxScale(); /** * Returns the current scale value * * @return float - current scale value */ float getScale(); /** * Return the current scale type in use by the ImageView. */ ImageView.ScaleType getScaleType(); /** * Whether to allow the ImageView's parent to intercept the touch event when the photo is scroll to it's horizontal edge. */ void setAllowParentInterceptOnEdge(boolean allow); /** * Sets the minimum scale level. What this value represents depends on the current {@link ImageView.ScaleType}. */ void setMinScale(float minScale); /** * Sets the middle scale level. What this value represents depends on the current {@link ImageView.ScaleType}. */ void setMidScale(float midScale); /** * Sets the maximum scale level. What this value represents depends on the current {@link ImageView.ScaleType}. */ void setMaxScale(float maxScale); /** * Register a callback to be invoked when the Photo displayed by this view is long-pressed. * * @param listener - Listener to be registered. */ void setOnLongClickListener(View.OnLongClickListener listener); /** * Register a callback to be invoked when the Matrix has changed for this * View. An example would be the user panning or scaling the Photo. * * @param listener - Listener to be registered. */ void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener listener); /** * Register a callback to be invoked when the Photo displayed by this View * is tapped with a single tap. * * @param listener - Listener to be registered. */ void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener listener); /** * Register a callback to be invoked when the View is tapped with a single * tap. * * @param listener - Listener to be registered. */ void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener listener); /** * Controls how the image should be resized or moved to match the size of * the ImageView. Any scaling or panning will happen within the confines of * this {@link ImageView.ScaleType}. * * @param scaleType - The desired scaling mode. */ void setScaleType(ImageView.ScaleType scaleType); /** * Allows you to enable/disable the zoom functionality on the ImageView. * When disable the ImageView reverts to using the FIT_CENTER matrix. * * @param zoomable - Whether the zoom functionality is enabled. */ void setZoomable(boolean zoomable); /** * Zooms to the specified scale, around the focal point given. * * @param scale - Scale to zoom to * @param focalX - X Focus Point * @param focalY - Y Focus Point */ void zoomTo(float scale, float focalX, float focalY); } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/photoview/PhotoView.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /******************************************************************************* * Copyright 2011, 2012 Chris Banes. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.htmessage.yichatopen.widget.photoview; import android.content.Context; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.net.Uri; import android.util.AttributeSet; import android.widget.ImageView; public class PhotoView extends ImageView implements IPhotoView { private final PhotoViewAttacher mAttacher; private ScaleType mPendingScaleType; public PhotoView(Context context) { this(context, null); } public PhotoView(Context context, AttributeSet attr) { this(context, attr, 0); } public PhotoView(Context context, AttributeSet attr, int defStyle) { super(context, attr, defStyle); super.setScaleType(ScaleType.MATRIX); mAttacher = new PhotoViewAttacher(this); if (null != mPendingScaleType) { setScaleType(mPendingScaleType); mPendingScaleType = null; } } @Override public boolean canZoom() { return mAttacher.canZoom(); } @Override public RectF getDisplayRect() { return mAttacher.getDisplayRect(); } @Override public float getMinScale() { return mAttacher.getMinScale(); } @Override public float getMidScale() { return mAttacher.getMidScale(); } @Override public float getMaxScale() { return mAttacher.getMaxScale(); } @Override public float getScale() { return mAttacher.getScale(); } @Override public ScaleType getScaleType() { return mAttacher.getScaleType(); } @Override public void setAllowParentInterceptOnEdge(boolean allow) { mAttacher.setAllowParentInterceptOnEdge(allow); } @Override public void setMinScale(float minScale) { mAttacher.setMinScale(minScale); } @Override public void setMidScale(float midScale) { mAttacher.setMidScale(midScale); } @Override public void setMaxScale(float maxScale) { mAttacher.setMaxScale(maxScale); } @Override // setImageBitmap calls through to this method public void setImageDrawable(Drawable drawable) { super.setImageDrawable(drawable); if (null != mAttacher) { mAttacher.update(); } } @Override public void setImageResource(int resId) { super.setImageResource(resId); if (null != mAttacher) { mAttacher.update(); } } @Override public void setImageURI(Uri uri) { super.setImageURI(uri); if (null != mAttacher) { mAttacher.update(); } } @Override public void setOnMatrixChangeListener(PhotoViewAttacher.OnMatrixChangedListener listener) { mAttacher.setOnMatrixChangeListener(listener); } @Override public void setOnLongClickListener(OnLongClickListener l) { mAttacher.setOnLongClickListener(l); } @Override public void setOnPhotoTapListener(PhotoViewAttacher.OnPhotoTapListener listener) { mAttacher.setOnPhotoTapListener(listener); } @Override public void setOnViewTapListener(PhotoViewAttacher.OnViewTapListener listener) { mAttacher.setOnViewTapListener(listener); } @Override public void setScaleType(ScaleType scaleType) { if (null != mAttacher) { mAttacher.setScaleType(scaleType); } else { mPendingScaleType = scaleType; } } @Override public void setZoomable(boolean zoomable) { mAttacher.setZoomable(zoomable); } @Override public void zoomTo(float scale, float focalX, float focalY) { mAttacher.zoomTo(scale, focalX, focalY); } @Override protected void onDetachedFromWindow() { mAttacher.cleanup(); super.onDetachedFromWindow(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/photoview/PhotoViewAttacher.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /******************************************************************************* * Copyright 2011, 2012 Chris Banes. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.htmessage.yichatopen.widget.photoview; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Matrix; import android.graphics.Matrix.ScaleToFit; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.View.OnLongClickListener; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.ImageView.ScaleType; import java.lang.ref.WeakReference; class PhotoViewAttacher implements IPhotoView, View.OnTouchListener, VersionedGestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, ViewTreeObserver.OnGlobalLayoutListener { static final String LOG_TAG = "PhotoViewAttacher"; // let debug flag be dynamic, but still Proguard can be used to remove from // release builds static final boolean DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG); static final int EDGE_NONE = -1; static final int EDGE_LEFT = 0; static final int EDGE_RIGHT = 1; static final int EDGE_BOTH = 2; public static final float DEFAULT_MAX_SCALE = 2.0f; // public static final float DEFAULT_MID_SCALE = 1.75f; public static final float DEFAULT_MIN_SCALE = 1.0f; private float mMinScale = DEFAULT_MIN_SCALE; // private float mMidScale = DEFAULT_MID_SCALE; private float mMaxScale = DEFAULT_MAX_SCALE; private boolean mAllowParentInterceptOnEdge = true; // private static void checkZoomLevels(float minZoom, float midZoom, // float maxZoom) { // if (minZoom >= midZoom) { // throw new IllegalArgumentException( // "MinZoom should be less than MidZoom"); // } else if (midZoom >= maxZoom) { // throw new IllegalArgumentException( // "MidZoom should be less than MaxZoom"); // } // } private static void checkZoomLevels(float minZoom,float maxZoom) { if (minZoom >= maxZoom) { throw new IllegalArgumentException("MinZoom should be less than maxZoom"); } /*else if (midZoom >= maxZoom) { throw new IllegalArgumentException("MidZoom should be less than MaxZoom"); }*/ } /** * @return true if the ImageView exists, and it's Drawable existss */ private static boolean hasDrawable(ImageView imageView) { return null != imageView && null != imageView.getDrawable(); } /** * @return true if the ScaleType is supported. */ private static boolean isSupportedScaleType(final ScaleType scaleType) { if (null == scaleType) { return false; } switch (scaleType) { case MATRIX: throw new IllegalArgumentException(scaleType.name() + " is not supported in PhotoView"); default: return true; } } /** * Set's the ImageView's ScaleType to Matrix. */ private static void setImageViewScaleTypeMatrix(ImageView imageView) { if (null != imageView) { if (imageView instanceof PhotoView) { /** * PhotoView sets it's own ScaleType to Matrix, then diverts all * calls setScaleType to this.setScaleType. Basically we don't * need to do anything here */ } else { imageView.setScaleType(ScaleType.MATRIX); } } } private WeakReference mImageView; private ViewTreeObserver mViewTreeObserver; // Gesture Detectors private GestureDetector mGestureDetector; private VersionedGestureDetector mScaleDragDetector; // These are set so we don't keep allocating them on the heap private final Matrix mBaseMatrix = new Matrix(); private final Matrix mDrawMatrix = new Matrix(); private final Matrix mSuppMatrix = new Matrix(); private final RectF mDisplayRect = new RectF(); private final float[] mMatrixValues = new float[9]; // Listeners private OnMatrixChangedListener mMatrixChangeListener; private OnPhotoTapListener mPhotoTapListener; private OnViewTapListener mViewTapListener; private OnLongClickListener mLongClickListener; private int mIvTop, mIvRight, mIvBottom, mIvLeft; private FlingRunnable mCurrentFlingRunnable; private int mScrollEdge = EDGE_BOTH; private boolean mZoomEnabled; private ScaleType mScaleType = ScaleType.FIT_CENTER; public PhotoViewAttacher(ImageView imageView) { mImageView = new WeakReference(imageView); imageView.setOnTouchListener(this); mViewTreeObserver = imageView.getViewTreeObserver(); mViewTreeObserver.addOnGlobalLayoutListener(this); // Make sure we using MATRIX Scale Type setImageViewScaleTypeMatrix(imageView); if (!imageView.isInEditMode()) { // Create Gesture Detectors... mScaleDragDetector = VersionedGestureDetector.newInstance(imageView.getContext(), this); mGestureDetector = new GestureDetector(imageView.getContext(), new GestureDetector.SimpleOnGestureListener() { // forward long click listener @Override public void onLongPress(MotionEvent e) { if (null != mLongClickListener) { mLongClickListener.onLongClick(mImageView.get()); } } }); mGestureDetector.setOnDoubleTapListener(this); // Finally, update the UI so that we're zoomable setZoomable(true); } } @Override public final boolean canZoom() { return mZoomEnabled; } /** * Clean-up the resources attached to this object. This needs to be called * when the ImageView is no longer used. A good example is from * {@link View#onDetachedFromWindow()} or from * {@link android.app.Activity#onDestroy()}. This is automatically called if * you are using {@link PhotoView.co.senab.photoview.PhotoView}. */ @SuppressLint("NewApi") @SuppressWarnings("deprecation") public final void cleanup() { if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { if (null != mImageView) { mImageView.get().getViewTreeObserver().removeOnGlobalLayoutListener(this); } if (null != mViewTreeObserver && mViewTreeObserver.isAlive()) { mViewTreeObserver.removeOnGlobalLayoutListener(this); mViewTreeObserver = null; // Clear listeners too mMatrixChangeListener = null; mPhotoTapListener = null; mViewTapListener = null; // Finally, clear ImageView mImageView = null; } } else { if (null != mImageView) { mImageView.get().getViewTreeObserver().removeGlobalOnLayoutListener(this); } if (null != mViewTreeObserver && mViewTreeObserver.isAlive()) { mViewTreeObserver.removeGlobalOnLayoutListener(this); mViewTreeObserver = null; // Clear listeners too mMatrixChangeListener = null; mPhotoTapListener = null; mViewTapListener = null; // Finally, clear ImageView mImageView = null; } } } @Override public final RectF getDisplayRect() { checkMatrixBounds(); return getDisplayRect(getDisplayMatrix()); } public final ImageView getImageView() { ImageView imageView = null; if (null != mImageView) { imageView = mImageView.get(); } // If we don't have an ImageView, call cleanup() if (null == imageView) { cleanup(); throw new IllegalStateException( "ImageView no longer exists. You should not use this PhotoViewAttacher any more."); } return imageView; } @Override public float getMinScale() { return mMinScale; } // @Override // public float getMidScale() { // return mMidScale; // } @Override public float getMaxScale() { return mMaxScale; } @Override public final float getScale() { return getValue(mSuppMatrix, Matrix.MSCALE_X); } @Override public final ScaleType getScaleType() { return mScaleType; } public final boolean onDoubleTap(MotionEvent ev) { try { float scale = getScale(); float x = ev.getX(); float y = ev.getY(); /* * if (scale < mMidScale) { zoomTo(mMidScale, x, y); } else */if (/* scale >= mMidScale && */scale < mMaxScale) { zoomTo(mMaxScale, x, y); } else { zoomTo(mMinScale, x, y); } } catch (ArrayIndexOutOfBoundsException e) { // Can sometimes happen when getX() and getY() is called } return true; } public final boolean onDoubleTapEvent(MotionEvent e) { // Wait for the confirmed onDoubleTap() instead return false; } public final void onDrag(float dx, float dy) { if (DEBUG) { Log.d(LOG_TAG, String.format("onDrag: dx: %.2f. dy: %.2f", dx, dy)); } ImageView imageView = getImageView(); if (null != imageView && hasDrawable(imageView)) { mSuppMatrix.postTranslate(dx, dy); checkAndDisplayMatrix(); /** * Here we decide whether to let the ImageView's parent to start * taking over the touch event. * * First we check whether this function is enabled. We never want * the parent to take over if we're scaling. We then check the edge * we're on, and the direction of the scroll (i.e. if we're pulling * against the edge, aka 'overscrolling', let the parent take over). */ if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling()) { if (mScrollEdge == EDGE_BOTH || (mScrollEdge == EDGE_LEFT && dx >= 1f) || (mScrollEdge == EDGE_RIGHT && dx <= -1f)) { imageView.getParent().requestDisallowInterceptTouchEvent(false); } } } } @Override public final void onFling(float startX, float startY, float velocityX, float velocityY) { if (DEBUG) { Log.d(LOG_TAG, "onFling. sX: " + startX + " sY: " + startY + " Vx: " + velocityX + " Vy: " + velocityY); } ImageView imageView = getImageView(); if (hasDrawable(imageView)) { mCurrentFlingRunnable = new FlingRunnable(imageView.getContext()); mCurrentFlingRunnable.fling(imageView.getWidth(), imageView.getHeight(), (int) velocityX, (int) velocityY); imageView.post(mCurrentFlingRunnable); } } @Override public final void onGlobalLayout() { ImageView imageView = getImageView(); if (null != imageView && mZoomEnabled) { final int top = imageView.getTop(); final int right = imageView.getRight(); final int bottom = imageView.getBottom(); final int left = imageView.getLeft(); /** * We need to check whether the ImageView's bounds have changed. * This would be easier if we targeted API 11+ as we could just use * View.OnLayoutChangeListener. Instead we have to replicate the * work, keeping track of the ImageView's bounds and then checking * if the values change. */ if (top != mIvTop || bottom != mIvBottom || left != mIvLeft || right != mIvRight) { // Update our base matrix, as the bounds have changed updateBaseMatrix(imageView.getDrawable()); // Update values as something has changed mIvTop = top; mIvRight = right; mIvBottom = bottom; mIvLeft = left; } } } public final void onScale(float scaleFactor, float focusX, float focusY) { if (DEBUG) { Log.d(LOG_TAG, String.format("onScale: scale: %.2f. fX: %.2f. fY: %.2f", scaleFactor, focusX, focusY)); } if (hasDrawable(getImageView()) && (getScale() < mMaxScale || scaleFactor < 1f)) { mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); checkAndDisplayMatrix(); } } public final boolean onSingleTapConfirmed(MotionEvent e) { ImageView imageView = getImageView(); if (null != imageView) { if (null != mPhotoTapListener) { final RectF displayRect = getDisplayRect(); if (null != displayRect) { final float x = e.getX(), y = e.getY(); // Check to see if the user tapped on the photo if (displayRect.contains(x, y)) { float xResult = (x - displayRect.left) / displayRect.width(); float yResult = (y - displayRect.top) / displayRect.height(); mPhotoTapListener.onPhotoTap(imageView, xResult, yResult); return true; } } } if (null != mViewTapListener) { mViewTapListener.onViewTap(imageView, e.getX(), e.getY()); } } return false; } @Override public final boolean onTouch(View v, MotionEvent ev) { boolean handled = false; if (mZoomEnabled) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: // First, disable the Parent from intercepting the touch // event v.getParent().requestDisallowInterceptTouchEvent(true); // If we're flinging, and the user presses down, cancel // fling cancelFling(); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: // If the user has zoomed less than min scale, zoom back // to min scale if (getScale() < mMinScale) { RectF rect = getDisplayRect(); if (null != rect) { v.post(new AnimatedZoomRunnable(getScale(), mMinScale, rect.centerX(), rect.centerY())); handled = true; } } break; } // Check to see if the user double tapped if (null != mGestureDetector && mGestureDetector.onTouchEvent(ev)) { handled = true; } // Finally, try the Scale/Drag detector if (null != mScaleDragDetector && mScaleDragDetector.onTouchEvent(ev)) { handled = true; } } return handled; } @Override public void setAllowParentInterceptOnEdge(boolean allow) { mAllowParentInterceptOnEdge = allow; } @Override public void setMinScale(float minScale) { // checkZoomLevels(minScale, mMidScale, mMaxScale); checkZoomLevels(minScale, mMaxScale); mMinScale = minScale; } // @Override // public void setMidScale(float midScale) { // checkZoomLevels(mMinScale, midScale, mMaxScale); // mMidScale = midScale; // } @Override public void setMaxScale(float maxScale) { // checkZoomLevels(mMinScale, mMidScale, maxScale); checkZoomLevels(mMinScale, maxScale); mMaxScale = maxScale; } @Override public final void setOnLongClickListener(OnLongClickListener listener) { mLongClickListener = listener; } @Override public final void setOnMatrixChangeListener(OnMatrixChangedListener listener) { mMatrixChangeListener = listener; } @Override public final void setOnPhotoTapListener(OnPhotoTapListener listener) { mPhotoTapListener = listener; } @Override public final void setOnViewTapListener(OnViewTapListener listener) { mViewTapListener = listener; } @Override public final void setScaleType(ScaleType scaleType) { if (isSupportedScaleType(scaleType) && scaleType != mScaleType) { mScaleType = scaleType; // Finally update update(); } } @Override public final void setZoomable(boolean zoomable) { mZoomEnabled = zoomable; update(); } public final void update() { ImageView imageView = getImageView(); if (null != imageView) { if (mZoomEnabled) { // Make sure we using MATRIX Scale Type setImageViewScaleTypeMatrix(imageView); // Update the base matrix using the current drawable updateBaseMatrix(imageView.getDrawable()); } else { // Reset the Matrix... resetMatrix(); } } } @Override public final void zoomTo(float scale, float focalX, float focalY) { ImageView imageView = getImageView(); if (null != imageView) { imageView.post(new AnimatedZoomRunnable(getScale(), scale, focalX, focalY)); } } protected Matrix getDisplayMatrix() { mDrawMatrix.set(mBaseMatrix); mDrawMatrix.postConcat(mSuppMatrix); return mDrawMatrix; } private void cancelFling() { if (null != mCurrentFlingRunnable) { mCurrentFlingRunnable.cancelFling(); mCurrentFlingRunnable = null; } } /** * Helper method that simply checks the Matrix, and then displays the result */ private void checkAndDisplayMatrix() { checkMatrixBounds(); setImageViewMatrix(getDisplayMatrix()); } private void checkImageViewScaleType() { ImageView imageView = getImageView(); /** * PhotoView's getScaleType() will just divert to this.getScaleType() so * only call if we're not attached to a PhotoView. */ if (null != imageView && !(imageView instanceof PhotoView)) { if (imageView.getScaleType() != ScaleType.MATRIX) { throw new IllegalStateException( "The ImageView's ScaleType has been changed since attaching a PhotoViewAttacher"); } } } private void checkMatrixBounds() { final ImageView imageView = getImageView(); if (null == imageView) { return; } final RectF rect = getDisplayRect(getDisplayMatrix()); if (null == rect) { return; } final float height = rect.height(), width = rect.width(); float deltaX = 0, deltaY = 0; final int viewHeight = imageView.getHeight(); if (height <= viewHeight) { switch (mScaleType) { case FIT_START: deltaY = -rect.top; break; case FIT_END: deltaY = viewHeight - height - rect.top; break; default: deltaY = (viewHeight - height) / 2 - rect.top; break; } } else if (rect.top > 0) { deltaY = -rect.top; } else if (rect.bottom < viewHeight) { deltaY = viewHeight - rect.bottom; } final int viewWidth = imageView.getWidth(); if (width <= viewWidth) { switch (mScaleType) { case FIT_START: deltaX = -rect.left; break; case FIT_END: deltaX = viewWidth - width - rect.left; break; default: deltaX = (viewWidth - width) / 2 - rect.left; break; } mScrollEdge = EDGE_BOTH; } else if (rect.left > 0) { mScrollEdge = EDGE_LEFT; deltaX = -rect.left; } else if (rect.right < viewWidth) { deltaX = viewWidth - rect.right; mScrollEdge = EDGE_RIGHT; } else { mScrollEdge = EDGE_NONE; } // Finally actually translate the matrix mSuppMatrix.postTranslate(deltaX, deltaY); } /** * Helper method that maps the supplied Matrix to the current Drawable * * @param matrix * - Matrix to map Drawable against * @return RectF - Displayed Rectangle */ private RectF getDisplayRect(Matrix matrix) { ImageView imageView = getImageView(); if (null != imageView) { Drawable d = imageView.getDrawable(); if (null != d) { mDisplayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); matrix.mapRect(mDisplayRect); return mDisplayRect; } } return null; } /** * Helper method that 'unpacks' a Matrix and returns the required value * * @param matrix * - Matrix to unpack * @param whichValue * - Which value from Matrix.M* to return * @return float - returned value */ private float getValue(Matrix matrix, int whichValue) { matrix.getValues(mMatrixValues); return mMatrixValues[whichValue]; } /** * Resets the Matrix back to FIT_CENTER, and then displays it.s */ private void resetMatrix() { mSuppMatrix.reset(); setImageViewMatrix(getDisplayMatrix()); checkMatrixBounds(); } private void setImageViewMatrix(Matrix matrix) { ImageView imageView = getImageView(); if (null != imageView) { checkImageViewScaleType(); imageView.setImageMatrix(matrix); // Call MatrixChangedListener if needed if (null != mMatrixChangeListener) { RectF displayRect = getDisplayRect(matrix); if (null != displayRect) { mMatrixChangeListener.onMatrixChanged(displayRect); } } } } /** * Calculate Matrix for FIT_CENTER * * @param d * - Drawable being displayed */ private void updateBaseMatrix(Drawable d) { ImageView imageView = getImageView(); if (null == imageView || null == d) { return; } final float viewWidth = imageView.getWidth(); final float viewHeight = imageView.getHeight(); final int drawableWidth = d.getIntrinsicWidth(); final int drawableHeight = d.getIntrinsicHeight(); mBaseMatrix.reset(); final float widthScale = viewWidth / drawableWidth; final float heightScale = viewHeight / drawableHeight; if (mScaleType == ScaleType.CENTER) { mBaseMatrix.postTranslate((viewWidth - drawableWidth) / 2F, (viewHeight - drawableHeight) / 2F); } else if (mScaleType == ScaleType.CENTER_CROP) { float scale = Math.max(widthScale, heightScale); mBaseMatrix.postScale(scale, scale); mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, (viewHeight - drawableHeight * scale) / 2F); } else if (mScaleType == ScaleType.CENTER_INSIDE) { float scale = Math.min(1.0f, Math.min(widthScale, heightScale)); mBaseMatrix.postScale(scale, scale); mBaseMatrix.postTranslate((viewWidth - drawableWidth * scale) / 2F, (viewHeight - drawableHeight * scale) / 2F); } else { RectF mTempSrc = new RectF(0, 0, drawableWidth, drawableHeight); RectF mTempDst = new RectF(0, 0, viewWidth, viewHeight); switch (mScaleType) { case FIT_CENTER: mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.CENTER); break; case FIT_START: mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.START); break; case FIT_END: mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.END); break; case FIT_XY: mBaseMatrix.setRectToRect(mTempSrc, mTempDst, ScaleToFit.FILL); break; default: break; } } resetMatrix(); } /** * Interface definition for a callback to be invoked when the internal * Matrix has changed for this View. * * @author Chris Banes */ public static interface OnMatrixChangedListener { /** * Callback for when the Matrix displaying the Drawable has changed. * This could be because the View's bounds have changed, or the user has * zoomed. * * @param rect * - Rectangle displaying the Drawable's new bounds. */ void onMatrixChanged(RectF rect); } /** * Interface definition for a callback to be invoked when the Photo is * tapped with a single tap. * * @author Chris Banes */ public static interface OnPhotoTapListener { /** * A callback to receive where the user taps on a photo. You will only * receive a callback if the user taps on the actual photo, tapping on * 'whitespace' will be ignored. * * @param view * - View the user tapped. * @param x * - where the user tapped from the of the Drawable, as * percentage of the Drawable width. * @param y * - where the user tapped from the top of the Drawable, as * percentage of the Drawable height. */ void onPhotoTap(View view, float x, float y); } /** * Interface definition for a callback to be invoked when the ImageView is * tapped with a single tap. * * @author Chris Banes */ public static interface OnViewTapListener { /** * A callback to receive where the user taps on a ImageView. You will * receive a callback if the user taps anywhere on the view, tapping on * 'whitespace' will not be ignored. * * @param view * - View the user tapped. * @param x * - where the user tapped from the left of the View. * @param y * - where the user tapped from the top of the View. */ void onViewTap(View view, float x, float y); } private class AnimatedZoomRunnable implements Runnable { // These are 'postScale' values, means they're compounded each iteration static final float ANIMATION_SCALE_PER_ITERATION_IN = 1.07f; static final float ANIMATION_SCALE_PER_ITERATION_OUT = 0.93f; private final float mFocalX, mFocalY; private final float mTargetZoom; private final float mDeltaScale; public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, final float focalX, final float focalY) { mTargetZoom = targetZoom; mFocalX = focalX; mFocalY = focalY; if (currentZoom < targetZoom) { mDeltaScale = ANIMATION_SCALE_PER_ITERATION_IN; } else { mDeltaScale = ANIMATION_SCALE_PER_ITERATION_OUT; } } public void run() { ImageView imageView = getImageView(); if (null != imageView) { mSuppMatrix.postScale(mDeltaScale, mDeltaScale, mFocalX, mFocalY); checkAndDisplayMatrix(); final float currentScale = getScale(); if ((mDeltaScale > 1f && currentScale < mTargetZoom) || (mDeltaScale < 1f && mTargetZoom < currentScale)) { // We haven't hit our target scale yet, so post ourselves // again Compat.postOnAnimation(imageView, this); } else { // We've scaled past our target zoom, so calculate the // necessary scale so we're back at target zoom final float delta = mTargetZoom / currentScale; mSuppMatrix.postScale(delta, delta, mFocalX, mFocalY); checkAndDisplayMatrix(); } } } } private class FlingRunnable implements Runnable { private final ScrollerProxy mScroller; private int mCurrentX, mCurrentY; public FlingRunnable(Context context) { mScroller = ScrollerProxy.getScroller(context); } public void cancelFling() { if (DEBUG) { Log.d(LOG_TAG, "Cancel Fling"); } mScroller.forceFinished(true); } public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) { final RectF rect = getDisplayRect(); if (null == rect) { return; } final int startX = Math.round(-rect.left); final int minX, maxX, minY, maxY; if (viewWidth < rect.width()) { minX = 0; maxX = Math.round(rect.width() - viewWidth); } else { minX = maxX = startX; } final int startY = Math.round(-rect.top); if (viewHeight < rect.height()) { minY = 0; maxY = Math.round(rect.height() - viewHeight); } else { minY = maxY = startY; } mCurrentX = startX; mCurrentY = startY; if (DEBUG) { Log.d(LOG_TAG, "fling. StartX:" + startX + " StartY:" + startY + " MaxX:" + maxX + " MaxY:" + maxY); } // If we actually can move, fling the scroller if (startX != maxX || startY != maxY) { mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0); } } @Override public void run() { ImageView imageView = getImageView(); if (null != imageView && mScroller.computeScrollOffset()) { final int newX = mScroller.getCurrX(); final int newY = mScroller.getCurrY(); if (DEBUG) { Log.d(LOG_TAG, "fling run(). CurrentX:" + mCurrentX + " CurrentY:" + mCurrentY + " NewX:" + newX + " NewY:" + newY); } mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY); setImageViewMatrix(getDisplayMatrix()); mCurrentX = newX; mCurrentY = newY; // Post On animation Compat.postOnAnimation(imageView, this); } } } @Override public float getMidScale() { return 0; } @Override public void setMidScale(float midScale) { } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/photoview/SDK16.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /******************************************************************************* * Copyright 2011, 2012 Chris Banes. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.htmessage.yichatopen.widget.photoview; import android.annotation.TargetApi; import android.view.View; @TargetApi(16) class SDK16 { public static void postOnAnimation(View view, Runnable r) { view.postOnAnimation(r); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/photoview/ScrollerProxy.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /******************************************************************************* * Copyright 2011, 2012 Chris Banes. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package com.htmessage.yichatopen.widget.photoview; import android.annotation.TargetApi; import android.content.Context; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.widget.OverScroller; import android.widget.Scroller; abstract class ScrollerProxy { public static ScrollerProxy getScroller(Context context) { if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) { return new PreGingerScroller(context); } else { return new GingerScroller(context); } } public abstract boolean computeScrollOffset(); public abstract void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY); public abstract void forceFinished(boolean finished); public abstract int getCurrX(); public abstract int getCurrY(); @TargetApi(9) private static class GingerScroller extends ScrollerProxy { private OverScroller mScroller; public GingerScroller(Context context) { mScroller = new OverScroller(context); } @Override public boolean computeScrollOffset() { return mScroller.computeScrollOffset(); } @Override public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY) { mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, overX, overY); } @Override public void forceFinished(boolean finished) { mScroller.forceFinished(finished); } @Override public int getCurrX() { return mScroller.getCurrX(); } @Override public int getCurrY() { return mScroller.getCurrY(); } } private static class PreGingerScroller extends ScrollerProxy { private Scroller mScroller; public PreGingerScroller(Context context) { mScroller = new Scroller(context); } @Override public boolean computeScrollOffset() { return mScroller.computeScrollOffset(); } @Override public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY, int overX, int overY) { mScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY); } @Override public void forceFinished(boolean finished) { mScroller.forceFinished(finished); } @Override public int getCurrX() { return mScroller.getCurrX(); } @Override public int getCurrY() { return mScroller.getCurrY(); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/photoview/VersionedGestureDetector.java ================================================ /** * Copyright (C) 2016 Hyphenate Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.photoview; /******************************************************************************* * Copyright 2011, 2012 Chris Banes. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ import android.annotation.TargetApi; import android.content.Context; import android.os.Build; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; import android.view.VelocityTracker; import android.view.ViewConfiguration; abstract class VersionedGestureDetector { static final String LOG_TAG = "VersionedGestureDetector"; OnGestureListener mListener; public static VersionedGestureDetector newInstance(Context context, OnGestureListener listener) { final int sdkVersion = Build.VERSION.SDK_INT; VersionedGestureDetector detector = null; if (sdkVersion < Build.VERSION_CODES.ECLAIR) { detector = new CupcakeDetector(context); } else if (sdkVersion < Build.VERSION_CODES.FROYO) { detector = new EclairDetector(context); } else { detector = new FroyoDetector(context); } detector.mListener = listener; return detector; } public abstract boolean onTouchEvent(MotionEvent ev); public abstract boolean isScaling(); public static interface OnGestureListener { public void onDrag(float dx, float dy); public void onFling(float startX, float startY, float velocityX, float velocityY); public void onScale(float scaleFactor, float focusX, float focusY); } private static class CupcakeDetector extends VersionedGestureDetector { float mLastTouchX; float mLastTouchY; final float mTouchSlop; final float mMinimumVelocity; public CupcakeDetector(Context context) { final ViewConfiguration configuration = ViewConfiguration.get(context); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mTouchSlop = configuration.getScaledTouchSlop(); } private VelocityTracker mVelocityTracker; private boolean mIsDragging; float getActiveX(MotionEvent ev) { return ev.getX(); } float getActiveY(MotionEvent ev) { return ev.getY(); } public boolean isScaling() { return false; } @Override public boolean onTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: { mVelocityTracker = VelocityTracker.obtain(); mVelocityTracker.addMovement(ev); mLastTouchX = getActiveX(ev); mLastTouchY = getActiveY(ev); mIsDragging = false; break; } case MotionEvent.ACTION_MOVE: { final float x = getActiveX(ev); final float y = getActiveY(ev); final float dx = x - mLastTouchX, dy = y - mLastTouchY; if (!mIsDragging) { // Use Pythagoras to see if drag length is larger than // touch slop mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; } if (mIsDragging) { mListener.onDrag(dx, dy); mLastTouchX = x; mLastTouchY = y; if (null != mVelocityTracker) { mVelocityTracker.addMovement(ev); } } break; } case MotionEvent.ACTION_CANCEL: { // Recycle Velocity Tracker if (null != mVelocityTracker) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; } case MotionEvent.ACTION_UP: { if (mIsDragging) { if (null != mVelocityTracker) { mLastTouchX = getActiveX(ev); mLastTouchY = getActiveY(ev); // Compute velocity within the last 1000ms mVelocityTracker.addMovement(ev); mVelocityTracker.computeCurrentVelocity(1000); final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker.getYVelocity(); // If the velocity is greater than minVelocity, call // listener if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) { mListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY); } } } // Recycle Velocity Tracker if (null != mVelocityTracker) { mVelocityTracker.recycle(); mVelocityTracker = null; } break; } } return true; } } @TargetApi(5) private static class EclairDetector extends CupcakeDetector { private static final int INVALID_POINTER_ID = -1; private int mActivePointerId = INVALID_POINTER_ID; private int mActivePointerIndex = 0; public EclairDetector(Context context) { super(context); } @Override float getActiveX(MotionEvent ev) { try { return ev.getX(mActivePointerIndex); } catch (Exception e) { return ev.getX(); } } @Override float getActiveY(MotionEvent ev) { try { return ev.getY(mActivePointerIndex); } catch (Exception e) { return ev.getY(); } } @Override public boolean onTouchEvent(MotionEvent ev) { final int action = ev.getAction(); switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: mActivePointerId = ev.getPointerId(0); break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mActivePointerId = INVALID_POINTER_ID; break; case MotionEvent.ACTION_POINTER_UP: final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; final int pointerId = ev.getPointerId(pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = ev.getPointerId(newPointerIndex); mLastTouchX = ev.getX(newPointerIndex); mLastTouchY = ev.getY(newPointerIndex); } break; } mActivePointerIndex = ev.findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0); return super.onTouchEvent(ev); } } @TargetApi(8) private static class FroyoDetector extends EclairDetector { private final ScaleGestureDetector mDetector; // Needs to be an inner class so that we don't hit // VerifyError's on API 4. private final OnScaleGestureListener mScaleListener = new OnScaleGestureListener() { @Override public boolean onScale(ScaleGestureDetector detector) { mListener.onScale(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY()); return true; } @Override public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { // NO-OP } }; public FroyoDetector(Context context) { super(context); mDetector = new ScaleGestureDetector(context, mScaleListener); } @Override public boolean isScaling() { return mDetector.isInProgress(); } @Override public boolean onTouchEvent(MotionEvent ev) { mDetector.onTouchEvent(ev); return super.onTouchEvent(ev); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/scan/CameraConfigurationManager.java ================================================ /* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.scan; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Point; import android.hardware.Camera; import android.util.Log; import android.view.Display; import android.view.WindowManager; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; /** * * 邮箱: 1076559197@qq.com | tauchen1990@gmail.com * * 作者: 陈涛 * * 日期: 2014年8月20日 * * 描述: 该类主要负责设置相机的参数信息,获取最佳的预览界面 * */ public final class CameraConfigurationManager { private static final String TAG = "CameraConfiguration"; private static final int MIN_PREVIEW_PIXELS = 480 * 320; private static final double MAX_ASPECT_DISTORTION = 0.15; private final Context context; // 屏幕分辨率 private Point screenResolution; // 相机分辨率 private Point cameraResolution; public CameraConfigurationManager(Context context) { this.context = context; } public void initFromCameraParameters(Camera camera) { Camera.Parameters parameters = camera.getParameters(); WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); Point theScreenResolution = new Point(); theScreenResolution = getDisplaySize(display); screenResolution = theScreenResolution; Log.i(TAG, "Screen resolution: " + screenResolution); /** 因为换成了竖屏显示,所以不替换屏幕宽高得出的预览图是变形的 */ Point screenResolutionForCamera = new Point(); screenResolutionForCamera.x = screenResolution.x; screenResolutionForCamera.y = screenResolution.y; if (screenResolution.x < screenResolution.y) { screenResolutionForCamera.x = screenResolution.y; screenResolutionForCamera.y = screenResolution.x; } cameraResolution = findBestPreviewSizeValue(parameters, screenResolutionForCamera); Log.i(TAG, "Camera resolution x: " + cameraResolution.x); Log.i(TAG, "Camera resolution y: " + cameraResolution.y); } @SuppressWarnings("deprecation") @SuppressLint("NewApi") private Point getDisplaySize(final Display display) { final Point point = new Point(); try { display.getSize(point); } catch (NoSuchMethodError ignore) { point.x = display.getWidth(); point.y = display.getHeight(); } return point; } public void setDesiredCameraParameters(Camera camera, boolean safeMode) { Camera.Parameters parameters = camera.getParameters(); if (parameters == null) { Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration."); return; } Log.i(TAG, "Initial camera parameters: " + parameters.flatten()); if (safeMode) { Log.w(TAG, "In camera config safe mode -- most settings will not be honored"); } parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); camera.setParameters(parameters); Camera.Parameters afterParameters = camera.getParameters(); Camera.Size afterSize = afterParameters.getPreviewSize(); if (afterSize != null && (cameraResolution.x != afterSize.width || cameraResolution.y != afterSize.height)) { Log.w(TAG, "Camera said it supported preview size " + cameraResolution.x + 'x' + cameraResolution.y + ", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height); cameraResolution.x = afterSize.width; cameraResolution.y = afterSize.height; } /** 设置相机预览为竖屏 */ camera.setDisplayOrientation(90); } public Point getCameraResolution() { return cameraResolution; } public Point getScreenResolution() { return screenResolution; } /** * 从相机支持的分辨率中计算出最适合的预览界面尺寸 * * @param parameters * @param screenResolution * @return */ private Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) { List rawSupportedSizes = parameters.getSupportedPreviewSizes(); if (rawSupportedSizes == null) { Log.w(TAG, "Device returned no supported preview sizes; using default"); Camera.Size defaultSize = parameters.getPreviewSize(); return new Point(defaultSize.width, defaultSize.height); } // Sort by size, descending List supportedPreviewSizes = new ArrayList(rawSupportedSizes); Collections.sort(supportedPreviewSizes, new Comparator() { @Override public int compare(Camera.Size a, Camera.Size b) { int aPixels = a.height * a.width; int bPixels = b.height * b.width; if (bPixels < aPixels) { return -1; } if (bPixels > aPixels) { return 1; } return 0; } }); if (Log.isLoggable(TAG, Log.INFO)) { StringBuilder previewSizesString = new StringBuilder(); for (Camera.Size supportedPreviewSize : supportedPreviewSizes) { previewSizesString.append(supportedPreviewSize.width).append('x').append(supportedPreviewSize.height).append(' '); } Log.i(TAG, "Supported preview sizes: " + previewSizesString); } double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y; // Remove sizes that are unsuitable Iterator it = supportedPreviewSizes.iterator(); while (it.hasNext()) { Camera.Size supportedPreviewSize = it.next(); int realWidth = supportedPreviewSize.width; int realHeight = supportedPreviewSize.height; if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { it.remove(); continue; } boolean isCandidatePortrait = realWidth < realHeight; int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; double distortion = Math.abs(aspectRatio - screenAspectRatio); if (distortion > MAX_ASPECT_DISTORTION) { it.remove(); continue; } if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) { Point exactPoint = new Point(realWidth, realHeight); Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint); return exactPoint; } } // If no exact match, use largest preview size. This was not a great // idea on older devices because // of the additional computation needed. We're likely to get here on // newer Android 4+ devices, where // the CPU is much more powerful. if (!supportedPreviewSizes.isEmpty()) { Camera.Size largestPreview = supportedPreviewSizes.get(0); Point largestSize = new Point(largestPreview.width, largestPreview.height); Log.i(TAG, "Using largest suitable preview size: " + largestSize); return largestSize; } // If there is nothing at all suitable, return current preview size Camera.Size defaultPreview = parameters.getPreviewSize(); Point defaultSize = new Point(defaultPreview.width, defaultPreview.height); Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize); return defaultSize; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/scan/CameraManager.java ================================================ /* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.scan; import android.content.Context; import android.graphics.Point; import android.hardware.Camera; import android.util.Log; import java.io.IOException; /** * * 邮箱: 1076559197@qq.com | tauchen1990@gmail.com * * 作者: 陈涛 * * 日期: 2014年8月20日 * * 描述: 该类主要负责对相机的操作 * */ public final class CameraManager { private static final String TAG = CameraManager.class.getSimpleName(); private final CameraConfigurationManager configManager; private Camera camera; private boolean initialized; public CameraManager(Context context) { this.configManager = new CameraConfigurationManager(context); } /** * Opens the camera driver and initializes the hardware parameters. * * @param * The surface object which the camera will draw preview frames * into. * @throws IOException * Indicates the camera driver failed to open. */ public synchronized void openDriver() throws IOException { Camera theCamera = camera; if (theCamera == null) { theCamera = Camera.open(); if (theCamera == null) { throw new IOException(); } camera = theCamera; } if (!initialized) { initialized = true; configManager.initFromCameraParameters(theCamera); } Camera.Parameters parameters = theCamera.getParameters(); String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save // these, // temporarily try { configManager.setDesiredCameraParameters(theCamera, false); } catch (RuntimeException re) { // Driver failed Log.w(TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters"); Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened); // Reset: if (parametersFlattened != null) { parameters = theCamera.getParameters(); parameters.unflatten(parametersFlattened); try { theCamera.setParameters(parameters); configManager.setDesiredCameraParameters(theCamera, true); } catch (RuntimeException re2) { // Well, darn. Give up Log.w(TAG, "Camera rejected even safe-mode parameters! No configuration"); } } } } public synchronized boolean isOpen() { return camera != null; } public Camera getCamera(){ return camera; } /** * Closes the camera driver if still in use. */ public synchronized void closeDriver() { if (camera != null) { camera.release(); camera = null; } } /** * 获取相机分辨率 * * @return */ public Point getCameraResolution() { return configManager.getCameraResolution(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/scan/CameraPreview.java ================================================ /* * Barebones implementation of displaying camera preview. * * Created by lisah0 on 2012-02-24 */ package com.htmessage.yichatopen.widget.scan; import android.content.Context; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.PreviewCallback; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.io.IOException; /** A basic Camera preview class */ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; private PreviewCallback previewCallback; private AutoFocusCallback autoFocusCallback; @SuppressWarnings("deprecation") public CameraPreview(Context context, Camera camera, PreviewCallback previewCb, AutoFocusCallback autoFocusCb) { super(context); mCamera = camera; previewCallback = previewCb; autoFocusCallback = autoFocusCb; /* * Set camera to continuous focus if supported, otherwise use software * auto-focus. Only works for API level >=9. */ /* * Camera.Parameters parameters = camera.getParameters(); for (String f * : parameters.getSupportedFocusModes()) { if (f == * Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) { * mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); * autoFocusCallback = null; break; } } */ // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed. mHolder = getHolder(); mHolder.addCallback(this); // deprecated setting, but required on Android versions prior to 3.0 mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { // The Surface has been created, now tell the camera where to draw the // preview. try { mCamera.setPreviewDisplay(holder); } catch (IOException e) { Log.d("DBG", "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { // Camera preview released in activity } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { /* * If your preview can change or rotate, take care of those events here. * Make sure to stop the preview before resizing or reformatting it. */ if (mHolder.getSurface() == null) { // preview surface does not exist return; } // stop preview before making changes try { mCamera.stopPreview(); } catch (Exception e) { // ignore: tried to stop a non-existent preview } try { // Hard code camera surface rotation 90 degs to match Activity view // in portrait mCamera.setDisplayOrientation(90); mCamera.setPreviewDisplay(mHolder); mCamera.setPreviewCallback(previewCallback); mCamera.startPreview(); mCamera.autoFocus(autoFocusCallback); } catch (Exception e) { Log.d("DBG", "Error starting camera preview: " + e.getMessage()); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/swipyrefresh/CircleImageView.java ================================================ package com.htmessage.yichatopen.widget.swipyrefresh; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RadialGradient; import android.graphics.Shader; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.support.v4.view.ViewCompat; import android.view.animation.Animation; import android.widget.ImageView; /** * Private class created to work around issues with AnimationListeners being * called before the animation is actually complete and support shadows on older * platforms. * * @hide */ class CircleImageView extends ImageView { private static final int KEY_SHADOW_COLOR = 0x1E000000; private static final int FILL_SHADOW_COLOR = 0x3D000000; // PX private static final float X_OFFSET = 0f; private static final float Y_OFFSET = 1.75f; private static final float SHADOW_RADIUS = 3.5f; private static final int SHADOW_ELEVATION = 4; private Animation.AnimationListener mListener; private int mShadowRadius; public CircleImageView(Context context, int color, final float radius) { super(context); final float density = getContext().getResources().getDisplayMetrics().density; final int diameter = (int) (radius * density * 2); final int shadowYOffset = (int) (density * Y_OFFSET); final int shadowXOffset = (int) (density * X_OFFSET); mShadowRadius = (int) (density * SHADOW_RADIUS); ShapeDrawable circle; if (elevationSupported()) { circle = new ShapeDrawable(new OvalShape()); ViewCompat.setElevation(this, SHADOW_ELEVATION * density); } else { OvalShape oval = new OvalShadow(mShadowRadius, diameter); circle = new ShapeDrawable(oval); ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint()); circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR); final int padding = (int) mShadowRadius; // set padding so the inner image sits correctly within the shadow. setPadding(padding, padding, padding, padding); } circle.getPaint().setColor(color); setBackgroundDrawable(circle); } private boolean elevationSupported() { return android.os.Build.VERSION.SDK_INT >= 21; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (!elevationSupported()) { setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight() + mShadowRadius * 2); } } public void setAnimationListener(Animation.AnimationListener listener) { mListener = listener; } @Override public void onAnimationStart() { super.onAnimationStart(); if (mListener != null) { mListener.onAnimationStart(getAnimation()); } } @Override public void onAnimationEnd() { super.onAnimationEnd(); if (mListener != null) { mListener.onAnimationEnd(getAnimation()); } } /** * Update the background color of the circle image view. */ @SuppressWarnings("ResourceType") public void setBackgroundColor(int colorRes) { if (getBackground() instanceof ShapeDrawable) { final Resources res = getResources(); ((ShapeDrawable) getBackground()).getPaint().setColor( res.getColor(colorRes)); } } private class OvalShadow extends OvalShape { private RadialGradient mRadialGradient; private int mShadowRadius; private Paint mShadowPaint; private int mCircleDiameter; public OvalShadow(int shadowRadius, int circleDiameter) { super(); mShadowPaint = new Paint(); mShadowRadius = shadowRadius; mCircleDiameter = circleDiameter; mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2, mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT }, null, Shader.TileMode.CLAMP); mShadowPaint.setShader(mRadialGradient); } @Override public void draw(Canvas canvas, Paint paint) { final int viewWidth = CircleImageView.this.getWidth(); final int viewHeight = CircleImageView.this.getHeight(); canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius), mShadowPaint); canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/swipyrefresh/MaterialProgressDrawable.java ================================================ package com.htmessage.yichatopen.widget.swipyrefresh; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Animatable; import android.graphics.drawable.Drawable; import android.support.annotation.IntDef; import android.support.annotation.NonNull; import android.util.DisplayMetrics; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Animation; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.Transformation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; /** * Fancy progress indicator for Material theme. * * @hide */ class MaterialProgressDrawable extends Drawable implements Animatable { private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); private static final Interpolator END_CURVE_INTERPOLATOR = new EndCurveInterpolator(); private static final Interpolator START_CURVE_INTERPOLATOR = new StartCurveInterpolator(); private static final Interpolator EASE_INTERPOLATOR = new AccelerateDecelerateInterpolator(); @Retention(RetentionPolicy.CLASS) @IntDef({LARGE, DEFAULT}) public @interface ProgressDrawableSize {} // Maps to ProgressBar.Large style static final int LARGE = 0; // Maps to ProgressBar default style static final int DEFAULT = 1; // Maps to ProgressBar default style private static final int CIRCLE_DIAMETER = 40; private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width private static final float STROKE_WIDTH = 2.5f; // Maps to ProgressBar.Large style private static final int CIRCLE_DIAMETER_LARGE = 56; private static final float CENTER_RADIUS_LARGE = 12.5f; private static final float STROKE_WIDTH_LARGE = 3f; private final int[] COLORS = new int[] { Color.BLACK }; /** The duration of a single progress spin in milliseconds. */ private static final int ANIMATION_DURATION = 1000 * 80 / 60; /** The number of points in the progress "star". */ private static final float NUM_POINTS = 5f; /** The list of animators operating on this drawable. */ private final ArrayList mAnimators = new ArrayList(); /** The indicator ring, used to manage animation state. */ private final Ring mRing; /** Canvas rotation in degrees. */ private float mRotation; /** Layout info for the arrowhead in dp */ private static final int ARROW_WIDTH = 10; private static final int ARROW_HEIGHT = 5; private static final float ARROW_OFFSET_ANGLE = 5; /** Layout info for the arrowhead for the large spinner in dp */ private static final int ARROW_WIDTH_LARGE = 12; private static final int ARROW_HEIGHT_LARGE = 6; private static final float MAX_PROGRESS_ARC = .8f; private Resources mResources; private View mParent; private Animation mAnimation; private float mRotationCount; private double mWidth; private double mHeight; private Animation mFinishAnimation; public MaterialProgressDrawable(Context context, View parent) { mParent = parent; mResources = context.getResources(); mRing = new Ring(mCallback); mRing.setColors(COLORS); updateSizes(DEFAULT); setupAnimators(); } private void setSizeParameters(double progressCircleWidth, double progressCircleHeight, double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) { final Ring ring = mRing; final DisplayMetrics metrics = mResources.getDisplayMetrics(); final float screenDensity = metrics.density; mWidth = progressCircleWidth * screenDensity; mHeight = progressCircleHeight * screenDensity; ring.setStrokeWidth((float) strokeWidth * screenDensity); ring.setCenterRadius(centerRadius * screenDensity); ring.setColorIndex(0); ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity); ring.setInsets((int) mWidth, (int) mHeight); } /** * Set the overall size for the progress spinner. This updates the radius * and stroke width of the ring. * * @param size One of {@link com.orangegangsters.github.swiperefreshlayout.MaterialProgressDrawable.LARGE} or * {@link com.orangegangsters.github.swiperefreshlayout.MaterialProgressDrawable.DEFAULT} */ public void updateSizes(@ProgressDrawableSize int size) { if (size == LARGE) { setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE, STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE); } else { setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH, ARROW_WIDTH, ARROW_HEIGHT); } } /** * @param show Set to true to display the arrowhead on the progress spinner. */ public void showArrow(boolean show) { mRing.setShowArrow(show); } /** * @param scale Set the scale of the arrowhead for the spinner. */ public void setArrowScale(float scale) { mRing.setArrowScale(scale); } /** * Set the start and end trim for the progress spinner arc. * * @param startAngle start angle * @param endAngle end angle */ public void setStartEndTrim(float startAngle, float endAngle) { mRing.setStartTrim(startAngle); mRing.setEndTrim(endAngle); } /** * Set the amount of rotation to apply to the progress spinner. * * @param rotation Rotation is from [0..1] */ public void setProgressRotation(float rotation) { mRing.setRotation(rotation); } /** * Update the background color of the circle image view. */ public void setBackgroundColor(int color) { mRing.setBackgroundColor(color); } /** * Set the colors used in the progress animation from color resources. * The first color will also be the color of the bar that grows in response * to a user swipe gesture. * * @param colors */ public void setColorSchemeColors(int... colors) { mRing.setColors(colors); mRing.setColorIndex(0); } @Override public int getIntrinsicHeight() { return (int) mHeight; } @Override public int getIntrinsicWidth() { return (int) mWidth; } @Override public void draw(Canvas c) { final Rect bounds = getBounds(); final int saveCount = c.save(); c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY()); mRing.draw(c, bounds); c.restoreToCount(saveCount); } @Override public void setAlpha(int alpha) { mRing.setAlpha(alpha); } public int getAlpha() { return mRing.getAlpha(); } @Override public void setColorFilter(ColorFilter colorFilter) { mRing.setColorFilter(colorFilter); } @SuppressWarnings("unused") void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @SuppressWarnings("unused") private float getRotation() { return mRotation; } @Override public int getOpacity() { return PixelFormat.TRANSLUCENT; } @Override public boolean isRunning() { final ArrayList animators = mAnimators; final int N = animators.size(); for (int i = 0; i < N; i++) { final Animation animator = animators.get(i); if (animator.hasStarted() && !animator.hasEnded()) { return true; } } return false; } @Override public void start() { mAnimation.reset(); mRing.storeOriginals(); // Already showing some part of the ring if (mRing.getEndTrim() != mRing.getStartTrim()) { mParent.startAnimation(mFinishAnimation); } else { mRing.setColorIndex(0); mRing.resetOriginals(); mParent.startAnimation(mAnimation); } } @Override public void stop() { mParent.clearAnimation(); setRotation(0); mRing.setShowArrow(false); mRing.setColorIndex(0); mRing.resetOriginals(); } private void setupAnimators() { final Ring ring = mRing; final Animation finishRingAnimation = new Animation() { public void applyTransformation(float interpolatedTime, Transformation t) { // shrink back down and complete a full rotation before starting other circles // Rotation goes between [0..1]. float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC) + 1f); final float startTrim = ring.getStartingStartTrim() + (ring.getStartingEndTrim() - ring.getStartingStartTrim()) * interpolatedTime; ring.setStartTrim(startTrim); final float rotation = ring.getStartingRotation() + ((targetRotation - ring.getStartingRotation()) * interpolatedTime); ring.setRotation(rotation); ring.setArrowScale(1 - interpolatedTime); } }; finishRingAnimation.setInterpolator(EASE_INTERPOLATOR); finishRingAnimation.setDuration(ANIMATION_DURATION/2); finishRingAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { ring.goToNextColor(); ring.storeOriginals(); ring.setShowArrow(false); mParent.startAnimation(mAnimation); } @Override public void onAnimationRepeat(Animation animation) { } }); final Animation animation = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { // The minProgressArc is calculated from 0 to create an angle that // matches the stroke width. final float minProgressArc = (float) Math.toRadians(ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius())); final float startingEndTrim = ring.getStartingEndTrim(); final float startingTrim = ring.getStartingStartTrim(); final float startingRotation = ring.getStartingRotation(); // Offset the minProgressArc to where the endTrim is located. final float minArc = MAX_PROGRESS_ARC - minProgressArc; final float endTrim = startingEndTrim + (minArc * START_CURVE_INTERPOLATOR.getInterpolation(interpolatedTime)); ring.setEndTrim(endTrim); final float startTrim = startingTrim + (MAX_PROGRESS_ARC * END_CURVE_INTERPOLATOR .getInterpolation(interpolatedTime)); ring.setStartTrim(startTrim); final float rotation = startingRotation + (0.25f * interpolatedTime); ring.setRotation(rotation); float groupRotation = ((720.0f / NUM_POINTS) * interpolatedTime) + (720.0f * (mRotationCount / NUM_POINTS)); setRotation(groupRotation); } }; animation.setRepeatCount(Animation.INFINITE); animation.setRepeatMode(Animation.RESTART); animation.setInterpolator(LINEAR_INTERPOLATOR); animation.setDuration(ANIMATION_DURATION); animation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mRotationCount = 0; } @Override public void onAnimationEnd(Animation animation) { // do nothing } @Override public void onAnimationRepeat(Animation animation) { ring.storeOriginals(); ring.goToNextColor(); ring.setStartTrim(ring.getEndTrim()); mRotationCount = (mRotationCount + 1) % (NUM_POINTS); } }); mFinishAnimation = finishRingAnimation; mAnimation = animation; } private final Callback mCallback = new Callback() { @Override public void invalidateDrawable(Drawable d) { invalidateSelf(); } @Override public void scheduleDrawable(Drawable d, Runnable what, long when) { scheduleSelf(what, when); } @Override public void unscheduleDrawable(Drawable d, Runnable what) { unscheduleSelf(what); } }; private static class Ring { private final RectF mTempBounds = new RectF(); private final Paint mPaint = new Paint(); private final Paint mArrowPaint = new Paint(); private final Callback mCallback; private float mStartTrim = 0.0f; private float mEndTrim = 0.0f; private float mRotation = 0.0f; private float mStrokeWidth = 5.0f; private float mStrokeInset = 2.5f; private int[] mColors; // mColorIndex represents the offset into the available mColors that the // progress circle should currently display. As the progress circle is // animating, the mColorIndex moves by one to the next available color. private int mColorIndex; private float mStartingStartTrim; private float mStartingEndTrim; private float mStartingRotation; private boolean mShowArrow; private Path mArrow; private float mArrowScale; private double mRingCenterRadius; private int mArrowWidth; private int mArrowHeight; private int mAlpha; private final Paint mCirclePaint = new Paint(); private int mBackgroundColor; public Ring(Callback callback) { mCallback = callback; mPaint.setStrokeCap(Paint.Cap.SQUARE); mPaint.setAntiAlias(true); mPaint.setStyle(Style.STROKE); mArrowPaint.setStyle(Style.FILL); mArrowPaint.setAntiAlias(true); } public void setBackgroundColor(int color) { mBackgroundColor = color; } /** * Set the dimensions of the arrowhead. * * @param width Width of the hypotenuse of the arrow head * @param height Height of the arrow point */ public void setArrowDimensions(float width, float height) { mArrowWidth = (int) width; mArrowHeight = (int) height; } /** * Draw the progress spinner */ public void draw(Canvas c, Rect bounds) { final RectF arcBounds = mTempBounds; arcBounds.set(bounds); arcBounds.inset(mStrokeInset, mStrokeInset); final float startAngle = (mStartTrim + mRotation) * 360; final float endAngle = (mEndTrim + mRotation) * 360; float sweepAngle = endAngle - startAngle; mPaint.setColor(mColors[mColorIndex]); c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint); drawTriangle(c, startAngle, sweepAngle, bounds); if (mAlpha < 255) { mCirclePaint.setColor(mBackgroundColor); mCirclePaint.setAlpha(255 - mAlpha); c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2, mCirclePaint); } } private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) { if (mShowArrow) { if (mArrow == null) { mArrow = new Path(); mArrow.setFillType(Path.FillType.EVEN_ODD); } else { mArrow.reset(); } // Adjust the position of the triangle so that it is inset as // much as the arc, but also centered on the arc. float inset = (int) mStrokeInset / 2 * mArrowScale; float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX()); float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY()); // Update the path each time. This works around an issue in SKIA // where concatenating a rotation matrix to a scale matrix // ignored a starting negative rotation. This appears to have // been fixed as of API 21. mArrow.moveTo(0, 0); mArrow.lineTo(mArrowWidth * mArrowScale, 0); mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight * mArrowScale)); mArrow.offset(x - inset, y); mArrow.close(); // draw a triangle mArrowPaint.setColor(mColors[mColorIndex]); c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(), bounds.exactCenterY()); c.drawPath(mArrow, mArrowPaint); } } /** * Set the colors the progress spinner alternates between. * * @param colors Array of integers describing the colors. Must be non-null. */ public void setColors(@NonNull int[] colors) { mColors = colors; // if colors are reset, make sure to reset the color index as well setColorIndex(0); } /** * @param index Index into the color array of the color to display in * the progress spinner. */ public void setColorIndex(int index) { mColorIndex = index; } /** * Proceed to the next available ring color. This will automatically * wrap back to the beginning of colors. */ public void goToNextColor() { mColorIndex = (mColorIndex + 1) % (mColors.length); } public void setColorFilter(ColorFilter filter) { mPaint.setColorFilter(filter); invalidateSelf(); } /** * @param alpha Set the alpha of the progress spinner and associated arrowhead. */ public void setAlpha(int alpha) { mAlpha = alpha; } /** * @return Current alpha of the progress spinner and arrowhead. */ public int getAlpha() { return mAlpha; } /** * @param strokeWidth Set the stroke width of the progress spinner in pixels. */ public void setStrokeWidth(float strokeWidth) { mStrokeWidth = strokeWidth; mPaint.setStrokeWidth(strokeWidth); invalidateSelf(); } @SuppressWarnings("unused") public float getStrokeWidth() { return mStrokeWidth; } @SuppressWarnings("unused") public void setStartTrim(float startTrim) { mStartTrim = startTrim; invalidateSelf(); } @SuppressWarnings("unused") public float getStartTrim() { return mStartTrim; } public float getStartingStartTrim() { return mStartingStartTrim; } public float getStartingEndTrim() { return mStartingEndTrim; } @SuppressWarnings("unused") public void setEndTrim(float endTrim) { mEndTrim = endTrim; invalidateSelf(); } @SuppressWarnings("unused") public float getEndTrim() { return mEndTrim; } @SuppressWarnings("unused") public void setRotation(float rotation) { mRotation = rotation; invalidateSelf(); } @SuppressWarnings("unused") public float getRotation() { return mRotation; } public void setInsets(int width, int height) { final float minEdge = (float) Math.min(width, height); float insets; if (mRingCenterRadius <= 0 || minEdge < 0) { insets = (float) Math.ceil(mStrokeWidth / 2.0f); } else { insets = (float) (minEdge / 2.0f - mRingCenterRadius); } mStrokeInset = insets; } @SuppressWarnings("unused") public float getInsets() { return mStrokeInset; } /** * @param centerRadius Inner radius in px of the circle the progress * spinner arc traces. */ public void setCenterRadius(double centerRadius) { mRingCenterRadius = centerRadius; } public double getCenterRadius() { return mRingCenterRadius; } /** * @param show Set to true to show the arrow head on the progress spinner. */ public void setShowArrow(boolean show) { if (mShowArrow != show) { mShowArrow = show; invalidateSelf(); } } /** * @param scale Set the scale of the arrowhead for the spinner. */ public void setArrowScale(float scale) { if (scale != mArrowScale) { mArrowScale = scale; invalidateSelf(); } } /** * @return The amount the progress spinner is currently rotated, between [0..1]. */ public float getStartingRotation() { return mStartingRotation; } /** * If the start / end trim are offset to begin with, store them so that * animation starts from that offset. */ public void storeOriginals() { mStartingStartTrim = mStartTrim; mStartingEndTrim = mEndTrim; mStartingRotation = mRotation; } /** * Reset the progress spinner to default rotation, start and end angles. */ public void resetOriginals() { mStartingStartTrim = 0; mStartingEndTrim = 0; mStartingRotation = 0; setStartTrim(0); setEndTrim(0); setRotation(0); } private void invalidateSelf() { mCallback.invalidateDrawable(null); } } /** * Squishes the interpolation curve into the second half of the animation. */ private static class EndCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.max(0, (input - 0.5f) * 2.0f)); } } /** * Squishes the interpolation curve into the first half of the animation. */ private static class StartCurveInterpolator extends AccelerateDecelerateInterpolator { @Override public float getInterpolation(float input) { return super.getInterpolation(Math.min(1, input * 2.0f)); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/swipyrefresh/SwipyRefreshLayout.java ================================================ package com.htmessage.yichatopen.widget.swipyrefresh; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Build; import android.support.v4.view.MotionEventCompat; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; import android.widget.AbsListView; import com.htmessage.yichatopen.R; /** * The SwipeRefreshLayout should be used whenever the user can refresh the * contents of a view via a vertical swipe gesture. The activity that * instantiates this view should add an OnRefreshListener to be notified * whenever the swipe to refresh gesture is completed. The SwipeRefreshLayout * will notify the listener each and every time the gesture is completed again; * the listener is responsible for correctly determining when to actually * initiate a refresh of its content. If the listener determines there should * not be a refresh, it must call setRefreshing(false) to cancel any visual * indication of a refresh. If an activity wishes to show just the progress * animation, it should call setRefreshing(true). To disable the gesture and * progress animation, call setEnabled(false) on the view. *

* This layout should be made the parent of the view that will be refreshed as a * result of the gesture and can only support one direct child. This view will * also be made the target of the gesture and will be forced to match both the * width and the height supplied in this layout. The SwipeRefreshLayout does not * provide accessibility events; instead, a menu item must be provided to allow * refresh of the content wherever this gesture is used. *

*/ /** * 下拉刷新、加载更多、分页索引 * @author xutao * */ public class SwipyRefreshLayout extends ViewGroup { /** 是不是下拉 **/ public boolean isTop; /** 第一页 **/ public int firstIndex = 0; /** 页数索引 **/ public int index = firstIndex; // Maps to ProgressBar.Large style public static final int LARGE = MaterialProgressDrawable.LARGE; // Maps to ProgressBar default style public static final int DEFAULT = MaterialProgressDrawable.DEFAULT; private static final String LOG_TAG = SwipyRefreshLayout.class.getSimpleName(); private static final int MAX_ALPHA = 255; private static final int STARTING_PROGRESS_ALPHA = (int) (.3f * MAX_ALPHA); private static final int CIRCLE_DIAMETER = 40; private static final int CIRCLE_DIAMETER_LARGE = 56; private static final float DECELERATE_INTERPOLATION_FACTOR = 2f; private static final int INVALID_POINTER = -1; private static final float DRAG_RATE = .5f; // Max amount of circle that can be filled by progress during swipe gesture, // where 1.0 is a full circle private static final float MAX_PROGRESS_ANGLE = .8f; private static final int SCALE_DOWN_DURATION = 150; private static final int ALPHA_ANIMATION_DURATION = 300; private static final int ANIMATE_TO_TRIGGER_DURATION = 200; private static final int ANIMATE_TO_START_DURATION = 200; // Default background for the progress spinner private static final int CIRCLE_BG_LIGHT = 0xFFFAFAFA; // Default offset in dips from the top of the view to where the progress spinner should stop private static final int DEFAULT_CIRCLE_TARGET = 64; private View mTarget; // the target of the gesture private SwipyRefreshLayoutDirection mDirection; private boolean mBothDirection; private OnRefreshListener mListener; private boolean mRefreshing = false; private int mTouchSlop; private float mTotalDragDistance = -1; private int mMediumAnimationDuration; private int mCurrentTargetOffsetTop; // Whether or not the starting offset has been determined. private boolean mOriginalOffsetCalculated = false; private float mInitialMotionY; private boolean mIsBeingDragged; private int mActivePointerId = INVALID_POINTER; // Whether this item is scaled up rather than clipped private boolean mScale; // Target is returning to its start offset because it was cancelled or a // refresh was triggered. private boolean mReturningToStart; private final DecelerateInterpolator mDecelerateInterpolator; private static final int[] LAYOUT_ATTRS = new int[]{ android.R.attr.enabled }; private CircleImageView mCircleView; private int mCircleViewIndex = -1; protected int mFrom; private float mStartingScale; protected int mOriginalOffsetTop; private MaterialProgressDrawable mProgress; private Animation mScaleAnimation; private Animation mScaleDownAnimation; private Animation mAlphaStartAnimation; private Animation mAlphaMaxAnimation; private Animation mScaleDownToStartAnimation; private float mSpinnerFinalOffset; private boolean mNotify; private int mCircleWidth; private int mCircleHeight; // Whether the client has set a custom starting position; private boolean mUsingCustomStart; private AnimationListener mRefreshListener = new AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (mRefreshing) { // Make sure the progress view is fully visible mProgress.setAlpha(MAX_ALPHA); mProgress.start(); if (mNotify) { if (mListener != null) { if (isTop) { index = firstIndex; mListener.onRefresh(index); }else { index ++; mListener.onLoad(index); } } } } else { mProgress.stop(); mCircleView.setVisibility(View.GONE); setColorViewAlpha(MAX_ALPHA); // Return the circle to its start position if (mScale) { setAnimationProgress(0 /* animation complete and view is hidden */); } else { setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop, true /* requires update */); } } mCurrentTargetOffsetTop = mCircleView.getTop(); } }; private void setColorViewAlpha(int targetAlpha) { mCircleView.getBackground().setAlpha(targetAlpha); mProgress.setAlpha(targetAlpha); } /** * The refresh indicator starting and resting position is always positioned * near the top of the refreshing content. This position is a consistent * location, but can be adjusted in either direction based on whether or not * there is a toolbar or actionbar present. * * @param scale Set to true if there is no view at a higher z-order than * where the progress spinner is set to appear. * @param start The offset in pixels from the top of this view at which the * progress spinner should appear. * @param end The offset in pixels from the top of this view at which the * progress spinner should come to rest after a successful swipe * gesture. */ /* public void setProgressViewOffset(boolean scale, int start, int end) { mScale = scale; mCircleView.setVisibility(View.GONE); mOriginalOffsetTop = mCurrentTargetOffsetTop = start; mSpinnerFinalOffset = end; mUsingCustomStart = true; mCircleView.invalidate(); }*/ /** * The refresh indicator resting position is always positioned near the top * of the refreshing content. This position is a consistent location, but * can be adjusted in either direction based on whether or not there is a * toolbar or actionbar present. * * @param scale Set to true if there is no view at a higher z-order than * where the progress spinner is set to appear. * @param end The offset in pixels from the top of this view at which the * progress spinner should come to rest after a successful swipe * gesture. */ /* public void setProgressViewEndTarget(boolean scale, int end) { mSpinnerFinalOffset = end; mScale = scale; mCircleView.invalidate(); }*/ /** * One of DEFAULT, or LARGE. */ public void setSize(int size) { if (size != MaterialProgressDrawable.LARGE && size != MaterialProgressDrawable.DEFAULT) { return; } final DisplayMetrics metrics = getResources().getDisplayMetrics(); if (size == MaterialProgressDrawable.LARGE) { mCircleHeight = mCircleWidth = (int) (CIRCLE_DIAMETER_LARGE * metrics.density); } else { mCircleHeight = mCircleWidth = (int) (CIRCLE_DIAMETER * metrics.density); } // force the bounds of the progress circle inside the circle view to // update by setting it to null before updating its size and then // re-setting it mCircleView.setImageDrawable(null); mProgress.updateSizes(size); mCircleView.setImageDrawable(mProgress); } /** * Simple constructor to use when creating a SwipeRefreshLayout from code. * * @param context */ public SwipyRefreshLayout(Context context) { this(context, null); } /** * Constructor that is called when inflating SwipeRefreshLayout from XML. * * @param context * @param attrs */ @TargetApi(Build.VERSION_CODES.CUPCAKE) public SwipyRefreshLayout(Context context, AttributeSet attrs) { super(context, attrs); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mMediumAnimationDuration = getResources().getInteger( android.R.integer.config_mediumAnimTime); setWillNotDraw(false); mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS); setEnabled(a.getBoolean(0, true)); a.recycle(); final TypedArray a2 = context.obtainStyledAttributes(attrs, R.styleable.SwipyRefreshLayout); SwipyRefreshLayoutDirection direction = SwipyRefreshLayoutDirection.getFromInt(a2.getInt(R.styleable.SwipyRefreshLayout_direction, 0)); if (direction != SwipyRefreshLayoutDirection.BOTH) { mDirection = direction; mBothDirection = false; } else { mDirection = SwipyRefreshLayoutDirection.TOP; mBothDirection = true; } a2.recycle(); final DisplayMetrics metrics = getResources().getDisplayMetrics(); mCircleWidth = (int) (CIRCLE_DIAMETER * metrics.density); mCircleHeight = (int) (CIRCLE_DIAMETER * metrics.density); createProgressView(); ViewCompat.setChildrenDrawingOrderEnabled(this, true); // the absolute offset has to take into account that the circle starts at an offset mSpinnerFinalOffset = DEFAULT_CIRCLE_TARGET * metrics.density; mTotalDragDistance = mSpinnerFinalOffset; //设置刷新动画颜色 setColorSchemeResources(android.R.color.holo_blue_light, android.R.color.holo_red_light, android.R.color.holo_orange_light, android.R.color.holo_green_light); } protected int getChildDrawingOrder(int childCount, int i) { if (mCircleViewIndex < 0) { return i; } else if (i == childCount - 1) { // Draw the selected child last return mCircleViewIndex; } else if (i >= mCircleViewIndex) { // Move the children after the selected child earlier one return i + 1; } else { // Keep the children before the selected child the same return i; } } private void createProgressView() { mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT, CIRCLE_DIAMETER / 2); mProgress = new MaterialProgressDrawable(getContext(), this); mProgress.setBackgroundColor(CIRCLE_BG_LIGHT); mCircleView.setImageDrawable(mProgress); mCircleView.setVisibility(View.GONE); addView(mCircleView); } /** * Set the listener to be notified when a refresh is triggered via the swipe * gesture. */ public void setOnRefreshListener(OnRefreshListener listener) { mListener = listener; } /** * Pre API 11, alpha is used to make the progress circle appear instead of scale. */ private boolean isAlphaUsedForScale() { return Build.VERSION.SDK_INT < 11; } /** * Notify the widget that refresh state has changed. Do not call this when * refresh is triggered by a swipe gesture. * * @param refreshing Whether or not the view should show refresh progress. */ public void setRefreshing(boolean refreshing) { if (refreshing && mRefreshing != refreshing) { // scale and show mRefreshing = refreshing; int endTarget = 0; if (!mUsingCustomStart) { endTarget = (int) (mSpinnerFinalOffset + mOriginalOffsetTop); } else { endTarget = (int) mSpinnerFinalOffset; } setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop, true /* requires update */); mNotify = false; startScaleUpAnimation(mRefreshListener); } else { setRefreshing(refreshing, false /* notify */); } } private void startScaleUpAnimation(AnimationListener listener) { mCircleView.setVisibility(View.VISIBLE); if (Build.VERSION.SDK_INT >= 11) { // Pre API 11, alpha is used in place of scale up to show the // progress circle appearing. // Don't adjust the alpha during appearance otherwise. mProgress.setAlpha(MAX_ALPHA); } mScaleAnimation = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { setAnimationProgress(interpolatedTime); } }; mScaleAnimation.setDuration(mMediumAnimationDuration); if (listener != null) { mCircleView.setAnimationListener(listener); } mCircleView.clearAnimation(); mCircleView.startAnimation(mScaleAnimation); } /** * Pre API 11, this does an alpha animation. * * @param progress */ private void setAnimationProgress(float progress) { if (isAlphaUsedForScale()) { setColorViewAlpha((int) (progress * MAX_ALPHA)); } else { ViewCompat.setScaleX(mCircleView, progress); ViewCompat.setScaleY(mCircleView, progress); } } private void setRefreshing(boolean refreshing, final boolean notify) { if (mRefreshing != refreshing) { mNotify = notify; ensureTarget(); mRefreshing = refreshing; if (mRefreshing) { animateOffsetToCorrectPosition(mCurrentTargetOffsetTop, mRefreshListener); } else { startScaleDownAnimation(mRefreshListener); } } } private void startScaleDownAnimation(AnimationListener listener) { mScaleDownAnimation = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { setAnimationProgress(1 - interpolatedTime); } }; mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION); mCircleView.setAnimationListener(listener); mCircleView.clearAnimation(); mCircleView.startAnimation(mScaleDownAnimation); } @SuppressLint("NewApi") private void startProgressAlphaStartAnimation() { mAlphaStartAnimation = startAlphaAnimation(mProgress.getAlpha(), STARTING_PROGRESS_ALPHA); } @SuppressLint("NewApi") private void startProgressAlphaMaxAnimation() { mAlphaMaxAnimation = startAlphaAnimation(mProgress.getAlpha(), MAX_ALPHA); } private Animation startAlphaAnimation(final int startingAlpha, final int endingAlpha) { // Pre API 11, alpha is used in place of scale. Don't also use it to // show the trigger point. if (mScale && isAlphaUsedForScale()) { return null; } Animation alpha = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { mProgress .setAlpha((int) (startingAlpha + ((endingAlpha - startingAlpha) * interpolatedTime))); } }; alpha.setDuration(ALPHA_ANIMATION_DURATION); // Clear out the previous animation listeners. mCircleView.setAnimationListener(null); mCircleView.clearAnimation(); mCircleView.startAnimation(alpha); return alpha; } /** * Set the background color of the progress spinner disc. * * @param colorRes Resource id of the color. */ public void setProgressBackgroundColor(int colorRes) { mCircleView.setBackgroundColor(colorRes); mProgress.setBackgroundColor(getResources().getColor(colorRes)); } /** * @deprecated Use {@link #setColorSchemeResources(int...)} */ @Deprecated public void setColorScheme(int... colors) { setColorSchemeResources(colors); } /** * Set the color resources used in the progress animation from color resources. * The first color will also be the color of the bar that grows in response * to a user swipe gesture. * * @param colorResIds */ public void setColorSchemeResources(int... colorResIds) { final Resources res = getResources(); int[] colorRes = new int[colorResIds.length]; for (int i = 0; i < colorResIds.length; i++) { colorRes[i] = res.getColor(colorResIds[i]); } setColorSchemeColors(colorRes); } /** * Set the colors used in the progress animation. The first * color will also be the color of the bar that grows in response to a user * swipe gesture. * * @param colors */ public void setColorSchemeColors(int... colors) { ensureTarget(); mProgress.setColorSchemeColors(colors); } /** * @return Whether the SwipeRefreshWidget is actively showing refresh * progress. */ public boolean isRefreshing() { return mRefreshing; } private void ensureTarget() { // Don't bother getting the parent height if the parent hasn't been laid // out yet. if (mTarget == null) { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); if (!child.equals(mCircleView)) { mTarget = child; break; } } } } /** * Set the distance to trigger a sync in dips * * @param distance */ public void setDistanceToTriggerSync(int distance) { mTotalDragDistance = distance; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { final int width = getMeasuredWidth(); final int height = getMeasuredHeight(); if (getChildCount() == 0) { return; } if (mTarget == null) { ensureTarget(); } if (mTarget == null) { return; } final View child = mTarget; final int childLeft = getPaddingLeft(); final int childTop = getPaddingTop(); final int childWidth = width - getPaddingLeft() - getPaddingRight(); final int childHeight = height - getPaddingTop() - getPaddingBottom(); child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); int circleWidth = mCircleView.getMeasuredWidth(); int circleHeight = mCircleView.getMeasuredHeight(); mCircleView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop, (width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight); } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mTarget == null) { ensureTarget(); } if (mTarget == null) { return; } mTarget.measure(MeasureSpec.makeMeasureSpec( getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY)); mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mCircleHeight, MeasureSpec.EXACTLY)); if (!mUsingCustomStart && !mOriginalOffsetCalculated) { mOriginalOffsetCalculated = true; switch (mDirection) { case BOTTOM: mCurrentTargetOffsetTop = mOriginalOffsetTop = getMeasuredHeight() - mCircleView.getMeasuredHeight(); break; case TOP: default: mCurrentTargetOffsetTop = mOriginalOffsetTop = -mCircleView.getMeasuredHeight(); break; } } mCircleViewIndex = -1; // Get the index of the circleview. for (int index = 0; index < getChildCount(); index++) { if (getChildAt(index) == mCircleView) { mCircleViewIndex = index; break; } } } /** * @return Whether it is possible for the child view of this layout to * scroll up. Override this if the child view is a custom view. */ public boolean canChildScrollUp() { if (Build.VERSION.SDK_INT < 14) { if (mTarget instanceof AbsListView) { final AbsListView absListView = (AbsListView) mTarget; return absListView.getChildCount() > 0 && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) .getTop() < absListView.getPaddingTop()); } else { return mTarget.getScrollY() > 0; } } else { return ViewCompat.canScrollVertically(mTarget, -1); } } // public boolean canChildScrollUp() { // if (android.os.Build.VERSION.SDK_INT < 14) { // if (mTarget instanceof AbsListView) { // final AbsListView absListView = (AbsListView) mTarget; // if (absListView.getLastVisiblePosition() + 1 == absListView.getCount()) { // int lastIndex = absListView.getLastVisiblePosition() - absListView.getFirstVisiblePosition(); // // boolean res = absListView.getChildAt(lastIndex).getBottom() == absListView.getPaddingBottom(); // // return res; // } // return true; // } else { // return mTarget.getScrollY() > 0; // } // } else { // return ViewCompat.canScrollVertically(mTarget, 1); // } // } public boolean canChildScrollDown() { if (Build.VERSION.SDK_INT < 14) { if (mTarget instanceof AbsListView) { final AbsListView absListView = (AbsListView) mTarget; try { if (absListView.getCount() > 0) { if (absListView.getLastVisiblePosition() + 1 == absListView.getCount()) { int lastIndex = absListView.getLastVisiblePosition() - absListView.getFirstVisiblePosition(); return absListView.getChildAt(lastIndex).getBottom() == absListView.getPaddingBottom(); } } } catch (Exception e) { e.printStackTrace(); } return true; } else { return true; } } else { return ViewCompat.canScrollVertically(mTarget, 1); } } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { ensureTarget(); final int action = MotionEventCompat.getActionMasked(ev); if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { mReturningToStart = false; } switch (mDirection) { case BOTTOM: if (!isEnabled() || mReturningToStart || (!mBothDirection && canChildScrollDown()) || mRefreshing) { // Fail fast if we're not in a state where a swipe is possible return false; } break; case TOP: default: if (!isEnabled() || mReturningToStart || (!mBothDirection && canChildScrollUp()) || mRefreshing) { // Fail fast if we're not in a state where a swipe is possible return false; } break; } switch (action) { case MotionEvent.ACTION_DOWN: setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop(), true); mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mIsBeingDragged = false; final float initialMotionY = getMotionEventY(ev, mActivePointerId); if (initialMotionY == -1) { return false; } mInitialMotionY = initialMotionY; case MotionEvent.ACTION_MOVE: if (mActivePointerId == INVALID_POINTER) { return false; } final float y = getMotionEventY(ev, mActivePointerId); if (y == -1) { return false; } if (mBothDirection) { if (y > mInitialMotionY) { setRawDirection(SwipyRefreshLayoutDirection.TOP); } else if (y < mInitialMotionY) { setRawDirection(SwipyRefreshLayoutDirection.BOTTOM); } if ((mDirection == SwipyRefreshLayoutDirection.BOTTOM && canChildScrollDown()) || (mDirection == SwipyRefreshLayoutDirection.TOP && canChildScrollUp())) { return false; } } float yDiff; switch (mDirection) { case BOTTOM: yDiff = mInitialMotionY - y; break; case TOP: default: yDiff = y - mInitialMotionY; break; } if (yDiff > mTouchSlop && !mIsBeingDragged) { mIsBeingDragged = true; mProgress.setAlpha(STARTING_PROGRESS_ALPHA); } break; case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mIsBeingDragged = false; mActivePointerId = INVALID_POINTER; break; } return mIsBeingDragged; } private float getMotionEventY(MotionEvent ev, int activePointerId) { final int index = MotionEventCompat.findPointerIndex(ev, activePointerId); if (index < 0) { return -1; } return MotionEventCompat.getY(ev, index); } @Override public void requestDisallowInterceptTouchEvent(boolean b) { // Nope. } private boolean isAnimationRunning(Animation animation) { return animation != null && animation.hasStarted() && !animation.hasEnded(); } @SuppressLint("NewApi") @Override public boolean onTouchEvent(MotionEvent ev) { final int action = MotionEventCompat.getActionMasked(ev); if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { mReturningToStart = false; } switch (mDirection) { case BOTTOM: if (!isEnabled() || mReturningToStart || canChildScrollDown() || mRefreshing) { // Fail fast if we're not in a state where a swipe is possible return false; } break; case TOP: default: if (!isEnabled() || mReturningToStart || canChildScrollUp() || mRefreshing) { // Fail fast if we're not in a state where a swipe is possible return false; } break; } switch (action) { case MotionEvent.ACTION_DOWN: mActivePointerId = MotionEventCompat.getPointerId(ev, 0); mIsBeingDragged = false; break; case MotionEvent.ACTION_MOVE: { final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); if (pointerIndex < 0) { return false; } final float y = MotionEventCompat.getY(ev, pointerIndex); float overscrollTop; switch (mDirection) { case BOTTOM: overscrollTop = (mInitialMotionY - y) * DRAG_RATE; break; case TOP: default: overscrollTop = (y - mInitialMotionY) * DRAG_RATE; break; } if (mIsBeingDragged) { mProgress.showArrow(true); float originalDragPercent = overscrollTop / mTotalDragDistance; if (originalDragPercent < 0) { return false; } float dragPercent = Math.min(1f, Math.abs(originalDragPercent)); float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3; float extraOS = Math.abs(overscrollTop) - mTotalDragDistance; float slingshotDist = mUsingCustomStart ? mSpinnerFinalOffset - mOriginalOffsetTop : mSpinnerFinalOffset; float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) / slingshotDist); float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow( (tensionSlingshotPercent / 4), 2)) * 2f; float extraMove = (slingshotDist) * tensionPercent * 2; // int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove); int targetY; if (mDirection == SwipyRefreshLayoutDirection.TOP) { targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove); } else { targetY = mOriginalOffsetTop - (int) ((slingshotDist * dragPercent) + extraMove); } // where 1.0f is a full circle if (mCircleView.getVisibility() != View.VISIBLE) { mCircleView.setVisibility(View.VISIBLE); } if (!mScale) { ViewCompat.setScaleX(mCircleView, 1f); ViewCompat.setScaleY(mCircleView, 1f); } if (overscrollTop < mTotalDragDistance) { if (mScale) { setAnimationProgress(overscrollTop / mTotalDragDistance); } if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA && !isAnimationRunning(mAlphaStartAnimation)) { // Animate the alpha startProgressAlphaStartAnimation(); } float strokeStart = (float) (adjustedPercent * .8f); mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart)); mProgress.setArrowScale(Math.min(1f, adjustedPercent)); } else { if (mProgress.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimation)) { // Animate the alpha startProgressAlphaMaxAnimation(); } } float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f; mProgress.setProgressRotation(rotation); setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop, true /* requires update */); } break; } case MotionEventCompat.ACTION_POINTER_DOWN: { final int index = MotionEventCompat.getActionIndex(ev); mActivePointerId = MotionEventCompat.getPointerId(ev, index); break; } case MotionEventCompat.ACTION_POINTER_UP: onSecondaryPointerUp(ev); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { if (mActivePointerId == INVALID_POINTER) { if (action == MotionEvent.ACTION_UP) { } return false; } final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); final float y = MotionEventCompat.getY(ev, pointerIndex); float overscrollTop; switch (mDirection) { case BOTTOM: overscrollTop = (mInitialMotionY - y) * DRAG_RATE; isTop = false; break; case TOP: default: overscrollTop = (y - mInitialMotionY) * DRAG_RATE; isTop = true; break; } mIsBeingDragged = false; if (overscrollTop > mTotalDragDistance) { setRefreshing(true, true /* notify */); } else { // cancel refresh mRefreshing = false; mProgress.setStartEndTrim(0f, 0f); AnimationListener listener = null; if (!mScale) { listener = new AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { if (!mScale) { startScaleDownAnimation(null); } } @Override public void onAnimationRepeat(Animation animation) { } }; } animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener); mProgress.showArrow(false); } mActivePointerId = INVALID_POINTER; return false; } } return true; } private void animateOffsetToCorrectPosition(int from, AnimationListener listener) { mFrom = from; mAnimateToCorrectPosition.reset(); mAnimateToCorrectPosition.setDuration(ANIMATE_TO_TRIGGER_DURATION); mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator); if (listener != null) { mCircleView.setAnimationListener(listener); } mCircleView.clearAnimation(); mCircleView.startAnimation(mAnimateToCorrectPosition); } private void animateOffsetToStartPosition(int from, AnimationListener listener) { if (mScale) { // Scale the item back down startScaleDownReturnToStartAnimation(from, listener); } else { mFrom = from; mAnimateToStartPosition.reset(); mAnimateToStartPosition.setDuration(ANIMATE_TO_START_DURATION); mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator); if (listener != null) { mCircleView.setAnimationListener(listener); } mCircleView.clearAnimation(); mCircleView.startAnimation(mAnimateToStartPosition); } } private final Animation mAnimateToCorrectPosition = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { int targetTop = 0; int endTarget = 0; if (!mUsingCustomStart) { switch (mDirection) { case BOTTOM: endTarget = getMeasuredHeight() - (int) (mSpinnerFinalOffset); break; case TOP: default: endTarget = (int) (mSpinnerFinalOffset - Math.abs(mOriginalOffsetTop)); break; } } else { endTarget = (int) mSpinnerFinalOffset; } targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime)); int offset = targetTop - mCircleView.getTop(); setTargetOffsetTopAndBottom(offset, false /* requires update */); } }; private void moveToStart(float interpolatedTime) { int targetTop = 0; targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime)); int offset = targetTop - mCircleView.getTop(); setTargetOffsetTopAndBottom(offset, false /* requires update */); } private final Animation mAnimateToStartPosition = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { moveToStart(interpolatedTime); } }; @SuppressLint("NewApi") private void startScaleDownReturnToStartAnimation(int from, AnimationListener listener) { mFrom = from; if (isAlphaUsedForScale()) { mStartingScale = mProgress.getAlpha(); } else { mStartingScale = ViewCompat.getScaleX(mCircleView); } mScaleDownToStartAnimation = new Animation() { @Override public void applyTransformation(float interpolatedTime, Transformation t) { float targetScale = (mStartingScale + (-mStartingScale * interpolatedTime)); setAnimationProgress(targetScale); moveToStart(interpolatedTime); } }; mScaleDownToStartAnimation.setDuration(SCALE_DOWN_DURATION); if (listener != null) { mCircleView.setAnimationListener(listener); } mCircleView.clearAnimation(); mCircleView.startAnimation(mScaleDownToStartAnimation); } private void setTargetOffsetTopAndBottom(int offset, boolean requiresUpdate) { mCircleView.bringToFront(); mCircleView.offsetTopAndBottom(offset); mCurrentTargetOffsetTop = mCircleView.getTop(); if (requiresUpdate && Build.VERSION.SDK_INT < 11) { invalidate(); } } private void onSecondaryPointerUp(MotionEvent ev) { final int pointerIndex = MotionEventCompat.getActionIndex(ev); final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); if (pointerId == mActivePointerId) { // This was our active pointer going up. Choose a new // active pointer and adjust accordingly. final int newPointerIndex = pointerIndex == 0 ? 1 : 0; mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); } } /** * Classes that wish to be notified when the swipe gesture correctly * triggers a refresh should implement this interface. */ public interface OnRefreshListener { public void onRefresh(int index); public void onLoad(int index); } public SwipyRefreshLayoutDirection getDirection() { return mBothDirection ? SwipyRefreshLayoutDirection.BOTH : mDirection; } public void setDirection(SwipyRefreshLayoutDirection direction) { if (direction == SwipyRefreshLayoutDirection.BOTH) { mBothDirection = true; } else { mBothDirection = false; mDirection = direction; } switch (mDirection) { case BOTTOM: mCurrentTargetOffsetTop = mOriginalOffsetTop = getMeasuredHeight() - mCircleView.getMeasuredHeight(); break; case TOP: default: mCurrentTargetOffsetTop = mOriginalOffsetTop = -mCircleView.getMeasuredHeight(); break; } } // only TOP or Bottom private void setRawDirection(SwipyRefreshLayoutDirection direction) { if (mDirection == direction) { return; } mDirection = direction; switch (mDirection) { case BOTTOM: mCurrentTargetOffsetTop = mOriginalOffsetTop = getMeasuredHeight() - mCircleView.getMeasuredHeight(); break; case TOP: default: mCurrentTargetOffsetTop = mOriginalOffsetTop = -mCircleView.getMeasuredHeight(); break; } } /** * @return 获得从第一页开始索引 */ public int getFirstIndex() { return firstIndex; } /** * 设置从第几页开始(默认值为0) * @param firstIndex 第几页 */ public void setFirstIndex(int firstIndex) { this.firstIndex = firstIndex; } /** * @return 获得当前索引 */ public int getIndex() { return index; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/swipyrefresh/SwipyRefreshLayoutDirection.java ================================================ package com.htmessage.yichatopen.widget.swipyrefresh; /** * * @author xutao * */ public enum SwipyRefreshLayoutDirection { TOP(0), // 只有下拉刷新 BOTTOM(1), // 只有加载更多 BOTH(2);// 全都有 private int mValue; SwipyRefreshLayoutDirection(int value) { this.mValue = value; } public static SwipyRefreshLayoutDirection getFromInt(int value) { for (SwipyRefreshLayoutDirection direction : SwipyRefreshLayoutDirection .values()) { if (direction.mValue == value) { return direction; } } return BOTH; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/activity/CaptureActivity.java ================================================ package com.htmessage.yichatopen.widget.zxing.activity; import android.app.Activity; import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.os.Bundle; import android.os.Handler; import android.os.Vibrator; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.widget.Toast; import com.htmessage.yichatopen.widget.zxing.camera.CameraManager; import com.htmessage.yichatopen.widget.zxing.decoding.CaptureActivityHandler; import com.htmessage.yichatopen.widget.zxing.decoding.InactivityTimer; import com.htmessage.yichatopen.widget.zxing.view.ViewfinderView; import com.google.zxing.BarcodeFormat; import com.google.zxing.Result; import java.io.IOException; import java.util.Vector; public class CaptureActivity extends Activity implements Callback { private CaptureActivityHandler handler; private ViewfinderView viewfinderView; private boolean hasSurface; private Vector decodeFormats; private String characterSet; private InactivityTimer inactivityTimer; private MediaPlayer mediaPlayer; private boolean playBeep; private static final float BEEP_VOLUME = 0.10f; private boolean vibrate; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // setContentView(R.layout.activity_camera); // //ViewUtil.addTopView(getApplicationContext(), this, R.string.scan_card); // CameraManager.init(getApplication(),false); // viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view); hasSurface = false; inactivityTimer = new InactivityTimer(this); } @SuppressWarnings("deprecation") @Override protected void onResume() { super.onResume(); SurfaceView surfaceView = null; SurfaceHolder surfaceHolder = surfaceView.getHolder(); if (hasSurface) { initCamera(surfaceHolder); } else { surfaceHolder.addCallback(this); surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } decodeFormats = null; characterSet = null; playBeep = true; AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE); if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { playBeep = false; } initBeepSound(); vibrate = true; } @Override protected void onPause() { super.onPause(); if (handler != null) { handler.quitSynchronously(); handler = null; } CameraManager.get().closeDriver(); } @Override protected void onDestroy() { inactivityTimer.shutdown(); super.onDestroy(); } /** * Handler scan result * @param result * @param barcode */ public void handleDecode(Result result, Bitmap barcode) { inactivityTimer.onActivity(); playBeepSoundAndVibrate(); String resultString = result.getText(); //FIXME if (resultString.equals("")) { Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show(); }else { // System.out.println("Result:"+resultString); // Intent resultIntent = new Intent(); // Bundle bundle = new Bundle(); // bundle.putString("result", resultString); // resultIntent.putExtras(bundle); //this.setResult(RESULT_OK, resultIntent); viewfinderView.drawResultBitmap(barcode); String Qruid =result.getText().toString().trim(); //传值 gongfan Intent intent = new Intent(); intent.putExtra("address", Qruid); setResult(RESULT_OK,intent); // intent.setClass(CaptureActivity.this, // AddFriendsTwoActivity.class); // startActivity(intent); //查询用户 // // searchUser(uid); } CaptureActivity.this.finish(); } /* public void handleDecode(Result obj, Bitmap barcode) { inactivityTimer.onActivity(); viewfinderView.drawResultBitmap(barcode); playBeepSoundAndVibrate(); txtResult.setText(obj.getBarcodeFormat().toString() + ":" + obj.getText()); }*/ private void initCamera(SurfaceHolder surfaceHolder) { try { CameraManager.get().openDriver(surfaceHolder); } catch (IOException ioe) { return; } catch (RuntimeException e) { return; } if (handler == null) { handler = new CaptureActivityHandler(this, decodeFormats, characterSet); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceCreated(SurfaceHolder holder) { if (!hasSurface) { hasSurface = true; initCamera(holder); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { hasSurface = false; } public ViewfinderView getViewfinderView() { return viewfinderView; } public Handler getHandler() { return handler; } public void drawViewfinder() { viewfinderView.drawViewfinder(); } private void initBeepSound() { if (playBeep && mediaPlayer == null) { // The volume on STREAM_SYSTEM is not adjustable, and users found it // too loud, // so we now play on the music stream. setVolumeControlStream(AudioManager.STREAM_MUSIC); mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setOnCompletionListener(beepListener); AssetFileDescriptor file =null; try { mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); file.close(); mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); mediaPlayer.prepare(); } catch (IOException e) { mediaPlayer = null; } } } private static final long VIBRATE_DURATION = 200L; private void playBeepSoundAndVibrate() { if (playBeep && mediaPlayer != null) { mediaPlayer.start(); } if (vibrate) { Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE); vibrator.vibrate(VIBRATE_DURATION); } } /** * When the beep has finished playing, rewind to queue up another one. */ private final OnCompletionListener beepListener = new OnCompletionListener() { public void onCompletion(MediaPlayer mediaPlayer) { mediaPlayer.seekTo(0); } }; } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/activity/DensityUtil.java ================================================ package com.htmessage.yichatopen.widget.zxing.activity; import android.app.Activity; import android.content.Context; import android.util.DisplayMetrics; /* * 屏幕分辨率获取的类 */ public class DensityUtil { private static int[] deviceWidthHeight = new int[2]; public static int[] getDeviceInfo(Context context) { if ((deviceWidthHeight[0] == 0) && (deviceWidthHeight[1] == 0)) { DisplayMetrics metrics = new DisplayMetrics(); ((Activity) context).getWindowManager().getDefaultDisplay() .getMetrics(metrics); deviceWidthHeight[0] = metrics.widthPixels; deviceWidthHeight[1] = metrics.heightPixels; } return deviceWidthHeight; } /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public static int dip2px(Context context, float dpValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } /** * 根据手机的分辨率从 px(像素) 的单位 转成为 dp */ public static int px2dip(Context context, float pxValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/camera/AutoFocusCallback.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.camera; import android.hardware.Camera; import android.os.Handler; import android.os.Message; import android.util.Log; final class AutoFocusCallback implements Camera.AutoFocusCallback { private static final String TAG = AutoFocusCallback.class.getSimpleName(); private static final long AUTOFOCUS_INTERVAL_MS = 1500L; private Handler autoFocusHandler; private int autoFocusMessage; public boolean isFocusing; void setHandler(Handler autoFocusHandler, int autoFocusMessage) { this.autoFocusHandler = autoFocusHandler; this.autoFocusMessage = autoFocusMessage; } public void onAutoFocus(boolean success, Camera camera) { isFocusing = true; if (autoFocusHandler != null) { Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success); autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS); autoFocusHandler = null; } else { Log.d(TAG, "Got auto-focus callback, but no handler for it"); } isFocusing = false; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/camera/CameraConfigurationManager.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.camera; import android.content.Context; import android.graphics.Point; import android.hardware.Camera; import android.os.Build; import android.util.Log; import android.view.Display; import android.view.WindowManager; import java.util.regex.Pattern; final class CameraConfigurationManager { private static final String TAG = CameraConfigurationManager.class.getSimpleName(); private static final int TEN_DESIRED_ZOOM = 27; private static final int DESIRED_SHARPNESS = 30; private static final Pattern COMMA_PATTERN = Pattern.compile(","); private final Context context; private Point screenResolution; private Point cameraResolution; private int previewFormat; private String previewFormatString; CameraConfigurationManager(Context context) { this.context = context; } /** * Reads, one time, values from the camera that are needed by the app. */ @SuppressWarnings("deprecation") void initFromCameraParameters(Camera camera) { Camera.Parameters parameters = camera.getParameters(); previewFormat = parameters.getPreviewFormat(); previewFormatString = parameters.get("preview-format"); Log.d(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString); WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); screenResolution = new Point(display.getWidth(), display.getHeight()); Log.d(TAG, "Screen resolution: " + screenResolution); cameraResolution = getCameraResolution(parameters, screenResolution); Log.d(TAG, "Camera resolution: " + screenResolution); } /** * Sets the camera up to take preview images which are used for both preview and decoding. * We detect the preview format here so that buildLuminanceSource() can build an appropriate * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest, * and the planar Y can be used for barcode scanning without a copy in some cases. */ void setDesiredCameraParameters(Camera camera) { Camera.Parameters parameters = camera.getParameters(); Log.d(TAG, "Setting preview size: " + cameraResolution); parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); setFlash(parameters); setZoom(parameters); //setSharpness(parameters); //modify here camera.setDisplayOrientation(90); camera.setParameters(parameters); } Point getCameraResolution() { return cameraResolution; } Point getScreenResolution() { return screenResolution; } int getPreviewFormat() { return previewFormat; } String getPreviewFormatString() { return previewFormatString; } private static Point getCameraResolution(Camera.Parameters parameters, Point screenResolution) { String previewSizeValueString = parameters.get("preview-size-values"); // saw this on Xperia if (previewSizeValueString == null) { previewSizeValueString = parameters.get("preview-size-value"); } Point cameraResolution = null; if (previewSizeValueString != null) { Log.d(TAG, "preview-size-values parameter: " + previewSizeValueString); cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution); } if (cameraResolution == null) { // Ensure that the camera resolution is a multiple of 8, as the screen may not be. cameraResolution = new Point( (screenResolution.x >> 3) << 3, (screenResolution.y >> 3) << 3); } return cameraResolution; } private static Point findBestPreviewSizeValue(CharSequence previewSizeValueString, Point screenResolution) { int bestX = 0; int bestY = 0; int diff = Integer.MAX_VALUE; for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) { previewSize = previewSize.trim(); int dimPosition = previewSize.indexOf('x'); if (dimPosition < 0) { Log.w(TAG, "Bad preview-size: " + previewSize); continue; } int newX; int newY; try { newX = Integer.parseInt(previewSize.substring(0, dimPosition)); newY = Integer.parseInt(previewSize.substring(dimPosition + 1)); } catch (NumberFormatException nfe) { Log.w(TAG, "Bad preview-size: " + previewSize); continue; } int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y); if (newDiff == 0) { bestX = newX; bestY = newY; break; } else if (newDiff < diff) { bestX = newX; bestY = newY; diff = newDiff; } } if (bestX > 0 && bestY > 0) { return new Point(bestX, bestY); } return null; } private static int findBestMotZoomValue(CharSequence stringValues, int tenDesiredZoom) { int tenBestValue = 0; for (String stringValue : COMMA_PATTERN.split(stringValues)) { stringValue = stringValue.trim(); double value; try { value = Double.parseDouble(stringValue); } catch (NumberFormatException nfe) { return tenDesiredZoom; } int tenValue = (int) (10.0 * value); if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) { tenBestValue = tenValue; } } return tenBestValue; } private void setFlash(Camera.Parameters parameters) { // FIXME: This is a hack to turn the flash off on the Samsung Galaxy. // And this is a hack-hack to work around a different value on the Behold II // Restrict Behold II check to Cupcake, per Samsung's advice //if (Build.MODEL.contains("Behold II") && // CameraManager.SDK_INT == Build.VERSION_CODES.CUPCAKE) { if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake parameters.set("flash-value", 1); } else { parameters.set("flash-value", 2); } // This is the standard setting to turn the flash off that all devices should honor. parameters.set("flash-mode", "off"); } private void setZoom(Camera.Parameters parameters) { String zoomSupportedString = parameters.get("zoom-supported"); if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) { return; } int tenDesiredZoom = TEN_DESIRED_ZOOM; String maxZoomString = parameters.get("max-zoom"); if (maxZoomString != null) { try { int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString)); if (tenDesiredZoom > tenMaxZoom) { tenDesiredZoom = tenMaxZoom; } } catch (NumberFormatException nfe) { Log.w(TAG, "Bad max-zoom: " + maxZoomString); } } String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max"); if (takingPictureZoomMaxString != null) { try { int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString); if (tenDesiredZoom > tenMaxZoom) { tenDesiredZoom = tenMaxZoom; } } catch (NumberFormatException nfe) { Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString); } } String motZoomValuesString = parameters.get("mot-zoom-values"); if (motZoomValuesString != null) { tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom); } String motZoomStepString = parameters.get("mot-zoom-step"); if (motZoomStepString != null) { try { double motZoomStep = Double.parseDouble(motZoomStepString.trim()); int tenZoomStep = (int) (10.0 * motZoomStep); if (tenZoomStep > 1) { tenDesiredZoom -= tenDesiredZoom % tenZoomStep; } } catch (NumberFormatException nfe) { // continue } } // Set zoom. This helps encourage the user to pull back. // Some devices like the Behold have a zoom parameter if (maxZoomString != null || motZoomValuesString != null) { parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0)); } // Most devices, like the Hero, appear to expose this zoom parameter. // It takes on values like "27" which appears to mean 2.7x zoom if (takingPictureZoomMaxString != null) { parameters.set("taking-picture-zoom", tenDesiredZoom); } } public static int getDesiredSharpness() { return DESIRED_SHARPNESS; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/camera/CameraManager.java ================================================ /* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.camera; import android.content.Context; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; import android.hardware.Camera; import android.os.Build; import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import com.htmessage.yichatopen.widget.zxing.activity.DensityUtil; import java.io.IOException; /** * This object wraps the Camera service object and expects to be the only one talking to it. The * implementation encapsulates the steps needed to take preview-sized images, which are used for * both preview and decoding. * */ @SuppressWarnings("deprecation") public final class CameraManager { private static final String TAG = CameraManager.class.getSimpleName(); private static final int MIN_FRAME_WIDTH = 340; private static final int MIN_FRAME_HEIGHT = 340; private static final int MAX_FRAME_WIDTH = 580; private static final int MAX_FRAME_HEIGHT = 460; private static CameraManager cameraManager; static final int SDK_INT; // Later we can use Build.VERSION.SDK_INT static { int sdkInt; try { sdkInt = Integer.parseInt(Build.VERSION.SDK); } catch (NumberFormatException nfe) { // Just to be safe sdkInt = 10000; } SDK_INT = sdkInt; } private final Context context; private final CameraConfigurationManager configManager; private Camera camera; private Rect framingRect; private Rect framingRectInPreview; private boolean initialized; private boolean previewing; private final boolean useOneShotPreviewCallback; private boolean is_top=false; /** * Preview frames are delivered here, which we pass on to the registered handler. Make sure to * clear the handler so it will only receive one message. */ private final PreviewCallback previewCallback; /** Autofocus callbacks arrive here, and are dispatched to the Handler which requested them. */ private final AutoFocusCallback autoFocusCallback; /** * Initializes this static object with the Context of the calling Activity. * * @param context The Activity which wants to use the camera. */ public static void init(Context context,boolean is_top) { // if (cameraManager == null) { cameraManager = new CameraManager(context,is_top); // } } /** * Gets the CameraManager singleton instance. * * @return A reference to the CameraManager singleton. */ public static CameraManager get() { return cameraManager; } private CameraManager(Context context,boolean is_top) { this.is_top=is_top; this.context = context; this.configManager = new CameraConfigurationManager(context); // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use // the more efficient one shot callback, as the older one can swamp the system and cause it // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK. //useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE; useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > 3; // 3 = Cupcake previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback); autoFocusCallback = new AutoFocusCallback(); } /** * Opens the camera driver and initializes the hardware parameters. * * @param holder The surface object which the camera will draw preview frames into. * @throws IOException Indicates the camera driver failed to open. */ public void openDriver(SurfaceHolder holder) throws IOException { if (camera == null) { camera = Camera.open(); if (camera == null) { throw new IOException(); } camera.setPreviewDisplay(holder); if (!initialized) { initialized = true; configManager.initFromCameraParameters(camera); } configManager.setDesiredCameraParameters(camera); //FIXME // SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); //�Ƿ�ʹ��ǰ�� // if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) { // FlashlightManager.enableFlashlight(); // } FlashlightManager.enableFlashlight(); } } /** * Closes the camera driver if still in use. */ public void closeDriver() { if (camera != null) { FlashlightManager.disableFlashlight(); camera.release(); camera = null; } } /** * Asks the camera hardware to begin drawing preview frames to the screen. */ public void startPreview() { if (camera != null && !previewing) { camera.startPreview(); previewing = true; } } /** * Tells the camera to stop drawing preview frames. */ public void stopPreview() { if (camera != null && previewing) { if (!useOneShotPreviewCallback) { camera.setPreviewCallback(null); } camera.stopPreview(); previewCallback.setHandler(null, 0); autoFocusCallback.setHandler(null, 0); previewing = false; } } /** * A single preview frame will be returned to the handler supplied. The data will arrive as byte[] * in the message.obj field, with width and height encoded as message.arg1 and message.arg2, * respectively. * * @param handler The handler to send the message to. * @param message The what field of the message to be sent. */ public void requestPreviewFrame(Handler handler, int message) { if (camera != null && previewing) { previewCallback.setHandler(handler, message); if (useOneShotPreviewCallback) { camera.setOneShotPreviewCallback(previewCallback); } else { camera.setPreviewCallback(previewCallback); } } } /** * Asks the camera hardware to perform an autofocus. * * @param handler The Handler to notify when the autofocus completes. * @param message The message to deliver. */ public void requestAutoFocus(Handler handler, int message) { if (camera != null && previewing ) { autoFocusCallback.setHandler(handler, message); //Log.d(TAG, "Requesting auto-focus callback"); try { camera.autoFocus(autoFocusCallback); } catch (Exception e) { } } } /** * Calculates the framing rect which the UI should draw to show the user where to place the * barcode. This target helps with alignment as well as forces the user to hold the device * far enough away to ensure the image will be in focus. * * @return The rectangle to draw on screen in window coordinates. */ public Rect getFramingRect() { Point screenResolution = configManager.getScreenResolution(); if (framingRect == null) { if (camera == null) { return null; } int width = screenResolution.x * 3 / 4; if (width < MIN_FRAME_WIDTH) { width = MIN_FRAME_WIDTH; } else if (width > MAX_FRAME_WIDTH) { width = MAX_FRAME_WIDTH; } int height = screenResolution.y * 3 / 4; if (height < MIN_FRAME_HEIGHT) { height = MIN_FRAME_HEIGHT; } else if (height > MAX_FRAME_HEIGHT) { height = MAX_FRAME_HEIGHT; } int leftOffset = (screenResolution.x - width) / 2; int topOffset=(screenResolution.y - height) / 2; if(is_top){ topOffset= DensityUtil.dip2px(getContext(),80); } framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height); Log.d(TAG, "Calculated framing rect: " + framingRect); } return framingRect; } /** * Like {@link #getFramingRect} but coordinates are in terms of the preview frame, * not UI / screen. */ public Rect getFramingRectInPreview() { if (framingRectInPreview == null) { Rect rect = new Rect(getFramingRect()); Point cameraResolution = configManager.getCameraResolution(); Point screenResolution = configManager.getScreenResolution(); //modify here // rect.left = rect.left * cameraResolution.x / screenResolution.x; // rect.right = rect.right * cameraResolution.x / screenResolution.x; // rect.top = rect.top * cameraResolution.y / screenResolution.y; // rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y; rect.left = rect.left * cameraResolution.y / screenResolution.x; rect.right = rect.right * cameraResolution.y / screenResolution.x; rect.top = rect.top * cameraResolution.x / screenResolution.y; rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y; framingRectInPreview = rect; } return framingRectInPreview; } /** * Converts the result points from still resolution coordinates to screen coordinates. * * @param points The points returned by the Reader subclass through Result.getResultPoints(). * @return An array of Points scaled to the size of the framing rect and offset appropriately * so they can be drawn in screen coordinates. */ /* public Point[] convertResultPoints(ResultPoint[] points) { Rect frame = getFramingRectInPreview(); int count = points.length; Point[] output = new Point[count]; for (int x = 0; x < count; x++) { output[x] = new Point(); output[x].x = frame.left + (int) (points[x].getX() + 0.5f); output[x].y = frame.top + (int) (points[x].getY() + 0.5f); } return output; } */ /** * A factory method to build the appropriate LuminanceSource object based on the format * of the preview buffers, as described by Camera.Parameters. * * @param data A preview frame. * @param width The width of the image. * @param height The height of the image. * @return A PlanarYUVLuminanceSource instance. */ public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) { Rect rect = getFramingRectInPreview(); int previewFormat = configManager.getPreviewFormat(); String previewFormatString = configManager.getPreviewFormatString(); switch (previewFormat) { // This is the standard Android format which all devices are REQUIRED to support. // In theory, it's the only one we should ever care about. case PixelFormat.YCbCr_420_SP: // This format has never been seen in the wild, but is compatible as we only care // about the Y channel, so allow it. case PixelFormat.YCbCr_422_SP: return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height()); default: // The Samsung Moment incorrectly uses this variant instead of the 'sp' version. // Fortunately, it too has all the Y data up front, so we can read it. if ("yuv420p".equals(previewFormatString)) { return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height()); } } throw new IllegalArgumentException("Unsupported picture format: " + previewFormat + '/' + previewFormatString); } public Context getContext() { return context; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/camera/FlashlightManager.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.camera; import android.os.IBinder; import android.util.Log; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * This class is used to activate the weak light on some camera phones (not flash) * in order to illuminate surfaces for scanning. There is no official way to do this, * but, classes which allow access to this function still exist on some devices. * This therefore proceeds through a great deal of reflection. * * See * http://almondmendoza.com/2009/01/05/changing-the-screen-brightness-programatically/ and * * http://code.google.com/p/droidled/source/browse/trunk/src/com/droidled/demo/DroidLED.java. * Thanks to Ryan Alford for pointing out the availability of this class. */ final class FlashlightManager { private static final String TAG = FlashlightManager.class.getSimpleName(); private static final Object iHardwareService; private static final Method setFlashEnabledMethod; static { iHardwareService = getHardwareService(); setFlashEnabledMethod = getSetFlashEnabledMethod(iHardwareService); if (iHardwareService == null) { Log.v(TAG, "This device does supports control of a flashlight"); } else { Log.v(TAG, "This device does not support control of a flashlight"); } } private FlashlightManager() { } //FIXME static void enableFlashlight() { setFlashlight(false); } static void disableFlashlight() { setFlashlight(false); } private static Object getHardwareService() { Class serviceManagerClass = maybeForName("android.os.ServiceManager"); if (serviceManagerClass == null) { return null; } Method getServiceMethod = maybeGetMethod(serviceManagerClass, "getService", String.class); if (getServiceMethod == null) { return null; } Object hardwareService = invoke(getServiceMethod, null, "hardware"); if (hardwareService == null) { return null; } Class iHardwareServiceStubClass = maybeForName("android.os.IHardwareService$Stub"); if (iHardwareServiceStubClass == null) { return null; } Method asInterfaceMethod = maybeGetMethod(iHardwareServiceStubClass, "asInterface", IBinder.class); if (asInterfaceMethod == null) { return null; } return invoke(asInterfaceMethod, null, hardwareService); } private static Method getSetFlashEnabledMethod(Object iHardwareService) { if (iHardwareService == null) { return null; } Class proxyClass = iHardwareService.getClass(); return maybeGetMethod(proxyClass, "setFlashlightEnabled", boolean.class); } private static Class maybeForName(String name) { try { return Class.forName(name); } catch (ClassNotFoundException cnfe) { // OK return null; } catch (RuntimeException re) { Log.w(TAG, "Unexpected error while finding class " + name, re); return null; } } private static Method maybeGetMethod(Class clazz, String name, Class... argClasses) { try { return clazz.getMethod(name, argClasses); } catch (NoSuchMethodException nsme) { // OK return null; } catch (RuntimeException re) { Log.w(TAG, "Unexpected error while finding method " + name, re); return null; } } private static Object invoke(Method method, Object instance, Object... args) { try { return method.invoke(instance, args); } catch (IllegalAccessException e) { Log.w(TAG, "Unexpected error while invoking " + method, e); return null; } catch (InvocationTargetException e) { Log.w(TAG, "Unexpected error while invoking " + method, e.getCause()); return null; } catch (RuntimeException re) { Log.w(TAG, "Unexpected error while invoking " + method, re); return null; } } private static void setFlashlight(boolean active) { if (iHardwareService != null) { invoke(setFlashEnabledMethod, iHardwareService, active); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/camera/PlanarYUVLuminanceSource.java ================================================ /* * Copyright 2009 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.camera; import android.graphics.Bitmap; import com.google.zxing.LuminanceSource; /** * This object extends LuminanceSource around an array of YUV data returned from the camera driver, * with the option to crop to a rectangle within the full data. This can be used to exclude * superfluous pixels around the perimeter and speed up decoding. * * It works for any pixel format where the Y channel is planar and appears first, including * YCbCr_420_SP and YCbCr_422_SP. * * @author dswitkin@google.com (Daniel Switkin) */ public final class PlanarYUVLuminanceSource extends LuminanceSource { private final byte[] yuvData; private final int dataWidth; private final int dataHeight; private final int left; private final int top; public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top, int width, int height) { super(width, height); if (left + width > dataWidth || top + height > dataHeight) { throw new IllegalArgumentException("Crop rectangle does not fit within image data."); } this.yuvData = yuvData; this.dataWidth = dataWidth; this.dataHeight = dataHeight; this.left = left; this.top = top; } @Override public byte[] getRow(int y, byte[] row) { if (y < 0 || y >= getHeight()) { throw new IllegalArgumentException("Requested row is outside the image: " + y); } int width = getWidth(); if (row == null || row.length < width) { row = new byte[width]; } int offset = (y + top) * dataWidth + left; System.arraycopy(yuvData, offset, row, 0, width); return row; } @Override public byte[] getMatrix() { int width = getWidth(); int height = getHeight(); // If the caller asks for the entire underlying image, save the copy and give them the // original data. The docs specifically warn that result.length must be ignored. if (width == dataWidth && height == dataHeight) { return yuvData; } int area = width * height; byte[] matrix = new byte[area]; int inputOffset = top * dataWidth + left; // If the width matches the full width of the underlying data, perform a single copy. if (width == dataWidth) { System.arraycopy(yuvData, inputOffset, matrix, 0, area); return matrix; } // Otherwise copy one cropped row at a time. byte[] yuv = yuvData; for (int y = 0; y < height; y++) { int outputOffset = y * width; System.arraycopy(yuv, inputOffset, matrix, outputOffset, width); inputOffset += dataWidth; } return matrix; } @Override public boolean isCropSupported() { return true; } public int getDataWidth() { return dataWidth; } public int getDataHeight() { return dataHeight; } public Bitmap renderCroppedGreyscaleBitmap() { int width = getWidth(); int height = getHeight(); int[] pixels = new int[width * height]; byte[] yuv = yuvData; int inputOffset = top * dataWidth + left; for (int y = 0; y < height; y++) { int outputOffset = y * width; for (int x = 0; x < width; x++) { int grey = yuv[inputOffset + x] & 0xff; pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101); } inputOffset += dataWidth; } Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/camera/PreviewCallback.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.camera; import android.graphics.Point; import android.hardware.Camera; import android.os.Handler; import android.os.Message; import android.util.Log; final class PreviewCallback implements Camera.PreviewCallback { private static final String TAG = PreviewCallback.class.getSimpleName(); private final CameraConfigurationManager configManager; private final boolean useOneShotPreviewCallback; private Handler previewHandler; private int previewMessage; PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) { this.configManager = configManager; this.useOneShotPreviewCallback = useOneShotPreviewCallback; } void setHandler(Handler previewHandler, int previewMessage) { this.previewHandler = previewHandler; this.previewMessage = previewMessage; } public void onPreviewFrame(byte[] data, Camera camera) { Point cameraResolution = configManager.getCameraResolution(); if (!useOneShotPreviewCallback) { camera.setPreviewCallback(null); } if (previewHandler != null) { Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x, cameraResolution.y, data); message.sendToTarget(); previewHandler = null; } else { Log.d(TAG, "Got preview callback, but no handler for it"); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/decoding/CaptureActivityHandler.java ================================================ /* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.decoding; import android.os.Handler; import android.os.Message; import com.htmessage.yichatopen.widget.zxing.activity.CaptureActivity; import com.htmessage.yichatopen.widget.zxing.camera.CameraManager; import com.htmessage.yichatopen.widget.zxing.view.ViewfinderResultPointCallback; import com.google.zxing.BarcodeFormat; import java.util.Vector; /** * This class handles all the messaging which comprises the state machine for capture. */ public final class CaptureActivityHandler extends Handler { private static final String TAG = CaptureActivityHandler.class.getSimpleName(); private final CaptureActivity activity; private final DecodeThread decodeThread; private State state; private enum State { PREVIEW, SUCCESS, DONE } public CaptureActivityHandler(CaptureActivity activity, Vector decodeFormats, String characterSet) { this.activity = activity; decodeThread = new DecodeThread(activity, decodeFormats, characterSet, new ViewfinderResultPointCallback(activity.getViewfinderView())); decodeThread.start(); state = State.SUCCESS; // Start ourselves capturing previews and decoding. CameraManager.get().startPreview(); restartPreviewAndDecode(); } @Override public void handleMessage(Message message) { switch (message.what) { } } public void quitSynchronously() { state = State.DONE; CameraManager.get().stopPreview(); Message quit = null; quit.sendToTarget(); try { decodeThread.join(); } catch (InterruptedException e) { // continue } // Be absolutely sure we don't send any queued up messages // removeMessages(R.id.decode_succeeded); // removeMessages(R.id.decode_failed); } private void restartPreviewAndDecode() { if (state == State.SUCCESS) { state = State.PREVIEW; // CameraManager.get().requestPreviewFrame(decodeThread.getHandler(), R.id.decode); // CameraManager.get().requestAutoFocus(this, R.id.auto_focus); activity.drawViewfinder(); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/decoding/DecodeFormatManager.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.decoding; import android.content.Intent; import android.net.Uri; import com.google.zxing.BarcodeFormat; import java.util.Arrays; import java.util.List; import java.util.Vector; import java.util.regex.Pattern; final class DecodeFormatManager { private static final Pattern COMMA_PATTERN = Pattern.compile(","); static final Vector PRODUCT_FORMATS; static final Vector ONE_D_FORMATS; static final Vector QR_CODE_FORMATS; static final Vector DATA_MATRIX_FORMATS; static { PRODUCT_FORMATS = new Vector(5); PRODUCT_FORMATS.add(BarcodeFormat.UPC_A); PRODUCT_FORMATS.add(BarcodeFormat.UPC_E); PRODUCT_FORMATS.add(BarcodeFormat.EAN_13); PRODUCT_FORMATS.add(BarcodeFormat.EAN_8); PRODUCT_FORMATS.add(BarcodeFormat.RSS14); ONE_D_FORMATS = new Vector(PRODUCT_FORMATS.size() + 4); ONE_D_FORMATS.addAll(PRODUCT_FORMATS); ONE_D_FORMATS.add(BarcodeFormat.CODE_39); ONE_D_FORMATS.add(BarcodeFormat.CODE_93); ONE_D_FORMATS.add(BarcodeFormat.CODE_128); ONE_D_FORMATS.add(BarcodeFormat.ITF); QR_CODE_FORMATS = new Vector(1); QR_CODE_FORMATS.add(BarcodeFormat.QR_CODE); DATA_MATRIX_FORMATS = new Vector(1); DATA_MATRIX_FORMATS.add(BarcodeFormat.DATA_MATRIX); } private DecodeFormatManager() {} static Vector parseDecodeFormats(Intent intent) { List scanFormats = null; String scanFormatsString = intent.getStringExtra(Intents.Scan.SCAN_FORMATS); if (scanFormatsString != null) { scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString)); } return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE)); } static Vector parseDecodeFormats(Uri inputUri) { List formats = inputUri.getQueryParameters(Intents.Scan.SCAN_FORMATS); if (formats != null && formats.size() == 1 && formats.get(0) != null){ formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0))); } return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE)); } private static Vector parseDecodeFormats(Iterable scanFormats, String decodeMode) { if (scanFormats != null) { Vector formats = new Vector(); try { for (String format : scanFormats) { formats.add(BarcodeFormat.valueOf(format)); } return formats; } catch (IllegalArgumentException iae) { // ignore it then } } if (decodeMode != null) { if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) { return PRODUCT_FORMATS; } if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) { return QR_CODE_FORMATS; } if (Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) { return DATA_MATRIX_FORMATS; } if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) { return ONE_D_FORMATS; } } return null; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/decoding/DecodeHandler.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.decoding; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import com.htmessage.yichatopen.widget.zxing.activity.CaptureActivity; import com.htmessage.yichatopen.widget.zxing.camera.CameraManager; import com.htmessage.yichatopen.widget.zxing.camera.PlanarYUVLuminanceSource; import com.google.zxing.BinaryBitmap; import com.google.zxing.DecodeHintType; import com.google.zxing.MultiFormatReader; import com.google.zxing.ReaderException; import com.google.zxing.Result; import com.google.zxing.common.HybridBinarizer; import java.util.Hashtable; final class DecodeHandler extends Handler { private static final String TAG = DecodeHandler.class.getSimpleName(); private final CaptureActivity activity; private final MultiFormatReader multiFormatReader; DecodeHandler(CaptureActivity activity, Hashtable hints) { multiFormatReader = new MultiFormatReader(); multiFormatReader.setHints(hints); this.activity = activity; } @Override public void handleMessage(Message message) { switch (message.what) { } } /** * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, * reuse the same reader objects from one decode to the next. * * @param data The YUV preview frame. * @param width The width of the preview frame. * @param height The height of the preview frame. */ private void decode(byte[] data, int width, int height) { long start = System.currentTimeMillis(); Result rawResult = null; //modify here byte[] rotatedData = new byte[data.length]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) rotatedData[x * height + height - y - 1] = data[x + y * width]; } int tmp = width; // Here we are swapping, that's the difference to #11 width = height; height = tmp; PlanarYUVLuminanceSource source = CameraManager.get().buildLuminanceSource(rotatedData, width, height); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); try { rawResult = multiFormatReader.decodeWithState(bitmap); } catch (ReaderException re) { // continue } finally { multiFormatReader.reset(); } if (rawResult != null) { long end = System.currentTimeMillis(); Log.d(TAG, "Found barcode (" + (end - start) + " ms):\n" + rawResult.toString()); Message message =null; Bundle bundle = new Bundle(); bundle.putParcelable(DecodeThread.BARCODE_BITMAP, source.renderCroppedGreyscaleBitmap()); message.setData(bundle); //Log.d(TAG, "Sending decode succeeded message..."); message.sendToTarget(); } else { Message message = Message.obtain(activity.getHandler(),null); message.sendToTarget(); } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/decoding/DecodeThread.java ================================================ /* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.decoding; import android.os.Handler; import android.os.Looper; import com.htmessage.yichatopen.widget.zxing.activity.CaptureActivity; import com.google.zxing.BarcodeFormat; import com.google.zxing.DecodeHintType; import com.google.zxing.ResultPointCallback; import java.util.Hashtable; import java.util.Vector; import java.util.concurrent.CountDownLatch; /** * This thread does all the heavy lifting of decoding the images. */ final class DecodeThread extends Thread { public static final String BARCODE_BITMAP = "barcode_bitmap"; private final CaptureActivity activity; private final Hashtable hints; private Handler handler; private final CountDownLatch handlerInitLatch; DecodeThread(CaptureActivity activity, Vector decodeFormats, String characterSet, ResultPointCallback resultPointCallback) { this.activity = activity; handlerInitLatch = new CountDownLatch(1); hints = new Hashtable(3); if (decodeFormats == null || decodeFormats.isEmpty()) { decodeFormats = new Vector(); decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS); decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS); decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS); } hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats); if (characterSet != null) { hints.put(DecodeHintType.CHARACTER_SET, characterSet); } hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback); } Handler getHandler() { try { handlerInitLatch.await(); } catch (InterruptedException ie) { // continue? } return handler; } @Override public void run() { Looper.prepare(); handler = new DecodeHandler(activity, hints); handlerInitLatch.countDown(); Looper.loop(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/decoding/FinishListener.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.decoding; import android.app.Activity; import android.content.DialogInterface; /** * Simple listener used to exit the app in a few cases. * */ public final class FinishListener implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, Runnable { private final Activity activityToFinish; public FinishListener(Activity activityToFinish) { this.activityToFinish = activityToFinish; } public void onCancel(DialogInterface dialogInterface) { run(); } public void onClick(DialogInterface dialogInterface, int i) { run(); } public void run() { activityToFinish.finish(); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/decoding/InactivityTimer.java ================================================ /* * Copyright (C) 2010 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.decoding; import android.app.Activity; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** * Finishes an activity after a period of inactivity. */ public final class InactivityTimer { private static final int INACTIVITY_DELAY_SECONDS = 5 * 60; private final ScheduledExecutorService inactivityTimer = Executors.newSingleThreadScheduledExecutor(new DaemonThreadFactory()); private final Activity activity; private ScheduledFuture inactivityFuture = null; public InactivityTimer(Activity activity) { this.activity = activity; onActivity(); } public void onActivity() { cancel(); inactivityFuture = inactivityTimer.schedule(new FinishListener(activity), INACTIVITY_DELAY_SECONDS, TimeUnit.SECONDS); } private void cancel() { if (inactivityFuture != null) { inactivityFuture.cancel(true); inactivityFuture = null; } } public void shutdown() { cancel(); inactivityTimer.shutdown(); } private static final class DaemonThreadFactory implements ThreadFactory { public Thread newThread(Runnable runnable) { Thread thread = new Thread(runnable); thread.setDaemon(true); return thread; } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/decoding/Intents.java ================================================ /* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.decoding; /** * This class provides the constants to use when sending an Intent to Barcode Scanner. * These strings are effectively API and cannot be changed. */ public final class Intents { private Intents() { } public static final class Scan { /** * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return * the results. */ public static final String ACTION = "com.google.zxing.client.android.SCAN"; /** * By default, sending Scan.ACTION will decode all barcodes that we understand. However it * may be useful to limit scanning to certain formats. Use Intent.putExtra(MODE, value) with * one of the values below ({@link #PRODUCT_MODE}, {@link #ONE_D_MODE}, {@link #QR_CODE_MODE}). * Optional. * * Setting this is effectively shorthnad for setting explicit formats with {@link #SCAN_FORMATS}. * It is overridden by that setting. */ public static final String MODE = "SCAN_MODE"; /** * Comma-separated list of formats to scan for. The values must match the names of * {@link com.google.zxing.BarcodeFormat}s, such as {@link com.google.zxing.BarcodeFormat#EAN_13}. * Example: "EAN_13,EAN_8,QR_CODE" * * This overrides {@link #MODE}. */ public static final String SCAN_FORMATS = "SCAN_FORMATS"; /** * @see com.google.zxing.DecodeHintType#CHARACTER_SET */ public static final String CHARACTER_SET = "CHARACTER_SET"; /** * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get * prices, reviews, etc. for products. */ public static final String PRODUCT_MODE = "PRODUCT_MODE"; /** * Decode only 1D barcodes (currently UPC, EAN, Code 39, and Code 128). */ public static final String ONE_D_MODE = "ONE_D_MODE"; /** * Decode only QR codes. */ public static final String QR_CODE_MODE = "QR_CODE_MODE"; /** * Decode only Data Matrix codes. */ public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE"; /** * If a barcode is found, Barcodes returns RESULT_OK to onActivityResult() of the app which * requested the scan via startSubActivity(). The barcodes contents can be retrieved with * intent.getStringExtra(RESULT). If the user presses Back, the result code will be * RESULT_CANCELED. */ public static final String RESULT = "SCAN_RESULT"; /** * Call intent.getStringExtra(RESULT_FORMAT) to determine which barcode format was found. * See Contents.Format for possible values. */ public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT"; /** * Setting this to false will not save scanned codes in the history. */ public static final String SAVE_HISTORY = "SAVE_HISTORY"; private Scan() { } } public static final class Encode { /** * Send this intent to encode a piece of data as a QR code and display it full screen, so * that another person can scan the barcode from your screen. */ public static final String ACTION = "com.google.zxing.client.android.ENCODE"; /** * The data to encode. Use Intent.putExtra(DATA, data) where data is either a String or a * Bundle, depending on the type and format specified. Non-QR Code formats should * just use a String here. For QR Code, see Contents for details. */ public static final String DATA = "ENCODE_DATA"; /** * The type of data being supplied if the format is QR Code. Use * Intent.putExtra(TYPE, type) with one of Contents.Type. */ public static final String TYPE = "ENCODE_TYPE"; /** * The barcode format to be displayed. If this isn't specified or is blank, * it defaults to QR Code. Use Intent.putExtra(FORMAT, format), where * format is one of Contents.Format. */ public static final String FORMAT = "ENCODE_FORMAT"; private Encode() { } } public static final class SearchBookContents { /** * Use Google Book Search to search the contents of the book provided. */ public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS"; /** * The book to search, identified by ISBN number. */ public static final String ISBN = "ISBN"; /** * An optional field which is the text to search for. */ public static final String QUERY = "QUERY"; private SearchBookContents() { } } public static final class WifiConnect { /** * Internal intent used to trigger connection to a wi-fi network. */ public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT"; /** * The network to connect to, all the configuration provided here. */ public static final String SSID = "SSID"; /** * The network to connect to, all the configuration provided here. */ public static final String TYPE = "TYPE"; /** * The network to connect to, all the configuration provided here. */ public static final String PASSWORD = "PASSWORD"; private WifiConnect() { } } public static final class Share { /** * Give the user a choice of items to encode as a barcode, then render it as a QR Code and * display onscreen for a friend to scan with their phone. */ public static final String ACTION = "com.google.zxing.client.android.SHARE"; private Share() { } } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/encoding/EncodingHandler.java ================================================ package com.htmessage.yichatopen.widget.zxing.encoding; import android.graphics.Bitmap; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.WriterException; import com.google.zxing.common.BitMatrix; import java.util.Hashtable; /** * @author Ryan Tang * */ public final class EncodingHandler { private static final int BLACK = 0xff000000; public static Bitmap createQRCode(String str,int widthAndHeight) throws WriterException { Hashtable hints = new Hashtable(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); BitMatrix matrix = new MultiFormatWriter().encode(str, BarcodeFormat.QR_CODE, widthAndHeight, widthAndHeight); int width = matrix.getWidth(); int height = matrix.getHeight(); int[] pixels = new int[width * height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { if (matrix.get(x, y)) { pixels[y * width + x] = BLACK; } } } Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/view/ViewfinderResultPointCallback.java ================================================ /* * Copyright (C) 2009 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.view; import com.google.zxing.ResultPoint; import com.google.zxing.ResultPointCallback; public final class ViewfinderResultPointCallback implements ResultPointCallback { private final ViewfinderView viewfinderView; public ViewfinderResultPointCallback(ViewfinderView viewfinderView) { this.viewfinderView = viewfinderView; } public void foundPossibleResultPoint(ResultPoint point) { viewfinderView.addPossibleResultPoint(point); } } ================================================ FILE: app/src/main/java/com/htmessage/yichatopen/widget/zxing/view/ViewfinderView.java ================================================ /* * Copyright (C) 2008 ZXing authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.htmessage.yichatopen.widget.zxing.view; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import com.htmessage.yichatopen.widget.zxing.camera.CameraManager; import com.google.zxing.ResultPoint; import java.util.Collection; import java.util.HashSet; /** * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial * transparency outside it, as well as the laser scanner animation and result points. * �Զ����View������ʱ�м���ʾ�� */ public final class ViewfinderView extends View { private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64}; private static final long ANIMATION_DELAY = 100L; private static final int OPAQUE = 0xFF; private final Paint paint; private Bitmap resultBitmap; private final int maskColor; private final int resultColor; private final int frameColor; private final int laserColor; private final int resultPointColor; private int scannerAlpha; private Collection possibleResultPoints; private Collection lastPossibleResultPoints; // This constructor is used when the class is built from an XML resource. public ViewfinderView(Context context, AttributeSet attrs) { super(context, attrs); // Initialize these once for performance rather than calling them every time in onDraw(). paint = new Paint(); Resources resources = getResources(); maskColor = 0; resultColor =0; frameColor = 0; laserColor = 0; resultPointColor =0; scannerAlpha = 0; possibleResultPoints = new HashSet(5); } @Override public void onDraw(Canvas canvas) { Rect frame = CameraManager.get().getFramingRect(); if (frame == null) { return; } int width = canvas.getWidth(); int height = canvas.getHeight(); // Draw the exterior (i.e. outside the framing rect) darkened paint.setColor(resultBitmap != null ? resultColor : maskColor); canvas.drawRect(0, 0, width, frame.top, paint); canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint); canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint); canvas.drawRect(0, frame.bottom + 1, width, height, paint); if (resultBitmap != null) { // Draw the opaque result bitmap over the scanning rectangle paint.setAlpha(OPAQUE); canvas.drawBitmap(resultBitmap, frame.left, frame.top, paint); } else { // Draw a two pixel solid black border inside the framing rect paint.setColor(frameColor); canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint); canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint); canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint); canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint); // Draw a red "laser scanner" line through the middle to show decoding is active paint.setColor(laserColor); paint.setAlpha(SCANNER_ALPHA[scannerAlpha]); scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length; int middle = frame.height() / 2 + frame.top; canvas.drawRect(frame.left + 2, middle - 1, frame.right - 1, middle + 2, paint); Collection currentPossible = possibleResultPoints; Collection currentLast = lastPossibleResultPoints; if (currentPossible.isEmpty()) { lastPossibleResultPoints = null; } else { possibleResultPoints = new HashSet(5); lastPossibleResultPoints = currentPossible; paint.setAlpha(OPAQUE); paint.setColor(resultPointColor); for (ResultPoint point : currentPossible) { canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 6.0f, paint); } } if (currentLast != null) { paint.setAlpha(OPAQUE / 2); paint.setColor(resultPointColor); for (ResultPoint point : currentLast) { canvas.drawCircle(frame.left + point.getX(), frame.top + point.getY(), 3.0f, paint); } } // Request another update at the animation interval, but only repaint the laser line, // not the entire viewfinder mask. postInvalidateDelayed(ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom); } } public void drawViewfinder() { resultBitmap = null; invalidate(); } /** * Draw a bitmap with the result points highlighted instead of the live scanning display. * * @param barcode An image of the decoded barcode. */ public void drawResultBitmap(Bitmap barcode) { resultBitmap = barcode; invalidate(); } public void addPossibleResultPoint(ResultPoint point) { possibleResultPoints.add(point); } } ================================================ FILE: app/src/main/res/anim/fade_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/fade_out.xml ================================================ ================================================ FILE: app/src/main/res/anim/head_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/head_out.xml ================================================ ================================================ FILE: app/src/main/res/anim/hold.xml ================================================ ================================================ FILE: app/src/main/res/anim/loading_animation.xml ================================================ ================================================ FILE: app/src/main/res/anim/push_bottom_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/push_bottom_out.xml ================================================ ================================================ FILE: app/src/main/res/anim/push_top_in.xml ================================================ ================================================ FILE: app/src/main/res/anim/push_top_in2.xml ================================================ ================================================ FILE: app/src/main/res/anim/push_top_out.xml ================================================ ================================================ FILE: app/src/main/res/anim/push_top_out2.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_in_from_left.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_in_from_right.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_out_to_left.xml ================================================ ================================================ FILE: app/src/main/res/anim/slide_out_to_right.xml ================================================ ================================================ FILE: app/src/main/res/anim/voice_from_icon.xml ================================================ ================================================ FILE: app/src/main/res/anim/voice_to_icon.xml ================================================ ================================================ FILE: app/src/main/res/color/login_btn_text_color.xml ================================================ ================================================ FILE: app/src/main/res/color/main_botton_text_color.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bg_btn_gray.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bg_btn_green.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bg_dialog.xml ================================================ ================================================ FILE: app/src/main/res/drawable/bg_et.xml ================================================ ================================================ FILE: app/src/main/res/drawable/btn_bottom_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/btn_more_type_msg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chat_error_item_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chat_image_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chat_press_speak_btn.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chat_takepic_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chatfrom_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chatting_setmode_keyboard_btn.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chatting_setmode_voice_btn.xml ================================================ ================================================ FILE: app/src/main/res/drawable/chatto_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/divider_horizontal.xml ================================================ ================================================ FILE: app/src/main/res/drawable/divider_vertical.xml ================================================ ================================================ FILE: app/src/main/res/drawable/dot_emoji.xml ================================================ ================================================ FILE: app/src/main/res/drawable/edit_text_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/emoji_bottom_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/item_pre_videocall_selector.xml ================================================ ================================================ FILE: app/src/main/res/drawable/list_item_bg_gray.xml ================================================ ================================================ FILE: app/src/main/res/drawable/list_item_bg_white.xml ================================================ ================================================ FILE: app/src/main/res/drawable/msg_state_failed_resend.xml ================================================ ================================================ FILE: app/src/main/res/drawable/progressbar_white.xml ================================================ ================================================ FILE: app/src/main/res/drawable/recording_hint_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/recording_text_hint_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/register_phone_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/sidebar_background_pressed.xml ================================================ ================================================ FILE: app/src/main/res/drawable/sign_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/tab_chat_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/tab_contact_list_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/tab_find_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/tab_profile_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/timestampe_bg.xml ================================================ ================================================ FILE: app/src/main/res/drawable/top_bar_back.xml ================================================ ================================================ FILE: app/src/main/res/drawable/topbar_back.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_addfriends_final.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_addfriends_next.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_addfriends_pre.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_base.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_base_input.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_base_main.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_chat_setting_single.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_check_people.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_new_friends.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_people_recently.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_psw_reset.xml ================================================