diff --git a/CoalMiningKit/.gitignore b/CoalMiningKit/.gitignore new file mode 100644 index 0000000..36b401f --- /dev/null +++ b/CoalMiningKit/.gitignore @@ -0,0 +1,12 @@ +# OS X Files +.DS_Store +**/.DS_Store + + +# Gradle files +*/Android/.gradle/ + +# IDEA Files +*/Android/.idea/ + +*/Android/local.properties \ No newline at end of file diff --git a/CoalMiningKit/README.md b/CoalMiningKit/README.md new file mode 100644 index 0000000..e1f7090 --- /dev/null +++ b/CoalMiningKit/README.md @@ -0,0 +1,92 @@ +# androidApp + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin http://192.168.153.128:8089/androidapp/androidapp.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](http://192.168.153.128:8089/androidapp/androidapp/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/CoalMiningKit/README_ZH.md b/CoalMiningKit/README_ZH.md new file mode 100644 index 0000000..de25910 --- /dev/null +++ b/CoalMiningKit/README_ZH.md @@ -0,0 +1,42 @@ +[English](./README.md) | 简体中文 + +# TUIKit(Android) + +## 下载地址 + +[最新 TUIChat 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUIChat.zip) + +[最新 TUIConversation 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUIConversation.zip) + +[最新 TUIContact 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUIContact.zip) + +[最新 TUISearch 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUISearch.zip) + +[最新 TUICallKit 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUICallKit.zip) + +[最新 TUICommunity 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUICommunity.zip) + +[最新 TUITranslation 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUITranslation.zip) + +[最新 TUIVoiceToTextPlugin 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUIVoiceToTextPlugin.zip) + +[最新 TUICustomerServicePlugin 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUICustomerServicePlugin.zip) + +[最新 TUICore 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUICore.zip) + +[最新 TIMCommon 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TIMCommon.zip) + +[最新 TUIRoomKit 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUIRoomKit.zip) + +[最新 TUIEmojiPlugin 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUIEmojiPlugin.zip) + +[最新 TUIMultimediaPlugin 下载](https://im.sdk.qcloud.com/download/tuikit/8.6.7040/android/TUIMultimediaPlugin.zip) + + +## 交流&反馈 + +如果您遇到 TUIKit 的 Bug,欢迎提交 Pull Request,Merge 成功后我们会及时更新 TUIKit 库 。 + +欢迎加入 QQ 群进行技术交流和反馈问题。 + +![img]( https://im.sdk.qcloud.com/tools/resource/officialwebsite/pictures/doc_tuikit_qq_group.jpg) diff --git a/CoalMiningKit/TIMCommon/timcommon/.gitignore b/CoalMiningKit/TIMCommon/timcommon/.gitignore new file mode 100644 index 0000000..84c048a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/CoalMiningKit/TIMCommon/timcommon/build.gradle b/CoalMiningKit/TIMCommon/timcommon/build.gradle new file mode 100644 index 0000000..d4a4fa8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/build.gradle @@ -0,0 +1,58 @@ +plugins { + id 'com.android.library' +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + namespace "com.tencent.qcloud.tuikit.timcommon" + defaultConfig { + minSdkVersion 19 + targetSdkVersion 30 + versionCode 1 + versionName "1.0" + } + buildFeatures { + buildConfig = true + } + buildTypes { + release { + minifyEnabled false + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + sourceSets { + main { + res.srcDirs += "src/main/res-light" + res.srcDirs += "src/main/res-lively" + res.srcDirs += "src/main/res-serious" + } + } +} + +dependencies { + /*plugin-build-Begin + + compileOnly fileTree(include: ['*.jar','*.aar'], dir: '../../../../tuikit/android/libs') + + plugin-build-End*/ + +// def projects = this.rootProject.getAllprojects().stream().map { project -> project.name }.collect() +// api projects.contains("tuicore") ? project(':tuicore') : "com.tencent.liteav.tuikit:tuicore:8.6.7040" + api project(':tuicore') + + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + implementation 'com.google.android.material:material:1.4.0' + implementation 'com.google.code.gson:gson:2.9.1' + implementation 'androidx.appcompat:appcompat:1.3.1' + implementation 'com.github.bumptech.glide:glide:4.12.0' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + implementation 'androidx.viewpager2:viewpager2:1.0.0' + annotationProcessor 'com.google.auto.service:auto-service:1.1.1' + +} + diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/AndroidManifest.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/AndroidManifest.xml new file mode 100644 index 0000000..3b4f17f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/AndroidManifest.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/TIMCommonConfig.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/TIMCommonConfig.java new file mode 100644 index 0000000..bd556a8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/TIMCommonConfig.java @@ -0,0 +1,61 @@ +package com.tencent.qcloud.tuikit.timcommon; + +public class TIMCommonConfig { + private static boolean enableGroupGridAvatar = true; + private static int defaultAvatarImage; + private static int defaultGroupAvatarImage; + + /** + * Gets whether to display the avatar in the nine-square grid style in the group conversation, the default is true + */ + public static boolean isEnableGroupGridAvatar() { + return enableGroupGridAvatar; + } + + /** + * Set whether to display the avatar in the nine-square grid style in group conversations + */ + public static void setEnableGroupGridAvatar(boolean enableGroupGridAvatar) { + TIMCommonConfig.enableGroupGridAvatar = enableGroupGridAvatar; + } + + /** + * + * Get the default avatar for c2c conversation + * + * @return + */ + public static int getDefaultAvatarImage() { + return defaultAvatarImage; + } + + /** + * + *Set the default avatar for c2c conversation + * + * @return + */ + public static void setDefaultAvatarImage(int defaultAvatarImage) { + TIMCommonConfig.defaultAvatarImage = defaultAvatarImage; + } + + /** + * + * Get the default avatar for group conversation + * + * @return + */ + public static int getDefaultGroupAvatarImage() { + return defaultGroupAvatarImage; + } + + /** + * + *Set the default avatar for group conversation + * + * @return + */ + public static void setDefaultGroupAvatarImage(int defaultGroupAvatarImage) { + TIMCommonConfig.defaultGroupAvatarImage = defaultGroupAvatarImage; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/TIMCommonService.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/TIMCommonService.java new file mode 100644 index 0000000..4de6813 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/TIMCommonService.java @@ -0,0 +1,26 @@ +package com.tencent.qcloud.tuikit.timcommon; + +import android.content.Context; +import android.text.TextUtils; + +import com.google.auto.service.AutoService; +import com.tencent.qcloud.tuicore.ServiceInitializer; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuicore.annotations.TUIInitializerID; +import com.tencent.qcloud.tuicore.interfaces.TUIInitializer; + +@AutoService(TUIInitializer.class) +@TUIInitializerID("TIMCommon") +public class TIMCommonService implements TUIInitializer { + + @Override + public void init(Context context) { + TUIThemeManager.addLightTheme(R.style.TIMCommonLightTheme); + TUIThemeManager.addLivelyTheme(R.style.TIMCommonLivelyTheme); + TUIThemeManager.addSeriousTheme(R.style.TIMCommonSeriousTheme); + } + + public static Context getAppContext() { + return ServiceInitializer.getAppContext(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/ChatFace.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/ChatFace.java new file mode 100644 index 0000000..0c2ab9f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/ChatFace.java @@ -0,0 +1,69 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import java.io.Serializable; + +public class ChatFace implements Serializable { + private int width; + private int height; + protected String faceUrl; + private FaceGroup faceGroup; + private String faceKey; + private String faceName; + private boolean autoMirrored = false; + + public void setFaceKey(String faceKey) { + this.faceKey = faceKey; + } + + public String getFaceKey() { + return faceKey; + } + + public void setFaceName(String faceName) { + this.faceName = faceName; + } + + public String getFaceName() { + return faceName; + } + + public void setFaceGroup(FaceGroup faceGroup) { + this.faceGroup = faceGroup; + } + + public FaceGroup getFaceGroup() { + return faceGroup; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setFaceUrl(String faceUrl) { + this.faceUrl = faceUrl; + } + + public String getFaceUrl() { + return faceUrl; + } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + + public void setAutoMirrored(boolean autoMirrored) { + this.autoMirrored = autoMirrored; + } + + public boolean isAutoMirrored() { + return autoMirrored; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/CustomFace.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/CustomFace.java new file mode 100644 index 0000000..462079f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/CustomFace.java @@ -0,0 +1,15 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +/** + * + * Custom expression attribute class + */ +public class CustomFace extends ChatFace { + /** + * + * @param assetPath + */ + public void setAssetPath(String assetPath) { + this.faceUrl = "file:///android_asset/" + assetPath; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/Emoji.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/Emoji.java new file mode 100644 index 0000000..3da7c63 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/Emoji.java @@ -0,0 +1,15 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import android.graphics.Bitmap; + +public class Emoji extends ChatFace { + private Bitmap icon; + + public Bitmap getIcon() { + return icon; + } + + public void setIcon(Bitmap icon) { + this.icon = icon; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/FaceGroup.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/FaceGroup.java new file mode 100644 index 0000000..8d63bfc --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/FaceGroup.java @@ -0,0 +1,98 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; + +public class FaceGroup { + private int groupID; + private String groupName; + private String desc; + private Object faceGroupIconUrl; + private int pageRowCount; + private int pageColumnCount; + private boolean isEmoji = false; + private final Map faces = new LinkedHashMap<>(); + + public int getGroupID() { + return groupID; + } + + public void setGroupID(int groupID) { + this.groupID = groupID; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getGroupName() { + return groupName; + } + + public boolean isEmojiGroup() { + return isEmoji; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public void setFaceGroupIconUrl(Object faceGroupIconUrl) { + this.faceGroupIconUrl = faceGroupIconUrl; + } + + public Object getFaceGroupIconUrl() { + return faceGroupIconUrl; + } + + public int getPageRowCount() { + return pageRowCount; + } + + public void setPageRowCount(int pageRowCount) { + this.pageRowCount = pageRowCount; + } + + public int getPageColumnCount() { + return pageColumnCount; + } + + public void setPageColumnCount(int pageColumnCount) { + this.pageColumnCount = pageColumnCount; + } + + public ArrayList getFaces() { + return new ArrayList<>(faces.values()); + } + + public void addFace(String faceKey, T face) { + if (face instanceof Emoji) { + isEmoji = true; + } + face.setFaceGroup(this); + faces.put(faceKey, face); + } + + public T getFace(String faceKey) { + if (TextUtils.isEmpty(faceKey)) { + return null; + } + T face = faces.get(faceKey); + if (face == null) { + int index = faceKey.lastIndexOf("@2x"); + if (index == -1) { + return null; + } + String oldFaceKey = faceKey.substring(0, index); + face = faces.get(oldFaceKey); + } + return face; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/FriendProfileBean.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/FriendProfileBean.java new file mode 100644 index 0000000..1ae95e9 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/FriendProfileBean.java @@ -0,0 +1,15 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import java.io.Serializable; + +public class FriendProfileBean extends UserBean implements Serializable { + private int allowType; + + public int getAllowType() { + return allowType; + } + + public void setAllowType(int allowType) { + this.allowType = allowType; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/GroupProfileBean.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/GroupProfileBean.java new file mode 100644 index 0000000..765bb06 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/GroupProfileBean.java @@ -0,0 +1,128 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import com.tencent.imsdk.group.GroupMemberInfo; + +import java.io.Serializable; + +public class GroupProfileBean implements Serializable { + private String groupName; + private String groupID; + private String groupType; + private String notification; + private String groupIntroduction; + private String groupFaceUrl; + private int memberCount; + private int roleInGroup; + private int recvOpt; + private int approveOpt; + private int addOpt; + private boolean isAllMuted = false; + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public String getGroupID() { + return groupID; + } + + public void setGroupID(String groupID) { + this.groupID = groupID; + } + + public String getGroupType() { + return groupType; + } + + public void setGroupType(String groupType) { + this.groupType = groupType; + } + + public String getNotification() { + return notification; + } + + public void setNotification(String notification) { + this.notification = notification; + } + + public String getGroupIntroduction() { + return groupIntroduction; + } + + public void setGroupIntroduction(String groupIntroduction) { + this.groupIntroduction = groupIntroduction; + } + + public String getGroupFaceUrl() { + return groupFaceUrl; + } + + public void setGroupFaceUrl(String groupFaceUrl) { + this.groupFaceUrl = groupFaceUrl; + } + + public int getMemberCount() { + return memberCount; + } + + public void setMemberCount(int memberCount) { + this.memberCount = memberCount; + } + + public int getRoleInGroup() { + return roleInGroup; + } + + public void setRoleInGroup(int roleInGroup) { + this.roleInGroup = roleInGroup; + } + + public boolean canManage() { + return roleInGroup == GroupMemberInfo.MEMBER_ROLE_OWNER || roleInGroup == GroupMemberInfo.MEMBER_ROLE_ADMINISTRATOR; + } + + public boolean isOwner() { + return roleInGroup == GroupMemberInfo.MEMBER_ROLE_OWNER; + } + + public boolean isAdmin() { + return roleInGroup == GroupMemberInfo.MEMBER_ROLE_ADMINISTRATOR; + } + + public void setAllMuted(boolean allMuted) { + isAllMuted = allMuted; + } + + public boolean isAllMuted() { + return isAllMuted; + } + + public void setApproveOpt(int approveOpt) { + this.approveOpt = approveOpt; + } + + public int getApproveOpt() { + return approveOpt; + } + + public int getAddOpt() { + return addOpt; + } + + public void setAddOpt(int addOpt) { + this.addOpt = addOpt; + } + + public void setRecvOpt(int recvOpt) { + this.recvOpt = recvOpt; + } + + public int getRecvOpt() { + return recvOpt; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageFeature.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageFeature.java new file mode 100644 index 0000000..79cc95d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageFeature.java @@ -0,0 +1,30 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import java.io.Serializable; + +/* + * Carrying function macros through messages,Mainly used to be compatible with old and new versions,Use the cloudCustomData field. + * Such as Typing function. + */ +public class MessageFeature implements Serializable { + public static final int VERSION = 1; + + private int needTyping = 1; // message typing feature ... + private int version = VERSION; + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public int getNeedTyping() { + return needTyping; + } + + public void setNeedTyping(int needTyping) { + this.needTyping = needTyping; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageReceiptInfo.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageReceiptInfo.java new file mode 100644 index 0000000..069db17 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageReceiptInfo.java @@ -0,0 +1,55 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import com.tencent.imsdk.v2.V2TIMMessageReceipt; + +import java.io.Serializable; + +public class MessageReceiptInfo implements Serializable { + private V2TIMMessageReceipt messageReceipt; + + public void setMessageReceipt(V2TIMMessageReceipt messageReceipt) { + this.messageReceipt = messageReceipt; + } + + public String getUserID() { + if (messageReceipt != null) { + return messageReceipt.getUserID(); + } + return null; + } + + public boolean isPeerRead() { + if (messageReceipt != null) { + return messageReceipt.isPeerRead(); + } + return false; + } + + public String getGroupID() { + if (messageReceipt != null) { + return messageReceipt.getGroupID(); + } + return null; + } + + public long getReadCount() { + if (messageReceipt != null) { + return messageReceipt.getReadCount(); + } + return 0; + } + + public long getUnreadCount() { + if (messageReceipt != null) { + return messageReceipt.getUnreadCount(); + } + return 0; + } + + public String getMsgID() { + if (messageReceipt != null) { + return messageReceipt.getMsgID(); + } + return null; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageRepliesBean.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageRepliesBean.java new file mode 100644 index 0000000..7e4ab04 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/MessageRepliesBean.java @@ -0,0 +1,117 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import android.text.TextUtils; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +public class MessageRepliesBean implements Serializable { + public static final int VERSION = 1; + private List replies; + private int version = VERSION; + + public void addReplyMessage(String messageId, String messageAbstract, String sender) { + if (replies == null) { + replies = new ArrayList<>(); + } + for (ReplyBean replyBean : replies) { + if (TextUtils.equals(replyBean.messageID, messageId)) { + return; + } + } + ReplyBean replyBean = new ReplyBean(); + replyBean.messageID = messageId; + replyBean.messageAbstract = messageAbstract; + replyBean.messageSender = sender; + replies.add(replyBean); + } + + public void removeReplyMessage(String messageID) { + if (replies == null) { + return; + } + for (ReplyBean replyBean : replies) { + if (TextUtils.equals(replyBean.messageID, messageID)) { + replies.remove(replyBean); + return; + } + } + } + + public void setVersion(int version) { + this.version = version; + } + + public int getVersion() { + return version; + } + + public List getReplies() { + return replies; + } + + public void setReplies(List replies) { + this.replies = replies; + } + + public int getRepliesSize() { + if (replies != null) { + return replies.size(); + } + return 0; + } + + + public static class ReplyBean implements Serializable { + private String messageID; + private String messageAbstract; + private String messageSender; + private transient String senderFaceUrl; + private transient String senderShowName; + + public String getMessageID() { + return messageID; + } + + public void setMessageID(String messageID) { + this.messageID = messageID; + } + + public String getMessageAbstract() { + return messageAbstract; + } + + public void setMessageAbstract(String messageAbstract) { + this.messageAbstract = messageAbstract; + } + + public String getMessageSender() { + return messageSender; + } + + public void setMessageSender(String messageSender) { + this.messageSender = messageSender; + } + + public void setSenderFaceUrl(String senderFaceUrl) { + this.senderFaceUrl = senderFaceUrl; + } + + public String getSenderFaceUrl() { + return senderFaceUrl; + } + + public void setSenderShowName(String senderShowName) { + this.senderShowName = senderShowName; + } + + public String getSenderShowName() { + if (TextUtils.isEmpty(senderShowName)) { + return messageSender; + } + return senderShowName; + } + } + +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/TUIMessageBean.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/TUIMessageBean.java new file mode 100644 index 0000000..f35aabc --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/TUIMessageBean.java @@ -0,0 +1,477 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import android.text.TextUtils; +import com.tencent.imsdk.v2.V2TIMManager; +import com.tencent.imsdk.v2.V2TIMMessage; +import com.tencent.imsdk.v2.V2TIMUserFullInfo; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.TIMCommonService; +import com.tencent.qcloud.tuikit.timcommon.util.MessageBuilder; +import com.tencent.qcloud.tuikit.timcommon.util.MessageParser; +import com.tencent.qcloud.tuikit.timcommon.util.TIMCommonConstants; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public abstract class TUIMessageBean implements Serializable { + + public static final int MSG_STATUS_SENDING = V2TIMMessage.V2TIM_MSG_STATUS_SENDING; + public static final int MSG_STATUS_SEND_SUCCESS = V2TIMMessage.V2TIM_MSG_STATUS_SEND_SUCC; + public static final int MSG_STATUS_SEND_FAIL = V2TIMMessage.V2TIM_MSG_STATUS_SEND_FAIL; + public static final int MSG_STATUS_REVOKE = V2TIMMessage.V2TIM_MSG_STATUS_LOCAL_REVOKED; + + public static final int MSG_SOURCE_UNKNOWN = 0; + + public static final int MSG_SOURCE_ONLINE_PUSH = 1; + + public static final int MSG_SOURCE_GET_HISTORY = 2; + + private V2TIMMessage v2TIMMessage; + private long msgTime; + private String extra; + private String id; + private boolean isGroup; + private boolean isSelf = true; + private int status; + private String selectText; + private boolean excludeFromHistory; + private boolean isUseMsgReceiverAvatar = false; + private boolean isEnableForward = true; + private UserBean revoker; + private boolean hasRiskContent = false; + private int messageSource = 0; + private MessageReceiptInfo messageReceiptInfo; + private MessageRepliesBean messageRepliesBean; + private boolean hasReaction = false; + private Map userBeanMap = new LinkedHashMap<>(); + private boolean isSending = false; + private boolean isProcessing = false; + private Object processingThumbnail; + private String userId = ""; + private String groupId = ""; + + public void setExcludeFromHistory(boolean excludeFromHistory) { + this.excludeFromHistory = excludeFromHistory; + } + + public boolean isExcludeFromHistory() { + return excludeFromHistory; + } + + public void setUseMsgReceiverAvatar(boolean useMsgReceiverAvatar) { + isUseMsgReceiverAvatar = useMsgReceiverAvatar; + } + + public boolean isUseMsgReceiverAvatar() { + return isUseMsgReceiverAvatar; + } + + public boolean isEnableForward() { + return isEnableForward; + } + + public void setEnableForward(boolean enableForward) { + isEnableForward = enableForward; + } + + public MessageRepliesBean getMessageRepliesBean() { + return messageRepliesBean; + } + + public void setMessageRepliesBean(MessageRepliesBean messageRepliesBean) { + this.messageRepliesBean = messageRepliesBean; + MessageBuilder.mergeCloudCustomData(this, TIMCommonConstants.MESSAGE_REPLIES_KEY, messageRepliesBean); + } + + public void setMessageReceiptInfo(MessageReceiptInfo messageReceiptInfo) { + this.messageReceiptInfo = messageReceiptInfo; + } + + public long getReadCount() { + if (messageReceiptInfo != null) { + return messageReceiptInfo.getReadCount(); + } + return 0; + } + + public long getUnreadCount() { + if (messageReceiptInfo != null) { + return messageReceiptInfo.getUnreadCount(); + } + return 0; + } + + public void setCommonAttribute(V2TIMMessage v2TIMMessage) { + msgTime = System.currentTimeMillis() / 1000; + this.v2TIMMessage = v2TIMMessage; + + if (v2TIMMessage == null) { + return; + } + + id = v2TIMMessage.getMsgID(); + isGroup = !TextUtils.isEmpty(v2TIMMessage.getGroupID()); + hasRiskContent = v2TIMMessage.hasRiskContent(); + if (v2TIMMessage.getStatus() == V2TIMMessage.V2TIM_MSG_STATUS_LOCAL_REVOKED) { + status = MSG_STATUS_REVOKE; + if (isSelf()) { + extra = TIMCommonService.getAppContext().getString(R.string.revoke_tips_you); + } else if (isGroup) { + extra = "\"" + getSender() + "\"" + TIMCommonService.getAppContext().getString(R.string.revoke_tips); + } else { + extra = TIMCommonService.getAppContext().getString(R.string.revoke_tips_other); + } + } else { + if (isSelf()) { + if (v2TIMMessage.getStatus() == V2TIMMessage.V2TIM_MSG_STATUS_SEND_FAIL) { + status = MSG_STATUS_SEND_FAIL; + } else if (v2TIMMessage.getStatus() == V2TIMMessage.V2TIM_MSG_STATUS_SEND_SUCC) { + status = MSG_STATUS_SEND_SUCCESS; + } else if (v2TIMMessage.getStatus() == V2TIMMessage.V2TIM_MSG_STATUS_SENDING) { + status = MSG_STATUS_SENDING; + } + } + } + + messageRepliesBean = MessageParser.parseMessageReplies(this); + } + + public boolean isPeerRead() { + if (messageReceiptInfo != null) { + return messageReceiptInfo.isPeerRead(); + } + return false; + } + + public boolean hasRiskContent() { + return hasRiskContent; + } + + public boolean isAllRead() { + return getUnreadCount() == 0 && getReadCount() > 0; + } + + public boolean isUnread() { + return getReadCount() == 0; + } + + /** + * + * Get a summary of messages to display in the conversation list + * @return + */ + public String onGetDisplayString() { + return getExtra(); + } + + public abstract void onProcessMessage(V2TIMMessage v2TIMMessage); + + public final long getMessageTime() { + if (v2TIMMessage != null) { + long timestamp = v2TIMMessage.getTimestamp(); + if (timestamp != 0) { + return timestamp; + } + } + return msgTime; + } + + public long getMsgSeq() { + if (v2TIMMessage != null) { + return v2TIMMessage.getSeq(); + } + return 0; + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return id; + } + + public String getUserId() { + if (v2TIMMessage != null) { + return v2TIMMessage.getUserID(); + } + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public boolean isSelf() { + if (v2TIMMessage != null) { + return v2TIMMessage.isSelf(); + } + return isSelf; + } + + public void setSelf(boolean self) { + isSelf = self; + } + + public String getSender() { + String sender = null; + if (v2TIMMessage != null) { + sender = v2TIMMessage.getSender(); + } + if (TextUtils.isEmpty(sender)) { + sender = V2TIMManager.getInstance().getLoginUser(); + } + return sender; + } + + public V2TIMMessage getV2TIMMessage() { + return v2TIMMessage; + } + + public boolean isGroup() { + return isGroup; + } + + public void setGroup(boolean group) { + isGroup = group; + } + + public String getGroupId() { + if (v2TIMMessage != null) { + return v2TIMMessage.getGroupID(); + } + return groupId; + } + + public String getNameCard() { + if (v2TIMMessage != null) { + return v2TIMMessage.getNameCard(); + } + return ""; + } + + public String getNickName() { + if (v2TIMMessage != null) { + return v2TIMMessage.getNickName(); + } + return ""; + } + + public String getFriendRemark() { + if (v2TIMMessage != null) { + return v2TIMMessage.getFriendRemark(); + } + return ""; + } + + public String getUserDisplayName() { + String displayName; + if (!TextUtils.isEmpty(getNameCard())) { + displayName = getNameCard(); + } else if (!TextUtils.isEmpty(getFriendRemark())) { + displayName = getFriendRemark(); + } else if (!TextUtils.isEmpty(getNickName())) { + displayName = getNickName(); + } else { + displayName = getSender(); + } + return displayName; + } + + public String getFaceUrl() { + if (v2TIMMessage != null) { + return v2TIMMessage.getFaceUrl(); + } + return ""; + } + + public void setStatus(int status) { + this.status = status; + } + + public int getStatus() { + return status; + } + + public void setExtra(String extra) { + this.extra = extra; + } + + public String getExtra() { + return extra; + } + + public int getMsgType() { + if (v2TIMMessage != null) { + return v2TIMMessage.getElemType(); + } else { + return V2TIMMessage.V2TIM_ELEM_TYPE_NONE; + } + } + + public boolean isNeedReadReceipt() { + if (v2TIMMessage != null) { + return v2TIMMessage.isNeedReadReceipt(); + } + return false; + } + + public void setNeedReadReceipt(boolean isNeedReceipt) { + if (v2TIMMessage != null) { + v2TIMMessage.setNeedReadReceipt(isNeedReceipt); + } + } + + public void setV2TIMMessage(V2TIMMessage v2TIMMessage) { + this.v2TIMMessage = v2TIMMessage; + setCommonAttribute(v2TIMMessage); + onProcessMessage(v2TIMMessage); + } + + public void update(TUIMessageBean messageBean) { + setV2TIMMessage(messageBean.getV2TIMMessage()); + } + + public String getSelectText() { + return selectText; + } + + public void setSelectText(String text) { + this.selectText = text; + } + + public MessageFeature isSupportTyping() { + return MessageParser.isSupportTyping(this); + } + + public void setMessageTypingFeature(MessageFeature messageFeature) { + MessageBuilder.mergeCloudCustomData(this, TIMCommonConstants.MESSAGE_FEATURE_KEY, messageFeature); + } + + public UserBean getRevoker() { + if (revoker != null) { + return revoker; + } + if (v2TIMMessage != null) { + V2TIMUserFullInfo fullInfo = v2TIMMessage.getRevokerInfo(); + if (fullInfo != null) { + revoker = new UserBean(); + revoker.setUserId(fullInfo.getUserID()); + revoker.setNickName(fullInfo.getNickName()); + revoker.setFaceUrl(fullInfo.getFaceUrl()); + return revoker; + } + } + return null; + } + + public void setRevoker(UserBean revoker) { + this.revoker = revoker; + } + + public String getRevokeReason() { + if (v2TIMMessage != null) { + return v2TIMMessage.getRevokeReason(); + } + return null; + } + + public void setHasRiskContent(boolean hasRiskContent) { + this.hasRiskContent = hasRiskContent; + } + + public int getMessageSource() { + return messageSource; + } + + public void setMessageSource(int messageSource) { + this.messageSource = messageSource; + } + + public boolean customReloadWithNewMsg(V2TIMMessage v2TIMMessage) { + return false; + } + + public boolean isHasReaction() { + return hasReaction; + } + + public void setHasReaction(boolean hasReaction) { + this.hasReaction = hasReaction; + } + + public boolean isRevoked() { + return getStatus() == TUIMessageBean.MSG_STATUS_REVOKE; + } + + public void setUserBean(String userID, UserBean userBean) { + userBeanMap.put(userID, userBean); + if (messageRepliesBean != null) { + List replyBeanList = messageRepliesBean.getReplies(); + if (replyBeanList != null && !replyBeanList.isEmpty()) { + for (MessageRepliesBean.ReplyBean replyBean : replyBeanList) { + if (userBean != null && TextUtils.equals(replyBean.getMessageSender(), userID)) { + replyBean.setSenderFaceUrl(userBean.getFaceUrl()); + replyBean.setSenderShowName(userBean.getDisplayName()); + } + } + } + } + } + + public UserBean getUserBean(String userID) { + return userBeanMap.get(userID); + } + + public boolean isSending() { + return isSending; + } + + public void setSending(boolean sending) { + this.isSending = sending; + } + + public Set getAdditionalUserIDList() { + Set userIdSet = new HashSet<>(); + MessageRepliesBean messageRepliesBean = getMessageRepliesBean(); + if (messageRepliesBean != null && messageRepliesBean.getRepliesSize() > 0) { + List replyBeanList = messageRepliesBean.getReplies(); + for (MessageRepliesBean.ReplyBean replyBean : replyBeanList) { + userIdSet.add(replyBean.getMessageSender()); + } + } + return userIdSet; + } + + public void setProcessing(boolean processing) { + isProcessing = processing; + } + + public boolean isProcessing() { + return isProcessing; + } + + public Object getProcessingThumbnail() { + return processingThumbnail; + } + + public void setProcessingThumbnail(Object processingThumbnail) { + this.processingThumbnail = processingThumbnail; + } + + public boolean needAsyncGetDisplayString() { + return false; + } + + public Class getReplyQuoteBeanClass() { + return null; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/TUIReplyQuoteBean.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/TUIReplyQuoteBean.java new file mode 100644 index 0000000..df29361 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/TUIReplyQuoteBean.java @@ -0,0 +1,42 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import java.io.Serializable; + +public abstract class TUIReplyQuoteBean implements Serializable { + private T messageBean; + protected String defaultAbstract; + protected int messageType; + + public abstract void onProcessReplyQuoteBean(T messageBean); + + public void setMessageBean(T messageBean) { + this.messageBean = messageBean; + } + + public void setDefaultAbstract(String defaultAbstract) { + this.defaultAbstract = defaultAbstract; + } + + public void setMessageType(int messageType) { + this.messageType = messageType; + } + + public int getMessageType() { + return messageType; + } + + public T getMessageBean() { + return messageBean; + } + + public boolean hasRiskContent() { + if (messageBean != null) { + return messageBean.hasRiskContent(); + } + return false; + } + + public String getDefaultAbstract() { + return defaultAbstract; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/UserBean.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/UserBean.java new file mode 100644 index 0000000..a13aad6 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/bean/UserBean.java @@ -0,0 +1,82 @@ +package com.tencent.qcloud.tuikit.timcommon.bean; + +import android.text.TextUtils; +import java.io.Serializable; + +public class UserBean implements Serializable { + protected String userId; + protected String nickName; + protected String nameCard; + protected String friendRemark; + protected String faceUrl; + protected String signature; + protected long birthday; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public String getFriendRemark() { + return friendRemark; + } + + public void setFriendRemark(String friendRemark) { + this.friendRemark = friendRemark; + } + + public void setNameCard(String nameCard) { + this.nameCard = nameCard; + } + + public String getNameCard() { + return nameCard; + } + + public String getDisplayName() { + if (!TextUtils.isEmpty(nameCard)) { + return nameCard; + } else if (!TextUtils.isEmpty(friendRemark)) { + return friendRemark; + } else if (!TextUtils.isEmpty(nickName)) { + return nickName; + } else { + return userId; + } + } + + public String getFaceUrl() { + return faceUrl; + } + + public void setFaceUrl(String faceUrl) { + this.faceUrl = faceUrl; + } + + public String getSignature() { + return signature; + } + + public long getBirthday() { + return birthday; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public void setBirthday(long birthday) { + this.birthday = birthday; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/MessageBaseHolder.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/MessageBaseHolder.java new file mode 100644 index 0000000..bfdc342 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/MessageBaseHolder.java @@ -0,0 +1,181 @@ +package com.tencent.qcloud.tuikit.timcommon.classicui.widget.message; + +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.CheckBox; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; +import androidx.recyclerview.widget.RecyclerView; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; +import com.tencent.qcloud.tuikit.timcommon.component.highlight.HighlightPresenter; +import com.tencent.qcloud.tuikit.timcommon.config.classicui.TUIConfigClassic; +import com.tencent.qcloud.tuikit.timcommon.interfaces.HighlightListener; +import com.tencent.qcloud.tuikit.timcommon.interfaces.ICommonMessageAdapter; +import com.tencent.qcloud.tuikit.timcommon.interfaces.OnItemClickListener; +import com.tencent.qcloud.tuikit.timcommon.util.DateTimeUtil; +import java.util.Date; + +public abstract class MessageBaseHolder extends RecyclerView.ViewHolder { + public ICommonMessageAdapter mAdapter; + protected OnItemClickListener onItemClickListener; + + public TextView chatTimeText; + public FrameLayout msgContentFrame; + public LinearLayout msgReplyDetailLayout; + public LinearLayout msgArea; + public LinearLayout msgAreaAndReply; + public FrameLayout reactionArea; + public CheckBox mMutiSelectCheckBox; + public RelativeLayout rightGroupLayout; + public RelativeLayout mContentLayout; + private HighlightListener highlightListener; + protected T currentMessageBean; + + public MessageBaseHolder(View itemView) { + super(itemView); + chatTimeText = itemView.findViewById(R.id.message_top_time_tv); + msgContentFrame = itemView.findViewById(R.id.msg_content_fl); + msgReplyDetailLayout = itemView.findViewById(R.id.msg_reply_detail_fl); + reactionArea = itemView.findViewById(R.id.message_reaction_area); + msgArea = itemView.findViewById(R.id.msg_area); + msgAreaAndReply = itemView.findViewById(R.id.msg_area_and_reply); + mMutiSelectCheckBox = itemView.findViewById(R.id.select_checkbox); + rightGroupLayout = itemView.findViewById(R.id.right_group_layout); + mContentLayout = itemView.findViewById(R.id.message_content_layout); + initVariableLayout(); + } + + public abstract int getVariableLayout(); + + private void setVariableLayout(int resId) { + if (msgContentFrame.getChildCount() == 0) { + View.inflate(itemView.getContext(), resId, msgContentFrame); + } + } + + private void initVariableLayout() { + if (getVariableLayout() != 0) { + setVariableLayout(getVariableLayout()); + } + } + + public void setAdapter(ICommonMessageAdapter adapter) { + mAdapter = adapter; + } + + public void setOnItemClickListener(OnItemClickListener listener) { + this.onItemClickListener = listener; + } + + public OnItemClickListener getOnItemClickListener() { + return this.onItemClickListener; + } + + public void layoutViews(final T msg, final int position) { + currentMessageBean = msg; + registerHighlightListener(msg.getId()); + setChatTimeStyle(); + + if (position > 1) { + TUIMessageBean last = mAdapter.getItem(position - 1); + if (last != null) { + if (msg.getMessageTime() - last.getMessageTime() >= 5 * 60) { + chatTimeText.setVisibility(View.VISIBLE); + chatTimeText.setText(DateTimeUtil.getTimeFormatText(new Date(msg.getMessageTime() * 1000))); + } else { + chatTimeText.setVisibility(View.GONE); + } + } + } else { + chatTimeText.setVisibility(View.VISIBLE); + chatTimeText.setText(DateTimeUtil.getTimeFormatText(new Date(msg.getMessageTime() * 1000))); + } + } + + private void setChatTimeStyle() { + Drawable chatTimeBubble = TUIConfigClassic.getChatTimeBubble(); + if (chatTimeBubble != null) { + chatTimeText.setBackground(chatTimeBubble); + } + int chatTimeFontColor = TUIConfigClassic.getChatTimeFontColor(); + if (chatTimeFontColor != TUIConfigClassic.UNDEFINED) { + chatTimeText.setTextColor(chatTimeFontColor); + } + int chatTimeFontSize = TUIConfigClassic.getChatTimeFontSize(); + if (chatTimeFontSize != TUIConfigClassic.UNDEFINED) { + chatTimeText.setTextSize(chatTimeFontSize); + } + } + + private void registerHighlightListener(String msgID) { + if (highlightListener == null) { + highlightListener = new HighlightListener() { + @Override + public void onHighlightStart() {} + + @Override + public void onHighlightEnd() { + clearHighLightBackground(); + } + + @Override + public void onHighlightUpdate(int color) { + setHighLightBackground(color); + } + }; + } + HighlightPresenter.registerHighlightListener(msgID, highlightListener); + } + + public void onRecycled() { + if (currentMessageBean != null) { + HighlightPresenter.unregisterHighlightListener(currentMessageBean.getId()); + } + } + + public void setMessageBubbleZeroPadding() { + if (msgArea == null) { + return; + } + msgArea.setPaddingRelative(0, 0, 0, 0); + } + + public void setMessageBubbleBackground(int resID) { + if (msgArea == null) { + return; + } + msgArea.setBackgroundResource(resID); + } + + public void setMessageBubbleBackground(Drawable drawable) { + if (msgArea == null) { + return; + } + msgArea.setBackground(drawable); + } + + public Drawable getMessageBubbleBackground() { + if (msgArea == null) { + return null; + } + return msgArea.getBackground(); + } + + public void setHighLightBackground(int color) { + Drawable drawable = getMessageBubbleBackground(); + if (drawable != null) { + drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + } + + public void clearHighLightBackground() { + Drawable drawable = getMessageBubbleBackground(); + if (drawable != null) { + drawable.setColorFilter(null); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/MessageContentHolder.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/MessageContentHolder.java new file mode 100644 index 0000000..8b13c0c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/MessageContentHolder.java @@ -0,0 +1,708 @@ +package com.tencent.qcloud.tuikit.timcommon.classicui.widget.message; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.TextView; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.RecyclerView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.tencent.imsdk.v2.V2TIMManager; +import com.tencent.imsdk.v2.V2TIMMessage; +import com.tencent.imsdk.v2.V2TIMUserFullInfo; +import com.tencent.imsdk.v2.V2TIMValueCallback; +import com.tencent.qcloud.tuicore.TUIConstants; +import com.tencent.qcloud.tuicore.TUICore; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.bean.MessageRepliesBean; +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; +import com.tencent.qcloud.tuikit.timcommon.config.classicui.TUIConfigClassic; +import com.tencent.qcloud.tuikit.timcommon.util.DateTimeUtil; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; +import com.tencent.qcloud.tuikit.timcommon.util.TUIUtil; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public abstract class MessageContentHolder extends MessageBaseHolder { + public ImageView leftUserIcon; + public ImageView rightUserIcon; + public TextView leftUserNameText; + public LinearLayout msgContentLinear; + public View riskContentLine; + public TextView riskContentText; + public ProgressBar sendingProgress; + public ImageView statusImage; + public TextView isReadText; + public TextView unreadAudioText; + public TextView messageDetailsTimeTv; + private FrameLayout bottomContentFrameLayout; + private View bottomFailedIv; + + public boolean isForwardMode = false; + public boolean isReplyDetailMode = false; + public boolean isMultiSelectMode = false; + + private List mForwardDataSource = new ArrayList<>(); + protected SelectionHelper selectionHelper; + + // Whether to display the bottom content. The merged-forwarded message details activity does not display the bottom content. + protected boolean isNeedShowBottomLayout = true; + protected boolean isShowRead = false; + private Fragment fragment; + private RecyclerView recyclerView; + protected boolean hasRiskContent = false; + protected boolean isLayoutOnStart = true; + + public MessageContentHolder(View itemView) { + super(itemView); + leftUserIcon = itemView.findViewById(R.id.left_user_icon_view); + rightUserIcon = itemView.findViewById(R.id.right_user_icon_view); + leftUserNameText = itemView.findViewById(R.id.left_user_name_tv); + msgContentLinear = itemView.findViewById(R.id.msg_content_ll); + riskContentLine = itemView.findViewById(R.id.risk_content_line); + riskContentText = itemView.findViewById(R.id.risk_content_text); + statusImage = itemView.findViewById(R.id.message_status_iv); + sendingProgress = itemView.findViewById(R.id.message_sending_pb); + sendingProgress.getIndeterminateDrawable().mutate(); + isReadText = itemView.findViewById(R.id.is_read_tv); + unreadAudioText = itemView.findViewById(R.id.audio_unread); + messageDetailsTimeTv = itemView.findViewById(R.id.msg_detail_time_tv); + bottomContentFrameLayout = itemView.findViewById(R.id.bottom_content_fl); + bottomFailedIv = itemView.findViewById(R.id.bottom_failed_iv); + } + + public void setFragment(Fragment fragment) { + this.fragment = fragment; + } + + public void setRecyclerView(RecyclerView recyclerView) { + this.recyclerView = recyclerView; + } + + public RecyclerView getRecyclerView() { + return this.recyclerView; + } + + public void setForwardDataSource(List dataSource) { + if (dataSource == null || dataSource.isEmpty()) { + mForwardDataSource = null; + } + + List mediaSource = new ArrayList<>(); + for (TUIMessageBean messageBean : dataSource) { + int type = messageBean.getMsgType(); + if (type == V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE || type == V2TIMMessage.V2TIM_ELEM_TYPE_VIDEO) { + mediaSource.add(messageBean); + } + } + mForwardDataSource = mediaSource; + } + + public List getForwardDataSource() { + return mForwardDataSource; + } + + @Override + public void layoutViews(final T msg, final int position) { + Context context = itemView.getContext(); + if (TUIUtil.isActivityDestroyed(context)) { + return; + } + + hasRiskContent = msg.hasRiskContent(); + super.layoutViews(msg, position); + setLayoutAlignment(msg); + setUserIcon(msg); + setUserName(msg); + loadAvatar(msg); + setSendingProgress(msg); + setStatusImage(msg); + setMessageBubbleBackground(); + setOnClickListener(msg, position); + + if (rightGroupLayout != null) { + rightGroupLayout.setVisibility(View.VISIBLE); + } + msgContentLinear.setVisibility(View.VISIBLE); + + setReadStatus(msg); + + if (isReplyDetailMode) { + chatTimeText.setVisibility(View.GONE); + } + + setReplyContent(msg); + setReactContent(msg); + if (isNeedShowBottomLayout) { + setBottomContent(msg); + } + bottomFailedIv.setVisibility(View.GONE); + if (hasRiskContent) { + bottomContentFrameLayout.setBackgroundResource(R.drawable.chat_message_bottom_area_risk_bg); + if (bottomContentFrameLayout.getVisibility() == View.VISIBLE) { + bottomFailedIv.setVisibility(View.VISIBLE); + } + riskContentLine.setVisibility(View.VISIBLE); + } else { + riskContentLine.setVisibility(View.GONE); + bottomContentFrameLayout.setBackgroundResource(R.drawable.chat_message_bottom_area_bg); + } + + setMessageBubbleDefaultPadding(); + layoutVariableViews(msg, position); + } + + private void setReadStatus(T msg) { + // clear isReadText status + isReadText.setTextColor(isReadText.getResources().getColor(R.color.text_gray1)); + isReadText.setOnClickListener(null); + + if (isForwardMode || isReplyDetailMode) { + isReadText.setVisibility(View.GONE); + unreadAudioText.setVisibility(View.GONE); + } else { + if (isShowRead) { + if (msg.isSelf() && TUIMessageBean.MSG_STATUS_SEND_SUCCESS == msg.getStatus()) { + if (!msg.isNeedReadReceipt()) { + isReadText.setVisibility(View.GONE); + } else { + showReadText(msg); + } + } else { + isReadText.setVisibility(View.GONE); + } + } + unreadAudioText.setVisibility(View.GONE); + } + } + + private void setLayoutAlignment(TUIMessageBean msg) { + if (isForwardMode || isReplyDetailMode) { + isLayoutOnStart = true; + } else { + if (msg.isSelf()) { + isLayoutOnStart = false; + } else { + isLayoutOnStart = true; + } + } + if (isForwardMode || isReplyDetailMode) { + msgContentLinear.removeView(msgAreaAndReply); + msgContentLinear.addView(msgAreaAndReply); + } else { + if (msg.isSelf()) { + msgContentLinear.removeView(msgAreaAndReply); + msgContentLinear.addView(msgAreaAndReply); + } else { + msgContentLinear.removeView(msgAreaAndReply); + msgContentLinear.addView(msgAreaAndReply, 0); + } + } + setGravity(isLayoutOnStart); + } + + private void setMessageBubbleBackground() { + if (!TUIConfigClassic.isEnableMessageBubbleStyle()) { + setMessageBubbleBackground(null); + return; + } + + Drawable sendBubble = TUIConfigClassic.getSendBubbleBackground(); + Drawable receiveBubble = TUIConfigClassic.getReceiveBubbleBackground(); + Drawable sendErrorBubble = TUIConfigClassic.getSendErrorBubbleBackground(); + Drawable receiveErrorBubble = TUIConfigClassic.getReceiveErrorBubbleBackground(); + + if (hasRiskContent) { + if (!isLayoutOnStart) { + if (sendErrorBubble != null) { + setMessageBubbleBackground(sendErrorBubble); + } else { + setMessageBubbleBackground(R.drawable.chat_message_popup_risk_content_border_right); + } + } else { + if (receiveErrorBubble != null) { + setMessageBubbleBackground(receiveErrorBubble); + } else { + setMessageBubbleBackground(R.drawable.chat_message_popup_risk_content_border_left); + } + } + } else { + setRiskContent(null); + if (isLayoutOnStart) { + if (receiveBubble != null) { + setMessageBubbleBackground(receiveBubble); + } else { + setMessageBubbleBackground(TUIThemeManager.getAttrResId(itemView.getContext(), R.attr.chat_bubble_other_bg)); + } + } else { + if (sendBubble != null) { + setMessageBubbleBackground(sendBubble); + } else { + setMessageBubbleBackground(TUIThemeManager.getAttrResId(itemView.getContext(), R.attr.chat_bubble_self_bg)); + } + } + } + } + + protected void setStatusImage(T msg) { + statusImage.setVisibility(View.GONE); + if (hasRiskContent) { + statusImage.setVisibility(View.VISIBLE); + } else { + if (msg.getStatus() == TUIMessageBean.MSG_STATUS_SEND_FAIL) { + statusImage.setVisibility(View.VISIBLE); + statusImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onSendFailBtnClick(statusImage, msg); + } + } + }); + } + } + } + + protected void setRiskContent(String riskContent) { + if (TextUtils.isEmpty(riskContent)) { + riskContentLine.setVisibility(View.GONE); + riskContentText.setVisibility(View.GONE); + } else { + riskContentLine.setVisibility(View.VISIBLE); + riskContentText.setVisibility(View.VISIBLE); + riskContentText.setText(riskContent); + } + } + + private void setOnClickListener(T msg, int position) { + msgContentFrame.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(v, msg); + } + return true; + } + }); + + msgArea.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgArea, msg); + } + return true; + } + }); + + leftUserIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (onItemClickListener != null) { + onItemClickListener.onUserIconClick(view, msg); + } + } + }); + leftUserIcon.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + if (onItemClickListener != null) { + onItemClickListener.onUserIconLongClick(view, msg); + } + return true; + } + }); + rightUserIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (onItemClickListener != null) { + onItemClickListener.onUserIconClick(view, msg); + } + } + }); + + if (msg.getStatus() == TUIMessageBean.MSG_STATUS_SEND_FAIL) { + msgContentFrame.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgContentFrame, msg); + } + } + }); + } else { + msgContentFrame.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageClick(msgContentFrame, msg); + } + } + }); + } + } + + private void setSendingProgress(T msg) { + if (isForwardMode || isReplyDetailMode) { + hideSendingProgress(); + } else { + if (msg.isSelf()) { + if (msg.isSending()) { + showSendingProgress(); + } else { + hideSendingProgress(); + } + } else { + hideSendingProgress(); + } + } + } + + protected void showSendingProgress() { + sendingProgress.setVisibility(View.VISIBLE); + Drawable drawable = sendingProgress.getIndeterminateDrawable(); + if (drawable instanceof Animatable) { + ((Animatable) drawable).start(); + } + } + + protected void hideSendingProgress() { + sendingProgress.setVisibility(View.GONE); + } + + @SuppressLint("WrongConstant") + private void setUserName(T msg) { + if (isForwardMode || isReplyDetailMode) { + leftUserNameText.setVisibility(View.VISIBLE); + } else { + if (isLayoutOnStart) { + if (TUIConfigClassic.getReceiveNickNameVisibility() != TUIConfigClassic.UNDEFINED) { + leftUserNameText.setVisibility(TUIConfigClassic.getReceiveNickNameVisibility()); + } else { + if (msg.isGroup()) { + leftUserNameText.setVisibility(View.VISIBLE); + } else { + leftUserNameText.setVisibility(View.GONE); + } + } + } else { + leftUserNameText.setVisibility(View.GONE); + } + } + if (TUIConfigClassic.getReceiveNickNameColor() != TUIConfigClassic.UNDEFINED) { + leftUserNameText.setTextColor(TUIConfigClassic.getReceiveNickNameColor()); + } + + if (TUIConfigClassic.getReceiveNickNameFontSize() != TUIConfigClassic.UNDEFINED) { + leftUserNameText.setTextSize(TUIConfigClassic.getReceiveNickNameFontSize()); + } + + leftUserNameText.setText(msg.getUserDisplayName()); + } + + private void setUserIcon(T msg) { + if (isForwardMode || isReplyDetailMode) { + leftUserIcon.setVisibility(View.VISIBLE); + rightUserIcon.setVisibility(View.GONE); + } else { + if (msg.isSelf()) { + leftUserIcon.setVisibility(View.GONE); + rightUserIcon.setVisibility(View.VISIBLE); + } else { + leftUserIcon.setVisibility(View.VISIBLE); + rightUserIcon.setVisibility(View.GONE); + } + } + } + + private void setBottomContent(TUIMessageBean msg) { + HashMap param = new HashMap<>(); + param.put(TUIConstants.TUIChat.MESSAGE_BEAN, msg); + param.put(TUIConstants.TUIChat.CHAT_RECYCLER_VIEW, recyclerView); + param.put(TUIConstants.TUIChat.FRAGMENT, fragment); + + TUICore.raiseExtension(TUIConstants.TUIChat.Extension.MessageBottom.CLASSIC_EXTENSION_ID, bottomContentFrameLayout, param); + } + + private void loadAvatar(TUIMessageBean msg) { + Drawable drawable = TUIConfigClassic.getDefaultAvatarImage(); + if (drawable != null) { + setupAvatar(drawable); + return; + } + + if (msg.isUseMsgReceiverAvatar() && mAdapter != null) { + String cachedFaceUrl = mAdapter.getUserFaceUrlCache().getCachedFaceUrl(msg.getSender()); + if (cachedFaceUrl == null) { + List idList = new ArrayList<>(); + idList.add(msg.getSender()); + V2TIMManager.getInstance().getUsersInfo(idList, new V2TIMValueCallback>() { + @Override + public void onSuccess(List v2TIMUserFullInfos) { + if (v2TIMUserFullInfos == null || v2TIMUserFullInfos.isEmpty()) { + return; + } + V2TIMUserFullInfo userInfo = v2TIMUserFullInfos.get(0); + String faceUrl = userInfo.getFaceUrl(); + if (TextUtils.isEmpty(userInfo.getFaceUrl())) { + faceUrl = ""; + } + mAdapter.getUserFaceUrlCache().pushFaceUrl(userInfo.getUserID(), faceUrl); + mAdapter.onItemRefresh(msg); + } + + @Override + public void onError(int code, String desc) { + setupAvatar(""); + } + }); + } else { + setupAvatar(cachedFaceUrl); + } + } else { + setupAvatar(msg.getFaceUrl()); + } + } + + private void setupAvatar(Object faceUrl) { + int avatarSize = TUIConfigClassic.getMessageListAvatarSize(); + if (avatarSize == TUIConfigClassic.UNDEFINED) { + avatarSize = ScreenUtil.dip2px(41); + } + ViewGroup.LayoutParams params = leftUserIcon.getLayoutParams(); + params.width = avatarSize; + if (leftUserIcon.getVisibility() == View.INVISIBLE) { + params.height = 1; + } else { + params.height = avatarSize; + } + leftUserIcon.setLayoutParams(params); + + params = rightUserIcon.getLayoutParams(); + params.width = avatarSize; + if (rightUserIcon.getVisibility() == View.INVISIBLE) { + params.height = 1; + } else { + params.height = avatarSize; + } + rightUserIcon.setLayoutParams(params); + + int radius = ScreenUtil.dip2px(4); + if (TUIConfigClassic.getMessageListAvatarRadius() != TUIConfigClassic.UNDEFINED) { + radius = TUIConfigClassic.getMessageListAvatarRadius(); + } + + ImageView renderedView; + if (isLayoutOnStart) { + renderedView = leftUserIcon; + } else { + renderedView = rightUserIcon; + } + Glide.with(itemView.getContext()).clear(renderedView); + int defaultIconResId = TUIThemeManager.getAttrResId(leftUserIcon.getContext(), com.tencent.qcloud.tuikit.timcommon.R.attr.core_default_user_icon); + RequestBuilder placeholderRequest = Glide.with(itemView.getContext()) + .load(defaultIconResId) + .transform(new RoundedCorners(radius)) + .diskCacheStrategy(DiskCacheStrategy.ALL); + RequestBuilder errorRequestBuilder = Glide.with(itemView.getContext()) + .load(defaultIconResId) + .transform(new RoundedCorners(radius)) + .diskCacheStrategy(DiskCacheStrategy.ALL); + Glide.with(itemView.getContext()) + .load(faceUrl) + .thumbnail(placeholderRequest) + .transform(new RoundedCorners(radius)) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .skipMemoryCache(false) + .dontAnimate() + .error(errorRequestBuilder) + .into(renderedView); + } + + protected void setMessageBubbleDefaultPadding() { + // after setting background, the padding will be reset + int paddingHorizontal = itemView.getResources().getDimensionPixelSize(R.dimen.chat_message_area_padding_left_right); + int paddingVertical = itemView.getResources().getDimensionPixelSize(R.dimen.chat_message_area_padding_top_bottom); + msgArea.setPaddingRelative(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical); + } + + protected void setGravity(boolean isStart) { + int gravity = isStart ? Gravity.START : Gravity.END; + msgAreaAndReply.setGravity(gravity); + ViewGroup.LayoutParams layoutParams = msgContentFrame.getLayoutParams(); + if (layoutParams instanceof FrameLayout.LayoutParams) { + ((FrameLayout.LayoutParams) layoutParams).gravity = gravity; + } else if (layoutParams instanceof LinearLayout.LayoutParams) { + ((LinearLayout.LayoutParams) layoutParams).gravity = gravity; + } + msgArea.setGravity(gravity); + msgContentFrame.setLayoutParams(layoutParams); + } + + private void setReplyContent(TUIMessageBean messageBean) { + MessageRepliesBean messageRepliesBean = messageBean.getMessageRepliesBean(); + if (messageRepliesBean != null && messageRepliesBean.getRepliesSize() > 0) { + TextView replyNumText = msgReplyDetailLayout.findViewById(R.id.reply_num); + replyNumText.setText(String.format(Locale.US, replyNumText.getResources().getString(R.string.chat_reply_num), messageRepliesBean.getRepliesSize())); + msgReplyDetailLayout.setVisibility(View.VISIBLE); + msgReplyDetailLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onReplyDetailClick(messageBean); + } + } + }); + } else { + msgReplyDetailLayout.setVisibility(View.GONE); + msgReplyDetailLayout.setOnClickListener(null); + } + if (!isReplyDetailMode) { + messageDetailsTimeTv.setVisibility(View.GONE); + } else { + messageDetailsTimeTv.setText(DateTimeUtil.getTimeFormatText(new Date(messageBean.getMessageTime() * 1000))); + messageDetailsTimeTv.setVisibility(View.VISIBLE); + msgReplyDetailLayout.setVisibility(View.GONE); + } + } + + private void setReactContent(TUIMessageBean messageBean) { + Map param = new HashMap<>(); + param.put(TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.MESSAGE, messageBean); + param.put(TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.VIEW_TYPE, + TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.VIEW_TYPE_CLASSIC); + TUICore.raiseExtension(TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.EXTENSION_ID, reactionArea, param); + } + + private void showReadText(TUIMessageBean msg) { + if (hasRiskContent) { + isReadText.setVisibility(View.GONE); + return; + } + if (msg.isGroup()) { + isReadText.setVisibility(View.VISIBLE); + if (msg.isAllRead()) { + isReadText.setText(R.string.has_all_read); + } else if (msg.isUnread()) { + isReadText.setTextColor( + isReadText.getResources().getColor(TUIThemeManager.getAttrResId(isReadText.getContext(), R.attr.chat_read_receipt_text_color))); + isReadText.setText(R.string.unread); + isReadText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onReadStatusClick(v, msg); + } + }); + } else { + long readCount = msg.getReadCount(); + if (readCount > 0) { + isReadText.setText(String.format(Locale.US, isReadText.getResources().getString(R.string.someone_has_read), readCount)); + isReadText.setTextColor( + isReadText.getResources().getColor(TUIThemeManager.getAttrResId(isReadText.getContext(), R.attr.chat_read_receipt_text_color))); + isReadText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onReadStatusClick(v, msg); + } + }); + } + } + } else { + isReadText.setVisibility(View.VISIBLE); + if (msg.isPeerRead()) { + isReadText.setText(R.string.has_read); + } else { + isReadText.setText(R.string.unread); + isReadText.setTextColor( + isReadText.getResources().getColor(TUIThemeManager.getAttrResId(isReadText.getContext(), R.attr.chat_read_receipt_text_color))); + isReadText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onReadStatusClick(v, msg); + } + }); + } + } + } + + public abstract void layoutVariableViews(final T msg, final int position); + + public void onRecycled() { + super.onRecycled(); + if (selectionHelper != null) { + selectionHelper.destroy(); + } + } + + public void onReadStatusClick(View view, TUIMessageBean messageBean) { + if (onItemClickListener != null) { + onItemClickListener.onMessageReadStatusClick(view, messageBean); + } + } + + protected void setSelectionHelper(TUIMessageBean msg, TextView textView, int position) { + if (selectionHelper == null) { + selectionHelper = new SelectionHelper(); + } + selectionHelper.setTextView(textView); + if (isMultiSelectMode || isForwardMode) { + selectionHelper.setFrozen(true); + } else { + selectionHelper.setFrozen(false); + } + selectionHelper.setSelectListener(new SelectionHelper.OnSelectListener() { + @Override + public void onTextSelected(CharSequence content) { + String selectedText = ""; + if (!TextUtils.isEmpty(content)) { + selectedText = content.toString(); + msg.setSelectText(selectedText); + SelectionHelper.setSelected(selectionHelper); + if (onItemClickListener != null) { + onItemClickListener.onTextSelected(msgArea, position, msg); + } + } + } + + @Override + public void onDismiss() { + msg.setSelectText(msg.getExtra()); + } + + @Override + public void onClickUrl(String url) {} + + @Override + public void onShowPop() {} + + @Override + public void onDismissPop() {} + }); + } + + public void setNeedShowBottomLayout(boolean needShowBottomLayout) { + isNeedShowBottomLayout = needShowBottomLayout; + } + + public void setShowRead(boolean showRead) { + isShowRead = showRead; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/SelectionHelper.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/SelectionHelper.java new file mode 100644 index 0000000..e12176f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/SelectionHelper.java @@ -0,0 +1,512 @@ +package com.tencent.qcloud.tuikit.timcommon.classicui.widget.message; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Region; +import android.text.Layout; +import android.text.Spannable; +import android.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.BackgroundColorSpan; +import android.text.style.ClickableSpan; +import android.util.Log; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.PopupWindow; +import android.widget.TextView; +import com.tencent.qcloud.tuikit.timcommon.component.face.CenterImageSpan; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; +import com.tencent.qcloud.tuikit.timcommon.util.TUIUtil; +import com.tencent.qcloud.tuikit.timcommon.util.TextUtil; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + +public class SelectionHelper { + private static final String TAG = "SelectionHelper"; + + private SelectionHandle startHandle; + private SelectionHandle endHandle; + private final SelectionInfo mSelectionInfo = new SelectionInfo(); + private OnSelectListener mSelectListener; + + private TextView textView; + private Spannable spannable; + + private int mTextViewMarginStart = 0; + private GestureDetector gestureDetector; + private GestureDetector.SimpleOnGestureListener gestureListener; + private int selectionColor; + private int handleColor; + private int handleSize; + private BackgroundColorSpan bgSpan; + private boolean frozen = false; + + private ViewTreeObserver.OnPreDrawListener mOnPreDrawListener; + private View.OnAttachStateChangeListener onAttachStateChangeListener; + private static WeakReference selectedReference; + + public static void setSelected(SelectionHelper selected) { + SelectionHelper oldSelected = getSelected(); + if (oldSelected != null && selected != oldSelected) { + oldSelected.clearSelection(); + } + selectedReference = new WeakReference<>(selected); + } + + public static void resetSelected() { + SelectionHelper selectionHelper = getSelected(); + if (selectionHelper != null) { + selectionHelper.clearSelection(); + } + } + + private static SelectionHelper getSelected() { + if (selectedReference != null) { + return selectedReference.get(); + } + return null; + } + + public void setFrozen(boolean frozen) { + this.frozen = frozen; + } + + public interface OnSelectListener { + void onTextSelected(CharSequence content); + + void onDismiss(); + + void onClickUrl(String url); + + void onShowPop(); + + void onDismissPop(); + } + + public SelectionHelper() { + selectionColor = 0x3f1470ff; + handleColor = 0xff1470ff; + handleSize = ScreenUtil.dip2px(16); + gestureListener = new GestureDetector.SimpleOnGestureListener() { + @Override + public void onShowPress(MotionEvent e) { + if (frozen) { + return; + } + initHandler(); + ClickableSpan[] spans = TextUtil.findSpansByLocation(textView, Math.round(e.getX()), Math.round(e.getY())); + if (spans != null && spans.length > 0) { + ClickableSpan span = spans[0]; + int spanStart = spannable.getSpanStart(span); + int spanEnd = spannable.getSpanEnd(span); + setSelection(spanStart, spanEnd); + } else { + selectAll(); + } + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + if (frozen) { + return super.onSingleTapUp(e); + } + ClickableSpan[] spans = TextUtil.findSpansByLocation(textView, Math.round(e.getX()), Math.round(e.getY())); + if (spans != null && spans.length > 0) { + ClickableSpan span = spans[0]; + span.onClick(textView); + } + return false; + } + }; + gestureDetector = new GestureDetector(gestureListener); + } + + public void setSelectListener(OnSelectListener selectListener) { + mSelectListener = selectListener; + } + + public void destroy() { + if (textView == null) { + return; + } + textView.removeOnAttachStateChangeListener(onAttachStateChangeListener); + textView.getViewTreeObserver().removeOnPreDrawListener(mOnPreDrawListener); + clearSelection(); + } + + public void selectAll() { + initHandler(); + if (textView.getText() instanceof Spannable) { + spannable = (Spannable) textView.getText(); + } + if (spannable == null) { + return; + } + setSelection(0, textView.getText().length()); + } + + public void setTextView(TextView textView) { + this.textView = textView; + if (textView.getText() instanceof Spannable) { + this.spannable = (Spannable) textView.getText(); + } + textView.removeOnAttachStateChangeListener(onAttachStateChangeListener); + onAttachStateChangeListener = new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) {} + + @Override + public void onViewDetachedFromWindow(View v) { + destroy(); + } + }; + textView.addOnAttachStateChangeListener(onAttachStateChangeListener); + textView.getViewTreeObserver().removeOnPreDrawListener(mOnPreDrawListener); + mOnPreDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + int[] location = new int[2]; + textView.getLocationInWindow(location); + mTextViewMarginStart = location[0]; + return true; + } + }; + textView.getViewTreeObserver().addOnPreDrawListener(mOnPreDrawListener); + textView.setMovementMethod(new LinkMovementMethodInterceptor()); + } + + private void initHandler() { + if (startHandle == null) { + startHandle = new SelectionHandle(true); + } + if (endHandle == null) { + endHandle = new SelectionHandle(false); + } + } + + private void showSelectionHandle(SelectionHandle selectionHandle) { + Layout layout = textView.getLayout(); + if (layout == null) { + return; + } + int offset = selectionHandle.isLeft ? mSelectionInfo.start : mSelectionInfo.end; + selectionHandle.show((int) layout.getPrimaryHorizontal(offset), layout.getLineBottom(layout.getLineForOffset(offset))); + } + + private void setSelection(int startPos, int endPos) { + initHandler(); + + if (startPos != -1) { + mSelectionInfo.start = startPos; + } + if (endPos != -1) { + mSelectionInfo.end = endPos; + } + if (mSelectionInfo.start > mSelectionInfo.end) { + int temp = mSelectionInfo.start; + mSelectionInfo.start = mSelectionInfo.end; + mSelectionInfo.end = temp; + } + + mSelectionInfo.selectionContent = spannable.subSequence(mSelectionInfo.start, mSelectionInfo.end).toString(); + setSelectionBg(spannable, mSelectionInfo.start, mSelectionInfo.end); + showSelectionHandle(startHandle); + showSelectionHandle(endHandle); + if (mSelectListener != null) { + mSelectListener.onTextSelected(mSelectionInfo.selectionContent); + } + } + + private void setSelectionBg(Spannable text, int start, int end) { + if (bgSpan == null) { + bgSpan = new BackgroundColorSpan(selectionColor); + } + text.setSpan(bgSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + CenterImageSpan[] allImageSpans = text.getSpans(0, text.length(), CenterImageSpan.class); + CenterImageSpan[] imageSpans = text.getSpans(start, end, CenterImageSpan.class); + if (allImageSpans != null) { + for (CenterImageSpan imageSpan : allImageSpans) { + imageSpan.setBgColor(-1); + } + } + if (imageSpans != null) { + for (CenterImageSpan imageSpan : imageSpans) { + imageSpan.setBgColor(selectionColor); + } + } + } + + private void clearSelection() { + mSelectionInfo.selectionContent = null; + clearSelectionBg(); + } + + private void clearSelectionBg() { + if (spannable == null) { + return; + } + if (bgSpan != null) { + spannable.removeSpan(bgSpan); + } + CenterImageSpan[] imageSpans = spannable.getSpans(0, spannable.length(), CenterImageSpan.class); + if (imageSpans != null) { + for (CenterImageSpan imageSpan : imageSpans) { + imageSpan.setBgColor(-1); + } + } + if (startHandle != null) { + startHandle.dismiss(); + } + if (endHandle != null) { + endHandle.dismiss(); + } + } + + private class SelectionHandle extends View { + private PopupWindow mPopupWindow; + private Paint mPaint; + + private int mCircleRadius = handleSize / 2; + private int mWidth = handleSize; + private int mHeight = handleSize; + private int mPadding = 32; + private boolean isLeft; + + public SelectionHandle(boolean isLeft) { + super(textView.getContext()); + this.isLeft = isLeft; + mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mPaint.setColor(handleColor); + + mPopupWindow = new PopupWindow(this); + mPopupWindow.setClippingEnabled(false); + mPopupWindow.setWidth(mWidth + mPadding * 2); + mPopupWindow.setHeight(mHeight + mPadding / 2); + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.drawCircle(mCircleRadius + mPadding, mCircleRadius, mCircleRadius, mPaint); + if (isLeft) { + canvas.drawRect(mCircleRadius + mPadding, 0, mCircleRadius * 2 + mPadding, mCircleRadius, mPaint); + } else { + canvas.drawRect(mPadding, 0, mCircleRadius + mPadding, mCircleRadius, mPaint); + } + } + + private int mAdjustX; + private int mAdjustY; + + private int mBeforeDragStart; + private int mBeforeDragEnd; + + @Override + public boolean onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + mBeforeDragStart = mSelectionInfo.start; + mBeforeDragEnd = mSelectionInfo.end; + mAdjustX = (int) event.getX(); + mAdjustY = (int) event.getY(); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + break; + case MotionEvent.ACTION_MOVE: + if (null != mSelectListener) { + mSelectListener.onDismissPop(); + } + int rawX = (int) event.getRawX(); + int rawY = (int) event.getRawY(); + + update(rawX + mAdjustX - mWidth - mTextViewMarginStart, rawY + mAdjustY - mHeight - (int) textView.getTextSize()); + break; + default: + break; + } + return true; + } + + private void changeDirection() { + isLeft = !isLeft; + invalidate(); + } + + public void dismiss() { + Log.e(TAG, "handler dismiss"); + mPopupWindow.dismiss(); + } + + private int[] mTempCoors = new int[2]; + + public void update(int x, int y) { + textView.getLocationInWindow(mTempCoors); + int oldOffset; + if (isLeft) { + oldOffset = mSelectionInfo.start; + } else { + oldOffset = mSelectionInfo.end; + } + + y -= mTempCoors[1]; + + int offset = getHysteresisOffset(textView, x, y, oldOffset); + + if (offset != oldOffset) { + mSelectionInfo.selectionContent = null; + if (isLeft) { + if (offset > mBeforeDragEnd) { + SelectionHandle handle = getSelectionHandle(false); + changeDirection(); + handle.changeDirection(); + mBeforeDragStart = mBeforeDragEnd; + setSelection(mBeforeDragEnd, offset); + handle.updateSelectionHandle(); + } else { + setSelection(offset, -1); + } + updateSelectionHandle(); + } else { + if (offset < mBeforeDragStart) { + SelectionHandle handle = getSelectionHandle(true); + handle.changeDirection(); + changeDirection(); + mBeforeDragEnd = mBeforeDragStart; + setSelection(offset, mBeforeDragStart); + handle.updateSelectionHandle(); + } else { + setSelection(mBeforeDragStart, offset); + } + updateSelectionHandle(); + } + } + } + + private void updateSelectionHandle() { + textView.getLocationInWindow(mTempCoors); + Layout layout = textView.getLayout(); + if (isLeft) { + mPopupWindow.update((int) layout.getPrimaryHorizontal(mSelectionInfo.start) - mWidth + getExtraX(), + layout.getLineBottom(layout.getLineForOffset(mSelectionInfo.start)) + getExtraY(), -1, -1); + } else { + mPopupWindow.update((int) layout.getPrimaryHorizontal(mSelectionInfo.end) + getExtraX(), + layout.getLineBottom(layout.getLineForOffset(mSelectionInfo.end)) + getExtraY(), -1, -1); + } + } + + public void show(int x, int y) { + textView.getLocationInWindow(mTempCoors); + int offset = isLeft ? mWidth : 0; + mPopupWindow.showAtLocation(textView, Gravity.NO_GRAVITY, x - offset + getExtraX(), y + getExtraY()); + } + + public int getExtraX() { + return mTempCoors[0] - mPadding + textView.getPaddingLeft(); + } + + public int getExtraY() { + return mTempCoors[1] + textView.getPaddingTop(); + } + } + + private SelectionHandle getSelectionHandle(boolean isLeft) { + if (startHandle.isLeft == isLeft) { + return startHandle; + } else { + return endHandle; + } + } + + private static class SelectionInfo { + public int start; + public int end; + public String selectionContent; + } + + private class LinkMovementMethodInterceptor extends LinkMovementMethod { + @Override + public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { + return gestureDetector.onTouchEvent(event); + } + } + + public static int getHysteresisOffset(TextView textView, int x, int y, int previousOffset) { + final Layout layout = textView.getLayout(); + if (layout == null) { + return -1; + } + + int line = layout.getLineForVertical(y); + + // The "HACK BLOCK"S in this function is required because of how Android Layout for + // TextView works - if 'offset' equals to the last character of a line, then + // + // * getLineForOffset(offset) will result the NEXT line + // * getPrimaryHorizontal(offset) will return 0 because the next insertion point is on the next line + // * getOffsetForHorizontal(line, x) will not return the last offset of a line no matter where x is + // These are highly undesired and is worked around with the HACK BLOCK + // + // @see Moon+ Reader/Color Note - see how it can't select the last character of a line unless you move + // the cursor to the beginning of the next line. + // + ////////////////////HACK BLOCK//////////////////////////////////////////////////// + + if (isEndOfLineOffset(layout, previousOffset)) { + // we have to minus one from the offset so that the code below to find + // the previous line can work correctly. + int left = (int) layout.getPrimaryHorizontal(previousOffset - 1); + int right = (int) layout.getLineRight(line); + int threshold = (right - left) / 2; // half the width of the last character + if (x > right - threshold) { + previousOffset -= 1; + } + } + /////////////////////////////////////////////////////////////////////////////////// + + final int previousLine = layout.getLineForOffset(previousOffset); + final int previousLineTop = layout.getLineTop(previousLine); + final int previousLineBottom = layout.getLineBottom(previousLine); + final int hysteresisThreshold = (previousLineBottom - previousLineTop) / 2; + + // If new line is just before or after previous line and y position is less than + // hysteresisThreshold away from previous line, keep cursor on previous line. + if (((line == previousLine + 1) && ((y - previousLineBottom) < hysteresisThreshold)) + || ((line == previousLine - 1) && ((previousLineTop - y) < hysteresisThreshold))) { + line = previousLine; + } + + int offset = layout.getOffsetForHorizontal(line, x); + + // This allow the user to select the last character of a line without moving the + // cursor to the next line. (As Layout.getOffsetForHorizontal does not return the + // offset of the last character of the specified line) + // + // But this function will probably get called again immediately, must decrement the offset + // by 1 to compensate for the change made below. (see previous HACK BLOCK) + /////////////////////HACK BLOCK/////////////////////////////////////////////////// + if (offset < textView.getText().length() - 1) { + if (isEndOfLineOffset(layout, offset + 1)) { + int left = (int) layout.getPrimaryHorizontal(offset); + int right = (int) layout.getLineRight(line); + int threshold = (right - left) / 2; // half the width of the last character + if (x > right - threshold) { + offset += 1; + } + } + } + ////////////////////////////////////////////////////////////////////////////////// + + return offset; + } + + private static boolean isEndOfLineOffset(Layout layout, int offset) { + return offset > 0 && layout.getLineForOffset(offset) == layout.getLineForOffset(offset - 1) + 1; + } +} \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/TUIReplyQuoteView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/TUIReplyQuoteView.java new file mode 100644 index 0000000..a0b4399 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/classicui/widget/message/TUIReplyQuoteView.java @@ -0,0 +1,31 @@ +package com.tencent.qcloud.tuikit.timcommon.classicui.widget.message; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import com.tencent.qcloud.tuikit.timcommon.bean.TUIReplyQuoteBean; + +public abstract class TUIReplyQuoteView> extends FrameLayout { + + public abstract int getLayoutResourceId(); + + public TUIReplyQuoteView(Context context) { + super(context); + int resId = getLayoutResourceId(); + if (resId != 0) { + LayoutInflater.from(context).inflate(resId, this, true); + } + } + + public abstract void onDrawReplyQuote(T quoteBean); + + /** + * + * Whether the original message sender is himself, used for different UI displays + * + * @param isSelf + */ + public void setSelf(boolean isSelf) {} + +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/BottomSelectSheet.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/BottomSelectSheet.java new file mode 100644 index 0000000..46eaa5d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/BottomSelectSheet.java @@ -0,0 +1,93 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.view.Display; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import com.tencent.qcloud.tuikit.timcommon.R; + +import java.util.ArrayList; +import java.util.List; + +public class BottomSelectSheet { + private List selectList = new ArrayList<>(); + private Dialog dialog; + private ArrayAdapter listAdapter; + private BottomSelectSheetOnClickListener onClickListener; + + public BottomSelectSheet(Context context) { + View view = View.inflate(context, R.layout.common_bottom_select_sheet, null); + dialog = new Dialog(context, R.style.BottomSelectSheet); + dialog.setContentView(view); + dialog.setCancelable(true); + dialog.setCanceledOnTouchOutside(true); + Window window = dialog.getWindow(); + window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + WindowManager m = window.getWindowManager(); + Display d = m.getDefaultDisplay(); + WindowManager.LayoutParams p = window.getAttributes(); + p.width = d.getWidth(); + p.height = ViewGroup.LayoutParams.WRAP_CONTENT; + window.setAttributes(p); + window.setGravity(Gravity.BOTTOM); + window.setWindowAnimations(R.style.BottomSelectSheet_Anim); + + final ListView listView = view.findViewById(R.id.item_list); + listAdapter = new ArrayAdapter<>(context, R.layout.common_bottom_sheet_item, R.id.sheet_item, selectList); + listView.setAdapter(listAdapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + dismiss(); + if (onClickListener != null) { + onClickListener.onSheetClick(position); + } + } + }); + + TextView cancelButton = view.findViewById(R.id.cancel_button); + cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + dismiss(); + } + }); + } + + public void dismiss() { + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + } + + public void show() { + if (dialog != null && !dialog.isShowing()) { + listAdapter.notifyDataSetChanged(); + dialog.show(); + } + } + + public void setSelectList(List selectList) { + this.selectList.clear(); + this.selectList.addAll(selectList); + } + + public void setOnClickListener(BottomSelectSheetOnClickListener onClickListener) { + this.onClickListener = onClickListener; + } + + public interface BottomSelectSheetOnClickListener { + void onSheetClick(int index); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/CustomLinearLayoutManager.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/CustomLinearLayoutManager.java new file mode 100644 index 0000000..d2ad0ea --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/CustomLinearLayoutManager.java @@ -0,0 +1,34 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +/** + * https://stackoverflow.com/questions/30458640/recyclerview-java-lang-indexoutofboundsexception-inconsistency-detected-inval + */ +public class CustomLinearLayoutManager extends LinearLayoutManager { + public CustomLinearLayoutManager(Context context) { + super(context); + } + + public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { + super(context, orientation, reverseLayout); + } + + public CustomLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + try { + super.onLayoutChildren(recycler, state); + } catch (Throwable e) { + Log.w("CustomLinearLayoutManager", "" + e.getLocalizedMessage()); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/GifSpan.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/GifSpan.java new file mode 100644 index 0000000..b47d8af --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/GifSpan.java @@ -0,0 +1,44 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.text.style.DynamicDrawableSpan; +import androidx.annotation.NonNull; + +public class GifSpan extends DynamicDrawableSpan { + private static final String TAG = "EmojiGifSpan"; + private Drawable mGifDrawable; + + public GifSpan(Drawable drawable) { + mGifDrawable = drawable; + if (mGifDrawable instanceof Animatable) { + ((Animatable) mGifDrawable).start(); + } + } + + @Override + public Drawable getDrawable() { + return mGifDrawable; + } + + @Override + public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { + Drawable drawable = getDrawable(); + Rect rect = drawable.getBounds(); + + if (fm != null) { + Paint.FontMetricsInt paintFm = paint.getFontMetricsInt(); + int drawableHeight = rect.height(); + + int center = (paintFm.ascent + paintFm.descent) / 2; + fm.ascent = center - drawableHeight / 2; + fm.descent = center + drawableHeight / 2; + fm.top = fm.ascent; + fm.bottom = fm.descent; + } + + return rect.right; + } +} \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/IndicatorView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/IndicatorView.java new file mode 100644 index 0000000..7615123 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/IndicatorView.java @@ -0,0 +1,126 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.AttributeSet; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; + +import java.util.ArrayList; + +public class IndicatorView extends LinearLayout { + private Context mContext; + private ArrayList mImageViews; + private Bitmap bmpSelect; + private Bitmap bmpNormal; + private int mHeight = 6; + private int mMaxHeight; + private AnimatorSet mPlayByInAnimatorSet; + private AnimatorSet mPlayByOutAnimatorSet; + + public IndicatorView(Context context, AttributeSet attrs) { + super(context, attrs); + this.mContext = context; + this.setOrientation(HORIZONTAL); + mMaxHeight = ScreenUtil.dip2px(mHeight); + bmpSelect = BitmapFactory.decodeResource(getResources(), R.drawable.indicator_point_select); + bmpNormal = BitmapFactory.decodeResource(getResources(), R.drawable.indicator_point_nomal); + } + + public IndicatorView(Context context) { + this(context, null); + } + + public void init(int count) { + mImageViews = new ArrayList<>(); + this.removeAllViews(); + for (int i = 0; i < count; i++) { + LayoutParams params = new LinearLayout.LayoutParams(mMaxHeight, mMaxHeight); + ImageView imageView = new ImageView(mContext); + params.setMarginStart(12); + params.setMarginEnd(12); + if (i == 0) { + imageView.setImageBitmap(bmpSelect); + } else { + imageView.setImageBitmap(bmpNormal); + } + this.addView(imageView, params); + mImageViews.add(imageView); + } + } + + public void playBy(int startPosition, int nextPosition) { + final boolean isShowInAnimOnly = false; + if (startPosition < 0 || nextPosition < 0 || nextPosition == startPosition) { + startPosition = nextPosition = 0; + } + if (mImageViews == null || mImageViews.isEmpty()) { + return; + } + if (startPosition >= mImageViews.size() || nextPosition >= mImageViews.size()) { + return; + } + + final ImageView imageViewStrat = mImageViews.get(startPosition); + final ImageView imageViewNext = mImageViews.get(nextPosition); + + ObjectAnimator anim1 = ObjectAnimator.ofFloat(imageViewStrat, "scaleX", 1.0f, 0.25f); + ObjectAnimator anim2 = ObjectAnimator.ofFloat(imageViewStrat, "scaleY", 1.0f, 0.25f); + + if (mPlayByOutAnimatorSet != null && mPlayByOutAnimatorSet.isRunning()) { + mPlayByOutAnimatorSet.cancel(); + mPlayByOutAnimatorSet = null; + } + mPlayByOutAnimatorSet = new AnimatorSet(); + mPlayByOutAnimatorSet.play(anim1).with(anim2); + mPlayByOutAnimatorSet.setDuration(100); + + ObjectAnimator animIn1 = ObjectAnimator.ofFloat(imageViewNext, "scaleX", 0.25f, 1.0f); + ObjectAnimator animIn2 = ObjectAnimator.ofFloat(imageViewNext, "scaleY", 0.25f, 1.0f); + + if (mPlayByInAnimatorSet != null && mPlayByInAnimatorSet.isRunning()) { + mPlayByInAnimatorSet.cancel(); + mPlayByInAnimatorSet = null; + } + mPlayByInAnimatorSet = new AnimatorSet(); + mPlayByInAnimatorSet.play(animIn1).with(animIn2); + mPlayByInAnimatorSet.setDuration(100); + + if (isShowInAnimOnly) { + mPlayByInAnimatorSet.start(); + return; + } + + anim1.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) {} + + @Override + public void onAnimationEnd(Animator animation) { + imageViewStrat.setImageBitmap(bmpNormal); + ObjectAnimator animFil1l = ObjectAnimator.ofFloat(imageViewStrat, "scaleX", 1.0f); + ObjectAnimator animFill2 = ObjectAnimator.ofFloat(imageViewStrat, "scaleY", 1.0f); + AnimatorSet mFillAnimatorSet = new AnimatorSet(); + mFillAnimatorSet.play(animFil1l).with(animFill2); + mFillAnimatorSet.start(); + imageViewNext.setImageBitmap(bmpSelect); + mPlayByInAnimatorSet.start(); + } + + @Override + public void onAnimationCancel(Animator animation) {} + + @Override + public void onAnimationRepeat(Animator animation) {} + }); + mPlayByOutAnimatorSet.start(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/LineControllerView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/LineControllerView.java new file mode 100644 index 0000000..d36cabc --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/LineControllerView.java @@ -0,0 +1,144 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.Switch; +import android.widget.TextView; + +import androidx.core.graphics.drawable.DrawableCompat; + +import com.tencent.qcloud.tuikit.timcommon.R; + +/** + * Custom LineControllerView + */ +public class LineControllerView extends RelativeLayout { + private String mName; + private boolean mIsBottom; + private boolean mIsTop; + private String mContent; + private boolean mIsJump; + private boolean mIsSwitch; + + protected TextView mNameText; + protected TextView mContentText; + private ImageView mNavArrowView; + protected Switch mSwitchView; + protected View bottomLine; + private View mMask; + private View container; + + public LineControllerView(Context context, AttributeSet attrs) { + super(context, attrs); + LayoutInflater.from(context).inflate(R.layout.timcommon_line_controller_view, this); + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LineControllerView, 0, 0); + try { + mName = ta.getString(R.styleable.LineControllerView_name); + mContent = ta.getString(R.styleable.LineControllerView_subject); + mIsBottom = ta.getBoolean(R.styleable.LineControllerView_isBottom, false); + mIsTop = ta.getBoolean(R.styleable.LineControllerView_isTop, false); + mIsJump = ta.getBoolean(R.styleable.LineControllerView_canNav, false); + mIsSwitch = ta.getBoolean(R.styleable.LineControllerView_isSwitch, false); + setUpView(); + } finally { + ta.recycle(); + } + } + + private void setUpView() { + mNameText = findViewById(R.id.name); + mNameText.setText(mName); + mContentText = findViewById(R.id.content); + mContentText.setText(mContent); + bottomLine = findViewById(R.id.bottom_line); + View topLine = findViewById(R.id.top_line); + bottomLine.setVisibility(mIsBottom ? VISIBLE : GONE); + topLine.setVisibility(mIsTop ? VISIBLE : GONE); + mNavArrowView = findViewById(R.id.rightArrow); + Drawable arrowDrawable = mNavArrowView.getDrawable(); + if (arrowDrawable != null) { + DrawableCompat.setAutoMirrored(arrowDrawable, true); + } + mNavArrowView.setVisibility(mIsJump ? VISIBLE : GONE); + ViewGroup contentLayout = findViewById(R.id.content_view); + contentLayout.setVisibility(mIsSwitch ? GONE : VISIBLE); + mSwitchView = findViewById(R.id.btnSwitch); + mSwitchView.setVisibility(mIsSwitch ? VISIBLE : GONE); + mMask = findViewById(R.id.disable_mask); + container = findViewById(R.id.view_container); + } + + public void setBackground(Drawable drawable) { + super.setBackground(drawable); + container.setBackground(drawable); + } + + public String getContent() { + return mContentText.getText().toString(); + } + + public void setContent(String content) { + this.mContent = content; + mContentText.setText(content); + mContentText.requestLayout(); + } + + public void setName(String name) { + mNameText.setText(name); + } + + public void setSingleLine(boolean singleLine) { + mContentText.setSingleLine(singleLine); + } + + /** + * Set whether to jump + * + * @param canNav + */ + public void setCanNav(boolean canNav) { + this.mIsJump = canNav; + mNavArrowView.setVisibility(canNav ? VISIBLE : GONE); + if (canNav) { + mContentText.setTextIsSelectable(false); + } else { + mContentText.setTextIsSelectable(true); + } + } + + public boolean isChecked() { + return mSwitchView.isChecked(); + } + + public void setChecked(boolean on) { + mSwitchView.setChecked(on); + } + + public void setCheckListener(CompoundButton.OnCheckedChangeListener listener) { + mSwitchView.setOnCheckedChangeListener(listener); + } + + public void setMask(boolean enableMask) { + if (enableMask) { + mNameText.setEnabled(false); + mContentText.setEnabled(false); + mNameText.setTextColor(getResources().getColor(R.color.text_color_gray)); + mContentText.setTextColor(getResources().getColor(R.color.text_color_gray)); + mSwitchView.setEnabled(false); + } else { + mNameText.setEnabled(true); + mContentText.setEnabled(true); + mNameText.setTextColor(getResources().getColor(R.color.core_line_controller_title_color)); + mContentText.setTextColor(getResources().getColor(R.color.core_line_controller_content_color)); + mSwitchView.setEnabled(true); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MaxWidthFrameLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MaxWidthFrameLayout.java new file mode 100644 index 0000000..bd76dc7 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MaxWidthFrameLayout.java @@ -0,0 +1,44 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; + +public class MaxWidthFrameLayout extends FrameLayout { + int maxWidthPx; + + public MaxWidthFrameLayout(@NonNull Context context) { + super(context); + } + + public MaxWidthFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MaxWidthFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attributeSet) { + TypedArray array = context.obtainStyledAttributes(attributeSet, R.styleable.max_width_style); + maxWidthPx = array.getDimensionPixelSize(R.styleable.max_width_style_maxWidth, 0); + array.recycle(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); + if (maxWidthPx > 0 && maxWidthPx < measuredWidth) { + widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidthPx, MeasureSpec.AT_MOST); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MaxWidthLinearLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MaxWidthLinearLayout.java new file mode 100644 index 0000000..3a416f3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MaxWidthLinearLayout.java @@ -0,0 +1,44 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; + +public class MaxWidthLinearLayout extends LinearLayout { + int maxWidthPx; + + public MaxWidthLinearLayout(@NonNull Context context) { + super(context); + } + + public MaxWidthLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MaxWidthLinearLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attributeSet) { + TypedArray array = context.obtainStyledAttributes(attributeSet, R.styleable.max_width_style); + maxWidthPx = array.getDimensionPixelSize(R.styleable.max_width_style_maxWidth, 0); + array.recycle(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); + if (maxWidthPx > 0 && maxWidthPx < measuredWidth) { + widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidthPx, MeasureSpec.AT_MOST); + } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MinimalistLineControllerView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MinimalistLineControllerView.java new file mode 100644 index 0000000..c8105f2 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MinimalistLineControllerView.java @@ -0,0 +1,159 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.appcompat.widget.SwitchCompat; +import androidx.core.graphics.drawable.DrawableCompat; + +import com.tencent.qcloud.tuikit.timcommon.R; + +/** + * Custom LineControllerView + */ +public class MinimalistLineControllerView extends RelativeLayout { + private String mName; + private boolean mIsBottom; + private boolean mIsTop; + private String mContent; + private boolean mIsJump; + private boolean mIsSwitch; + + protected TextView mNameText; + protected TextView mContentText; + private ImageView mNavArrowView; + protected SwitchCompat mSwitchView; + protected View bottomLine; + private View mMask; + private View container; + + public MinimalistLineControllerView(Context context, AttributeSet attrs) { + super(context, attrs); + LayoutInflater.from(context).inflate(R.layout.minimalist_line_controller_view, this); + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LineControllerView, 0, 0); + try { + mName = ta.getString(R.styleable.LineControllerView_name); + mContent = ta.getString(R.styleable.LineControllerView_subject); + mIsBottom = ta.getBoolean(R.styleable.LineControllerView_isBottom, false); + mIsTop = ta.getBoolean(R.styleable.LineControllerView_isTop, false); + mIsJump = ta.getBoolean(R.styleable.LineControllerView_canNav, false); + mIsSwitch = ta.getBoolean(R.styleable.LineControllerView_isSwitch, false); + setUpView(); + } finally { + ta.recycle(); + } + } + + private void setUpView() { + mNameText = findViewById(R.id.name); + mNameText.setText(mName); + mContentText = findViewById(R.id.content); + mContentText.setText(mContent); + mContentText.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + MinimalistLineControllerView.this.performClick(); + } + }); + bottomLine = findViewById(R.id.bottom_line); + View topLine = findViewById(R.id.top_line); + bottomLine.setVisibility(mIsBottom ? VISIBLE : GONE); + topLine.setVisibility(mIsTop ? VISIBLE : GONE); + mNavArrowView = findViewById(R.id.rightArrow); + Drawable arrowDrawable = mNavArrowView.getDrawable(); + if (arrowDrawable != null) { + DrawableCompat.setAutoMirrored(arrowDrawable, true); + } + mNavArrowView.setVisibility(mIsJump ? VISIBLE : GONE); + ViewGroup contentLayout = findViewById(R.id.content_view); + contentLayout.setVisibility(mIsSwitch ? GONE : VISIBLE); + mSwitchView = findViewById(R.id.btnSwitch); + mSwitchView.setVisibility(mIsSwitch ? VISIBLE : GONE); + mMask = findViewById(R.id.disable_mask); + container = findViewById(R.id.view_container); + } + + public void setBackground(Drawable drawable) { + super.setBackground(drawable); + if (container != null) { + container.setBackground(drawable); + } + } + + public void setBackgroundColor(int color) { + super.setBackgroundColor(color); + if (container != null) { + container.setBackgroundColor(color); + } + } + + public String getContent() { + return mContentText.getText().toString(); + } + + public void setContent(String content) { + this.mContent = content; + mContentText.setText(content); + mContentText.requestLayout(); + } + + public void setSingleLine(boolean singleLine) { + mContentText.setSingleLine(singleLine); + } + + /** + * Set whether to jump + * + * @param canNav + */ + public void setCanNav(boolean canNav) { + this.mIsJump = canNav; + mNavArrowView.setVisibility(canNav ? VISIBLE : GONE); + } + + public boolean isChecked() { + return mSwitchView.isChecked(); + } + + public void setChecked(boolean on) { + mSwitchView.setChecked(on); + } + + public void setCheckListener(CompoundButton.OnCheckedChangeListener listener) { + mSwitchView.setOnCheckedChangeListener(listener); + } + + public void setMask(boolean enableMask) { + if (enableMask) { + mNameText.setEnabled(false); + mNameText.setTextColor(getResources().getColor(R.color.text_color_gray)); + mContentText.setEnabled(false); + mContentText.setTextColor(getResources().getColor(R.color.text_color_gray)); + mSwitchView.setEnabled(false); + } else { + mNameText.setEnabled(true); + mNameText.setTextColor(getResources().getColor(R.color.core_line_controller_title_color)); + mContentText.setEnabled(true); + mContentText.setTextColor(getResources().getColor(R.color.core_line_controller_content_color)); + mSwitchView.setEnabled(true); + } + } + + public void setNameColor(int color) { + mNameText.setTextColor(color); + } + + public void setName(String name) { + this.mName = name; + mNameText.setText(name); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MinimalistTitleBar.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MinimalistTitleBar.java new file mode 100644 index 0000000..4159247 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/MinimalistTitleBar.java @@ -0,0 +1,39 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; + +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; + +public class MinimalistTitleBar extends TitleBarLayout { + public MinimalistTitleBar(Context context) { + super(context); + initView(context); + } + + public MinimalistTitleBar(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initView(context); + } + + public MinimalistTitleBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initView(context); + } + + private void initView(Context context) { + setLeftReturnListener(context); + setBackgroundColor(Color.WHITE); + getLeftIcon().setBackgroundResource(R.drawable.core_minimalist_back_icon); + Drawable leftIconDrawable = getLeftIcon().getBackground(); + if (leftIconDrawable != null) { + leftIconDrawable.setAutoMirrored(true); + } + getLeftTitle().setTextColor(0xFF0365F9); + getRightTitle().setTextColor(0xFF0365F9); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/PopupInputCard.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/PopupInputCard.java new file mode 100644 index 0000000..51a15c5 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/PopupInputCard.java @@ -0,0 +1,278 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.drawable.ColorDrawable; +import android.text.Editable; +import android.text.InputFilter; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.animation.LinearInterpolator; +import android.widget.Button; +import android.widget.EditText; +import android.widget.PopupWindow; +import android.widget.TextView; +import com.tencent.qcloud.tuicore.util.ToastUtil; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.SoftKeyBoardUtil; +import java.util.regex.Pattern; + +public class PopupInputCard { + private PopupWindow popupWindow; + + private TextView titleTv; + private EditText editText; + private TextView descriptionTv; + private Button positiveBtn; + private View closeBtn; + private OnClickListener positiveOnClickListener; + private OnTextExceedListener textExceedListener; + + private int minLimit = 0; + private int maxLimit = Integer.MAX_VALUE; + private String rule; + private String notMachRuleTip; + private ByteLengthFilter lengthFilter = new ByteLengthFilter(); + + public PopupInputCard(Activity activity) { + View popupView = LayoutInflater.from(activity).inflate(R.layout.timcommon_layout_popup_card, null); + titleTv = popupView.findViewById(R.id.popup_card_title); + editText = popupView.findViewById(R.id.popup_card_edit); + descriptionTv = popupView.findViewById(R.id.popup_card_description); + positiveBtn = popupView.findViewById(R.id.popup_card_positive_btn); + closeBtn = popupView.findViewById(R.id.close_btn); + + popupWindow = new PopupWindow(popupView, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT, true) { + @Override + public void showAtLocation(View anchor, int gravity, int x, int y) { + if (activity != null && !activity.isFinishing()) { + Window dialogWindow = activity.getWindow(); + startAnimation(dialogWindow, true); + } + editText.requestFocus(); + if (activity.getWindow() != null) { + SoftKeyBoardUtil.showKeyBoard(activity.getWindow()); + } + super.showAtLocation(anchor, gravity, x, y); + } + + @Override + public void dismiss() { + if (activity != null && !activity.isFinishing()) { + Window dialogWindow = activity.getWindow(); + startAnimation(dialogWindow, false); + } + + super.dismiss(); + } + }; + popupWindow.setBackgroundDrawable(new ColorDrawable()); + popupWindow.setTouchable(true); + popupWindow.setOutsideTouchable(false); + popupWindow.setAnimationStyle(R.style.PopupInputCardAnim); + popupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); + popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { + @Override + public void onDismiss() { + if (activity.getWindow() != null) { + SoftKeyBoardUtil.hideKeyBoard(activity.getWindow()); + } + } + }); + + positiveBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String result = editText.getText().toString(); + + if (result.length() < minLimit || result.length() > maxLimit) { + ToastUtil.toastShortMessage(notMachRuleTip); + return; + } + + if (!TextUtils.isEmpty(rule) && !Pattern.matches(rule, result)) { + ToastUtil.toastShortMessage(notMachRuleTip); + return; + } + + if (positiveOnClickListener != null) { + positiveOnClickListener.onClick(editText.getText().toString()); + } + popupWindow.dismiss(); + } + }); + + closeBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + popupWindow.dismiss(); + } + }); + editText.setFilters(new InputFilter[] {lengthFilter}); + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + + @Override + public void afterTextChanged(Editable s) { + if (!TextUtils.isEmpty(rule)) { + if (!Pattern.matches(rule, s.toString())) { + positiveBtn.setEnabled(false); + } else { + positiveBtn.setEnabled(true); + } + } + } + }); + } + + private void startAnimation(Window window, boolean isShow) { + ValueAnimator animator; + if (isShow) { + animator = ValueAnimator.ofFloat(1.0f, 0.5f); + } else { + animator = ValueAnimator.ofFloat(0.5f, 1.0f); + } + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + WindowManager.LayoutParams lp = window.getAttributes(); + lp.alpha = (float) animation.getAnimatedValue(); + window.setAttributes(lp); + } + }); + LinearInterpolator interpolator = new LinearInterpolator(); + animator.setDuration(200); + animator.setInterpolator(interpolator); + animator.start(); + } + + public void show(View rootView, int gravity) { + if (popupWindow != null) { + popupWindow.showAtLocation(rootView, gravity, 0, 0); + } + } + + public void setTitle(String title) { + titleTv.setText(title); + } + + public void setDescription(String description) { + if (!TextUtils.isEmpty(description)) { + descriptionTv.setVisibility(View.VISIBLE); + descriptionTv.setText(description); + } + } + + public void setContent(String content) { + editText.setText(content); + } + + public void setOnPositive(OnClickListener clickListener) { + positiveOnClickListener = clickListener; + } + + public void setTextExceedListener(OnTextExceedListener textExceedListener) { + this.textExceedListener = textExceedListener; + } + + public void setSingleLine(boolean isSingleLine) { + editText.setSingleLine(isSingleLine); + } + + public void setMaxLimit(int maxLimit) { + this.maxLimit = maxLimit; + lengthFilter.setLength(maxLimit); + } + + public void setMinLimit(int minLimit) { + this.minLimit = minLimit; + } + + public void setRule(String rule) { + if (TextUtils.isEmpty(rule)) { + this.rule = ""; + } else { + this.rule = rule; + } + } + + public void setNotMachRuleTip(String notMachRuleTip) { + this.notMachRuleTip = notMachRuleTip; + } + + class ByteLengthFilter implements InputFilter { + private int length = Integer.MAX_VALUE; + + public ByteLengthFilter() {} + + public void setLength(int length) { + this.length = length; + } + + @Override + public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { + int destLength = 0; + int destReplaceLength = 0; + int sourceLength = 0; + if (!TextUtils.isEmpty(dest)) { + destLength = dest.toString().getBytes().length; + destReplaceLength = dest.subSequence(dstart, dend).toString().getBytes().length; + } + if (!TextUtils.isEmpty(source)) { + sourceLength = source.subSequence(start, end).toString().getBytes().length; + } + int keepBytesLength = length - (destLength - destReplaceLength); + if (keepBytesLength <= 0) { + if (textExceedListener != null) { + textExceedListener.onTextExceedMax(); + } + return ""; + } else if (keepBytesLength >= sourceLength) { + return null; + } else { + if (textExceedListener != null) { + textExceedListener.onTextExceedMax(); + } + return getSource(source, start, keepBytesLength); + } + } + + private CharSequence getSource(CharSequence sequence, int start, int keepLength) { + int sequenceLength = sequence.length(); + int end = 0; + for (int i = 1; i <= sequenceLength; i++) { + if (sequence.subSequence(0, i).toString().getBytes().length <= keepLength) { + end = i; + } else { + break; + } + } + if (end > 0 && Character.isHighSurrogate(sequence.charAt(end - 1))) { + --end; + if (end == start) { + return ""; + } + } + return sequence.subSequence(start, end); + } + } + + @FunctionalInterface + public interface OnClickListener { + void onClick(String result); + } + + public interface OnTextExceedListener { + void onTextExceedMax(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/RoundCornerImageView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/RoundCornerImageView.java new file mode 100644 index 0000000..6712bb9 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/RoundCornerImageView.java @@ -0,0 +1,126 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Path; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatImageView; + +import com.tencent.qcloud.tuikit.timcommon.R; + +public class RoundCornerImageView extends AppCompatImageView { + private final Path path = new Path(); + private final RectF rectF = new RectF(); + private final PaintFlagsDrawFilter aliasFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private int radius; + private int leftTopRadius; + private int rightTopRadius; + private int rightBottomRadius; + private int leftBottomRadius; + + public RoundCornerImageView(@NonNull Context context) { + super(context); + init(context, null); + } + + public RoundCornerImageView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public RoundCornerImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + private void init(Context context, AttributeSet attrs) { + setLayerType(View.LAYER_TYPE_HARDWARE, null); + int defaultRadius = 0; + if (attrs != null) { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundCornerImageView); + radius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_corner_radius, defaultRadius); + leftTopRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_left_top_corner_radius, defaultRadius); + rightTopRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_right_top_corner_radius, defaultRadius); + rightBottomRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_right_bottom_corner_radius, defaultRadius); + leftBottomRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_left_bottom_corner_radius, defaultRadius); + array.recycle(); + } + + if (defaultRadius == leftTopRadius) { + leftTopRadius = radius; + } + if (defaultRadius == rightTopRadius) { + rightTopRadius = radius; + } + if (defaultRadius == rightBottomRadius) { + rightBottomRadius = radius; + } + if (defaultRadius == leftBottomRadius) { + leftBottomRadius = radius; + } + } + + public void setLeftBottomRadius(int leftBottomRadius) { + this.leftBottomRadius = leftBottomRadius; + } + + public void setLeftTopRadius(int leftTopRadius) { + this.leftTopRadius = leftTopRadius; + } + + public void setRadius(int radius) { + this.radius = radius; + leftBottomRadius = radius; + rightBottomRadius = radius; + rightTopRadius = radius; + leftTopRadius = radius; + } + + public void setRightBottomRadius(int rightBottomRadius) { + this.rightBottomRadius = rightBottomRadius; + } + + public void setRightTopRadius(int rightTopRadius) { + this.rightTopRadius = rightTopRadius; + } + + public int getLeftBottomRadius() { + return leftBottomRadius; + } + + public int getLeftTopRadius() { + return leftTopRadius; + } + + public int getRadius() { + return radius; + } + + public int getRightBottomRadius() { + return rightBottomRadius; + } + + public int getRightTopRadius() { + return rightTopRadius; + } + + @Override + protected void onDraw(Canvas canvas) { + path.reset(); + canvas.setDrawFilter(aliasFilter); + rectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + // left-top -> right-top -> right-bottom -> left-bottom + float[] radius = { + leftTopRadius, leftTopRadius, rightTopRadius, rightTopRadius, rightBottomRadius, rightBottomRadius, leftBottomRadius, leftBottomRadius}; + path.addRoundRect(rectF, radius, Path.Direction.CW); + canvas.clipPath(path); + super.onDraw(canvas); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/RoundFrameLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/RoundFrameLayout.java new file mode 100644 index 0000000..fc9e056 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/RoundFrameLayout.java @@ -0,0 +1,127 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Path; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; + +public class RoundFrameLayout extends FrameLayout { + private final Path path = new Path(); + private final RectF rectF = new RectF(); + private final PaintFlagsDrawFilter aliasFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private int radius; + private int leftTopRadius; + private int rightTopRadius; + private int rightBottomRadius; + private int leftBottomRadius; + + public RoundFrameLayout(@NonNull Context context) { + super(context); + init(context, null); + } + + public RoundFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public RoundFrameLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + private void init(Context context, AttributeSet attrs) { + setLayerType(View.LAYER_TYPE_HARDWARE, null); + int defaultRadius = 0; + if (attrs != null) { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundFrameLayout); + radius = array.getDimensionPixelOffset(R.styleable.RoundFrameLayout_corner_radius, defaultRadius); + leftTopRadius = array.getDimensionPixelOffset(R.styleable.RoundFrameLayout_left_top_corner_radius, defaultRadius); + rightTopRadius = array.getDimensionPixelOffset(R.styleable.RoundFrameLayout_right_top_corner_radius, defaultRadius); + rightBottomRadius = array.getDimensionPixelOffset(R.styleable.RoundFrameLayout_right_bottom_corner_radius, defaultRadius); + leftBottomRadius = array.getDimensionPixelOffset(R.styleable.RoundFrameLayout_left_bottom_corner_radius, defaultRadius); + array.recycle(); + } + + if (defaultRadius == leftTopRadius) { + leftTopRadius = radius; + } + if (defaultRadius == rightTopRadius) { + rightTopRadius = radius; + } + if (defaultRadius == rightBottomRadius) { + rightBottomRadius = radius; + } + if (defaultRadius == leftBottomRadius) { + leftBottomRadius = radius; + } + } + + + public void setLeftBottomRadius(int leftBottomRadius) { + this.leftBottomRadius = leftBottomRadius; + } + + public void setLeftTopRadius(int leftTopRadius) { + this.leftTopRadius = leftTopRadius; + } + + public void setRadius(int radius) { + this.radius = radius; + leftBottomRadius = radius; + rightBottomRadius = radius; + rightTopRadius = radius; + leftTopRadius = radius; + } + + public void setRightBottomRadius(int rightBottomRadius) { + this.rightBottomRadius = rightBottomRadius; + } + + public void setRightTopRadius(int rightTopRadius) { + this.rightTopRadius = rightTopRadius; + } + + public int getLeftBottomRadius() { + return leftBottomRadius; + } + + public int getLeftTopRadius() { + return leftTopRadius; + } + + public int getRadius() { + return radius; + } + + public int getRightBottomRadius() { + return rightBottomRadius; + } + + public int getRightTopRadius() { + return rightTopRadius; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + path.reset(); + canvas.setDrawFilter(aliasFilter); + rectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + // left-top -> right-top -> right-bottom -> left-bottom + float[] radius = { + leftTopRadius, leftTopRadius, rightTopRadius, rightTopRadius, rightBottomRadius, rightBottomRadius, leftBottomRadius, leftBottomRadius}; + path.addRoundRect(rectF, radius, Path.Direction.CW); + canvas.clipPath(path); + super.dispatchDraw(canvas); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/SwitchCustomWidth.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/SwitchCustomWidth.java new file mode 100644 index 0000000..afff975 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/SwitchCustomWidth.java @@ -0,0 +1,59 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.SwitchCompat; + +import com.tencent.qcloud.tuikit.timcommon.R; + +import java.lang.reflect.Field; + +public class SwitchCustomWidth extends SwitchCompat { + private static final String TAG = "SwitchCustomWidth"; + + private int customSwitchWidth; + + public SwitchCustomWidth(@NonNull Context context) { + super(context); + initCustomAttr(context, null); + } + + public SwitchCustomWidth(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initCustomAttr(context, attrs); + } + + public SwitchCustomWidth(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initCustomAttr(context, attrs); + } + + public void initCustomAttr(Context context, AttributeSet attributeSet) { + if (attributeSet != null) { + TypedArray array = context.obtainStyledAttributes(attributeSet, R.styleable.SwitchCustomWidth); + customSwitchWidth = array.getDimensionPixelSize(R.styleable.SwitchCustomWidth_custom_width, 0); + array.recycle(); + } + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + try { + if (customSwitchWidth == 0) { + return; + } + Class clazz = SwitchCompat.class; + Field mSwitchWidthFiled = clazz.getDeclaredField("mSwitchWidth"); + mSwitchWidthFiled.setAccessible(true); + mSwitchWidthFiled.set(this, customSwitchWidth); + } catch (Exception e) { + Log.w(TAG, e.getMessage()); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/TitleBarLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/TitleBarLayout.java new file mode 100644 index 0000000..bada9ae --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/TitleBarLayout.java @@ -0,0 +1,186 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.app.Activity; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.component.interfaces.ITitleBarLayout; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; + +public class TitleBarLayout extends LinearLayout implements ITitleBarLayout { + private LinearLayout mLeftGroup; + private LinearLayout mRightGroup; + private TextView mLeftTitle; + private TextView mCenterTitle; + private TextView mRightTitle; + private ImageView mLeftIcon; + private ImageView mRightIcon; + private RelativeLayout mTitleLayout; + private UnreadCountTextView unreadCountTextView; + + public TitleBarLayout(Context context) { + super(context); + init(context, null); + } + + public TitleBarLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public TitleBarLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, @Nullable AttributeSet attrs) { + String middleTitle = null; + boolean canReturn = false; + if (attrs != null) { + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TitleBarLayout); + middleTitle = array.getString(R.styleable.TitleBarLayout_title_bar_middle_title); + canReturn = array.getBoolean(R.styleable.TitleBarLayout_title_bar_can_return, false); + array.recycle(); + } + inflate(context, R.layout.timcommon_title_bar_layout, this); + mTitleLayout = findViewById(R.id.page_title_layout); + mLeftGroup = findViewById(R.id.page_title_left_group); + mRightGroup = findViewById(R.id.page_title_right_group); + mLeftTitle = findViewById(R.id.page_title_left_text); + mRightTitle = findViewById(R.id.page_title_right_text); + mCenterTitle = findViewById(R.id.page_title); + mLeftIcon = findViewById(R.id.page_title_left_icon); + Drawable leftIconDrawable = mLeftIcon.getBackground(); + if (leftIconDrawable != null) { + leftIconDrawable.setAutoMirrored(true); + } + mRightIcon = findViewById(R.id.page_title_right_icon); + unreadCountTextView = findViewById(R.id.new_message_total_unread); + + LayoutParams params = (LayoutParams) mTitleLayout.getLayoutParams(); + params.height = ScreenUtil.getPxByDp(50); + mTitleLayout.setLayoutParams(params); + setBackgroundResource(TUIThemeManager.getAttrResId(getContext(), R.attr.core_title_bar_bg)); + + int iconSize = ScreenUtil.dip2px(20); + ViewGroup.LayoutParams iconParams = mLeftIcon.getLayoutParams(); + iconParams.width = iconSize; + iconParams.height = iconSize; + mLeftIcon.setLayoutParams(iconParams); + iconParams = mRightIcon.getLayoutParams(); + iconParams.width = iconSize; + iconParams.height = iconSize; + + mRightIcon.setLayoutParams(iconParams); + + if (canReturn) { + setLeftReturnListener(context); + } + if (!TextUtils.isEmpty(middleTitle)) { + mCenterTitle.setText(middleTitle); + } + } + + public void setLeftReturnListener(Context context) { + mLeftGroup.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (context instanceof Activity) { + InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(TitleBarLayout.this.getWindowToken(), 0); + ((Activity) context).finish(); + } + } + }); + } + + @Override + public void setOnLeftClickListener(OnClickListener listener) { + mLeftGroup.setOnClickListener(listener); + } + + @Override + public void setOnRightClickListener(OnClickListener listener) { + mRightGroup.setOnClickListener(listener); + } + + @Override + public void setTitle(String title, Position position) { + switch (position) { + case LEFT: + mLeftTitle.setText(title); + break; + case RIGHT: + mRightTitle.setText(title); + break; + case MIDDLE: + mCenterTitle.setText(title); + break; + default: + break; + } + } + + @Override + public LinearLayout getLeftGroup() { + return mLeftGroup; + } + + @Override + public LinearLayout getRightGroup() { + return mRightGroup; + } + + @Override + public ImageView getLeftIcon() { + return mLeftIcon; + } + + @Override + public void setLeftIcon(int resId) { + mLeftIcon.setBackgroundResource(resId); + } + + @Override + public ImageView getRightIcon() { + return mRightIcon; + } + + @Override + public void setRightIcon(int resId) { + mRightIcon.setBackgroundResource(resId); + } + + @Override + public TextView getLeftTitle() { + return mLeftTitle; + } + + @Override + public TextView getMiddleTitle() { + return mCenterTitle; + } + + @Override + public TextView getRightTitle() { + return mRightTitle; + } + + public UnreadCountTextView getUnreadCountTextView() { + return unreadCountTextView; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/UnreadCountTextView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/UnreadCountTextView.java new file mode 100644 index 0000000..078f05a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/UnreadCountTextView.java @@ -0,0 +1,84 @@ +package com.tencent.qcloud.tuikit.timcommon.component; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; + +import androidx.appcompat.widget.AppCompatTextView; + +import com.tencent.qcloud.tuikit.timcommon.R; + +public class UnreadCountTextView extends AppCompatTextView { + private int mNormalSize; + private Paint mPaint; + + public UnreadCountTextView(Context context) { + super(context); + init(context, null); + } + + public UnreadCountTextView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public UnreadCountTextView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + setTextDirection(View.TEXT_DIRECTION_LTR); + mNormalSize = dp2px(18.4f); + TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.UnreadCountTextView); + int paintColor = typedArray.getColor(R.styleable.UnreadCountTextView_paint_color, getResources().getColor(R.color.read_dot_bg)); + typedArray.recycle(); + + mPaint = new Paint(); + mPaint.setColor(paintColor); + mPaint.setAntiAlias(true); + } + + public void setPaintColor(int color) { + if (mPaint != null) { + mPaint.setColor(color); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (getText().length() == 0) { + int l = (getMeasuredWidth() - dp2px(6)) / 2; + int t = l; + int r = getMeasuredWidth() - l; + int b = r; + canvas.drawOval(new RectF(l, t, r, b), mPaint); + } else if (getText().length() == 1) { + canvas.drawOval(new RectF(0, 0, mNormalSize, mNormalSize), mPaint); + } else if (getText().length() > 1) { + canvas.drawRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), getMeasuredHeight() / 2, getMeasuredHeight() / 2, mPaint); + } + super.onDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int width = mNormalSize; + int height = mNormalSize; + if (getText().length() > 1) { + width = mNormalSize + dp2px((getText().length() - 1) * 10); + } + setMeasuredDimension(width, height); + } + + private int dp2px(float dp) { + DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); + return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopActionClickListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopActionClickListener.java new file mode 100644 index 0000000..0e9f9f3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopActionClickListener.java @@ -0,0 +1,5 @@ +package com.tencent.qcloud.tuikit.timcommon.component.action; + +public interface PopActionClickListener { + void onActionClick(int index, Object data); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopDialogAdapter.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopDialogAdapter.java new file mode 100644 index 0000000..4ed6ebf --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopDialogAdapter.java @@ -0,0 +1,63 @@ +package com.tencent.qcloud.tuikit.timcommon.component.action; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.ThreadUtils; + +import java.util.ArrayList; +import java.util.List; + +public class PopDialogAdapter extends BaseAdapter { + private List dataSource = new ArrayList<>(); + + public void setDataSource(final List datas) { + dataSource = datas; + ThreadUtils.postOnUiThread(new Runnable() { + @Override + public void run() { + notifyDataSetChanged(); + } + }); + } + + @Override + public int getCount() { + return dataSource.size(); + } + + @Override + public Object getItem(int position) { + return dataSource.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 = LayoutInflater.from(TUIConfig.getAppContext()).inflate(R.layout.pop_dialog_adapter, parent, false); + holder = new ViewHolder(); + holder.text = convertView.findViewById(R.id.pop_dialog_text); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + PopMenuAction action = (PopMenuAction) getItem(position); + holder.text.setText(action.getActionName()); + return convertView; + } + + static class ViewHolder { + TextView text; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopMenuAction.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopMenuAction.java new file mode 100644 index 0000000..b7f150a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopMenuAction.java @@ -0,0 +1,51 @@ +package com.tencent.qcloud.tuikit.timcommon.component.action; + +import android.graphics.Bitmap; + +public class PopMenuAction { + private String actionName; + private Bitmap icon; + private int iconResId; + private PopActionClickListener actionClickListener; + private int weight; + + public String getActionName() { + return actionName; + } + + public void setActionName(String actionName) { + this.actionName = actionName; + } + + public Bitmap getIcon() { + return icon; + } + + public void setIcon(Bitmap mIcon) { + this.icon = mIcon; + } + + public int getIconResId() { + return iconResId; + } + + public void setIconResId(int iconResId) { + this.iconResId = iconResId; + } + + public PopActionClickListener getActionClickListener() { + return actionClickListener; + } + + public void setActionClickListener(PopActionClickListener actionClickListener) { + this.actionClickListener = actionClickListener; + } + + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopMenuAdapter.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopMenuAdapter.java new file mode 100644 index 0000000..1bb3eb3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/action/PopMenuAdapter.java @@ -0,0 +1,81 @@ +package com.tencent.qcloud.tuikit.timcommon.component.action; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.TextView; +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.ThreadUtils; +import java.util.ArrayList; +import java.util.List; + +public class PopMenuAdapter extends BaseAdapter { + private List dataSource = new ArrayList<>(); + + public PopMenuAdapter() {} + + public void setDataSource(final List datas) { + dataSource = datas; + ThreadUtils.postOnUiThread(new Runnable() { + @Override + public void run() { + notifyDataSetChanged(); + } + }); + } + + @Override + public int getCount() { + return dataSource.size(); + } + + @Override + public Object getItem(int position) { + return dataSource.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 = LayoutInflater.from(TUIConfig.getAppContext()).inflate(R.layout.pop_menu_adapter, parent, false); + holder = new ViewHolder(); + holder.menuIcon = convertView.findViewById(R.id.pop_menu_icon); + + int iconSize = convertView.getResources().getDimensionPixelSize(R.dimen.core_pop_menu_icon_size); + ViewGroup.LayoutParams params = holder.menuIcon.getLayoutParams(); + params.width = iconSize; + params.height = iconSize; + holder.menuIcon.setLayoutParams(params); + + holder.menuLable = convertView.findViewById(R.id.pop_menu_label); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + PopMenuAction action = (PopMenuAction) getItem(position); + holder.menuIcon.setVisibility(View.VISIBLE); + if (action.getIcon() != null) { + holder.menuIcon.setImageBitmap(action.getIcon()); + } else if (action.getIconResId() > 0) { + holder.menuIcon.setImageResource(action.getIconResId()); + } else { + holder.menuIcon.setVisibility(View.GONE); + } + holder.menuLable.setText(action.getActionName()); + return convertView; + } + + static class ViewHolder { + TextView menuLable; + ImageView menuIcon; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/BaseLightActivity.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/BaseLightActivity.java new file mode 100644 index 0000000..220e120 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/BaseLightActivity.java @@ -0,0 +1,48 @@ +package com.tencent.qcloud.tuikit.timcommon.component.activities; + +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; + +public class BaseLightActivity extends AppCompatActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + getWindow().setStatusBarColor( + getResources().getColor(TUIThemeManager.getAttrResId(this, com.tencent.qcloud.tuicore.R.attr.core_header_start_color))); + getWindow().setNavigationBarColor(getResources().getColor(R.color.navigation_bar_color)); + int vis = getWindow().getDecorView().getSystemUiVisibility(); + vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + vis |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + getWindow().getDecorView().setSystemUiVisibility(vis); + } + } + + @Override + public void finish() { + hideSoftInput(); + super.finish(); + } + + public void hideSoftInput() { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + Window window = getWindow(); + if (window != null) { + imm.hideSoftInputFromWindow(window.getDecorView().getWindowToken(), 0); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/BaseMinimalistLightActivity.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/BaseMinimalistLightActivity.java new file mode 100644 index 0000000..99c069e --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/BaseMinimalistLightActivity.java @@ -0,0 +1,44 @@ +package com.tencent.qcloud.tuikit.timcommon.component.activities; + +import android.content.Context; +import android.os.Build; +import android.os.Bundle; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +public class BaseMinimalistLightActivity extends AppCompatActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + getWindow().setStatusBarColor(0xFFFFFFFF); + getWindow().setNavigationBarColor(0xFFFFFFFF); + int vis = getWindow().getDecorView().getSystemUiVisibility(); + vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + vis |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + getWindow().getDecorView().setSystemUiVisibility(vis); + } + } + + @Override + public void finish() { + hideSoftInput(); + super.finish(); + } + + public void hideSoftInput() { + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + Window window = getWindow(); + if (window != null) { + imm.hideSoftInputFromWindow(window.getDecorView().getWindowToken(), 0); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/ImageSelectActivity.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/ImageSelectActivity.java new file mode 100644 index 0000000..24ab1c7 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/ImageSelectActivity.java @@ -0,0 +1,450 @@ +package com.tencent.qcloud.tuikit.timcommon.component.activities; + +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.Target; +import com.tencent.qcloud.tuicore.TUIConstants; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuicore.util.ToastUtil; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.component.TitleBarLayout; +import com.tencent.qcloud.tuikit.timcommon.component.gatherimage.SynthesizedImageView; +import com.tencent.qcloud.tuikit.timcommon.component.interfaces.ITitleBarLayout; +import com.tencent.qcloud.tuikit.timcommon.util.LayoutUtil; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; +import java.io.File; +import java.io.Serializable; +import java.util.List; + +public class ImageSelectActivity extends BaseLightActivity { + private static final String TAG = ImageSelectActivity.class.getSimpleName(); + + public static final String CHAT_CONVERSATION_BACKGROUND_DEFAULT_URL = "chat/conversation/background/default/url"; + public static final int RESULT_CODE_ERROR = -1; + public static final int RESULT_CODE_SUCCESS = 0; + public static final String TITLE = "title"; + public static final String SPAN_COUNT = "spanCount"; + public static final String DATA = "data"; + public static final String ITEM_HEIGHT = "itemHeight"; + public static final String ITEM_WIDTH = "itemWidth"; + public static final String SELECTED = "selected"; + public static final String PLACEHOLDER = "placeholder"; + public static final String NEED_DOWNLOAD_LOCAL = "needDownload"; + + private int defaultSpacing; + + private List data; + private ImageBean selected; + private int placeHolder; + private int columnNum; + private RecyclerView imageGrid; + private GridLayoutManager gridLayoutManager; + private ImageGridAdapter gridAdapter; + private TitleBarLayout titleBarLayout; + private int itemHeight; + private int itemWidth; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + defaultSpacing = ScreenUtil.dip2px(12); + setContentView(R.layout.core_activity_image_select_layout); + Intent intent = getIntent(); + String title = intent.getStringExtra(TITLE); + titleBarLayout = findViewById(R.id.image_select_title); + titleBarLayout.setTitle(title, ITitleBarLayout.Position.MIDDLE); + titleBarLayout.setTitle(getString(com.tencent.qcloud.tuicore.R.string.sure), ITitleBarLayout.Position.RIGHT); + titleBarLayout.getRightIcon().setVisibility(View.GONE); + titleBarLayout.getRightTitle().setTextColor(0xFF006EFF); + titleBarLayout.setOnLeftClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setResult(RESULT_CODE_ERROR); + finish(); + } + }); + boolean needDownload = intent.getBooleanExtra(NEED_DOWNLOAD_LOCAL, false); + titleBarLayout.setOnRightClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (selected == null) { + return; + } + if (needDownload) { + downloadUrl(); + } else { + Intent resultIntent = new Intent(); + resultIntent.putExtra(DATA, (Serializable) selected); + setResult(RESULT_CODE_SUCCESS, resultIntent); + finish(); + } + } + }); + + data = (List) intent.getSerializableExtra(DATA); + selected = (ImageBean) intent.getSerializableExtra(SELECTED); + placeHolder = intent.getIntExtra(PLACEHOLDER, 0); + itemHeight = intent.getIntExtra(ITEM_HEIGHT, 0); + itemWidth = intent.getIntExtra(ITEM_WIDTH, 0); + columnNum = intent.getIntExtra(SPAN_COUNT, 2); + gridLayoutManager = new GridLayoutManager(this, columnNum); + imageGrid = findViewById(R.id.image_select_grid); + imageGrid.addItemDecoration(new GridDecoration(columnNum, defaultSpacing, defaultSpacing)); + imageGrid.setLayoutManager(gridLayoutManager); + imageGrid.setItemAnimator(null); + gridAdapter = new ImageGridAdapter(); + gridAdapter.setPlaceHolder(placeHolder); + gridAdapter.setSelected(selected); + gridAdapter.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onClick(ImageBean obj) { + selected = obj; + setSelectedStatus(); + } + }); + gridAdapter.setItemWidth(itemWidth); + gridAdapter.setItemHeight(itemHeight); + imageGrid.setAdapter(gridAdapter); + gridAdapter.setData(data); + setSelectedStatus(); + gridAdapter.notifyDataSetChanged(); + } + + private void downloadUrl() { + if (selected == null) { + return; + } + + if (selected.isDefault()) { + selected.setLocalPath(CHAT_CONVERSATION_BACKGROUND_DEFAULT_URL); + setResult(selected); + ToastUtil.toastShortMessage(getResources().getString(R.string.setting_success)); + finish(); + return; + } + + String url = selected.getImageUri(); + if (TextUtils.isEmpty(url)) { + Log.d(TAG, "DownloadUrl is null"); + return; + } + + final ProgressDialog dialog = new ProgressDialog(this); + dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + dialog.setCancelable(false); + dialog.setCanceledOnTouchOutside(false); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + // TODO Auto-generated method stub + finish(); + } + }); + dialog.setMessage(getResources().getString(R.string.setting)); + dialog.show(); + + ImageBean finalBean = selected; + Glide.with(this) + .downloadOnly() + .load(url) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + dialog.cancel(); + Log.e(TAG, "DownloadUrl onLoadFailed e = " + e); + ToastUtil.toastShortMessage(getResources().getString(R.string.setting_fail)); + return false; + } + + @Override + public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + dialog.cancel(); + String path = resource.getAbsolutePath(); + Log.e(TAG, "DownloadUrl resource path = " + path); + finalBean.setLocalPath(path); + setResult(finalBean); + ToastUtil.toastShortMessage(getResources().getString(R.string.setting_success)); + return false; + } + }) + .preload(); + } + + private void setResult(ImageBean bean) { + Intent resultIntent = new Intent(); + resultIntent.putExtra(DATA, (Serializable) bean); + setResult(RESULT_CODE_SUCCESS, resultIntent); + finish(); + } + + private void setSelectedStatus() { + if (selected != null && data != null && data.contains(selected)) { + titleBarLayout.getRightTitle().setEnabled(true); + titleBarLayout.getRightTitle().setTextColor( + getResources().getColor(TUIThemeManager.getAttrResId(this, com.tencent.qcloud.tuicore.R.attr.core_primary_color))); + } else { + titleBarLayout.getRightTitle().setEnabled(false); + titleBarLayout.getRightTitle().setTextColor(0xFF666666); + } + gridAdapter.setSelected(selected); + } + + public static class ImageGridAdapter extends RecyclerView.Adapter { + private int itemWidth; + private int itemHeight; + + private List data; + private ImageBean selected; + private int placeHolder; + private OnItemClickListener onItemClickListener; + + public void setData(List data) { + this.data = data; + } + + public void setSelected(ImageBean selected) { + if (data == null || data.isEmpty()) { + this.selected = selected; + } else { + this.selected = selected; + notifyDataSetChanged(); + } + } + + public void setPlaceHolder(int placeHolder) { + this.placeHolder = placeHolder; + } + + public void setItemHeight(int itemHeight) { + this.itemHeight = itemHeight; + } + + public void setItemWidth(int itemWidth) { + this.itemWidth = itemWidth; + } + + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + this.onItemClickListener = onItemClickListener; + } + + @NonNull + @Override + public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.core_select_image_item_layout, parent, false); + return new ImageViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) { + ImageView imageView = holder.imageView; + setItemLayoutParams(holder); + ImageBean imageBean = data.get(position); + if (selected != null && imageBean != null && TextUtils.equals(selected.getThumbnailUri(), imageBean.getThumbnailUri())) { + holder.selectBorderLayout.setVisibility(View.VISIBLE); + } else { + holder.selectBorderLayout.setVisibility(View.GONE); + } + + if (imageBean.getGroupGridAvatar() != null) { + holder.defaultLayout.setVisibility(View.GONE); + if (imageView instanceof SynthesizedImageView) { + SynthesizedImageView synthesizedImageView = ((SynthesizedImageView) (imageView)); + String imageId = imageBean.getImageId(); + synthesizedImageView.setImageId(imageId); + synthesizedImageView.displayImage(imageBean.getGroupGridAvatar()).load(imageId); + } + } else if (imageBean.isDefault()) { + holder.defaultLayout.setVisibility(View.VISIBLE); + imageView.setImageResource(android.R.color.transparent); + } else { + holder.defaultLayout.setVisibility(View.GONE); + Glide.with(holder.itemView.getContext()) + .asBitmap() + .load(imageBean.getThumbnailUri()) + .placeholder(placeHolder) + .apply(new RequestOptions().error(placeHolder)) + .into(imageView); + } + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onClick(imageBean); + } + } + }); + } + + private void setItemLayoutParams(ImageViewHolder holder) { + if (itemHeight > 0 && itemWidth > 0) { + ViewGroup.LayoutParams itemViewLayoutParams = holder.itemView.getLayoutParams(); + itemViewLayoutParams.width = itemWidth; + itemViewLayoutParams.height = itemHeight; + holder.itemView.setLayoutParams(itemViewLayoutParams); + + ViewGroup.LayoutParams params = holder.imageView.getLayoutParams(); + params.width = itemWidth; + params.height = itemHeight; + holder.imageView.setLayoutParams(params); + + ViewGroup.LayoutParams borderLayoutParams = holder.selectBorderLayout.getLayoutParams(); + borderLayoutParams.width = itemWidth; + borderLayoutParams.height = itemHeight; + holder.selectBorderLayout.setLayoutParams(borderLayoutParams); + + ViewGroup.LayoutParams borderParams = holder.selectedBorder.getLayoutParams(); + borderParams.width = itemWidth; + borderParams.height = itemHeight; + holder.selectedBorder.setLayoutParams(borderParams); + } + } + + @Override + public int getItemCount() { + if (data == null || data.isEmpty()) { + return 0; + } + return data.size(); + } + + public static class ImageViewHolder extends RecyclerView.ViewHolder { + private final ImageView imageView; + private final ImageView selectedBorder; + private final RelativeLayout selectBorderLayout; + private final Button defaultLayout; + + public ImageViewHolder(@NonNull View itemView) { + super(itemView); + imageView = itemView.findViewById(R.id.content_image); + selectedBorder = itemView.findViewById(R.id.select_border); + selectBorderLayout = itemView.findViewById(R.id.selected_border_area); + defaultLayout = itemView.findViewById(R.id.default_image_layout); + } + } + } + + /** + * add spacing + */ + public static class GridDecoration extends RecyclerView.ItemDecoration { + private final int columnNum; // span count + private final int leftRightSpace; // vertical spacing + private final int topBottomSpace; // horizontal spacing + + public GridDecoration(int columnNum, int leftRightSpace, int topBottomSpace) { + this.columnNum = columnNum; + this.leftRightSpace = leftRightSpace; + this.topBottomSpace = topBottomSpace; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + int position = parent.getChildAdapterPosition(view); + int column = position % columnNum; + + int left = column * leftRightSpace / columnNum; + int right = leftRightSpace * (columnNum - 1 - column) / columnNum; + if (LayoutUtil.isRTL()) { + outRect.left = right; + outRect.right = left; + } else { + outRect.left = left; + outRect.right = right; + } + // add top spacing + if (position >= columnNum) { + outRect.top = topBottomSpace; + } + } + } + + public interface OnItemClickListener { + void onClick(ImageBean obj); + } + + public static class ImageBean implements Serializable { + String thumbnailUri; // for display + String imageUri; // for download + String localPath; // for local path + boolean isDefault = false; // for default display + List groupGridAvatar = null; // for group grid avatar + String imageId; + + public ImageBean() {} + + public ImageBean(String thumbnailUri, String imageUri, boolean isDefault) { + this.thumbnailUri = thumbnailUri; + this.imageUri = imageUri; + this.isDefault = isDefault; + } + + public String getImageUri() { + return imageUri; + } + + public String getThumbnailUri() { + return thumbnailUri; + } + + public void setImageUri(String imageUri) { + this.imageUri = imageUri; + } + + public void setThumbnailUri(String thumbnailUri) { + this.thumbnailUri = thumbnailUri; + } + + public String getLocalPath() { + return localPath; + } + + public void setLocalPath(String localPath) { + this.localPath = localPath; + } + + public boolean isDefault() { + return isDefault; + } + + public void setDefault(boolean aDefault) { + isDefault = aDefault; + } + + public List getGroupGridAvatar() { + return groupGridAvatar; + } + + public void setGroupGridAvatar(List groupGridAvatar) { + this.groupGridAvatar = groupGridAvatar; + } + + public String getImageId() { + return imageId; + } + + public void setImageId(String imageId) { + this.imageId = imageId; + } + } +} \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/ImageSelectMinimalistActivity.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/ImageSelectMinimalistActivity.java new file mode 100644 index 0000000..673126f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/ImageSelectMinimalistActivity.java @@ -0,0 +1,483 @@ +package com.tencent.qcloud.tuikit.timcommon.component.activities; + +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Rect; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.Target; +import com.tencent.qcloud.tuicore.TUIConstants; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuicore.util.ToastUtil; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.component.TitleBarLayout; +import com.tencent.qcloud.tuikit.timcommon.component.gatherimage.SynthesizedImageView; +import com.tencent.qcloud.tuikit.timcommon.component.interfaces.ITitleBarLayout; +import com.tencent.qcloud.tuikit.timcommon.util.LayoutUtil; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; +import java.io.File; +import java.io.Serializable; +import java.util.List; + +public class ImageSelectMinimalistActivity extends BaseMinimalistLightActivity { + private static final String TAG = ImageSelectMinimalistActivity.class.getSimpleName(); + + public static final int RESULT_CODE_ERROR = -1; + public static final int RESULT_CODE_SUCCESS = 0; + public static final String TITLE = "title"; + public static final String SPAN_COUNT = "spanCount"; + public static final String DATA = "data"; + public static final String ITEM_HEIGHT = "itemHeight"; + public static final String ITEM_WIDTH = "itemWidth"; + public static final String SELECTED = "selected"; + public static final String PLACEHOLDER = "placeholder"; + public static final String NEED_DOWLOAD_LOCAL = "needdowmload"; + public static final String CHAT_CONVERSATION_BACKGROUND_DEFAULT_URL = "chat/conversation/background/default/url"; + + private int defaultSpacing; + + private List data; + private ImageBean selected; + private int placeHolder; + private int columnNum; + private RecyclerView imageGrid; + private GridLayoutManager gridLayoutManager; + private ImageGridAdapter gridAdapter; + private TitleBarLayout titleBarLayout; + private int itemHeight; + private int itemWidth; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + defaultSpacing = ScreenUtil.dip2px(12); + setContentView(R.layout.core_minimalist_activity_image_select_layout); + Intent intent = getIntent(); + String title = intent.getStringExtra(TITLE); + titleBarLayout = findViewById(R.id.image_select_title); + titleBarLayout.setTitle(title, ITitleBarLayout.Position.MIDDLE); + titleBarLayout.setTitle(getString(com.tencent.qcloud.tuicore.R.string.sure), ITitleBarLayout.Position.RIGHT); + titleBarLayout.getRightIcon().setVisibility(View.GONE); + titleBarLayout.getRightTitle().setTextColor(0xFF006EFF); + titleBarLayout.setOnLeftClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setResult(RESULT_CODE_ERROR); + finish(); + } + }); + boolean needDownload = intent.getBooleanExtra(NEED_DOWLOAD_LOCAL, false); + titleBarLayout.setOnRightClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (selected == null) { + return; + } + if (needDownload) { + downloadUrl(); + } else { + Intent resultIntent = new Intent(); + resultIntent.putExtra(DATA, (Serializable) selected); + setResult(RESULT_CODE_SUCCESS, resultIntent); + finish(); + } + } + }); + + data = (List) intent.getSerializableExtra(DATA); + selected = (ImageBean) intent.getSerializableExtra(SELECTED); + placeHolder = intent.getIntExtra(PLACEHOLDER, 0); + itemHeight = intent.getIntExtra(ITEM_HEIGHT, 0); + itemWidth = intent.getIntExtra(ITEM_WIDTH, 0); + columnNum = intent.getIntExtra(SPAN_COUNT, 2); + gridLayoutManager = new GridLayoutManager(this, columnNum); + imageGrid = findViewById(R.id.image_select_grid); + imageGrid.addItemDecoration(new GridDecoration(columnNum, defaultSpacing, defaultSpacing)); + imageGrid.setLayoutManager(gridLayoutManager); + imageGrid.setItemAnimator(null); + gridAdapter = new ImageGridAdapter(); + gridAdapter.setPlaceHolder(placeHolder); + gridAdapter.setSelected(selected); + gridAdapter.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onClick(ImageBean obj) { + selected = obj; + setSelectedStatus(); + } + }); + gridAdapter.setItemWidth(itemWidth); + gridAdapter.setItemHeight(itemHeight); + gridAdapter.setData(data); + imageGrid.setAdapter(gridAdapter); + setSelectedStatus(); + } + + private void downloadUrl() { + if (selected == null) { + return; + } + + if (selected.isDefault()) { + selected.setLocalPath(CHAT_CONVERSATION_BACKGROUND_DEFAULT_URL); + setResult(selected); + ToastUtil.toastShortMessage(getResources().getString(R.string.setting_success)); + finish(); + return; + } + + String url = selected.getImageUri(); + if (TextUtils.isEmpty(url)) { + Log.d(TAG, "DownloadUrl is null"); + return; + } + + final ProgressDialog dialog = new ProgressDialog(this); + dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + dialog.setCancelable(false); + dialog.setCanceledOnTouchOutside(false); + dialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + // TODO Auto-generated method stub + finish(); + } + }); + dialog.setMessage(getResources().getString(R.string.setting)); + dialog.show(); + + ImageBean finalBean = selected; + Glide.with(this) + .downloadOnly() + .load(url) + .listener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + dialog.cancel(); + Log.e(TAG, "DownloadUrl onLoadFailed e = " + e); + ToastUtil.toastShortMessage(getResources().getString(R.string.setting_fail)); + return false; + } + + @Override + public boolean onResourceReady(File resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + dialog.cancel(); + String path = resource.getAbsolutePath(); + Log.e(TAG, "DownloadUrl resource path = " + path); + finalBean.setLocalPath(path); + setResult(finalBean); + ToastUtil.toastShortMessage(getResources().getString(R.string.setting_success)); + return false; + } + }) + .preload(); + } + + private void setResult(ImageBean bean) { + Intent resultIntent = new Intent(); + resultIntent.putExtra(DATA, (Serializable) bean); + setResult(RESULT_CODE_SUCCESS, resultIntent); + finish(); + } + + private void setSelectedStatus() { + if (selected != null && data != null && data.contains(selected)) { + titleBarLayout.getRightTitle().setEnabled(true); + titleBarLayout.getRightTitle().setTextColor( + getResources().getColor(TUIThemeManager.getAttrResId(this, com.tencent.qcloud.tuicore.R.attr.core_primary_color))); + } else { + titleBarLayout.getRightTitle().setEnabled(false); + titleBarLayout.getRightTitle().setTextColor(0xFF666666); + } + gridAdapter.setSelected(selected); + } + + public static class ImageGridAdapter extends RecyclerView.Adapter { + private int itemWidth; + private int itemHeight; + + private List data; + private ImageBean selected; + private int placeHolder; + private OnItemClickListener onItemClickListener; + + public void setData(List data) { + this.data = data; + } + + public ImageBean getDataByPosition(int position) { + if (data != null) { + return data.get(position); + } else { + return null; + } + } + + public void setSelected(ImageBean selected) { + if (data == null || data.isEmpty()) { + this.selected = selected; + } else { + int index = indexOf(selected); + int index2 = indexOf(this.selected); + this.selected = selected; + notifyItemChanged(index, this.selected); + notifyItemChanged(index2, this.selected); + } + } + + private int indexOf(ImageBean imageBean) { + if (data == null || data.isEmpty()) { + return -1; + } else { + int originIndex = -1; + for (int i = 0; i < data.size(); i++) { + ImageBean item = data.get(i); + if (TextUtils.equals(item.thumbnailUri, imageBean.thumbnailUri)) { + originIndex = i; + break; + } + } + return originIndex; + } + } + + public void setPlaceHolder(int placeHolder) { + this.placeHolder = placeHolder; + } + + public void setItemHeight(int itemHeight) { + this.itemHeight = itemHeight; + } + + public void setItemWidth(int itemWidth) { + this.itemWidth = itemWidth; + } + + public void setOnItemClickListener(OnItemClickListener onItemClickListener) { + this.onItemClickListener = onItemClickListener; + } + + @NonNull + @Override + public ImageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.core_select_image_item_layout, parent, false); + return new ImageViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull ImageViewHolder holder, int position) {} + + @Override + public void onBindViewHolder(@NonNull ImageViewHolder holder, int position, @NonNull List payload) { + ImageView imageView = holder.imageView; + setItemLayoutParams(holder); + ImageBean imageBean = data.get(position); + if (selected != null && imageBean != null && TextUtils.equals(selected.getThumbnailUri(), imageBean.getThumbnailUri())) { + holder.selectBorderLayout.setVisibility(View.VISIBLE); + } else { + holder.selectBorderLayout.setVisibility(View.GONE); + } + + if (imageBean.getGroupGridAvatar() != null) { + holder.defaultLayout.setVisibility(View.GONE); + if (!payload.isEmpty()) { + return; + } + if (imageView instanceof SynthesizedImageView) { + SynthesizedImageView synthesizedImageView = ((SynthesizedImageView) (imageView)); + String imageId = imageBean.getImageId(); + synthesizedImageView.setImageId(imageId); + synthesizedImageView.displayImage(imageBean.getGroupGridAvatar()).load(imageId); + } + } else if (imageBean.isDefault()) { + holder.defaultLayout.setVisibility(View.VISIBLE); + imageView.setImageResource(android.R.color.transparent); + } else { + holder.defaultLayout.setVisibility(View.GONE); + Glide.with(holder.itemView.getContext()) + .asBitmap() + .load(imageBean.getThumbnailUri()) + .placeholder(placeHolder) + .apply(new RequestOptions().error(placeHolder)) + .into(imageView); + } + + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onClick(imageBean); + } + } + }); + } + + private void setItemLayoutParams(ImageViewHolder holder) { + if (itemHeight > 0 && itemWidth > 0) { + ViewGroup.LayoutParams itemViewLayoutParams = holder.itemView.getLayoutParams(); + itemViewLayoutParams.width = itemWidth; + itemViewLayoutParams.height = itemHeight; + holder.itemView.setLayoutParams(itemViewLayoutParams); + + ViewGroup.LayoutParams params = holder.imageView.getLayoutParams(); + params.width = itemWidth; + params.height = itemHeight; + holder.imageView.setLayoutParams(params); + + ViewGroup.LayoutParams borderLayoutParams = holder.selectBorderLayout.getLayoutParams(); + borderLayoutParams.width = itemWidth; + borderLayoutParams.height = itemHeight; + holder.selectBorderLayout.setLayoutParams(borderLayoutParams); + + ViewGroup.LayoutParams borderParams = holder.selectedBorder.getLayoutParams(); + borderParams.width = itemWidth; + borderParams.height = itemHeight; + holder.selectedBorder.setLayoutParams(borderParams); + } + } + + @Override + public int getItemCount() { + if (data == null || data.isEmpty()) { + return 0; + } + return data.size(); + } + + public static class ImageViewHolder extends RecyclerView.ViewHolder { + private final ImageView imageView; + private final ImageView selectedBorder; + private final RelativeLayout selectBorderLayout; + private final Button defaultLayout; + + public ImageViewHolder(@NonNull View itemView) { + super(itemView); + imageView = itemView.findViewById(R.id.content_image); + selectedBorder = itemView.findViewById(R.id.select_border); + selectBorderLayout = itemView.findViewById(R.id.selected_border_area); + defaultLayout = itemView.findViewById(R.id.default_image_layout); + } + } + } + + /** + * add spacing + */ + public static class GridDecoration extends RecyclerView.ItemDecoration { + private final int columnNum; // span count + private final int leftRightSpace; // vertical spacing + private final int topBottomSpace; // horizontal spacing + + public GridDecoration(int columnNum, int leftRightSpace, int topBottomSpace) { + this.columnNum = columnNum; + this.leftRightSpace = leftRightSpace; + this.topBottomSpace = topBottomSpace; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + int position = parent.getChildAdapterPosition(view); + int column = position % columnNum; + + int left = column * leftRightSpace / columnNum; + int right = leftRightSpace * (columnNum - 1 - column) / columnNum; + if (LayoutUtil.isRTL()) { + outRect.left = right; + outRect.right = left; + } else { + outRect.left = left; + outRect.right = right; + } + + // add top spacing + if (position >= columnNum) { + outRect.top = topBottomSpace; + } + } + } + + public interface OnItemClickListener { + void onClick(ImageBean obj); + } + + public static class ImageBean implements Serializable { + String thumbnailUri; // for display + String imageUri; // for download + String localPath; // for local path + boolean isDefault = false; // for default display + List groupGridAvatar = null; // for group grid avatar + String imageId; + + public ImageBean() {} + + public ImageBean(String thumbnailUri, String imageUri, boolean isDefault) { + this.thumbnailUri = thumbnailUri; + this.imageUri = imageUri; + this.isDefault = isDefault; + } + + public String getImageUri() { + return imageUri; + } + + public String getThumbnailUri() { + return thumbnailUri; + } + + public void setImageUri(String imageUri) { + this.imageUri = imageUri; + } + + public void setThumbnailUri(String thumbnailUri) { + this.thumbnailUri = thumbnailUri; + } + + public String getLocalPath() { + return localPath; + } + + public void setLocalPath(String localPath) { + this.localPath = localPath; + } + + public boolean isDefault() { + return isDefault; + } + + public void setDefault(boolean aDefault) { + isDefault = aDefault; + } + + public List getGroupGridAvatar() { + return groupGridAvatar; + } + + public void setGroupGridAvatar(List groupGridAvatar) { + this.groupGridAvatar = groupGridAvatar; + } + + public String getImageId() { + return imageId; + } + + public void setImageId(String imageId) { + this.imageId = imageId; + } + } +} \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/SelectionActivity.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/SelectionActivity.java new file mode 100644 index 0000000..ad79fb5 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/SelectionActivity.java @@ -0,0 +1,241 @@ +package com.tencent.qcloud.tuikit.timcommon.component.activities; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.InputFilter; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.RecyclerView; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.component.CustomLinearLayoutManager; +import com.tencent.qcloud.tuikit.timcommon.component.TitleBarLayout; +import com.tencent.qcloud.tuikit.timcommon.component.interfaces.ITitleBarLayout; +import java.util.ArrayList; + +public class SelectionActivity extends BaseLightActivity { + private static OnResultReturnListener sOnResultReturnListener; + + private RecyclerView selectListView; + private SelectAdapter selectListAdapter; + private EditText input; + private int mSelectionType; + private ArrayList selectList = new ArrayList<>(); + private int selectedItem = -1; + private OnItemClickListener onItemClickListener; + private boolean needConfirm = true; + private boolean returnNow = true; + + public static void startTextSelection(Context context, Bundle bundle, OnResultReturnListener listener) { + bundle.putInt(Selection.TYPE, Selection.TYPE_TEXT); + startSelection(context, bundle, listener); + } + + public static void startListSelection(Context context, Bundle bundle, OnResultReturnListener listener) { + bundle.putInt(Selection.TYPE, Selection.TYPE_LIST); + startSelection(context, bundle, listener); + } + + private static void startSelection(Context context, Bundle bundle, OnResultReturnListener listener) { + Intent intent = new Intent(context, SelectionActivity.class); + intent.putExtra(Selection.CONTENT, bundle); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + sOnResultReturnListener = listener; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.tuicore_selection_activity); + final TitleBarLayout titleBar = findViewById(R.id.edit_title_bar); + selectListView = findViewById(R.id.select_list); + selectListAdapter = new SelectAdapter(); + selectListView.setAdapter(selectListAdapter); + selectListView.setLayoutManager(new CustomLinearLayoutManager(this)); + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL); + dividerItemDecoration.setDrawable(getResources().getDrawable(R.drawable.core_list_divider)); + selectListView.addItemDecoration(dividerItemDecoration); + onItemClickListener = new OnItemClickListener() { + @Override + public void onClick(int position) { + selectedItem = position; + selectListAdapter.setSelectedItem(position); + selectListAdapter.notifyDataSetChanged(); + if (!needConfirm) { + echoClick(); + } + } + }; + input = findViewById(R.id.edit_content_et); + + Bundle bundle = getIntent().getBundleExtra(Selection.CONTENT); + switch (bundle.getInt(Selection.TYPE)) { + case Selection.TYPE_TEXT: + selectListView.setVisibility(View.GONE); + String defaultString = bundle.getString(Selection.INIT_CONTENT); + int limit = bundle.getInt(Selection.LIMIT); + if (!TextUtils.isEmpty(defaultString)) { + input.setText(defaultString); + input.setSelection(defaultString.length()); + } + if (limit > 0) { + input.setFilters(new InputFilter[] {new InputFilter.LengthFilter(limit)}); + } + break; + case Selection.TYPE_LIST: + input.setVisibility(View.GONE); + ArrayList list = bundle.getStringArrayList(Selection.LIST); + selectedItem = bundle.getInt(Selection.DEFAULT_SELECT_ITEM_INDEX); + if (list == null || list.size() == 0) { + return; + } + selectList.clear(); + selectList.addAll(list); + selectListAdapter.setSelectedItem(selectedItem); + selectListAdapter.setData(selectList); + selectListAdapter.notifyDataSetChanged(); + + break; + default: + finish(); + return; + } + mSelectionType = bundle.getInt(Selection.TYPE); + + final String title = bundle.getString(Selection.TITLE); + + needConfirm = bundle.getBoolean(Selection.NEED_CONFIRM, true); + returnNow = bundle.getBoolean(Selection.RETURN_NOW, true); + + titleBar.setTitle(title, ITitleBarLayout.Position.MIDDLE); + titleBar.setOnLeftClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + titleBar.getRightIcon().setVisibility(View.GONE); + if (needConfirm) { + titleBar.getRightTitle().setText(getResources().getString(com.tencent.qcloud.tuicore.R.string.sure)); + titleBar.setOnRightClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + echoClick(); + } + }); + } else { + titleBar.getRightGroup().setVisibility(View.GONE); + } + } + + private void echoClick() { + switch (mSelectionType) { + case Selection.TYPE_TEXT: + if (sOnResultReturnListener != null) { + sOnResultReturnListener.onReturn(input.getText().toString()); + } + break; + case Selection.TYPE_LIST: + if (sOnResultReturnListener != null) { + sOnResultReturnListener.onReturn(selectedItem); + } + break; + default: + break; + } + if (returnNow) { + finish(); + } + } + + @Override + protected void onStop() { + super.onStop(); + sOnResultReturnListener = null; + } + + class SelectAdapter extends RecyclerView.Adapter { + int selectedItem = -1; + ArrayList data = new ArrayList<>(); + + public void setData(ArrayList data) { + this.data.clear(); + this.data.addAll(data); + } + + public void setSelectedItem(int selectedItem) { + this.selectedItem = selectedItem; + } + + @NonNull + @Override + public SelectViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(SelectionActivity.this).inflate(R.layout.core_select_item_layout, parent, false); + return new SelectViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull SelectViewHolder holder, int position) { + String nameStr = data.get(position); + holder.name.setText(nameStr); + if (selectedItem == position) { + holder.selectedIcon.setVisibility(View.VISIBLE); + } else { + holder.selectedIcon.setVisibility(View.GONE); + } + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onItemClickListener.onClick(position); + } + }); + } + + @Override + public int getItemCount() { + return data.size(); + } + + class SelectViewHolder extends RecyclerView.ViewHolder { + TextView name; + ImageView selectedIcon; + + public SelectViewHolder(@NonNull View itemView) { + super(itemView); + name = itemView.findViewById(R.id.name); + selectedIcon = itemView.findViewById(R.id.selected_icon); + } + } + } + + public interface OnResultReturnListener { + void onReturn(Object res); + } + + public interface OnItemClickListener { + void onClick(int position); + } + + public static class Selection { + public static final String SELECT_ALL = "select_all"; + public static final String CONTENT = "content"; + public static final String TYPE = "type"; + public static final String TITLE = "title"; + public static final String INIT_CONTENT = "init_content"; + public static final String DEFAULT_SELECT_ITEM_INDEX = "default_select_item_index"; + public static final String LIST = "list"; + public static final String LIMIT = "limit"; + public static final String NEED_CONFIRM = "needConfirm"; + public static final String RETURN_NOW = "returnNow"; + public static final int TYPE_TEXT = 1; + public static final int TYPE_LIST = 2; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/SelectionMinimalistActivity.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/SelectionMinimalistActivity.java new file mode 100644 index 0000000..67d9f91 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/activities/SelectionMinimalistActivity.java @@ -0,0 +1,241 @@ +package com.tencent.qcloud.tuikit.timcommon.component.activities; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.InputFilter; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.RecyclerView; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.component.CustomLinearLayoutManager; +import com.tencent.qcloud.tuikit.timcommon.component.TitleBarLayout; +import com.tencent.qcloud.tuikit.timcommon.component.interfaces.ITitleBarLayout; +import java.util.ArrayList; + +public class SelectionMinimalistActivity extends BaseMinimalistLightActivity { + private static OnResultReturnListener sOnResultReturnListener; + + private RecyclerView selectListView; + private SelectAdapter selectListAdapter; + private EditText input; + private int mSelectionType; + private ArrayList selectList = new ArrayList<>(); + private int selectedItem = -1; + private OnItemClickListener onItemClickListener; + private boolean needConfirm = true; + private boolean returnNow = true; + + public static void startTextSelection(Context context, Bundle bundle, OnResultReturnListener listener) { + bundle.putInt(Selection.TYPE, Selection.TYPE_TEXT); + startSelection(context, bundle, listener); + } + + public static void startListSelection(Context context, Bundle bundle, OnResultReturnListener listener) { + bundle.putInt(Selection.TYPE, Selection.TYPE_LIST); + startSelection(context, bundle, listener); + } + + private static void startSelection(Context context, Bundle bundle, OnResultReturnListener listener) { + Intent intent = new Intent(context, SelectionMinimalistActivity.class); + intent.putExtra(Selection.CONTENT, bundle); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + sOnResultReturnListener = listener; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.core_minimalist_selection_activity); + final TitleBarLayout titleBar = findViewById(R.id.edit_title_bar); + selectListView = findViewById(R.id.select_list); + selectListAdapter = new SelectAdapter(); + selectListView.setAdapter(selectListAdapter); + selectListView.setLayoutManager(new CustomLinearLayoutManager(this)); + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL); + dividerItemDecoration.setDrawable(getResources().getDrawable(R.drawable.core_list_divider)); + selectListView.addItemDecoration(dividerItemDecoration); + onItemClickListener = new OnItemClickListener() { + @Override + public void onClick(int position) { + selectedItem = position; + selectListAdapter.setSelectedItem(position); + selectListAdapter.notifyDataSetChanged(); + if (!needConfirm) { + echoClick(); + } + } + }; + input = findViewById(R.id.edit_content_et); + + Bundle bundle = getIntent().getBundleExtra(Selection.CONTENT); + switch (bundle.getInt(Selection.TYPE)) { + case Selection.TYPE_TEXT: + selectListView.setVisibility(View.GONE); + String defaultString = bundle.getString(Selection.INIT_CONTENT); + int limit = bundle.getInt(Selection.LIMIT); + if (!TextUtils.isEmpty(defaultString)) { + input.setText(defaultString); + input.setSelection(defaultString.length()); + } + if (limit > 0) { + input.setFilters(new InputFilter[] {new InputFilter.LengthFilter(limit)}); + } + break; + case Selection.TYPE_LIST: + input.setVisibility(View.GONE); + ArrayList list = bundle.getStringArrayList(Selection.LIST); + selectedItem = bundle.getInt(Selection.DEFAULT_SELECT_ITEM_INDEX); + if (list == null || list.size() == 0) { + return; + } + selectList.clear(); + selectList.addAll(list); + selectListAdapter.setSelectedItem(selectedItem); + selectListAdapter.setData(selectList); + selectListAdapter.notifyDataSetChanged(); + + break; + default: + finish(); + return; + } + mSelectionType = bundle.getInt(Selection.TYPE); + + final String title = bundle.getString(Selection.TITLE); + + needConfirm = bundle.getBoolean(Selection.NEED_CONFIRM, true); + returnNow = bundle.getBoolean(Selection.RETURN_NOW, true); + + titleBar.setTitle(title, ITitleBarLayout.Position.MIDDLE); + titleBar.setOnLeftClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + titleBar.getRightIcon().setVisibility(View.GONE); + if (needConfirm) { + titleBar.getRightTitle().setText(getResources().getString(com.tencent.qcloud.tuicore.R.string.sure)); + titleBar.setOnRightClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + echoClick(); + } + }); + } else { + titleBar.getRightGroup().setVisibility(View.GONE); + } + } + + private void echoClick() { + switch (mSelectionType) { + case Selection.TYPE_TEXT: + if (sOnResultReturnListener != null) { + sOnResultReturnListener.onReturn(input.getText().toString()); + } + break; + case Selection.TYPE_LIST: + if (sOnResultReturnListener != null) { + sOnResultReturnListener.onReturn(selectedItem); + } + break; + default: + break; + } + if (returnNow) { + finish(); + } + } + + @Override + protected void onStop() { + super.onStop(); + sOnResultReturnListener = null; + } + + class SelectAdapter extends RecyclerView.Adapter { + int selectedItem = -1; + ArrayList data = new ArrayList<>(); + + public void setData(ArrayList data) { + this.data.clear(); + this.data.addAll(data); + } + + public void setSelectedItem(int selectedItem) { + this.selectedItem = selectedItem; + } + + @NonNull + @Override + public SelectViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(SelectionMinimalistActivity.this).inflate(R.layout.core_select_item_layout, parent, false); + return new SelectViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull SelectViewHolder holder, int position) { + String nameStr = data.get(position); + holder.name.setText(nameStr); + if (selectedItem == position) { + holder.selectedIcon.setVisibility(View.VISIBLE); + } else { + holder.selectedIcon.setVisibility(View.GONE); + } + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onItemClickListener.onClick(position); + } + }); + } + + @Override + public int getItemCount() { + return data.size(); + } + + class SelectViewHolder extends RecyclerView.ViewHolder { + TextView name; + ImageView selectedIcon; + + public SelectViewHolder(@NonNull View itemView) { + super(itemView); + name = itemView.findViewById(R.id.name); + selectedIcon = itemView.findViewById(R.id.selected_icon); + } + } + } + + public interface OnResultReturnListener { + void onReturn(Object res); + } + + public interface OnItemClickListener { + void onClick(int position); + } + + public static class Selection { + public static final String SELECT_ALL = "select_all"; + public static final String CONTENT = "content"; + public static final String TYPE = "type"; + public static final String TITLE = "title"; + public static final String INIT_CONTENT = "init_content"; + public static final String DEFAULT_SELECT_ITEM_INDEX = "default_select_item_index"; + public static final String LIST = "list"; + public static final String LIMIT = "limit"; + public static final String NEED_CONFIRM = "needConfirm"; + public static final String RETURN_NOW = "returnNow"; + public static final int TYPE_TEXT = 1; + public static final int TYPE_LIST = 2; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/dialog/MinimalistToast.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/dialog/MinimalistToast.java new file mode 100644 index 0000000..3ba6c9b --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/dialog/MinimalistToast.java @@ -0,0 +1,199 @@ +package com.tencent.qcloud.tuikit.timcommon.component.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.GradientDrawable; +import android.os.Handler; +import android.os.Looper; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +public class MinimalistToast { + + public static final int TYPE_SUCCESS = 1; + public static final int TYPE_ERROR = 2; + public static final int TYPE_WARNING = 3; + public static final int TYPE_INFO = 4; + + private static final int DURATION_SHORT = 2000; + private static final int DURATION_LONG = 3500; + + private Context mContext; + private Dialog mDialog; + private Handler mHandler; + + private MinimalistToast(Context context) { + mContext = context; + mHandler = new Handler(Looper.getMainLooper()); + } + + public static void show(Context context, String message) { + show(context, message, TYPE_INFO, DURATION_SHORT); + } + + public static void show(Context context, String message, int type) { + show(context, message, type, DURATION_SHORT); + } + + public static void show(Context context, String message, int type, int duration) { + new MinimalistToast(context).showToast(message, type, duration); + } + + public static void showSuccess(Context context, String message) { + show(context, message, TYPE_SUCCESS); + } + + public static void showError(Context context, String message) { + show(context, message, TYPE_ERROR); + } + + public static void showWarning(Context context, String message) { + show(context, message, TYPE_WARNING); + } + + public static void showInfo(Context context, String message) { + show(context, message, TYPE_INFO); + } + + private void showToast(String message, int type, int duration) { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + + View toastView = createToastView(message, type); + + mDialog = new Dialog(mContext); + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setContentView(toastView); + mDialog.setCancelable(true); + mDialog.setCanceledOnTouchOutside(true); + + Window window = mDialog.getWindow(); + if (window != null) { + window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + window.setGravity(Gravity.CENTER); + window.setDimAmount(0f); + window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL); + WindowManager.LayoutParams params = window.getAttributes(); + params.width = WindowManager.LayoutParams.WRAP_CONTENT; + params.height = WindowManager.LayoutParams.WRAP_CONTENT; + window.setAttributes(params); + } + + mDialog.show(); + + mHandler.postDelayed(() -> { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + } + }, duration); + } + + private View createToastView(String message, int type) { + FrameLayout container = new FrameLayout(mContext); + FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + container.setLayoutParams(containerParams); + + // Create multiple shadow layers for better effect + for (int i = 4; i >= 1; i--) { + FrameLayout shadowLayer = new FrameLayout(mContext); + FrameLayout.LayoutParams shadowParams = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + shadowParams.setMargins(dp2px(i), dp2px(i), 0, 0); + shadowLayer.setLayoutParams(shadowParams); + + GradientDrawable shadowBg = new GradientDrawable(); + shadowBg.setShape(GradientDrawable.RECTANGLE); + shadowBg.setCornerRadius(dp2px(6)); + int alpha = (int) (255 * 0.3f / i); // Increased opacity for more visible shadow + shadowBg.setColor(Color.argb(alpha, 0, 0, 0)); + shadowLayer.setBackground(shadowBg); + + container.addView(shadowLayer); + } + + FrameLayout contentLayout = new FrameLayout(mContext); + FrameLayout.LayoutParams contentParams = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + contentLayout.setLayoutParams(contentParams); + + GradientDrawable background = new GradientDrawable(); + background.setShape(GradientDrawable.RECTANGLE); + background.setCornerRadius(dp2px(6)); + background.setColor(Color.parseColor("#EBF3FF")); + contentLayout.setBackground(background); + + if (hasIcon(type)) { + ImageView iconView = new ImageView(mContext); + FrameLayout.LayoutParams iconParams = new FrameLayout.LayoutParams( + dp2px(16), dp2px(16) + ); + iconParams.gravity = Gravity.CENTER_VERTICAL | Gravity.START; + iconParams.setMargins(dp2px(16), 0, 0, 0); + iconView.setLayoutParams(iconParams); + iconView.setImageResource(getIconResource(type)); + contentLayout.addView(iconView); + } + + TextView textView = new TextView(mContext); + FrameLayout.LayoutParams textParams = new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ); + textParams.gravity = Gravity.CENTER; + if (hasIcon(type)) { + textParams.setMargins(dp2px(48), dp2px(8), dp2px(16), dp2px(8)); + } else { + textParams.setMargins(dp2px(16), dp2px(8), dp2px(16), dp2px(8)); + } + textView.setLayoutParams(textParams); + textView.setText(message); + textView.setTextColor(Color.BLACK); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); + textView.setMaxLines(2); + textView.setGravity(Gravity.CENTER); + contentLayout.addView(textView); + + container.addView(contentLayout); + return container; + } + + private boolean hasIcon(int type) { + return type != TYPE_INFO; + } + + private int getIconResource(int type) { + switch (type) { + case TYPE_SUCCESS: + return android.R.drawable.ic_menu_info_details; + case TYPE_ERROR: + return android.R.drawable.ic_dialog_alert; + case TYPE_WARNING: + return android.R.drawable.ic_dialog_info; + default: + return 0; + } + } + + private int dp2px(float dp) { + float density = mContext.getResources().getDisplayMetrics().density; + return (int) (dp * density + 0.5f); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/dialog/TUIKitDialog.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/dialog/TUIKitDialog.java new file mode 100644 index 0000000..268ea2e --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/dialog/TUIKitDialog.java @@ -0,0 +1,329 @@ +package com.tencent.qcloud.tuikit.timcommon.component.dialog; + +import static com.tencent.qcloud.tuicore.TUIConfig.TUICORE_SETTINGS_SP_NAME; + +import android.app.Dialog; +import android.content.Context; +import android.text.method.MovementMethod; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuicore.util.SPUtils; +import com.tencent.qcloud.tuikit.timcommon.BuildConfig; +import com.tencent.qcloud.tuikit.timcommon.R; + +import java.lang.ref.WeakReference; + +public class TUIKitDialog { + private Context mContext; + protected Dialog dialog; + private LinearLayout mBackgroundLayout; + private LinearLayout mMainLayout; + protected TextView mTitleTv; + private Button mCancelButton; + private Button mSureButton; + private ImageView mLineImg; + private Display mDisplay; + + private boolean showTitle = false; + private boolean showPosBtn = false; + private boolean showNegBtn = false; + + private float dialogWidth = 0.7f; + + public TUIKitDialog(Context context) { + this.mContext = context; + WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + mDisplay = windowManager.getDefaultDisplay(); + } + + public TUIKitDialog builder() { + View view = LayoutInflater.from(mContext).inflate(R.layout.common_dialog_view_layout, null); + mBackgroundLayout = view.findViewById(R.id.ll_background); + mMainLayout = view.findViewById(R.id.ll_alert); + mMainLayout.setVerticalGravity(View.GONE); + mTitleTv = view.findViewById(R.id.tv_title); + mTitleTv.setVisibility(View.GONE); + mCancelButton = view.findViewById(R.id.btn_neg); + mCancelButton.setVisibility(View.GONE); + mSureButton = view.findViewById(R.id.btn_pos); + mSureButton.setVisibility(View.GONE); + mLineImg = view.findViewById(R.id.img_line); + mLineImg.setVisibility(View.GONE); + + dialog = new Dialog(mContext, R.style.TUIKit_AlertDialogStyle); + dialog.setContentView(view); + + mBackgroundLayout.setLayoutParams(new FrameLayout.LayoutParams((int) (mDisplay.getWidth() * dialogWidth), LayoutParams.WRAP_CONTENT)); + return this; + } + + public TUIKitDialog setTitle(@NonNull CharSequence title) { + showTitle = true; + mTitleTv.setText(title); + return this; + } + + /*** + * Whether to click back to cancel + * @param cancel + * @return + */ + public TUIKitDialog setCancelable(boolean cancel) { + dialog.setCancelable(cancel); + return this; + } + + /** + * Whether the setting can be canceled + * + * @param isCancelOutside + * @return + */ + public TUIKitDialog setCancelOutside(boolean isCancelOutside) { + dialog.setCanceledOnTouchOutside(isCancelOutside); + return this; + } + + public TUIKitDialog setPositiveButton(final OnClickListener listener) { + setPositiveButton(TUIConfig.getAppContext().getString(com.tencent.qcloud.tuicore.R.string.sure), listener); + return this; + } + + public TUIKitDialog setPositiveButton(CharSequence text, final OnClickListener listener) { + showPosBtn = true; + mSureButton.setText(text); + mSureButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + listener.onClick(v); + dialog.dismiss(); + } + }); + return this; + } + + public void setTitleGravity(int gravity) { + mTitleTv.setGravity(gravity); + } + + public TUIKitDialog setNegativeButton(CharSequence text, final OnClickListener listener) { + showNegBtn = true; + mCancelButton.setText(text); + mCancelButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + listener.onClick(v); + dialog.dismiss(); + } + }); + return this; + } + + public TUIKitDialog setNegativeButton(final OnClickListener listener) { + setNegativeButton(TUIConfig.getAppContext().getString(com.tencent.qcloud.tuicore.R.string.cancel), listener); + return this; + } + + private void setLayout() { + if (!showTitle) { + mTitleTv.setVisibility(View.GONE); + } + + if (showTitle) { + mTitleTv.setVisibility(View.VISIBLE); + } + + if (!showPosBtn && !showNegBtn) { + mSureButton.setVisibility(View.GONE); + mSureButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + dialog.dismiss(); + } + }); + } + + if (showPosBtn && showNegBtn) { + mSureButton.setVisibility(View.VISIBLE); + mCancelButton.setVisibility(View.VISIBLE); + mLineImg.setVisibility(View.VISIBLE); + } + + if (showPosBtn && !showNegBtn) { + mSureButton.setVisibility(View.VISIBLE); + } + + if (!showPosBtn && showNegBtn) { + mCancelButton.setVisibility(View.VISIBLE); + } + } + + public void show() { + setLayout(); + dialog.show(); + } + + public void dismiss() { + if (dialog != null && dialog.isShowing()) { + dialog.dismiss(); + } + } + + public boolean isShowing() { + return dialog != null && dialog.isShowing(); + } + + /** + * + * @param dialogWidth + * @return + */ + public TUIKitDialog setDialogWidth(float dialogWidth) { + if (mBackgroundLayout != null) { + mBackgroundLayout.setLayoutParams(new FrameLayout.LayoutParams((int) (mDisplay.getWidth() * dialogWidth), LayoutParams.WRAP_CONTENT)); + } + this.dialogWidth = dialogWidth; + return this; + } + + public static class TUIIMUpdateDialog { + private static final class TUIIMUpdateDialogHolder { + private static final TUIIMUpdateDialog instance = new TUIIMUpdateDialog(); + } + + public static final String KEY_NEVER_SHOW = "neverShow"; + + private boolean isNeverShow; + private boolean isShowOnlyDebug = false; + private String dialogFeatureName; + + private WeakReference tuiKitDialog; + + public static TUIIMUpdateDialog getInstance() { + return TUIIMUpdateDialogHolder.instance; + } + + private TUIIMUpdateDialog() { + isNeverShow = SPUtils.getInstance(TUICORE_SETTINGS_SP_NAME).getBoolean(getDialogFeatureName(), false); + } + + public TUIIMUpdateDialog createDialog(Context context) { + tuiKitDialog = new WeakReference<>(new TUIKitDialog(context)); + tuiKitDialog.get().builder(); + return this; + } + + public void setNeverShow(boolean neverShowAlert) { + this.isNeverShow = neverShowAlert; + SPUtils.getInstance(TUICORE_SETTINGS_SP_NAME).put(getDialogFeatureName(), neverShowAlert); + } + + public TUIIMUpdateDialog setShowOnlyDebug(boolean isShowOnlyDebug) { + this.isShowOnlyDebug = isShowOnlyDebug; + return this; + } + + public TUIIMUpdateDialog setMovementMethod(MovementMethod movementMethod) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().mTitleTv.setMovementMethod(movementMethod); + } + return this; + } + + public TUIIMUpdateDialog setHighlightColor(int color) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().mTitleTv.setHighlightColor(color); + } + return this; + } + + public TUIIMUpdateDialog setCancelable(boolean cancelable) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().setCancelable(cancelable); + } + return this; + } + + public TUIIMUpdateDialog setCancelOutside(boolean cancelOutside) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().setCancelOutside(cancelOutside); + } + return this; + } + + public TUIIMUpdateDialog setTitle(CharSequence charSequence) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().setTitle(charSequence); + } + return this; + } + + public TUIIMUpdateDialog setDialogWidth(float dialogWidth) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().setDialogWidth(dialogWidth); + } + return this; + } + + public TUIIMUpdateDialog setPositiveButton(CharSequence text, OnClickListener clickListener) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().setPositiveButton(text, clickListener); + } + return this; + } + + public TUIIMUpdateDialog setNegativeButton(CharSequence text, OnClickListener clickListener) { + if (tuiKitDialog != null && tuiKitDialog.get() != null) { + tuiKitDialog.get().setNegativeButton(text, clickListener); + } + return this; + } + + public TUIIMUpdateDialog setDialogFeatureName(String featureName) { + this.dialogFeatureName = featureName; + return this; + } + + private String getDialogFeatureName() { + return dialogFeatureName; + } + + public void show() { + if (tuiKitDialog == null || tuiKitDialog.get() == null) { + return; + } + isNeverShow = SPUtils.getInstance(TUICORE_SETTINGS_SP_NAME).getBoolean(getDialogFeatureName(), false); + Dialog dialog = tuiKitDialog.get().dialog; + if (dialog == null || dialog.isShowing()) { + return; + } + if (isNeverShow) { + return; + } + if (isShowOnlyDebug && !BuildConfig.DEBUG) { + return; + } + tuiKitDialog.get().show(); + } + + public void dismiss() { + if (tuiKitDialog == null || tuiKitDialog.get() == null) { + return; + } + tuiKitDialog.get().dismiss(); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/CenterImageSpan.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/CenterImageSpan.java new file mode 100644 index 0000000..f62f7d9 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/CenterImageSpan.java @@ -0,0 +1,51 @@ +package com.tencent.qcloud.tuikit.timcommon.component.face; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.text.style.ImageSpan; + +import androidx.annotation.NonNull; + +public class CenterImageSpan extends ImageSpan { + + private int bgColor = -1; + + public CenterImageSpan(@NonNull Drawable drawable) { + super(drawable); + } + + public void setBgColor(int bgColor) { + this.bgColor = bgColor; + } + + @Override + public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { + Drawable drawable = getDrawable(); + Rect rect = drawable.getBounds(); + Paint.FontMetricsInt paintFm = paint.getFontMetricsInt(); + int center = (paintFm.top + paintFm.bottom) / 2; + if (fm != null) { + fm.ascent = center - (rect.height() / 2); + fm.descent = center + (rect.height() / 2); + fm.top = fm.ascent; + fm.bottom = fm.descent; + } + + return rect.right; + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + if (bgColor == -1) { + super.draw(canvas, text, start, end, x, top, y, bottom, paint); + } else { + canvas.save(); + paint.setColor(bgColor); + canvas.drawRect(x, top, getDrawable().getBounds().right + x, bottom, paint); + canvas.restore(); + super.draw(canvas, text, start, end, x, top, y, bottom, paint); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/FaceManager.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/FaceManager.java new file mode 100644 index 0000000..41bac0c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/FaceManager.java @@ -0,0 +1,546 @@ +package com.tencent.qcloud.tuikit.timcommon.component.face; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.text.Editable; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; +import android.text.style.ImageSpan; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.Nullable; +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.DataSource; +import com.bumptech.glide.load.engine.GlideException; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.Target; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.TIMCommonService; +import com.tencent.qcloud.tuikit.timcommon.bean.ChatFace; +import com.tencent.qcloud.tuikit.timcommon.bean.Emoji; +import com.tencent.qcloud.tuikit.timcommon.bean.FaceGroup; +import com.tencent.qcloud.tuikit.timcommon.util.TIMCommonLog; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class FaceManager { + private static final String TAG = "FaceManager"; + + private static final class FaceManagerHolder { + @SuppressLint("StaticFieldLeak") private static final FaceManager instance = new FaceManager(); + } + + public static final int EMOJI_GROUP_ID = 0; + public static final int EMOJI_COLUMN_COUNT = 8; + public static final int EMOJI_ROW_COUNT = 3; + + private final Map emojiMap = new LinkedHashMap<>(); + private final Context context; + + private final Map> faceGroupMap = new ConcurrentHashMap<>(); + + private FaceManager() { + context = TIMCommonService.getAppContext(); + } + + private static FaceManager getInstance() { + return FaceManagerHolder.instance; + } + + public static ArrayList getEmojiList() { + return new ArrayList<>(getInstance().emojiMap.values()); + } + + public static Map getEmojiMap() { + return Collections.unmodifiableMap(getInstance().emojiMap); + } + + public static int getEmojiCount() { + return getInstance().emojiMap.size(); + } + + /** + * add a new faceGroup + * @param groupID must >= 1 + * @param faceGroup the faceGroup be added + */ + public static synchronized void addFaceGroup(int groupID, FaceGroup faceGroup) { + faceGroup.setGroupID(groupID); + getInstance().faceGroupMap.put(groupID, faceGroup); + if (faceGroup.isEmojiGroup()) { + List faces = faceGroup.getFaces(); + for (T face : faces) { + getInstance().emojiMap.put(face.getFaceKey(), (Emoji) face); + } + } + } + + public static List getFaceGroupList() { + return new ArrayList<>(getInstance().faceGroupMap.values()); + } + + public static Emoji loadAssetEmoji(String emojiKey, String assetFilePath, int size) { + String realPath = "file:///android_asset/" + assetFilePath; + Bitmap bitmap = loadBitmap(realPath, size, size); + if (bitmap == null) { + TIMCommonLog.e(TAG, "load bitmap failed : " + realPath); + return null; + } + Emoji emoji = new Emoji(); + emoji.setIcon(bitmap); + emoji.setFaceKey(emojiKey); + return emoji; + } + + private static Bitmap loadBitmap(String resUrl, int width, int height) { + Bitmap bitmap = null; + try { + bitmap = Glide.with(TIMCommonService.getAppContext()) + .asBitmap() + .load(resUrl) + .apply(new RequestOptions().error(android.R.drawable.ic_menu_report_image)) + .submit(width, height) + .get(); + } catch (InterruptedException | ExecutionException e) { + TIMCommonLog.e(TAG, "load bitmap failed : " + e.getMessage()); + } + return bitmap; + } + + public static void loadFace(ChatFace chatFace, ImageView imageView) { + getInstance().internalLoadFace(chatFace, imageView, true); + } + + public static void loadFace(int faceGroupID, String faceKey, ImageView view) { + getInstance().internalLoadFace(faceGroupID, faceKey, view); + } + + private void internalLoadFace(int faceGroupID, String faceKey, ImageView imageView) { + if (imageView == null) { + return; + } + if (TextUtils.isEmpty(faceKey)) { + Glide.with(TIMCommonService.getAppContext()).load(android.R.drawable.ic_menu_report_image).centerInside().into(imageView); + return; + } + String faceUrl = ""; + FaceGroup faceGroup = faceGroupMap.get(faceGroupID); + ChatFace face = null; + if (faceGroup != null) { + face = faceGroup.getFace(faceKey); + if (face != null) { + faceUrl = face.getFaceUrl(); + } + } + final ChatFace finalFace = face; + Glide.with(TIMCommonService.getAppContext()) + .load(faceUrl) + .centerInside() + .apply(new RequestOptions().error(android.R.drawable.ic_menu_report_image)) + .addListener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + if (finalFace != null && finalFace.isAutoMirrored()) { + resource.setAutoMirrored(true); + } + return false; + } + }) + .into(imageView); + } + + private void internalLoadFace(ChatFace chatFace, ImageView imageView, boolean isBitMap) { + if (imageView == null || chatFace == null) { + return; + } + if (chatFace instanceof Emoji) { + Glide.with(TIMCommonService.getAppContext()) + .load(((Emoji) chatFace).getIcon()) + .centerInside() + .apply(new RequestOptions().error(android.R.drawable.ic_menu_report_image)) + .addListener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + if (chatFace.isAutoMirrored()) { + resource.setAutoMirrored(true); + } + return false; + } + }) + .into(imageView); + return; + } + String faceUrl = ""; + FaceGroup faceGroup = chatFace.getFaceGroup(); + ChatFace face = null; + if (faceGroup != null) { + face = faceGroup.getFace(chatFace.getFaceKey()); + if (face != null) { + faceUrl = face.getFaceUrl(); + } + } + final ChatFace finalFace = face; + if (isBitMap) { + Glide.with(TIMCommonService.getAppContext()) + .asBitmap() + .load(faceUrl) + .centerInside() + .apply(new RequestOptions().error(android.R.drawable.ic_menu_report_image)) + .addListener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(Bitmap resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + if (finalFace.isAutoMirrored()) { + imageView.setImageBitmap(resource); + imageView.getDrawable().setAutoMirrored(true); + return true; + } else { + return false; + } + } + }) + .into(imageView); + } else { + Glide.with(TIMCommonService.getAppContext()) + .load(faceUrl) + .centerInside() + .apply(new RequestOptions().error(android.R.drawable.ic_menu_report_image)) + .addListener(new RequestListener() { + @Override + public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) { + return false; + } + + @Override + public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) { + if (finalFace != null && finalFace.isAutoMirrored()) { + resource.setAutoMirrored(true); + } + return false; + } + }) + .into(imageView); + } + } + + public static boolean isFaceChar(String faceChar) { + return getEmojiMap().get(faceChar) != null; + } + + public static boolean handlerEmojiText(TextView comment, CharSequence content, boolean typing) { + if (comment == null) { + return false; + } + if (content == null) { + comment.setText(null); + return false; + } + + Spannable spannable; + if (comment instanceof EditText && content instanceof Editable) { + spannable = (Editable) content; + ImageSpan[] imageSpans = ((Editable) content).getSpans(0, content.length(), ImageSpan.class); + for (ImageSpan span : imageSpans) { + ((Editable) content).removeSpan(span); + } + } else { + spannable = new SpannableStringBuilder(content); + } + String regex = "\\[(\\S+?)\\]"; + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(content); + boolean imageFound = false; + while (m.find()) { + String emojiName = m.group(); + Emoji emoji = getEmojiMap().get(emojiName); + if (emoji != null) { + Bitmap bitmap = emoji.getIcon(); + if (bitmap != null) { + imageFound = true; + + BitmapDrawable bitmapDrawable = new BitmapDrawable(getInstance().context.getResources(), bitmap); + int size = getInstance().context.getResources().getDimensionPixelSize(R.dimen.common_default_emoji_size); + bitmapDrawable.setBounds(0, 0, size, size); + ImageSpan imageSpan = new CenterImageSpan(bitmapDrawable); + spannable.setSpan(imageSpan, m.start(), m.end(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); + } + } + } + + // If no emoticon picture is found, and it is currently in the input state, the input box will not be reset. + if (!imageFound && typing) { + return false; + } + int selection = comment.getSelectionStart(); + if (!(comment instanceof EditText)) { + comment.setText(spannable); + } + if (comment instanceof EditText) { + ((EditText) comment).setSelection(selection); + } + + return true; + } + + public static Bitmap getEmoji(String name) { + Emoji emoji = getEmojiMap().get(name); + if (emoji != null) { + return emoji.getIcon(); + } + return null; + } + + public static CharSequence emojiJudge(CharSequence text) { + if (TextUtils.isEmpty(text)) { + return ""; + } + + if (getEmojiCount() == 0) { + return text; + } + + SpannableStringBuilder sb = new SpannableStringBuilder(text); + String regex = "\\[(\\S+?)\\]"; + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(text); + ArrayList emojiDataArrayList = new ArrayList<>(); + + // Traverse to find matching characters and store + while (m.find()) { + String emojiKey = m.group(); + int start = m.start(); + int end = m.end(); + + Emoji emoji = getEmojiMap().get(emojiKey); + if (emoji == null) { + continue; + } + EmojiData emojiData = new EmojiData(); + emojiData.setStart(start); + emojiData.setEnd(end); + emojiData.setEmojiText(emoji.getFaceName()); + + emojiDataArrayList.add(emojiData); + } + + // flashback replacement + if (emojiDataArrayList.isEmpty()) { + return text; + } + for (int i = emojiDataArrayList.size() - 1; i >= 0; i--) { + EmojiData emojiData = emojiDataArrayList.get(i); + String emojiName = emojiData.getEmojiText(); + int start = emojiData.getStart(); + int end = emojiData.getEnd(); + + if (!TextUtils.isEmpty(emojiName) && start != -1 && end != -1) { + sb.replace(start, end, emojiName); + } + } + return sb; + } + + public static List splitEmojiText(String text) { + String regex = "\\[(\\S+?)\\]"; + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(text); + ArrayList emojiDataList = new ArrayList<>(); + int lastMentionIndex = -1; + while (m.find()) { + String emojiKey = m.group(); + int start; + if (lastMentionIndex != -1) { + start = text.indexOf(emojiKey, lastMentionIndex); + } else { + start = text.indexOf(emojiKey); + } + int end = start + emojiKey.length(); + lastMentionIndex = end; + + Emoji emoji = getEmojiMap().get(emojiKey); + if (emoji == null) { + continue; + } + EmojiData emojiData = new EmojiData(); + emojiData.setStart(start); + emojiData.setEnd(end); + emojiDataList.add(emojiData); + } + List stringList = new ArrayList<>(); + int offset = 0; + for (EmojiData emojiData : emojiDataList) { + int start = emojiData.getStart() - offset; + int end = emojiData.getEnd() - offset; + String startStr = text.substring(0, start); + String middleStr = text.substring(start, end); + text = text.substring(end); + if (!TextUtils.isEmpty(startStr)) { + stringList.add(startStr); + } + stringList.add(middleStr); + offset += startStr.length() + middleStr.length(); + } + if (!TextUtils.isEmpty(text)) { + stringList.add(text); + } + return stringList; + } + + public static List findEmojiKeyListFromText(String text) { + if (TextUtils.isEmpty(text)) { + return null; + } + List emojiKeyList = new ArrayList<>(); + // TUIKit custom emoji. + String regexOfCustomEmoji = "\\[(\\S+?)\\]"; + Pattern patternOfCustomEmoji = Pattern.compile(regexOfCustomEmoji); + Matcher matcherOfCustomEmoji = patternOfCustomEmoji.matcher(text); + while (matcherOfCustomEmoji.find()) { + String emojiName = matcherOfCustomEmoji.group(); + Emoji emoji = getEmojiMap().get(emojiName); + if (emoji != null) { + Bitmap bitmap = emoji.getIcon(); + if (bitmap != null) { + emojiKeyList.add(emojiName); + } + } + } + + // Universal standard emoji. + String regexOfUniversalEmoji = getRegexOfUniversalEmoji(); + Pattern patternOfUniversalEmoji = Pattern.compile(regexOfUniversalEmoji); + Matcher matcherOfUniversalEmoji = patternOfUniversalEmoji.matcher(text); + while (matcherOfUniversalEmoji.find()) { + String emojiKey = matcherOfUniversalEmoji.group(); + if (!TextUtils.isEmpty(emojiKey)) { + emojiKeyList.add(matcherOfUniversalEmoji.group()); + } + } + + return emojiKeyList; + } + + private static class EmojiData { + private int start; + private int end; + private String emojiText; + + public int getEnd() { + return end; + } + + public void setEnd(int end) { + this.end = end; + } + + public int getStart() { + return start; + } + + public void setStart(int start) { + this.start = start; + } + + public String getEmojiText() { + return emojiText; + } + + public void setEmojiText(String emojiText) { + this.emojiText = emojiText; + } + } + + // Regex of universal emoji, refer to https://unicode.org/reports/tr51/#EBNF_and_Regex + private static String getRegexOfUniversalEmoji() { + String ri = "[\\U0001F1E6-\\U0001F1FF]"; + // \u0023(#), \u002A(*), \u0030(keycap 0), \u0039(keycap 9), \u00A9(©), \u00AE(®) couldn't be added to NSString directly, need to transform a little + // bit. + String support = "\\U000000A9|\\U000000AE|\\u203C|\\u2049|\\u2122|\\u2139|[\\u2194-\\u2199]|[\\u21A9-\\u21AA]" + + "|[\\u231A-\\u231B]|\\u2328|\\u23CF|[\\u23E9-\\u23EF]|[\\u23F0-\\u23F3]|[\\u23F8-\\u23FA]|\\u24C2" + + "|[\\u25AA-\\u25AB]|\\u25B6|\\u25C0|[\\u25FB-\\u25FE]|[\\u2600-\\u2604]|\\u260E|\\u2611|[\\u2614-\\u2615]" + + "|\\u2618|\\u261D|\\u2620|[\\u2622-\\u2623]|\\u2626|\\u262A|[\\u262E-\\u262F]|[\\u2638-\\u263A]|\\u2640" + + "|\\u2642|[\\u2648-\\u264F]|[\\u2650-\\u2653]|\\u265F|\\u2660|\\u2663|[\\u2665-\\u2666]|\\u2668|\\u267B" + + "|[\\u267E-\\u267F]|[\\u2692-\\u2697]|\\u2699|[\\u269B-\\u269C]|[\\u26A0-\\u26A1]|\\u26A7|[\\u26AA-\\u26AB]" + + "|[\\u26B0-\\u26B1]|[\\u26BD-\\u26BE]|[\\u26C4-\\u26C5]|\\u26C8|[\\u26CE-\\u26CF]|\\u26D1|[\\u26D3-\\u26D4]" + + "|[\\u26E9-\\u26EA]|[\\u26F0-\\u26F5]|[\\u26F7-\\u26FA]|\\u26FD|\\u2702|\\u2705|[\\u2708-\\u270D]|\\u270F|\\u2712" + + "|\\u2714|\\u2716|\\u271D|\\u2721|\\u2728|[\\u2733-\\u2734]|\\u2744|\\u2747|\\u274C|\\u274E|[\\u2753-\\u2755]" + + "|\\u2757|[\\u2763-\\u2764]|[\\u2795-\\u2797]|\\u27A1|\\u27B0|\\u27BF|[\\u2934-\\u2935]|[\\u2B05-\\u2B07]" + + "|[\\u2B1B-\\u2B1C]|\\u2B50|\\u2B55|\\u3030|\\u303D|\\u3297|\\u3299|\\U0001F004|\\U0001F0CF|[\\U0001F170-\\U0001F171]" + + "|[\\U0001F17E-\\U0001F17F]|\\U0001F18E|[\\U0001F191-\\U0001F19A]|[\\U0001F1E6-\\U0001F1FF]|[\\U0001F201-\\U0001F202]" + + "|\\U0001F21A|\\U0001F22F|[\\U0001F232-\\U0001F23A]|[\\U0001F250-\\U0001F251]|[\\U0001F300-\\U0001F30F]" + + "|[\\U0001F310-\\U0001F31F]|[\\U0001F320-\\U0001F321]|[\\U0001F324-\\U0001F32F]|[\\U0001F330-\\U0001F33F]" + + "|[\\U0001F340-\\U0001F34F]|[\\U0001F350-\\U0001F35F]|[\\U0001F360-\\U0001F36F]|[\\U0001F370-\\U0001F37F]" + + "|[\\U0001F380-\\U0001F38F]|[\\U0001F390-\\U0001F393]|[\\U0001F396-\\U0001F397]|[\\U0001F399-\\U0001F39B]" + + "|[\\U0001F39E-\\U0001F39F]|[\\U0001F3A0-\\U0001F3AF]|[\\U0001F3B0-\\U0001F3BF]|[\\U0001F3C0-\\U0001F3CF]" + + "|[\\U0001F3D0-\\U0001F3DF]|[\\U0001F3E0-\\U0001F3EF]|\\U0001F3F0|[\\U0001F3F3-\\U0001F3F5]|[\\U0001F3F7-\\U0001F3FF]" + + "|[\\U0001F400-\\U0001F40F]|[\\U0001F410-\\U0001F41F]|[\\U0001F420-\\U0001F42F]|[\\U0001F430-\\U0001F43F]" + + "|[\\U0001F440-\\U0001F44F]|[\\U0001F450-\\U0001F45F]|[\\U0001F460-\\U0001F46F]|[\\U0001F470-\\U0001F47F]" + + "|[\\U0001F480-\\U0001F48F]|[\\U0001F490-\\U0001F49F]|[\\U0001F4A0-\\U0001F4AF]|[\\U0001F4B0-\\U0001F4BF]" + + "|[\\U0001F4C0-\\U0001F4CF]|[\\U0001F4D0-\\U0001F4DF]|[\\U0001F4E0-\\U0001F4EF]|[\\U0001F4F0-\\U0001F4FF]" + + "|[\\U0001F500-\\U0001F50F]|[\\U0001F510-\\U0001F51F]|[\\U0001F520-\\U0001F52F]|[\\U0001F530-\\U0001F53D]" + + "|[\\U0001F549-\\U0001F54E]|[\\U0001F550-\\U0001F55F]|[\\U0001F560-\\U0001F567]|\\U0001F56F|\\U0001F570" + + "|[\\U0001F573-\\U0001F57A]|\\U0001F587|[\\U0001F58A-\\U0001F58D]|\\U0001F590|[\\U0001F595-\\U0001F596]" + + "|[\\U0001F5A4-\\U0001F5A5]|\\U0001F5A8|[\\U0001F5B1-\\U0001F5B2]|\\U0001F5BC|[\\U0001F5C2-\\U0001F5C4]" + + "|[\\U0001F5D1-\\U0001F5D3]|[\\U0001F5DC-\\U0001F5DE]|\\U0001F5E1|\\U0001F5E3|\\U0001F5E8|\\U0001F5EF|\\U0001F5F3" + + "|[\\U0001F5FA-\\U0001F5FF]|[\\U0001F600-\\U0001F60F]|[\\U0001F610-\\U0001F61F]|[\\U0001F620-\\U0001F62F]" + + "|[\\U0001F630-\\U0001F63F]|[\\U0001F640-\\U0001F64F]|[\\U0001F650-\\U0001F65F]|[\\U0001F660-\\U0001F66F]" + + "|[\\U0001F670-\\U0001F67F]|[\\U0001F680-\\U0001F68F]|[\\U0001F690-\\U0001F69F]|[\\U0001F6A0-\\U0001F6AF]" + + "|[\\U0001F6B0-\\U0001F6BF]|[\\U0001F6C0-\\U0001F6C5]|[\\U0001F6CB-\\U0001F6CF]|[\\U0001F6D0-\\U0001F6D2]" + + "|[\\U0001F6D5-\\U0001F6D7]|[\\U0001F6DD-\\U0001F6DF]|[\\U0001F6E0-\\U0001F6E5]|\\U0001F6E9|[\\U0001F6EB-\\U0001F6EC]" + + "|\\U0001F6F0|[\\U0001F6F3-\\U0001F6FC]|[\\U0001F7E0-\\U0001F7EB]|\\U0001F7F0|[\\U0001F90C-\\U0001F90F]" + + "|[\\U0001F910-\\U0001F91F]|[\\U0001F920-\\U0001F92F]|[\\U0001F930-\\U0001F93A]|[\\U0001F93C-\\U0001F93F]" + + "|[\\U0001F940-\\U0001F945]|[\\U0001F947-\\U0001F94C]|[\\U0001F94D-\\U0001F94F]|[\\U0001F950-\\U0001F95F]" + + "|[\\U0001F960-\\U0001F96F]|[\\U0001F970-\\U0001F97F]|[\\U0001F980-\\U0001F98F]|[\\U0001F990-\\U0001F99F]" + + "|[\\U0001F9A0-\\U0001F9AF]|[\\U0001F9B0-\\U0001F9BF]|[\\U0001F9C0-\\U0001F9CF]|[\\U0001F9D0-\\U0001F9DF]" + + "|[\\U0001F9E0-\\U0001F9EF]|[\\U0001F9F0-\\U0001F9FF]|[\\U0001FA70-\\U0001FA74]|[\\U0001FA78-\\U0001FA7C]" + + "|[\\U0001FA80-\\U0001FA86]|[\\U0001FA90-\\U0001FA9F]|[\\U0001FAA0-\\U0001FAAC]|[\\U0001FAB0-\\U0001FABA]" + + "|[\\U0001FAC0-\\U0001FAC5]|[\\U0001FAD0-\\U0001FAD9]|[\\U0001FAE0-\\U0001FAE7]|[\\U0001FAF0-\\U0001FAF6]"; + String unsupport = "\\u0023|\\u002A|[\\u0030-\\u0039]|"; + String emoji = unsupport + support; + + // Construct regex of emoji by the rules above. + String eMod = "[\\U0001F3FB-\\U0001F3FF]"; + + String variationSelector = "\\uFE0F"; + String keycap = "\\u20E3"; + String tags = "[\\U000E0020-\\U000E007E]"; + String termTag = "\\U000E007F"; + String zwj = "\\u200D"; + + String risequence = "[" + ri + "]" + + "[" + ri + "]"; + String element = "[" + emoji + "]" + + "(" + + "[" + eMod + "]|" + variationSelector + keycap + "?|[" + tags + "]+" + termTag + "?)?"; + String regexEmoji = risequence + "|" + element + "(" + zwj + "(" + risequence + "|" + element + "))*"; + + return regexEmoji; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/RecentEmojiManager.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/RecentEmojiManager.java new file mode 100644 index 0000000..f11c403 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/face/RecentEmojiManager.java @@ -0,0 +1,87 @@ +package com.tencent.qcloud.tuikit.timcommon.component.face; + +import android.text.TextUtils; +import android.util.Base64; +import com.tencent.qcloud.tuicore.util.SPUtils; +import com.tencent.qcloud.tuikit.timcommon.bean.Emoji; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.List; + +public class RecentEmojiManager { + public static final String PREFERENCE_NAME = "recentFace"; + public static final int DEFAULT_RECENT_NUM = 10; + private static final String DEFAULT_RECENT_EMOJI_KEY = "recentEmoji"; + + private static final RecentEmojiManager instance = new RecentEmojiManager(); + + private RecentEmojiManager() {} + + public static RecentEmojiManager getInstance() { + return instance; + } + + public String getString(String key) { + return SPUtils.getInstance(PREFERENCE_NAME).getString(key); + } + + public RecentEmojiManager putString(String key, String value) { + SPUtils.getInstance(PREFERENCE_NAME).put(key, value); + return this; + } + + public static void putCollection(List emojiList) { + getInstance().putCollection(DEFAULT_RECENT_EMOJI_KEY, emojiList); + } + + public RecentEmojiManager putCollection(String key, List emojiList) { + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + objectOutputStream.writeObject(emojiList); + String collectionString = new String(Base64.encode(byteArrayOutputStream.toByteArray(), Base64.DEFAULT)); + objectOutputStream.close(); + return putString(key, collectionString); + } catch (Exception e) { + e.printStackTrace(); + } + return this; + } + + public List getCollection(String key) { + try { + String collectionString = getString(key); + if (TextUtils.isEmpty(collectionString) || TextUtils.isEmpty(collectionString.trim())) { + return null; + } + byte[] mobileBytes = Base64.decode(collectionString.getBytes(), Base64.DEFAULT); + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(mobileBytes); + ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); + Object collectionObj = objectInputStream.readObject(); + List collection = null; + if (collectionObj instanceof List) { + collection = (List) collectionObj; + } + return collection; + } catch (Exception e) { + return null; + } + } + + public static List getCollection() { + return getInstance().getCollection(DEFAULT_RECENT_EMOJI_KEY); + } + + public static void updateRecentUseEmoji(String emojiKey) { + List recentList = getCollection(); + recentList.remove(emojiKey); + recentList.add(0, emojiKey); + if (recentList.size() > DEFAULT_RECENT_NUM) { + recentList.remove(recentList.size() - 1); + } + putCollection(recentList); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/MultiImageData.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/MultiImageData.java new file mode 100644 index 0000000..8230a7c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/MultiImageData.java @@ -0,0 +1,99 @@ +package com.tencent.qcloud.tuikit.timcommon.component.gatherimage; + +import android.graphics.Bitmap; +import android.graphics.Color; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Multiple image data + */ + +public class MultiImageData implements Cloneable { + static final int maxSize = 9; + List imageUrls; + int defaultImageResId; + Map bitmapMap; + int bgColor = Color.parseColor("#cfd3d8"); + + int targetImageSize; + int maxWidth; + int maxHeight; + int rowCount; + int columnCount; + int gap = 6; + + public MultiImageData() {} + + public MultiImageData(int defaultImageResId) { + this.defaultImageResId = defaultImageResId; + } + + public MultiImageData(List imageUrls, int defaultImageResId) { + this.imageUrls = imageUrls; + this.defaultImageResId = defaultImageResId; + } + + public int getDefaultImageResId() { + return defaultImageResId; + } + + public void setDefaultImageResId(int defaultImageResId) { + this.defaultImageResId = defaultImageResId; + } + + public List getImageUrls() { + return imageUrls; + } + + public void setImageUrls(List imageUrls) { + this.imageUrls = imageUrls; + } + + public void putBitmap(Bitmap bitmap, int position) { + if (null != bitmapMap) { + synchronized (bitmapMap) { + bitmapMap.put(position, bitmap); + } + } else { + bitmapMap = new HashMap<>(); + synchronized (bitmapMap) { + bitmapMap.put(position, bitmap); + } + } + } + + public Bitmap getBitmap(int position) { + if (null != bitmapMap) { + synchronized (bitmapMap) { + return bitmapMap.get(position); + } + } + return null; + } + + public int size() { + if (null != imageUrls) { + return imageUrls.size() > maxSize ? maxSize : imageUrls.size(); + } else { + return 0; + } + } + + @Override + protected MultiImageData clone() throws CloneNotSupportedException { + MultiImageData multiImageData = (MultiImageData) super.clone(); + if (imageUrls != null) { + multiImageData.imageUrls = new ArrayList<>(imageUrls.size()); + multiImageData.imageUrls.addAll(imageUrls); + } + if (bitmapMap != null) { + multiImageData.bitmapMap = new HashMap<>(); + multiImageData.bitmapMap.putAll(bitmapMap); + } + return multiImageData; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/ShadeImageView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/ShadeImageView.java new file mode 100644 index 0000000..2f45592 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/ShadeImageView.java @@ -0,0 +1,92 @@ +package com.tencent.qcloud.tuikit.timcommon.component.gatherimage; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.widget.ImageView; + +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; + +@SuppressLint("AppCompatCustomView") +public class ShadeImageView extends ImageView { + private static SparseArray sRoundBitmapArray = new SparseArray(); + private Paint mShadePaint = new Paint(); + private Bitmap mRoundBitmap; + private int radius; + + public ShadeImageView(Context context) { + super(context); + } + + public ShadeImageView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public ShadeImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + radius = (int) ScreenUtil.dp2px(4.0f, getResources().getDisplayMetrics()); + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.core_round_rect_image_style); + radius = array.getDimensionPixelSize(R.styleable.core_round_rect_image_style_round_radius, radius); + array.recycle(); + setLayerType(LAYER_TYPE_HARDWARE, null); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + mShadePaint.setColor(Color.RED); + mShadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + mRoundBitmap = sRoundBitmapArray.get(getMeasuredWidth() + radius); + if (mRoundBitmap == null) { + mRoundBitmap = getRoundBitmap(); + sRoundBitmapArray.put(getMeasuredWidth() + radius, mRoundBitmap); + } + canvas.drawBitmap(mRoundBitmap, 0, 0, mShadePaint); + } + + /** + * Get rounded rectangle + * + * @return Bitmap + */ + private Bitmap getRoundBitmap() { + Bitmap output = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + final int color = Color.parseColor("#cfd3d8"); + final Rect rect = new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight()); + final RectF rectF = new RectF(rect); + Paint paint = new Paint(); + paint.setAntiAlias(true); + canvas.drawARGB(0, 0, 0, 0); + paint.setColor(color); + canvas.drawRoundRect(rectF, radius, radius, paint); + return output; + } + + public int getRadius() { + return radius; + } + + public void setRadius(int radius) { + this.radius = radius; + invalidate(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/SynthesizedImageView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/SynthesizedImageView.java new file mode 100644 index 0000000..83ef654 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/SynthesizedImageView.java @@ -0,0 +1,87 @@ +package com.tencent.qcloud.tuikit.timcommon.component.gatherimage; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.util.AttributeSet; + +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; + +import java.util.List; + +public class SynthesizedImageView extends ShadeImageView { + /** + * + * Group Chat Avatar Synthesizer + */ + TeamHeadSynthesizer teamHeadSynthesizer; + int imageSize = 100; + int synthesizedBg = Color.parseColor("#cfd3d8"); + int defaultImageResId = 0; + int imageGap = 6; + + public SynthesizedImageView(Context context) { + super(context); + init(context); + } + + public SynthesizedImageView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + initAttrs(attrs); + init(context); + } + + public SynthesizedImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initAttrs(attrs); + init(context); + } + + private void initAttrs(AttributeSet attributeSet) { + TypedArray ta = getContext().obtainStyledAttributes(attributeSet, R.styleable.SynthesizedImageView); + if (null != ta) { + synthesizedBg = ta.getColor(R.styleable.SynthesizedImageView_synthesized_image_bg, synthesizedBg); + defaultImageResId = ta.getResourceId(R.styleable.SynthesizedImageView_synthesized_default_image, defaultImageResId); + imageSize = ta.getDimensionPixelSize(R.styleable.SynthesizedImageView_synthesized_image_size, imageSize); + imageGap = ta.getDimensionPixelSize(R.styleable.SynthesizedImageView_synthesized_image_gap, imageGap); + ta.recycle(); + } + } + + private void init(Context context) { + teamHeadSynthesizer = new TeamHeadSynthesizer(context, this); + teamHeadSynthesizer.setMaxWidthHeight(imageSize, imageSize); + teamHeadSynthesizer.setDefaultImage(defaultImageResId); + teamHeadSynthesizer.setBgColor(synthesizedBg); + teamHeadSynthesizer.setGap(imageGap); + } + + public SynthesizedImageView displayImage(List imageUrls) { + teamHeadSynthesizer.getMultiImageData().setImageUrls(imageUrls); + return this; + } + + public SynthesizedImageView defaultImage(int defaultImage) { + teamHeadSynthesizer.setDefaultImage(defaultImage); + return this; + } + + public SynthesizedImageView synthesizedWidthHeight(int maxWidth, int maxHeight) { + teamHeadSynthesizer.setMaxWidthHeight(maxWidth, maxHeight); + return this; + } + + public void setImageId(String id) { + teamHeadSynthesizer.setImageId(id); + } + + public void load(String imageId) { + teamHeadSynthesizer.load(imageId); + } + + public void clear() { + teamHeadSynthesizer.clearImage(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/Synthesizer.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/Synthesizer.java new file mode 100644 index 0000000..25d46fd --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/Synthesizer.java @@ -0,0 +1,12 @@ +package com.tencent.qcloud.tuikit.timcommon.component.gatherimage; + +import android.graphics.Bitmap; +import android.graphics.Canvas; + +public interface Synthesizer { + Bitmap synthesizeImageList(MultiImageData imageData); + + boolean asyncLoadImageList(MultiImageData imageData); + + void drawDrawable(Canvas canvas, MultiImageData imageData); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/TeamHeadSynthesizer.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/TeamHeadSynthesizer.java new file mode 100644 index 0000000..679196a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/TeamHeadSynthesizer.java @@ -0,0 +1,334 @@ +package com.tencent.qcloud.tuikit.timcommon.component.gatherimage; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.text.TextUtils; +import android.widget.ImageView; +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuikit.timcommon.TIMCommonConfig; +import com.tencent.qcloud.tuikit.timcommon.component.impl.GlideEngine; +import com.tencent.qcloud.tuikit.timcommon.util.ImageUtil; +import com.tencent.qcloud.tuikit.timcommon.util.ThreadUtils; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +public class TeamHeadSynthesizer implements Synthesizer { + MultiImageData multiImageData; + Context mContext; + + ImageView imageView; + + // It is safe to set and get only in the main thread + private String currentImageId = ""; + Callback callback = new Callback() { + @Override + public void onCall(Bitmap bitmap, String targetID) { + if (!TextUtils.equals(getImageId(), targetID)) { + return; + } + GlideEngine.loadUserIcon(imageView, bitmap); + } + }; + + public TeamHeadSynthesizer(Context mContext, ImageView imageView) { + this.mContext = mContext; + this.imageView = imageView; + init(); + } + + private void init() { + multiImageData = new MultiImageData(); + } + + public void setMaxWidthHeight(int maxWidth, int maxHeight) { + multiImageData.maxWidth = maxWidth; + multiImageData.maxHeight = maxHeight; + } + + public MultiImageData getMultiImageData() { + return multiImageData; + } + + public int getDefaultImage() { + return multiImageData.getDefaultImageResId(); + } + + public void setDefaultImage(int defaultImageResId) { + multiImageData.setDefaultImageResId(defaultImageResId); + } + + public void setBgColor(int bgColor) { + multiImageData.bgColor = bgColor; + } + + public void setGap(int gap) { + multiImageData.gap = gap; + } + + /** + * Set Grid params + * + * @param imagesSize Number of pictures + * @return gridParam[0] Rows gridParam[1] columns + */ + protected int[] calculateGridParam(int imagesSize) { + int[] gridParam = new int[2]; + if (imagesSize < 3) { + gridParam[0] = 1; + gridParam[1] = imagesSize; + } else if (imagesSize <= 4) { + gridParam[0] = 2; + gridParam[1] = 2; + } else { + gridParam[0] = imagesSize / 3 + (imagesSize % 3 == 0 ? 0 : 1); + gridParam[1] = 3; + } + return gridParam; + } + + @Override + public Bitmap synthesizeImageList(MultiImageData imageData) { + Bitmap mergeBitmap = Bitmap.createBitmap(imageData.maxWidth, imageData.maxHeight, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(mergeBitmap); + drawDrawable(canvas, imageData); + canvas.save(); + canvas.restore(); + return mergeBitmap; + } + + @Override + public boolean asyncLoadImageList(MultiImageData imageData) { + boolean loadSuccess = true; + List imageUrls = imageData.getImageUrls(); + for (int i = 0; i < imageUrls.size(); i++) { + Bitmap defaultIcon = BitmapFactory.decodeResource(mContext.getResources(), TIMCommonConfig.getDefaultAvatarImage()); + try { + Bitmap bitmap = asyncLoadImage(imageUrls.get(i), imageData.targetImageSize); + imageData.putBitmap(bitmap, i); + } catch (InterruptedException e) { + e.printStackTrace(); + imageData.putBitmap(defaultIcon, i); + } catch (ExecutionException e) { + e.printStackTrace(); + imageData.putBitmap(defaultIcon, i); + } + } + return loadSuccess; + } + + @Override + public void drawDrawable(Canvas canvas, MultiImageData imageData) { + canvas.drawColor(imageData.bgColor); + int size = imageData.size(); + int tCenter = (imageData.maxHeight + imageData.gap) / 2; + int bCenter = (imageData.maxHeight - imageData.gap) / 2; + int lCenter = (imageData.maxWidth + imageData.gap) / 2; + int rCenter = (imageData.maxWidth - imageData.gap) / 2; + int center = (imageData.maxHeight - imageData.targetImageSize) / 2; + for (int i = 0; i < size; i++) { + int rowNum = i / imageData.columnCount; + int columnNum = i % imageData.columnCount; + + int left = ((int) (imageData.targetImageSize * (imageData.columnCount == 1 ? columnNum + 0.5 : columnNum) + imageData.gap * (columnNum + 1))); + int top = ((int) (imageData.targetImageSize * (imageData.columnCount == 1 ? rowNum + 0.5 : rowNum) + imageData.gap * (rowNum + 1))); + int right = left + imageData.targetImageSize; + int bottom = top + imageData.targetImageSize; + + Bitmap bitmap = imageData.getBitmap(i); + if (size == 1) { + drawBitmapAtPosition(canvas, left, top, right, bottom, bitmap); + } else if (size == 2) { + drawBitmapAtPosition(canvas, left, center, right, center + imageData.targetImageSize, bitmap); + } else if (size == 3) { + if (i == 0) { + drawBitmapAtPosition(canvas, center, top, center + imageData.targetImageSize, bottom, bitmap); + } else { + drawBitmapAtPosition(canvas, imageData.gap * i + imageData.targetImageSize * (i - 1), tCenter, + imageData.gap * i + imageData.targetImageSize * i, tCenter + imageData.targetImageSize, bitmap); + } + } else if (size == 4) { + drawBitmapAtPosition(canvas, left, top, right, bottom, bitmap); + } else if (size == 5) { + if (i == 0) { + drawBitmapAtPosition(canvas, rCenter - imageData.targetImageSize, rCenter - imageData.targetImageSize, rCenter, rCenter, bitmap); + } else if (i == 1) { + drawBitmapAtPosition(canvas, lCenter, rCenter - imageData.targetImageSize, lCenter + imageData.targetImageSize, rCenter, bitmap); + } else { + drawBitmapAtPosition(canvas, imageData.gap * (i - 1) + imageData.targetImageSize * (i - 2), tCenter, + imageData.gap * (i - 1) + imageData.targetImageSize * (i - 1), tCenter + imageData.targetImageSize, bitmap); + } + } else if (size == 6) { + if (i < 3) { + drawBitmapAtPosition(canvas, imageData.gap * (i + 1) + imageData.targetImageSize * i, bCenter - imageData.targetImageSize, + imageData.gap * (i + 1) + imageData.targetImageSize * (i + 1), bCenter, bitmap); + } else { + drawBitmapAtPosition(canvas, imageData.gap * (i - 2) + imageData.targetImageSize * (i - 3), tCenter, + imageData.gap * (i - 2) + imageData.targetImageSize * (i - 2), tCenter + imageData.targetImageSize, bitmap); + } + } else if (size == 7) { + if (i == 0) { + drawBitmapAtPosition(canvas, center, imageData.gap, center + imageData.targetImageSize, imageData.gap + imageData.targetImageSize, bitmap); + } else if (i > 0 && i < 4) { + drawBitmapAtPosition(canvas, imageData.gap * i + imageData.targetImageSize * (i - 1), center, + imageData.gap * i + imageData.targetImageSize * i, center + imageData.targetImageSize, bitmap); + } else { + drawBitmapAtPosition(canvas, imageData.gap * (i - 3) + imageData.targetImageSize * (i - 4), tCenter + imageData.targetImageSize / 2, + imageData.gap * (i - 3) + imageData.targetImageSize * (i - 3), tCenter + imageData.targetImageSize / 2 + imageData.targetImageSize, + bitmap); + } + } else if (size == 8) { + if (i == 0) { + drawBitmapAtPosition( + canvas, rCenter - imageData.targetImageSize, imageData.gap, rCenter, imageData.gap + imageData.targetImageSize, bitmap); + } else if (i == 1) { + drawBitmapAtPosition( + canvas, lCenter, imageData.gap, lCenter + imageData.targetImageSize, imageData.gap + imageData.targetImageSize, bitmap); + } else if (i > 1 && i < 5) { + drawBitmapAtPosition(canvas, imageData.gap * (i - 1) + imageData.targetImageSize * (i - 2), center, + imageData.gap * (i - 1) + imageData.targetImageSize * (i - 1), center + imageData.targetImageSize, bitmap); + } else { + drawBitmapAtPosition(canvas, imageData.gap * (i - 4) + imageData.targetImageSize * (i - 5), tCenter + imageData.targetImageSize / 2, + imageData.gap * (i - 4) + imageData.targetImageSize * (i - 4), tCenter + imageData.targetImageSize / 2 + imageData.targetImageSize, + bitmap); + } + } else if (size == 9) { + drawBitmapAtPosition(canvas, left, top, right, bottom, bitmap); + } + } + } + + /** + * DrawBitmap + * + * @param canvas + * @param left + * @param top + * @param right + * @param bottom + * @param bitmap + */ + public void drawBitmapAtPosition(Canvas canvas, int left, int top, int right, int bottom, Bitmap bitmap) { + if (null == bitmap) { + if (multiImageData.getDefaultImageResId() > 0) { + bitmap = BitmapFactory.decodeResource(mContext.getResources(), multiImageData.getDefaultImageResId()); + } + } + if (null != bitmap) { + Rect rect = new Rect(left, top, right, bottom); + canvas.drawBitmap(bitmap, null, rect, null); + } + } + + private Bitmap asyncLoadImage(Object imageUrl, int targetImageSize) throws ExecutionException, InterruptedException { + return GlideEngine.loadBitmap(imageUrl, targetImageSize); + } + + public void setImageId(String id) { + currentImageId = id; + } + + public String getImageId() { + return currentImageId; + } + + public void load(String imageId) { + if (multiImageData.size() == 0) { + + // The image id when the request is initiated is inconsistent with the current image id, + // indicating that multiplexing has occurred, and the image should not be set at this time. + if (imageId != null && !TextUtils.equals(imageId, currentImageId)) { + return; + } + GlideEngine.loadUserIcon(imageView, getDefaultImage()); + return; + } + + if (multiImageData.size() == 1) { + + // The image id when the request is initiated is inconsistent with the current image id, + // indicating that multiplexing has occurred, and the image should not be set at this time. + if (imageId != null && !TextUtils.equals(imageId, currentImageId)) { + return; + } + GlideEngine.loadUserIcon(imageView, multiImageData.getImageUrls().get(0)); + return; + } + + + // Clear the content before loading images asynchronously to avoid flickering + clearImage(); + + + + // Initialize the image information. Since it is asynchronous loading and synthesizing the avatar, + // a local object needs to be passed to the synthesis thread, which is only used in the asynchronous + // loading thread, so that when the image is reused, the external thread will not overwrite the local + // object by setting the url again. + MultiImageData copyMultiImageData; + try { + copyMultiImageData = multiImageData.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + List urlList = new ArrayList(); + if (multiImageData.imageUrls != null) { + urlList.addAll(multiImageData.imageUrls); + } + copyMultiImageData = new MultiImageData(urlList, multiImageData.defaultImageResId); + } + int[] gridParam = calculateGridParam(multiImageData.size()); + copyMultiImageData.rowCount = gridParam[0]; + copyMultiImageData.columnCount = gridParam[1]; + copyMultiImageData.targetImageSize = (copyMultiImageData.maxWidth - (copyMultiImageData.columnCount + 1) * copyMultiImageData.gap) + / (copyMultiImageData.columnCount == 1 ? 2 : copyMultiImageData.columnCount); + final String finalImageId = imageId; + final MultiImageData finalCopyMultiImageData = copyMultiImageData; + ThreadUtils.execute(new Runnable() { + @Override + public void run() { + final File file = new File(TUIConfig.getImageBaseDir() + finalImageId); + boolean cacheBitmapExists = false; + Bitmap existsBitmap = null; + if (file.exists() && file.isFile()) { + BitmapFactory.Options options = new BitmapFactory.Options(); + existsBitmap = BitmapFactory.decodeFile(file.getPath(), options); + if (options.outWidth > 0 && options.outHeight > 0) { + cacheBitmapExists = true; + } + } + if (!cacheBitmapExists) { + asyncLoadImageList(finalCopyMultiImageData); + final Bitmap bitmap = synthesizeImageList(finalCopyMultiImageData); + ImageUtil.storeBitmap(file, bitmap); + ImageUtil.setGroupConversationAvatar(finalImageId, file.getAbsolutePath()); + ThreadUtils.postOnUiThread(new Runnable() { + @Override + public void run() { + callback.onCall(bitmap, finalImageId); + } + }); + } else { + final Bitmap finalExistsBitmap = existsBitmap; + ThreadUtils.postOnUiThread(new Runnable() { + @Override + public void run() { + callback.onCall(finalExistsBitmap, finalImageId); + } + }); + } + } + }); + } + + public void clearImage() { + GlideEngine.clear(imageView); + } + + interface Callback { + void onCall(Bitmap bitmap, String targetID); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/UserIconView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/UserIconView.java new file mode 100644 index 0000000..35cf3b8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/gatherimage/UserIconView.java @@ -0,0 +1,65 @@ +package com.tencent.qcloud.tuikit.timcommon.component.gatherimage; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.RelativeLayout; + +import com.tencent.qcloud.tuikit.timcommon.R; + +import java.util.List; + +public class UserIconView extends RelativeLayout { + private SynthesizedImageView mIconView; + private int mDefaultImageResId; + private int mIconRadius; + + public UserIconView(Context context) { + super(context); + init(null); + } + + public UserIconView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public UserIconView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + private void init(AttributeSet attributeSet) { + inflate(getContext(), R.layout.common_profile_icon_view, this); + if (attributeSet != null) { + TypedArray ta = getContext().obtainStyledAttributes(attributeSet, R.styleable.UserIconView); + if (null != ta) { + mDefaultImageResId = ta.getResourceId(R.styleable.UserIconView_default_image, mDefaultImageResId); + mIconRadius = ta.getDimensionPixelSize(R.styleable.UserIconView_image_radius, mIconRadius); + ta.recycle(); + } + } + + mIconView = findViewById(R.id.profile_icon); + if (mDefaultImageResId > 0) { + mIconView.defaultImage(mDefaultImageResId); + } + if (mIconRadius > 0) { + mIconView.setRadius(mIconRadius); + } + } + + public void setDefaultImageResId(int resId) { + mDefaultImageResId = resId; + mIconView.defaultImage(resId); + } + + public void setRadius(int radius) { + mIconRadius = radius; + mIconView.setRadius(mIconRadius); + } + + public void setIconUrls(List iconUrls) { + mIconView.displayImage(iconUrls).load(null); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/highlight/HighlightPresenter.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/highlight/HighlightPresenter.java new file mode 100644 index 0000000..3bc6e7a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/highlight/HighlightPresenter.java @@ -0,0 +1,136 @@ +package com.tencent.qcloud.tuikit.timcommon.component.highlight; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.TIMCommonService; +import com.tencent.qcloud.tuikit.timcommon.interfaces.HighlightListener; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; + +public class HighlightPresenter { + public static final int DEFAULT_DURATION = 250; + public static final int DEFAULT_REPEAT_COUNT = 3; + + private static final class HighlightPresenterHolder { + private static final HighlightPresenter INSTANCE = new HighlightPresenter(); + } + + private static HighlightPresenter getInstance() { + return HighlightPresenterHolder.INSTANCE; + } + + private final Map> highlightListenerMap = new HashMap<>(); + + private final Map highlightMap = new HashMap<>(); + + private int highLightDarkColor = -1; + private int highLightLightColor = -1; + + private HighlightPresenter() {} + + public static void registerHighlightListener(String highlightID, HighlightListener listener) { + if (listener == null) { + return; + } + getInstance().highlightListenerMap.put(highlightID, new WeakReference<>(listener)); + } + + public static void unregisterHighlightListener(String highlightID) { + getInstance().highlightListenerMap.remove(highlightID); + } + + public static void startHighlight(String highlightID) { + getInstance().internalStartHighlight(highlightID); + } + + public static void stopHighlight(String highlightID) { + getInstance().internalStopHighlight(highlightID); + } + + public static void setHighlightDarkColor(int color) { + getInstance().highLightDarkColor = color; + } + + public static void setHighlightLightColor(int color) { + getInstance().highLightLightColor = color; + } + + private void internalStartHighlight(String highlightID) { + ValueAnimator highlightAnimator = new ValueAnimator(); + if (highLightDarkColor == highLightLightColor && highLightLightColor == -1) { + highLightDarkColor = TIMCommonService.getAppContext().getResources().getColor(R.color.chat_message_bubble_high_light_dark_color); + highLightLightColor = TIMCommonService.getAppContext().getResources().getColor(R.color.chat_message_bubble_high_light_light_color); + } + + highlightAnimator.setIntValues(highLightDarkColor, highLightLightColor); + highlightAnimator.setEvaluator(new ArgbEvaluator()); + highlightAnimator.setRepeatCount(DEFAULT_REPEAT_COUNT); + highlightAnimator.setDuration(DEFAULT_DURATION); + highlightAnimator.setRepeatMode(ValueAnimator.REVERSE); + highlightAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + onHighlightStart(highlightID); + } + + @Override + public void onAnimationCancel(Animator animation) { + onHighlightEnd(highlightID); + } + + @Override + public void onAnimationEnd(Animator animation) { + onHighlightEnd(highlightID); + } + }); + highlightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + onHighlightUpdate(highlightID, (Integer) animation.getAnimatedValue()); + } + }); + highlightAnimator.start(); + highlightMap.put(highlightID, highlightAnimator); + } + + private void internalStopHighlight(String highlightID) { + ValueAnimator highlightAnimator = highlightMap.get(highlightID); + if (highlightAnimator != null) { + highlightAnimator.cancel(); + } + } + + private void onHighlightStart(String highlightID) { + HighlightListener lightListener = getInstance().getHighlightListener(highlightID); + if (lightListener != null) { + lightListener.onHighlightStart(); + } + } + + private void onHighlightEnd(String highlightID) { + highlightMap.remove(highlightID); + HighlightListener lightListener = getInstance().getHighlightListener(highlightID); + if (lightListener != null) { + lightListener.onHighlightEnd(); + } + } + + private void onHighlightUpdate(String highlightID, int color) { + HighlightListener lightListener = getHighlightListener(highlightID); + if (lightListener != null) { + lightListener.onHighlightUpdate(color); + } + } + + private HighlightListener getHighlightListener(String highlightID) { + WeakReference listener = highlightListenerMap.get(highlightID); + if (listener != null) { + return listener.get(); + } + return null; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/impl/GlideEngine.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/impl/GlideEngine.java new file mode 100644 index 0000000..94378b4 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/impl/GlideEngine.java @@ -0,0 +1,120 @@ +package com.tencent.qcloud.tuikit.timcommon.component.impl; + +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; +import android.widget.ImageView; +import com.bumptech.glide.Glide; +import com.bumptech.glide.Priority; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.bumptech.glide.request.RequestListener; +import com.bumptech.glide.request.RequestOptions; +import com.tencent.qcloud.tuicore.TUILogin; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; +import java.io.File; +import java.util.concurrent.ExecutionException; + +public class GlideEngine { + public static void loadCornerImageWithoutPlaceHolder(ImageView imageView, Object uri, RequestListener listener, float radius) { + RoundedCorners transform = null; + if ((int) radius > 0) { + transform = new RoundedCorners((int) radius); + } + + RequestOptions options = new RequestOptions().centerCrop(); + if (transform != null) { + options = options.transform(transform); + } + Glide.with(TUILogin.getAppContext()).load(uri).apply(options).listener(listener).into(imageView); + } + + public static void clear(ImageView imageView) { + Glide.with(TUILogin.getAppContext()).clear(imageView); + } + + public static void loadImage(ImageView imageView, String filePath, RequestListener listener) { + Glide.with(TUILogin.getAppContext()) + .load(filePath) + .listener(listener) + .apply(new RequestOptions().error(TUIThemeManager.getAttrResId(TUILogin.getAppContext(), R.attr.core_default_user_icon))) + .into(imageView); + } + + public static void loadImage(ImageView imageView, String filePath) { + Glide.with(TUILogin.getAppContext()) + .load(filePath) + .apply(new RequestOptions().error(TUIThemeManager.getAttrResId(TUILogin.getAppContext(), R.attr.core_default_user_icon))) + .into(imageView); + } + + public static void loadImage(ImageView imageView, Uri uri) { + if (uri == null) { + return; + } + Glide.with(TUILogin.getAppContext()) + .load(uri) + .apply(new RequestOptions().error(TUIThemeManager.getAttrResId(TUILogin.getAppContext(), R.attr.core_default_user_icon))) + .into(imageView); + } + + public static void loadImage(String filePath, String url) { + try { + File file = Glide.with(TUILogin.getAppContext()).asFile().load(url).submit().get(); + File destFile = new File(filePath); + file.renameTo(destFile); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + + public void loadImage(Context context, int resizeX, int resizeY, ImageView imageView, Uri uri) { + Glide.with(context).load(uri).apply(new RequestOptions().override(resizeX, resizeY).priority(Priority.HIGH).fitCenter()).into(imageView); + } + + public static void loadImage(ImageView imageView, Object uri) { + if (uri == null) { + return; + } + Glide.with(TUILogin.getAppContext()) + .load(uri) + .apply(new RequestOptions().error(TUIThemeManager.getAttrResId(TUILogin.getAppContext(), R.attr.core_default_user_icon))) + .into(imageView); + } + + public static void loadUserIcon(ImageView imageView, Object uri) { + loadUserIcon(imageView, uri, 0); + } + + public static void loadUserIcon(ImageView imageView, Object uri, int radius) { + Glide.with(TUILogin.getAppContext()) + .load(uri) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .placeholder(TUIThemeManager.getAttrResId(TUILogin.getAppContext(), R.attr.core_default_user_icon)) + .apply(new RequestOptions().centerCrop().error(TUIThemeManager.getAttrResId(TUILogin.getAppContext(), R.attr.core_default_user_icon))) + .into(imageView); + } + + public static void loadUserIcon(ImageView imageView, Object uri, int defaultResId, int radius) { + Glide.with(TUILogin.getAppContext()).load(uri).placeholder(defaultResId).apply(new RequestOptions().centerCrop().error(defaultResId)).into(imageView); + } + + public static Bitmap loadBitmap(Object imageUrl, int targetImageSize) throws InterruptedException, ExecutionException { + if (imageUrl == null) { + return null; + } + return Glide.with(TUILogin.getAppContext()) + .asBitmap() + .load(imageUrl) + .apply(new RequestOptions().error(TUIThemeManager.getAttrResId(TUILogin.getAppContext(), R.attr.core_default_user_icon))) + .into(targetImageSize, targetImageSize) + .get(); + } + + public static void loadImageSetDefault(ImageView imageView, Object uri, int defaultResId) { + Glide.with(TUILogin.getAppContext()).load(uri).placeholder(defaultResId).apply(new RequestOptions().centerCrop().error(defaultResId)).into(imageView); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/ILayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/ILayout.java new file mode 100644 index 0000000..88879bb --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/ILayout.java @@ -0,0 +1,19 @@ +package com.tencent.qcloud.tuikit.timcommon.component.interfaces; + +import com.tencent.qcloud.tuikit.timcommon.component.TitleBarLayout; + +public interface ILayout { + /** + * get title bar + * + * @return + */ + TitleBarLayout getTitleBar(); + + /** + * Set the parent container of this Layout + * + * @param parent + */ + void setParentLayout(Object parent); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/ITitleBarLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/ITitleBarLayout.java new file mode 100644 index 0000000..0331c7c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/ITitleBarLayout.java @@ -0,0 +1,131 @@ +package com.tencent.qcloud.tuikit.timcommon.component.interfaces; + +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * Conversation list window {@link ConversationLayout}、chat window {@link ChatLayout} have title bar, + * The title bar is designed as a three-part title on the left, middle and right. The left can be + * picture + text, the middle is text, and the right can also be picture + text. These areas return the + * standard Android View,These Views can be interactively processed according to business needs。 + */ +public interface ITitleBarLayout { + /** + * + * Set the click event of the left header + * + * @param listener + */ + void setOnLeftClickListener(View.OnClickListener listener); + + /** + * + * Set the click event of the right title + * + * @param listener + */ + void setOnRightClickListener(View.OnClickListener listener); + + /** + * + * set Title + * + */ + void setTitle(String title, Position position); + + /** + * + * Return to the left header area + * + * @return + */ + LinearLayout getLeftGroup(); + + /** + * + * Return to the right header area + * + * @return + */ + LinearLayout getRightGroup(); + + /** + * + * Returns the image for the left header + * + * @return + */ + ImageView getLeftIcon(); + + /** + * + * Set the image for the left header + * + * @param resId + */ + void setLeftIcon(int resId); + + /** + * + * Returns the image with the right header + * + * @return + */ + ImageView getRightIcon(); + + /** + * + * Set the image for the title on the right + * + * @param resId + */ + void setRightIcon(int resId); + + /** + * + * Returns the text of the left header + * + * @return + */ + TextView getLeftTitle(); + + /** + * + * Returns the text of the middle title + * + * @return + */ + TextView getMiddleTitle(); + + /** + * + * Returns the text of the title on the right + * + * @return + */ + TextView getRightTitle(); + + /** + * + * enumeration value of the header area + */ + enum Position { + /** + * + * left title + */ + LEFT, + /** + * + * middle title + */ + MIDDLE, + /** + * + * right title + */ + RIGHT + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/IUIKitCallback.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/IUIKitCallback.java new file mode 100644 index 0000000..0b181a7 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/interfaces/IUIKitCallback.java @@ -0,0 +1,23 @@ +package com.tencent.qcloud.tuikit.timcommon.component.interfaces; + +public abstract class IUIKitCallback { + public void onSuccess(T data){} + + public void onError(String module, int errCode, String errMsg) {} + + public void onError(int errCode, String errMsg, T data) {} + + public void onProgress(Object data) {} + + public static void callbackOnSuccess(IUIKitCallback callback, O data) { + if (callback != null) { + callback.onSuccess(data); + } + } + + public static void callbackOnError(IUIKitCallback callback, int errCode, String errMsg, O data) { + if (callback != null) { + callback.onError(errCode, errMsg, data); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/Compat.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/Compat.java new file mode 100644 index 0000000..d0909d0 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/Compat.java @@ -0,0 +1,40 @@ +/* + 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.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.annotation.TargetApi; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.view.View; + +class Compat { + + private static final int SIXTY_FPS_INTERVAL = 1000 / 60; + + public static void postOnAnimation(View view, Runnable runnable) { + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + postOnAnimationJellyBean(view, runnable); + } else { + view.postDelayed(runnable, SIXTY_FPS_INTERVAL); + } + } + + @TargetApi(16) + private static void postOnAnimationJellyBean(View view, Runnable runnable) { + view.postOnAnimation(runnable); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/CustomGestureDetector.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/CustomGestureDetector.java new file mode 100644 index 0000000..8e5a846 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/CustomGestureDetector.java @@ -0,0 +1,221 @@ +/* + 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.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.content.Context; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; + +/** + * Does a whole lot of gesture detecting. + */ +class CustomGestureDetector { + + private static final int INVALID_POINTER_ID = -1; + + private int mActivePointerId = INVALID_POINTER_ID; + private int mActivePointerIndex = 0; + private final ScaleGestureDetector mDetector; + + private VelocityTracker mVelocityTracker; + private boolean mIsDragging; + private float mLastTouchX; + private float mLastTouchY; + private final float mTouchSlop; + private final float mMinimumVelocity; + private OnGestureListener mListener; + + CustomGestureDetector(Context context, OnGestureListener listener) { + final ViewConfiguration configuration = ViewConfiguration + .get(context); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + mTouchSlop = configuration.getScaledTouchSlop(); + + mListener = listener; + ScaleGestureDetector.OnScaleGestureListener mScaleListener = new ScaleGestureDetector.OnScaleGestureListener() { + private float lastFocusX = 0; + private float lastFocusY = 0; + + @Override + public boolean onScale(ScaleGestureDetector detector) { + float scaleFactor = detector.getScaleFactor(); + + if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) { + return false; + } + + if (scaleFactor >= 0) { + mListener.onScale(scaleFactor, + detector.getFocusX(), + detector.getFocusY(), + detector.getFocusX() - lastFocusX, + detector.getFocusY() - lastFocusY + ); + lastFocusX = detector.getFocusX(); + lastFocusY = detector.getFocusY(); + } + return true; + } + + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + lastFocusX = detector.getFocusX(); + lastFocusY = detector.getFocusY(); + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + // NO-OP + } + }; + mDetector = new ScaleGestureDetector(context, mScaleListener); + } + + private float getActiveX(MotionEvent ev) { + try { + return ev.getX(mActivePointerIndex); + } catch (Exception e) { + return ev.getX(); + } + } + + private float getActiveY(MotionEvent ev) { + try { + return ev.getY(mActivePointerIndex); + } catch (Exception e) { + return ev.getY(); + } + } + + public boolean isScaling() { + return mDetector.isInProgress(); + } + + public boolean isDragging() { + return mIsDragging; + } + + public boolean onTouchEvent(MotionEvent ev) { + try { + mDetector.onTouchEvent(ev); + return processTouchEvent(ev); + } catch (IllegalArgumentException e) { + // Fix for support lib bug, happening when onDestroy is called + return true; + } + } + + private boolean processTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + mActivePointerId = ev.getPointerId(0); + + mVelocityTracker = VelocityTracker.obtain(); + if (null != mVelocityTracker) { + 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; + final float 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: + mActivePointerId = INVALID_POINTER_ID; + // Recycle Velocity Tracker + if (null != mVelocityTracker) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + break; + case MotionEvent.ACTION_UP: + mActivePointerId = INVALID_POINTER_ID; + 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(); + final float 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; + case MotionEvent.ACTION_POINTER_UP: + final int pointerIndex = Util.getPointerIndex(ev.getAction()); + 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; + default: + break; + } + + mActivePointerIndex = ev + .findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId + : 0); + return true; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnGestureListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnGestureListener.java new file mode 100644 index 0000000..f3190ba --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnGestureListener.java @@ -0,0 +1,29 @@ +/* + 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.tencent.qcloud.tuikit.timcommon.component.photoview; + +interface OnGestureListener { + + void onDrag(float dx, float dy); + + void onFling(float startX, float startY, float velocityX, + float velocityY); + + void onScale(float scaleFactor, float focusX, float focusY); + + void onScale(float scaleFactor, float focusX, float focusY, float dx, float dy); +} \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnMatrixChangedListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnMatrixChangedListener.java new file mode 100644 index 0000000..97528d7 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnMatrixChangedListener.java @@ -0,0 +1,18 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.graphics.RectF; + +/** + * Interface definition for a callback to be invoked when the internal Matrix has changed for + * this View. + */ +public 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); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnOutsidePhotoTapListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnOutsidePhotoTapListener.java new file mode 100644 index 0000000..3c67b76 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnOutsidePhotoTapListener.java @@ -0,0 +1,14 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.widget.ImageView; + +/** + * Callback when the user tapped outside of the photo + */ +public interface OnOutsidePhotoTapListener { + + /** + * The outside of the photo has been tapped + */ + void onOutsidePhotoTap(ImageView imageView); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnPhotoTapListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnPhotoTapListener.java new file mode 100644 index 0000000..a03a564 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnPhotoTapListener.java @@ -0,0 +1,22 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.widget.ImageView; + +/** + * A callback to be invoked when the Photo is tapped with a single + * tap. + */ +public 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 ImageView 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(ImageView view, float x, float y); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnScaleChangedListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnScaleChangedListener.java new file mode 100644 index 0000000..3c82f20 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnScaleChangedListener.java @@ -0,0 +1,17 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + + +/** + * Interface definition for callback to be invoked when attached ImageView scale changes + */ +public interface OnScaleChangedListener { + + /** + * Callback for when the scale changes + * + * @param scaleFactor the scale factor (less than 1 for zoom out, greater than 1 for zoom in) + * @param focusX focal point X position + * @param focusY focal point Y position + */ + void onScaleChange(float scaleFactor, float focusX, float focusY); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnSingleFlingListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnSingleFlingListener.java new file mode 100644 index 0000000..46c9310 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnSingleFlingListener.java @@ -0,0 +1,21 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.view.MotionEvent; + +/** + * A callback to be invoked when the ImageView is flung with a single + * touch + */ +public interface OnSingleFlingListener { + + /** + * A callback to receive where the user flings on a ImageView. You will receive a callback if + * the user flings anywhere on the view. + * + * @param e1 MotionEvent the user first touch. + * @param e2 MotionEvent the user last touch. + * @param velocityX distance of user's horizontal fling. + * @param velocityY distance of user's vertical fling. + */ + boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnViewDragListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnViewDragListener.java new file mode 100644 index 0000000..5e5d2c3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnViewDragListener.java @@ -0,0 +1,16 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + +/** + * Interface definition for a callback to be invoked when the photo is experiencing a drag event + */ +public interface OnViewDragListener { + + /** + * Callback for when the photo is experiencing a drag event. This cannot be invoked when the + * user is scaling. + * + * @param dx The change of the coordinates in the x-direction + * @param dy The change of the coordinates in the y-direction + */ + void onDrag(float dx, float dy); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnViewTapListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnViewTapListener.java new file mode 100644 index 0000000..00e5eb6 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/OnViewTapListener.java @@ -0,0 +1,16 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.view.View; + +public 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); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/PhotoView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/PhotoView.java new file mode 100644 index 0000000..493d84f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/PhotoView.java @@ -0,0 +1,257 @@ +/* + 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.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.util.AttributeSet; +import android.view.GestureDetector; + +import androidx.appcompat.widget.AppCompatImageView; + +/** + * A zoomable ImageView. See {@link PhotoViewAttacher} for most of the details on how the zooming + * is accomplished + */ +@SuppressWarnings("unused") +public class PhotoView extends AppCompatImageView { + + private PhotoViewAttacher attacher; + private ScaleType pendingScaleType; + + 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); + init(); + } + + private void init() { + attacher = new PhotoViewAttacher(this); + //We always pose as a Matrix scale type, though we can change to another scale type + //via the attacher + super.setScaleType(ScaleType.MATRIX); + //apply the previously applied scale type + if (pendingScaleType != null) { + setScaleType(pendingScaleType); + pendingScaleType = null; + } + } + + /** + * Get the current {@link PhotoViewAttacher} for this view. Be wary of holding on to references + * to this attacher, as it has a reference to this view, which, if a reference is held in the + * wrong place, can cause memory leaks. + * + * @return the attacher. + */ + public PhotoViewAttacher getAttacher() { + return attacher; + } + + @Override + public ScaleType getScaleType() { + return attacher.getScaleType(); + } + + @Override + public Matrix getImageMatrix() { + return attacher.getImageMatrix(); + } + + @Override + public void setOnLongClickListener(OnLongClickListener l) { + attacher.setOnLongClickListener(l); + } + + @Override + public void setOnClickListener(OnClickListener l) { + attacher.setOnClickListener(l); + } + + @Override + public void setScaleType(ScaleType scaleType) { + if (attacher == null) { + pendingScaleType = scaleType; + } else { + attacher.setScaleType(scaleType); + } + } + + @Override + public void setImageDrawable(Drawable drawable) { + super.setImageDrawable(drawable); + // setImageBitmap calls through to this method + if (attacher != null) { + attacher.update(); + } + } + + @Override + public void setImageResource(int resId) { + super.setImageResource(resId); + if (attacher != null) { + attacher.update(); + } + } + + @Override + public void setImageURI(Uri uri) { + super.setImageURI(uri); + if (attacher != null) { + attacher.update(); + } + } + + @Override + protected boolean setFrame(int l, int t, int r, int b) { + boolean changed = super.setFrame(l, t, r, b); + if (changed) { + attacher.update(); + } + return changed; + } + + public void setRotationTo(float rotationDegree) { + attacher.setRotationTo(rotationDegree); + } + + public void setRotationBy(float rotationDegree) { + attacher.setRotationBy(rotationDegree); + } + + public boolean isZoomable() { + return attacher.isZoomable(); + } + + public void setZoomable(boolean zoomable) { + attacher.setZoomable(zoomable); + } + + public RectF getDisplayRect() { + return attacher.getDisplayRect(); + } + + public void getDisplayMatrix(Matrix matrix) { + attacher.getDisplayMatrix(matrix); + } + + @SuppressWarnings("UnusedReturnValue") public boolean setDisplayMatrix(Matrix finalRectangle) { + return attacher.setDisplayMatrix(finalRectangle); + } + + public void getSuppMatrix(Matrix matrix) { + attacher.getSuppMatrix(matrix); + } + + public boolean setSuppMatrix(Matrix matrix) { + return attacher.setDisplayMatrix(matrix); + } + + public float getMinimumScale() { + return attacher.getMinimumScale(); + } + + public float getMediumScale() { + return attacher.getMediumScale(); + } + + public float getMaximumScale() { + return attacher.getMaximumScale(); + } + + public float getScale() { + return attacher.getScale(); + } + + public void setAllowParentInterceptOnEdge(boolean allow) { + attacher.setAllowParentInterceptOnEdge(allow); + } + + public void setMinimumScale(float minimumScale) { + attacher.setMinimumScale(minimumScale); + } + + public void setMediumScale(float mediumScale) { + attacher.setMediumScale(mediumScale); + } + + public void setMaximumScale(float maximumScale) { + attacher.setMaximumScale(maximumScale); + } + + public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) { + attacher.setScaleLevels(minimumScale, mediumScale, maximumScale); + } + + public void setOnMatrixChangeListener(OnMatrixChangedListener listener) { + attacher.setOnMatrixChangeListener(listener); + } + + public void setOnPhotoTapListener(OnPhotoTapListener listener) { + attacher.setOnPhotoTapListener(listener); + } + + public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener listener) { + attacher.setOnOutsidePhotoTapListener(listener); + } + + public void setOnViewTapListener(OnViewTapListener listener) { + attacher.setOnViewTapListener(listener); + } + + public void setOnViewDragListener(OnViewDragListener listener) { + attacher.setOnViewDragListener(listener); + } + + public void setScale(float scale) { + attacher.setScale(scale); + } + + public void setScale(float scale, boolean animate) { + attacher.setScale(scale, animate); + } + + public void setScale(float scale, float focalX, float focalY, boolean animate) { + attacher.setScale(scale, focalX, focalY, animate); + } + + public void setZoomTransitionDuration(int milliseconds) { + attacher.setZoomTransitionDuration(milliseconds); + } + + public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener onDoubleTapListener) { + attacher.setOnDoubleTapListener(onDoubleTapListener); + } + + public void setOnScaleChangeListener(OnScaleChangedListener onScaleChangedListener) { + attacher.setOnScaleChangeListener(onScaleChangedListener); + } + + public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) { + attacher.setOnSingleFlingListener(onSingleFlingListener); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/PhotoViewAttacher.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/PhotoViewAttacher.java new file mode 100644 index 0000000..1dc0e99 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/PhotoViewAttacher.java @@ -0,0 +1,807 @@ +/* + 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.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Matrix.ScaleToFit; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnLongClickListener; +import android.view.ViewParent; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.ImageView; +import android.widget.ImageView.ScaleType; +import android.widget.OverScroller; + +/** + * The component of {@link PhotoView} which does the work allowing for zooming, scaling, panning, etc. + * It is made public in case you need to subclass something other than AppCompatImageView and still + * gain the functionality that {@link PhotoView} offers + */ +public class PhotoViewAttacher implements View.OnTouchListener, View.OnLayoutChangeListener { + private static float DEFAULT_MAX_SCALE = 3.0f; + private static float DEFAULT_MID_SCALE = 1.75f; + private static float DEFAULT_MIN_SCALE = 1.0f; + private static int DEFAULT_ZOOM_DURATION = 200; + + private static final int HORIZONTAL_EDGE_NONE = -1; + private static final int HORIZONTAL_EDGE_LEFT = 0; + private static final int HORIZONTAL_EDGE_RIGHT = 1; + private static final int HORIZONTAL_EDGE_BOTH = 2; + private static final int VERTICAL_EDGE_NONE = -1; + private static final int VERTICAL_EDGE_TOP = 0; + private static final int VERTICAL_EDGE_BOTTOM = 1; + private static final int VERTICAL_EDGE_BOTH = 2; + private static int SINGLE_TOUCH = 1; + + private Interpolator mInterpolator = new AccelerateDecelerateInterpolator(); + private int mZoomDuration = DEFAULT_ZOOM_DURATION; + private float mMinScale = DEFAULT_MIN_SCALE; + private float mMidScale = DEFAULT_MID_SCALE; + private float mMaxScale = DEFAULT_MAX_SCALE; + + private boolean mAllowParentInterceptOnEdge = true; + private boolean mBlockParentIntercept = false; + + private ImageView mImageView; + + // Gesture Detectors + private GestureDetector mGestureDetector; + private CustomGestureDetector 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 OnOutsidePhotoTapListener mOutsidePhotoTapListener; + private OnViewTapListener mViewTapListener; + private View.OnClickListener mOnClickListener; + private OnLongClickListener mLongClickListener; + private OnScaleChangedListener mScaleChangeListener; + private OnSingleFlingListener mSingleFlingListener; + private OnViewDragListener mOnViewDragListener; + + private FlingRunnable mCurrentFlingRunnable; + private int mHorizontalScrollEdge = HORIZONTAL_EDGE_BOTH; + private int mVerticalScrollEdge = VERTICAL_EDGE_BOTH; + private float mBaseRotation; + + private boolean mZoomEnabled = true; + private ScaleType mScaleType = ScaleType.FIT_CENTER; + + private OnGestureListener onGestureListener = new OnGestureListener() { + @Override + public void onDrag(float dx, float dy) { + if (mScaleDragDetector.isScaling()) { + return; // Do not drag if we are already scaling + } + if (mOnViewDragListener != null) { + mOnViewDragListener.onDrag(dx, dy); + } + 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). + */ + ViewParent parent = mImageView.getParent(); + if (mAllowParentInterceptOnEdge && !mScaleDragDetector.isScaling() && !mBlockParentIntercept) { + if (mHorizontalScrollEdge == HORIZONTAL_EDGE_BOTH || (mHorizontalScrollEdge == HORIZONTAL_EDGE_LEFT && dx >= 1f) + || (mHorizontalScrollEdge == HORIZONTAL_EDGE_RIGHT && dx <= -1f) || (mVerticalScrollEdge == VERTICAL_EDGE_TOP && dy >= 1f) + || (mVerticalScrollEdge == VERTICAL_EDGE_BOTTOM && dy <= -1f)) { + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(false); + } + } + } else { + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(true); + } + } + } + + @Override + public void onFling(float startX, float startY, float velocityX, float velocityY) { + mCurrentFlingRunnable = new FlingRunnable(mImageView.getContext()); + mCurrentFlingRunnable.fling(getImageViewWidth(mImageView), getImageViewHeight(mImageView), (int) velocityX, (int) velocityY); + mImageView.post(mCurrentFlingRunnable); + } + + @Override + public void onScale(float scaleFactor, float focusX, float focusY) { + onScale(scaleFactor, focusX, focusY, 0, 0); + } + + @Override + public void onScale(float scaleFactor, float focusX, float focusY, float dx, float dy) { + if (getScale() < mMaxScale || scaleFactor < 1f) { + if (mScaleChangeListener != null) { + mScaleChangeListener.onScaleChange(scaleFactor, focusX, focusY); + } + mSuppMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); + mSuppMatrix.postTranslate(dx, dy); + checkAndDisplayMatrix(); + } + } + }; + + public PhotoViewAttacher(ImageView imageView) { + mImageView = imageView; + imageView.setOnTouchListener(this); + imageView.addOnLayoutChangeListener(this); + if (imageView.isInEditMode()) { + return; + } + mBaseRotation = 0.0f; + // Create Gesture Detectors... + mScaleDragDetector = new CustomGestureDetector(imageView.getContext(), onGestureListener); + mGestureDetector = new GestureDetector(imageView.getContext(), new GestureDetector.SimpleOnGestureListener() { + // forward long click listener + @Override + public void onLongPress(MotionEvent e) { + if (mLongClickListener != null) { + mLongClickListener.onLongClick(mImageView); + } + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (mSingleFlingListener != null) { + if (getScale() > DEFAULT_MIN_SCALE) { + return false; + } + if (e1.getPointerCount() > SINGLE_TOUCH || e2.getPointerCount() > SINGLE_TOUCH) { + return false; + } + return mSingleFlingListener.onFling(e1, e2, velocityX, velocityY); + } + return false; + } + }); + mGestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() { + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + if (mOnClickListener != null) { + mOnClickListener.onClick(mImageView); + } + final RectF displayRect = getDisplayRect(); + final float x = e.getX(); + final float y = e.getY(); + if (mViewTapListener != null) { + mViewTapListener.onViewTap(mImageView, x, y); + } + if (displayRect != null) { + // 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(); + if (mPhotoTapListener != null) { + mPhotoTapListener.onPhotoTap(mImageView, xResult, yResult); + } + return true; + } else { + if (mOutsidePhotoTapListener != null) { + mOutsidePhotoTapListener.onOutsidePhotoTap(mImageView); + } + } + } + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent ev) { + try { + float scale = getScale(); + float x = ev.getX(); + float y = ev.getY(); + if (scale < getMediumScale()) { + setScale(getMediumScale(), x, y, true); + } else if (scale >= getMediumScale() && scale < getMaximumScale()) { + setScale(getMaximumScale(), x, y, true); + } else { + setScale(getMinimumScale(), x, y, true); + } + } catch (ArrayIndexOutOfBoundsException e) { + // Can sometimes happen when getX() and getY() is called + } + return true; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + // Wait for the confirmed onDoubleTap() instead + return false; + } + }); + } + + public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener newOnDoubleTapListener) { + this.mGestureDetector.setOnDoubleTapListener(newOnDoubleTapListener); + } + + public void setOnScaleChangeListener(OnScaleChangedListener onScaleChangeListener) { + this.mScaleChangeListener = onScaleChangeListener; + } + + public void setOnSingleFlingListener(OnSingleFlingListener onSingleFlingListener) { + this.mSingleFlingListener = onSingleFlingListener; + } + + @Deprecated + public boolean isZoomEnabled() { + return mZoomEnabled; + } + + + /** + * 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) { + Drawable d = mImageView.getDrawable(); + if (d != null) { + mDisplayRect.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); + matrix.mapRect(mDisplayRect); + return mDisplayRect; + } + return null; + } + + public RectF getDisplayRect() { + checkMatrixBounds(); + return getDisplayRect(getDrawMatrix()); + } + + public boolean setDisplayMatrix(Matrix finalMatrix) { + if (finalMatrix == null) { + throw new IllegalArgumentException("Matrix cannot be null"); + } + if (mImageView.getDrawable() == null) { + return false; + } + mSuppMatrix.set(finalMatrix); + checkAndDisplayMatrix(); + return true; + } + + public void setBaseRotation(final float degrees) { + mBaseRotation = degrees % 360; + update(); + setRotationBy(mBaseRotation); + checkAndDisplayMatrix(); + } + + public void setRotationTo(float degrees) { + mSuppMatrix.setRotate(degrees % 360); + checkAndDisplayMatrix(); + } + + public void setRotationBy(float degrees) { + mSuppMatrix.postRotate(degrees % 360); + checkAndDisplayMatrix(); + } + + public float getMinimumScale() { + return mMinScale; + } + + public float getMediumScale() { + return mMidScale; + } + + public float getMaximumScale() { + return mMaxScale; + } + + public float getScale() { + return (float) Math.sqrt((float) Math.pow(getValue(mSuppMatrix, Matrix.MSCALE_X), 2) + (float) Math.pow(getValue(mSuppMatrix, Matrix.MSKEW_Y), 2)); + } + + public ScaleType getScaleType() { + return mScaleType; + } + + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + // Update our base matrix, as the bounds have changed + if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) { + updateBaseMatrix(mImageView.getDrawable()); + } + } + + @Override + public boolean onTouch(View v, MotionEvent ev) { + boolean handled = false; + if (mZoomEnabled && Util.hasDrawable((ImageView) v)) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + ViewParent parent = v.getParent(); + // First, disable the Parent from intercepting the touch + // event + if (parent != null) { + parent.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 (rect != null) { + v.post(new AnimatedZoomRunnable(getScale(), mMinScale, rect.centerX(), rect.centerY())); + handled = true; + } + } else if (getScale() > mMaxScale) { + RectF rect = getDisplayRect(); + if (rect != null) { + v.post(new AnimatedZoomRunnable(getScale(), mMaxScale, rect.centerX(), rect.centerY())); + handled = true; + } + } + break; + default: + break; + } + // Try the Scale/Drag detector + if (mScaleDragDetector != null) { + boolean wasScaling = mScaleDragDetector.isScaling(); + boolean wasDragging = mScaleDragDetector.isDragging(); + handled = mScaleDragDetector.onTouchEvent(ev); + boolean didntScale = !wasScaling && !mScaleDragDetector.isScaling(); + boolean didntDrag = !wasDragging && !mScaleDragDetector.isDragging(); + mBlockParentIntercept = didntScale && didntDrag; + } + // Check to see if the user double tapped + if (mGestureDetector != null && mGestureDetector.onTouchEvent(ev)) { + handled = true; + } + } + return handled; + } + + public void setAllowParentInterceptOnEdge(boolean allow) { + mAllowParentInterceptOnEdge = allow; + } + + public void setMinimumScale(float minimumScale) { + Util.checkZoomLevels(minimumScale, mMidScale, mMaxScale); + mMinScale = minimumScale; + } + + public void setMediumScale(float mediumScale) { + Util.checkZoomLevels(mMinScale, mediumScale, mMaxScale); + mMidScale = mediumScale; + } + + public void setMaximumScale(float maximumScale) { + Util.checkZoomLevels(mMinScale, mMidScale, maximumScale); + mMaxScale = maximumScale; + } + + public void setScaleLevels(float minimumScale, float mediumScale, float maximumScale) { + Util.checkZoomLevels(minimumScale, mediumScale, maximumScale); + mMinScale = minimumScale; + mMidScale = mediumScale; + mMaxScale = maximumScale; + } + + public void setOnLongClickListener(OnLongClickListener listener) { + mLongClickListener = listener; + } + + public void setOnClickListener(View.OnClickListener listener) { + mOnClickListener = listener; + } + + public void setOnMatrixChangeListener(OnMatrixChangedListener listener) { + mMatrixChangeListener = listener; + } + + public void setOnPhotoTapListener(OnPhotoTapListener listener) { + mPhotoTapListener = listener; + } + + public void setOnOutsidePhotoTapListener(OnOutsidePhotoTapListener mOutsidePhotoTapListener) { + this.mOutsidePhotoTapListener = mOutsidePhotoTapListener; + } + + public void setOnViewTapListener(OnViewTapListener listener) { + mViewTapListener = listener; + } + + public void setOnViewDragListener(OnViewDragListener listener) { + mOnViewDragListener = listener; + } + + public void setScale(float scale) { + setScale(scale, false); + } + + public void setScale(float scale, boolean animate) { + setScale(scale, (mImageView.getRight()) / 2, (mImageView.getBottom()) / 2, animate); + } + + public void setScale(float scale, float focalX, float focalY, boolean animate) { + // Check to see if the scale is within bounds + if (scale < mMinScale || scale > mMaxScale) { + throw new IllegalArgumentException("Scale must be within the range of minScale and maxScale"); + } + if (animate) { + mImageView.post(new AnimatedZoomRunnable(getScale(), scale, focalX, focalY)); + } else { + mSuppMatrix.setScale(scale, scale, focalX, focalY); + checkAndDisplayMatrix(); + } + } + + /** + * Set the zoom interpolator + * + * @param interpolator the zoom interpolator + */ + public void setZoomInterpolator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + public void setScaleType(ScaleType scaleType) { + if (Util.isSupportedScaleType(scaleType) && scaleType != mScaleType) { + mScaleType = scaleType; + update(); + } + } + + public boolean isZoomable() { + return mZoomEnabled; + } + + public void setZoomable(boolean zoomable) { + mZoomEnabled = zoomable; + update(); + } + + public void update() { + if (mZoomEnabled) { + // Update the base matrix using the current drawable + updateBaseMatrix(mImageView.getDrawable()); + } else { + // Reset the Matrix... + resetMatrix(); + } + } + + /** + * Get the display matrix + * + * @param matrix target matrix to copy to + */ + public void getDisplayMatrix(Matrix matrix) { + matrix.set(getDrawMatrix()); + } + + /** + * Get the current support matrix + */ + public void getSuppMatrix(Matrix matrix) { + matrix.set(mSuppMatrix); + } + + private Matrix getDrawMatrix() { + mDrawMatrix.set(mBaseMatrix); + mDrawMatrix.postConcat(mSuppMatrix); + return mDrawMatrix; + } + + public Matrix getImageMatrix() { + return mDrawMatrix; + } + + public void setZoomTransitionDuration(int milliseconds) { + this.mZoomDuration = milliseconds; + } + + /** + * 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 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 its contents + */ + private void resetMatrix() { + mSuppMatrix.reset(); + setRotationBy(mBaseRotation); + setImageViewMatrix(getDrawMatrix()); + checkMatrixBounds(); + } + + private void setImageViewMatrix(Matrix matrix) { + mImageView.setImageMatrix(matrix); + // Call MatrixChangedListener if needed + if (mMatrixChangeListener != null) { + RectF displayRect = getDisplayRect(matrix); + if (displayRect != null) { + mMatrixChangeListener.onMatrixChanged(displayRect); + } + } + } + + /** + * Helper method that simply checks the Matrix, and then displays the result + */ + private void checkAndDisplayMatrix() { + if (checkMatrixBounds()) { + setImageViewMatrix(getDrawMatrix()); + } + } + + /** + * Calculate Matrix for FIT_CENTER + * + * @param drawable - Drawable being displayed + */ + private void updateBaseMatrix(Drawable drawable) { + if (drawable == null) { + return; + } + final float viewWidth = getImageViewWidth(mImageView); + final float viewHeight = getImageViewHeight(mImageView); + final int drawableWidth = drawable.getIntrinsicWidth(); + final int drawableHeight = drawable.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); + if ((int) mBaseRotation % 180 != 0) { + mTempSrc = new RectF(0, 0, drawableHeight, drawableWidth); + } + 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(); + } + + private boolean checkMatrixBounds() { + final RectF rect = getDisplayRect(getDrawMatrix()); + if (rect == null) { + return false; + } + final float height = rect.height(); + final float width = rect.width(); + float deltaX = 0; + float deltaY = 0; + final int viewHeight = getImageViewHeight(mImageView); + 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; + } + mVerticalScrollEdge = VERTICAL_EDGE_BOTH; + } else if (rect.top > 0) { + mVerticalScrollEdge = VERTICAL_EDGE_TOP; + deltaY = -rect.top; + } else if (rect.bottom < viewHeight) { + mVerticalScrollEdge = VERTICAL_EDGE_BOTTOM; + deltaY = viewHeight - rect.bottom; + } else { + mVerticalScrollEdge = VERTICAL_EDGE_NONE; + } + final int viewWidth = getImageViewWidth(mImageView); + 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; + } + mHorizontalScrollEdge = HORIZONTAL_EDGE_BOTH; + } else if (rect.left > 0) { + mHorizontalScrollEdge = HORIZONTAL_EDGE_LEFT; + deltaX = -rect.left; + } else if (rect.right < viewWidth) { + deltaX = viewWidth - rect.right; + mHorizontalScrollEdge = HORIZONTAL_EDGE_RIGHT; + } else { + mHorizontalScrollEdge = HORIZONTAL_EDGE_NONE; + } + // Finally actually translate the matrix + mSuppMatrix.postTranslate(deltaX, deltaY); + return true; + } + + private int getImageViewWidth(ImageView imageView) { + return imageView.getWidth() - imageView.getPaddingLeft() - imageView.getPaddingRight(); + } + + private int getImageViewHeight(ImageView imageView) { + return imageView.getHeight() - imageView.getPaddingTop() - imageView.getPaddingBottom(); + } + + private void cancelFling() { + if (mCurrentFlingRunnable != null) { + mCurrentFlingRunnable.cancelFling(); + mCurrentFlingRunnable = null; + } + } + + private class AnimatedZoomRunnable implements Runnable { + private final float mFocalX; + private final float mFocalY; + private final long mStartTime; + private final float mZoomStart; + private final float mZoomEnd; + + public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, final float focalX, final float focalY) { + mFocalX = focalX; + mFocalY = focalY; + mStartTime = System.currentTimeMillis(); + mZoomStart = currentZoom; + mZoomEnd = targetZoom; + } + + @Override + public void run() { + float t = interpolate(); + float scale = mZoomStart + t * (mZoomEnd - mZoomStart); + float deltaScale = scale / getScale(); + onGestureListener.onScale(deltaScale, mFocalX, mFocalY); + // We haven't hit our target scale yet, so post ourselves again + if (t < 1f) { + Compat.postOnAnimation(mImageView, this); + } + } + + private float interpolate() { + float t = 1f * (System.currentTimeMillis() - mStartTime) / mZoomDuration; + t = Math.min(1f, t); + t = mInterpolator.getInterpolation(t); + return t; + } + } + + private class FlingRunnable implements Runnable { + private final OverScroller mScroller; + private int mCurrentX; + private int mCurrentY; + + public FlingRunnable(Context context) { + mScroller = new OverScroller(context); + } + + public void cancelFling() { + mScroller.forceFinished(true); + } + + public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) { + final RectF rect = getDisplayRect(); + if (rect == null) { + return; + } + final int startX = Math.round(-rect.left); + final int minX; + final int maxX; + final int minY; + final int 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 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() { + if (mScroller.isFinished()) { + return; // remaining post that should not be handled + } + if (mScroller.computeScrollOffset()) { + final int newX = mScroller.getCurrX(); + final int newY = mScroller.getCurrY(); + mSuppMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY); + checkAndDisplayMatrix(); + mCurrentX = newX; + mCurrentY = newY; + // Post On animation + Compat.postOnAnimation(mImageView, this); + } + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/Util.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/Util.java new file mode 100644 index 0000000..3b5ce76 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/photoview/Util.java @@ -0,0 +1,39 @@ +package com.tencent.qcloud.tuikit.timcommon.component.photoview; + +import android.view.MotionEvent; +import android.widget.ImageView; + +class Util { + + static void checkZoomLevels(float minZoom, float midZoom, + float maxZoom) { + if (minZoom >= midZoom) { + throw new IllegalArgumentException( + "Minimum zoom has to be less than Medium zoom. Call setMinimumZoom() with a more appropriate value"); + } else if (midZoom >= maxZoom) { + throw new IllegalArgumentException( + "Medium zoom has to be less than Maximum zoom. Call setMaximumZoom() with a more appropriate value"); + } + } + + static boolean hasDrawable(ImageView imageView) { + return imageView.getDrawable() != null; + } + + static boolean isSupportedScaleType(final ImageView.ScaleType scaleType) { + if (scaleType == null) { + return false; + } + switch (scaleType) { + case MATRIX: + throw new IllegalStateException("Matrix scale type is not supported"); + default: + break; + } + return true; + } + + static int getPointerIndex(int action) { + return (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/scroller/CenteredSmoothScroller.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/scroller/CenteredSmoothScroller.java new file mode 100644 index 0000000..2003da3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/scroller/CenteredSmoothScroller.java @@ -0,0 +1,40 @@ +package com.tencent.qcloud.tuikit.timcommon.component.scroller; + +import android.content.Context; +import android.view.View; + +import androidx.recyclerview.widget.LinearSmoothScroller; +import androidx.recyclerview.widget.OrientationHelper; +import androidx.recyclerview.widget.RecyclerView; + +public class CenteredSmoothScroller extends LinearSmoothScroller { + + public CenteredSmoothScroller(Context context) { + super(context); + } + + @Override + protected void onTargetFound(View targetView, RecyclerView.State state, Action action) { + RecyclerView.LayoutManager layoutManager = getLayoutManager(); + if (layoutManager == null) { + return; + } + int distance = calculateDistanceToCenter(targetView, layoutManager); + int time = calculateTimeForDeceleration(distance); + if (time > 0) { + action.update(0, distance, time, mDecelerateInterpolator); + } + } + + private int calculateDistanceToCenter(View targetView, RecyclerView.LayoutManager layoutManager) { + OrientationHelper helper = OrientationHelper.createVerticalHelper(layoutManager); + int childCenter = helper.getDecoratedStart(targetView) + helper.getDecoratedMeasurement(targetView) / 2; + int containerCenter; + if (layoutManager.getClipToPadding()) { + containerCenter = helper.getStartAfterPadding() + helper.getTotalSpace() / 2; + } else { + containerCenter = helper.getEnd() / 2; + } + return childCenter - containerCenter; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/Attributes.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/Attributes.java new file mode 100644 index 0000000..a3ffdaa --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/Attributes.java @@ -0,0 +1,5 @@ +package com.tencent.qcloud.tuikit.timcommon.component.swipe; + +public class Attributes { + public enum Mode { Single, Multiple } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/RecyclerSwipeAdapter.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/RecyclerSwipeAdapter.java new file mode 100644 index 0000000..95b2c0d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/RecyclerSwipeAdapter.java @@ -0,0 +1,85 @@ +package com.tencent.qcloud.tuikit.timcommon.component.swipe; + +import android.view.ViewGroup; + +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +public abstract class RecyclerSwipeAdapter + extends RecyclerView.Adapter implements SwipeItemMangerInterface, SwipeAdapterInterface { + public SwipeItemMangerImpl mItemManger = new SwipeItemMangerImpl(this); + + @Override public abstract VH onCreateViewHolder(ViewGroup parent, int viewType); + + @Override public abstract void onBindViewHolder(VH viewHolder, final int position); + + @Override + public void notifyDatasetChanged() { + super.notifyDataSetChanged(); + } + + @Override + public void notifySwipeItemChanged(int position) { + super.notifyItemChanged(position); + } + + @Override + public void openItem(int position) { + mItemManger.openItem(position); + } + + @Override + public void closeItem(int position) { + mItemManger.closeItem(position); + } + + @Override + public void closeAllExcept(SwipeLayout layout) { + mItemManger.closeAllExcept(layout); + } + + @Override + public void closeAllSwipeItems() { + mItemManger.closeAllSwipeItems(); + } + + @Override + public List getOpenItems() { + return mItemManger.getOpenItems(); + } + + @Override + public List getOpenLayouts() { + return mItemManger.getOpenLayouts(); + } + + @Override + public void removeShownLayouts(SwipeLayout layout) { + mItemManger.removeShownLayouts(layout); + } + + @Override + public boolean isOpen(int position) { + return mItemManger.isOpen(position); + } + + @Override + public Attributes.Mode getMode() { + return mItemManger.getMode(); + } + + @Override + public void setMode(Attributes.Mode mode) { + mItemManger.setMode(mode); + } + + @Override + public void switchAllSwipeEnable(boolean enable) { + mItemManger.switchAllSwipeEnable(enable); + } + + public void setSwipeEnabled(boolean enabled) { + mItemManger.setSwipeEnabled(enabled); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SimpleSwipeListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SimpleSwipeListener.java new file mode 100644 index 0000000..286c607 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SimpleSwipeListener.java @@ -0,0 +1,21 @@ +package com.tencent.qcloud.tuikit.timcommon.component.swipe; + +public class SimpleSwipeListener implements SwipeLayout.SwipeListener { + @Override + public void onStartOpen(SwipeLayout layout) {} + + @Override + public void onOpen(SwipeLayout layout) {} + + @Override + public void onStartClose(SwipeLayout layout) {} + + @Override + public void onClose(SwipeLayout layout) {} + + @Override + public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset) {} + + @Override + public void onHandRelease(SwipeLayout layout, float xvel, float yvel) {} +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeAdapterInterface.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeAdapterInterface.java new file mode 100644 index 0000000..35bd296 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeAdapterInterface.java @@ -0,0 +1,9 @@ +package com.tencent.qcloud.tuikit.timcommon.component.swipe; + +public interface SwipeAdapterInterface { + int getSwipeLayoutResourceId(int position); + + void notifyDatasetChanged(); + + void notifySwipeItemChanged(int position); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeItemMangerImpl.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeItemMangerImpl.java new file mode 100644 index 0000000..2fd1ff5 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeItemMangerImpl.java @@ -0,0 +1,219 @@ +package com.tencent.qcloud.tuikit.timcommon.component.swipe; + +import android.view.View; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class SwipeItemMangerImpl implements SwipeItemMangerInterface { + private Attributes.Mode mode = Attributes.Mode.Single; + public static final int INVALID_POSITION = -1; + + protected int mOpenPosition = INVALID_POSITION; + + protected Set mOpenPositions = new HashSet(); + protected Set mShownLayouts = new HashSet(); + + protected boolean isSwipeEnabled = true; + protected SwipeAdapterInterface swipeAdapterInterface; + + public SwipeItemMangerImpl(SwipeAdapterInterface swipeAdapterInterface) { + if (swipeAdapterInterface == null) { + throw new IllegalArgumentException("SwipeAdapterInterface can not be null"); + } + + this.swipeAdapterInterface = swipeAdapterInterface; + } + + public Attributes.Mode getMode() { + return mode; + } + + public void setMode(Attributes.Mode mode) { + this.mode = mode; + mOpenPositions.clear(); + mShownLayouts.clear(); + mOpenPosition = INVALID_POSITION; + } + + public void bind(View view, int position) { + int resId = swipeAdapterInterface.getSwipeLayoutResourceId(position); + SwipeLayout swipeLayout = (SwipeLayout) view.findViewById(resId); + if (swipeLayout == null) { + throw new IllegalStateException("can not find SwipeLayout in target view"); + } + + swipeLayout.setSwipeEnabled(isSwipeEnabled); + if (swipeLayout.getTag(resId) == null) { + OnLayoutListener onLayoutListener = new OnLayoutListener(position); + SwipeMemory swipeMemory = new SwipeMemory(position); + swipeLayout.addSwipeListener(swipeMemory); + swipeLayout.addOnLayoutListener(onLayoutListener); + swipeLayout.setTag(resId, new ValueBox(position, swipeMemory, onLayoutListener)); + mShownLayouts.add(swipeLayout); + } else { + ValueBox valueBox = (ValueBox) swipeLayout.getTag(resId); + valueBox.swipeMemory.setPosition(position); + valueBox.onLayoutListener.setPosition(position); + valueBox.position = position; + } + } + + @Override + public void openItem(int position) { + if (mode == Attributes.Mode.Multiple) { + if (!mOpenPositions.contains(position)) { + mOpenPositions.add(position); + } + } else { + mOpenPosition = position; + } + swipeAdapterInterface.notifySwipeItemChanged(position); + } + + @Override + public void closeItem(int position) { + if (mode == Attributes.Mode.Multiple) { + mOpenPositions.remove(position); + } else { + if (mOpenPosition == position) { + mOpenPosition = INVALID_POSITION; + } + } + swipeAdapterInterface.notifySwipeItemChanged(position); + } + + @Override + public void closeAllExcept(SwipeLayout layout) { + for (SwipeLayout s : mShownLayouts) { + if (s != layout) { + s.close(); + } + } + } + + @Override + public void closeAllSwipeItems() { + if (mode == Attributes.Mode.Multiple) { + mOpenPositions.clear(); + } else { + mOpenPosition = INVALID_POSITION; + } + for (SwipeLayout s : mShownLayouts) { + s.close(); + } + } + + @Override + public void switchAllSwipeEnable(boolean enable) { + for (SwipeLayout s : mShownLayouts) { + s.setSwipeEnabled(enable); + } + } + + @Override + public void removeShownLayouts(SwipeLayout layout) { + mShownLayouts.remove(layout); + } + + @Override + public List getOpenItems() { + if (mode == Attributes.Mode.Multiple) { + return new ArrayList(mOpenPositions); + } else { + return Collections.singletonList(mOpenPosition); + } + } + + @Override + public List getOpenLayouts() { + return new ArrayList(mShownLayouts); + } + + @Override + public boolean isOpen(int position) { + if (mode == Attributes.Mode.Multiple) { + return mOpenPositions.contains(position); + } else { + return mOpenPosition == position; + } + } + + public void setSwipeEnabled(boolean swipeEnabled) { + isSwipeEnabled = swipeEnabled; + } + + class ValueBox { + OnLayoutListener onLayoutListener; + SwipeMemory swipeMemory; + int position; + + ValueBox(int position, SwipeMemory swipeMemory, OnLayoutListener onLayoutListener) { + this.swipeMemory = swipeMemory; + this.onLayoutListener = onLayoutListener; + this.position = position; + } + } + + class OnLayoutListener implements SwipeLayout.OnLayout { + private int position; + + OnLayoutListener(int position) { + this.position = position; + } + + public void setPosition(int position) { + this.position = position; + } + + @Override + public void onLayout(SwipeLayout v) { + if (isOpen(position)) { + v.open(false, false); + } else { + v.close(false, false); + } + } + } + + class SwipeMemory extends SimpleSwipeListener { + private int position; + + SwipeMemory(int position) { + this.position = position; + } + + @Override + public void onClose(SwipeLayout layout) { + if (mode == Attributes.Mode.Multiple) { + mOpenPositions.remove(position); + } else { + mOpenPosition = INVALID_POSITION; + } + } + + @Override + public void onStartOpen(SwipeLayout layout) { + if (mode == Attributes.Mode.Single) { + closeAllExcept(layout); + } + } + + @Override + public void onOpen(SwipeLayout layout) { + if (mode == Attributes.Mode.Multiple) { + mOpenPositions.add(position); + } else { + closeAllExcept(layout); + mOpenPosition = position; + } + } + + public void setPosition(int position) { + this.position = position; + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeItemMangerInterface.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeItemMangerInterface.java new file mode 100644 index 0000000..137441e --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeItemMangerInterface.java @@ -0,0 +1,27 @@ +package com.tencent.qcloud.tuikit.timcommon.component.swipe; + +import java.util.List; + +public interface SwipeItemMangerInterface { + void openItem(int position); + + void closeItem(int position); + + void closeAllExcept(SwipeLayout layout); + + void closeAllSwipeItems(); + + List getOpenItems(); + + List getOpenLayouts(); + + void removeShownLayouts(SwipeLayout layout); + + boolean isOpen(int position); + + Attributes.Mode getMode(); + + void setMode(Attributes.Mode mode); + + void switchAllSwipeEnable(boolean enable); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeLayout.java new file mode 100644 index 0000000..72a757f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/swipe/SwipeLayout.java @@ -0,0 +1,1806 @@ +package com.tencent.qcloud.tuikit.timcommon.component.swipe; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.widget.AbsListView; +import android.widget.AdapterView; +import android.widget.FrameLayout; + +import androidx.core.view.GravityCompat; +import androidx.core.view.ViewCompat; +import androidx.customview.widget.ViewDragHelper; + +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.LayoutUtil; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class SwipeLayout extends FrameLayout { + @Deprecated public static final int EMPTY_LAYOUT = -1; + private static final int DRAG_LEFT = 1; + private static final int DRAG_RIGHT = 2; + private static final int DRAG_TOP = 4; + private static final int DRAG_BOTTOM = 8; + private static final DragEdge DefaultDragEdge = DragEdge.Right; + + private int mTouchSlop; + + private DragEdge mCurrentDragEdge = DefaultDragEdge; + private ViewDragHelper mDragHelper; + + private int mDragDistance = 0; + private LinkedHashMap mDragEdges = new LinkedHashMap<>(); + private ShowMode mShowMode; + + private float[] mEdgeSwipesOffset = new float[4]; + + private List mSwipeListeners = new ArrayList<>(); + private List mSwipeDeniers = new ArrayList<>(); + private Map> mRevealListeners = new HashMap<>(); + private Map mShowEntirely = new HashMap<>(); + private Map mViewBoundCache = new HashMap<>(); // save all children's bound, restore in onLayout + + private DoubleClickListener mDoubleClickListener; + + private boolean mSwipeEnabled = true; + private boolean[] mSwipesEnabled = new boolean[] {true, true, true, true}; + private boolean mClickToClose = false; + private float mWillOpenPercentAfterOpen = 0.75f; + private float mWillOpenPercentAfterClose = 0.25f; + + public enum DragEdge { Left, Top, Right, Bottom } + + public enum ShowMode { LayDown, PullOut } + + public SwipeLayout(Context context) { + this(context, null); + } + + public SwipeLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public SwipeLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mDragHelper = ViewDragHelper.create(this, mDragHelperCallback); + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SwipeLayout); + mEdgeSwipesOffset[DragEdge.Left.ordinal()] = a.getDimension(R.styleable.SwipeLayout_leftEdgeSwipeOffset, 0); + mEdgeSwipesOffset[DragEdge.Right.ordinal()] = a.getDimension(R.styleable.SwipeLayout_rightEdgeSwipeOffset, 0); + mEdgeSwipesOffset[DragEdge.Top.ordinal()] = a.getDimension(R.styleable.SwipeLayout_topEdgeSwipeOffset, 0); + mEdgeSwipesOffset[DragEdge.Bottom.ordinal()] = a.getDimension(R.styleable.SwipeLayout_bottomEdgeSwipeOffset, 0); + setClickToClose(a.getBoolean(R.styleable.SwipeLayout_clickToClose, mClickToClose)); + int defaultDragEdge = DRAG_RIGHT; + if (LayoutUtil.isRTL()) { + defaultDragEdge = DRAG_LEFT; + } + int dragEdgeChoices = a.getInt(R.styleable.SwipeLayout_drag_edge, defaultDragEdge); + if ((dragEdgeChoices & DRAG_LEFT) == DRAG_LEFT) { + mDragEdges.put(DragEdge.Left, null); + } + if ((dragEdgeChoices & DRAG_TOP) == DRAG_TOP) { + mDragEdges.put(DragEdge.Top, null); + } + if ((dragEdgeChoices & DRAG_RIGHT) == DRAG_RIGHT) { + mDragEdges.put(DragEdge.Right, null); + } + if ((dragEdgeChoices & DRAG_BOTTOM) == DRAG_BOTTOM) { + mDragEdges.put(DragEdge.Bottom, null); + } + int ordinal = a.getInt(R.styleable.SwipeLayout_show_mode, ShowMode.PullOut.ordinal()); + mShowMode = ShowMode.values()[ordinal]; + a.recycle(); + } + + public interface SwipeListener { + void onStartOpen(SwipeLayout layout); + + void onOpen(SwipeLayout layout); + + void onStartClose(SwipeLayout layout); + + void onClose(SwipeLayout layout); + + void onUpdate(SwipeLayout layout, int leftOffset, int topOffset); + + void onHandRelease(SwipeLayout layout, float xvel, float yvel); + } + + public void addSwipeListener(SwipeListener l) { + mSwipeListeners.add(l); + } + + public void removeSwipeListener(SwipeListener l) { + mSwipeListeners.remove(l); + } + + public void removeAllSwipeListener() { + mSwipeListeners.clear(); + } + + public interface SwipeDenier { + boolean shouldDenySwipe(MotionEvent ev); + } + + public void addSwipeDenier(SwipeDenier denier) { + mSwipeDeniers.add(denier); + } + + public void removeSwipeDenier(SwipeDenier denier) { + mSwipeDeniers.remove(denier); + } + + public void removeAllSwipeDeniers() { + mSwipeDeniers.clear(); + } + + public interface OnRevealListener { + void onReveal(View child, DragEdge edge, float fraction, int distance); + } + + /** + * bind a view with a specific + * {@link com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.OnRevealListener} + * + * @param childId the view id. + * @param l the target + * {@link com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.OnRevealListener} + */ + public void addRevealListener(int childId, OnRevealListener l) { + View child = findViewById(childId); + if (child == null) { + throw new IllegalArgumentException("Child does not belong to SwipeListener."); + } + + if (!mShowEntirely.containsKey(child)) { + mShowEntirely.put(child, false); + } + if (mRevealListeners.get(child) == null) { + mRevealListeners.put(child, new ArrayList()); + } + + mRevealListeners.get(child).add(l); + } + + /** + * bind multiple views with an + * {@link com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.OnRevealListener}. + * + * @param childIds the view id. + * @param l the {@link com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.OnRevealListener} + */ + public void addRevealListener(int[] childIds, OnRevealListener l) { + for (int i : childIds) { + addRevealListener(i, l); + } + } + + public void removeRevealListener(int childId, OnRevealListener l) { + View child = findViewById(childId); + + if (child == null) { + return; + } + + mShowEntirely.remove(child); + if (mRevealListeners.containsKey(child)) { + mRevealListeners.get(child).remove(l); + } + } + + public void removeAllRevealListeners(int childId) { + View child = findViewById(childId); + if (child != null) { + mRevealListeners.remove(child); + mShowEntirely.remove(child); + } + } + + private ViewDragHelper.Callback mDragHelperCallback = new ViewDragHelper.Callback() { + @Override + public int clampViewPositionHorizontal(View child, int left, int dx) { + return handleClampHorizontal(child, left); + } + + @Override + public int clampViewPositionVertical(View child, int top, int dy) { + return handleClampVertical(child, top, dy); + } + + @Override + public boolean tryCaptureView(View child, int pointerId) { + boolean result = child == getSurfaceView() || getBottomViews().contains(child); + if (result) { + isCloseBeforeDrag = getOpenStatus() == Status.Close; + } + return result; + } + + @Override + public int getViewHorizontalDragRange(View child) { + return mDragDistance; + } + + @Override + public int getViewVerticalDragRange(View child) { + return mDragDistance; + } + + boolean isCloseBeforeDrag = true; + + @Override + public void onViewReleased(View releasedChild, float xvel, float yvel) { + super.onViewReleased(releasedChild, xvel, yvel); + processHandRelease(xvel, yvel, isCloseBeforeDrag); + for (SwipeListener l : mSwipeListeners) { + l.onHandRelease(SwipeLayout.this, xvel, yvel); + } + + invalidate(); + } + + @Override + public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { + View surfaceView = getSurfaceView(); + if (surfaceView == null) { + return; + } + View currentBottomView = getCurrentBottomView(); + int evLeft = surfaceView.getLeft(); + int evRight = surfaceView.getRight(); + int evTop = surfaceView.getTop(); + int evBottom = surfaceView.getBottom(); + if (changedView == surfaceView) { + if (mShowMode == ShowMode.PullOut && currentBottomView != null) { + if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) { + currentBottomView.offsetLeftAndRight(dx); + } else { + currentBottomView.offsetTopAndBottom(dy); + } + } + + } else if (getBottomViews().contains(changedView)) { + if (mShowMode == ShowMode.PullOut) { + surfaceView.offsetLeftAndRight(dx); + surfaceView.offsetTopAndBottom(dy); + } else { + Rect rect = computeBottomLayDown(mCurrentDragEdge); + if (currentBottomView != null) { + currentBottomView.layout(rect.left, rect.top, rect.right, rect.bottom); + } + + int newLeft = surfaceView.getLeft() + dx; + int newTop = surfaceView.getTop() + dy; + + if (mCurrentDragEdge == DragEdge.Left && newLeft < getPaddingLeft()) { + newLeft = getPaddingLeft(); + } else if (mCurrentDragEdge == DragEdge.Right && newLeft > getPaddingLeft()) { + newLeft = getPaddingLeft(); + } else if (mCurrentDragEdge == DragEdge.Top && newTop < getPaddingTop()) { + newTop = getPaddingTop(); + } else if (mCurrentDragEdge == DragEdge.Bottom && newTop > getPaddingTop()) { + newTop = getPaddingTop(); + } + + surfaceView.layout(newLeft, newTop, newLeft + getMeasuredWidth(), newTop + getMeasuredHeight()); + } + } + + dispatchRevealEvent(evLeft, evTop, evRight, evBottom); + + dispatchSwipeEvent(evLeft, evTop, dx, dy); + + invalidate(); + + captureChildrenBound(); + } + }; + + private int handleClampVertical(View child, int top, int dy) { + if (child == getSurfaceView()) { + switch (mCurrentDragEdge) { + case Left: + return getPaddingTop(); + case Right: + return getPaddingTop(); + case Top: + if (top < getPaddingTop()) { + return getPaddingTop(); + } + if (top > getPaddingTop() + mDragDistance) { + return getPaddingTop() + mDragDistance; + } + break; + case Bottom: + if (top < getPaddingTop() - mDragDistance) { + return getPaddingTop() - mDragDistance; + } + if (top > getPaddingTop()) { + return getPaddingTop(); + } + break; + default: + break; + } + } else { + View surfaceView = getSurfaceView(); + int surfaceViewTop = surfaceView == null ? 0 : surfaceView.getTop(); + switch (mCurrentDragEdge) { + case Left: + return getPaddingTop(); + case Right: + return getPaddingTop(); + case Top: + if (mShowMode == ShowMode.PullOut) { + if (top > getPaddingTop()) { + return getPaddingTop(); + } + } else { + if (surfaceViewTop + dy < getPaddingTop()) { + return getPaddingTop(); + } + if (surfaceViewTop + dy > getPaddingTop() + mDragDistance) { + return getPaddingTop() + mDragDistance; + } + } + break; + case Bottom: + if (mShowMode == ShowMode.PullOut) { + if (top < getMeasuredHeight() - mDragDistance) { + return getMeasuredHeight() - mDragDistance; + } + } else { + if (surfaceViewTop + dy >= getPaddingTop()) { + return getPaddingTop(); + } + if (surfaceViewTop + dy <= getPaddingTop() - mDragDistance) { + return getPaddingTop() - mDragDistance; + } + } + break; + default: + break; + } + } + return top; + } + + private int handleClampHorizontal(View child, int left) { + if (child == getSurfaceView()) { + switch (mCurrentDragEdge) { + case Top: + return getPaddingLeft(); + case Bottom: + return getPaddingLeft(); + case Left: + if (left < getPaddingLeft()) { + return getPaddingLeft(); + } + if (left > getPaddingLeft() + mDragDistance) { + return getPaddingLeft() + mDragDistance; + } + break; + case Right: + if (left > getPaddingLeft()) { + return getPaddingLeft(); + } + if (left < getPaddingLeft() - mDragDistance) { + return getPaddingLeft() - mDragDistance; + } + break; + default: + break; + } + } else if (getCurrentBottomView() == child) { + switch (mCurrentDragEdge) { + case Top: + return getPaddingLeft(); + case Bottom: + return getPaddingLeft(); + case Left: + if (mShowMode == ShowMode.PullOut) { + if (left > getPaddingLeft()) { + return getPaddingLeft(); + } + } + break; + case Right: + if (mShowMode == ShowMode.PullOut) { + if (left < getMeasuredWidth() - mDragDistance) { + return getMeasuredWidth() - mDragDistance; + } + } + break; + default: + break; + } + } + return left; + } + + private void captureChildrenBound() { + View currentBottomView = getCurrentBottomView(); + if (getOpenStatus() == Status.Close) { + mViewBoundCache.remove(currentBottomView); + return; + } + + View[] views = new View[] {getSurfaceView(), currentBottomView}; + for (View child : views) { + Rect rect = mViewBoundCache.get(child); + if (rect == null) { + rect = new Rect(); + mViewBoundCache.put(child, rect); + } + rect.left = child.getLeft(); + rect.top = child.getTop(); + rect.right = child.getRight(); + rect.bottom = child.getBottom(); + } + } + + /** + * the dispatchRevealEvent method may not always get accurate position, it + * makes the view may not always get the event when the view is totally + * show( fraction = 1), so , we need to calculate every time. + */ + protected boolean isViewTotallyFirstShowed( + View child, Rect relativePosition, DragEdge edge, int surfaceLeft, int surfaceTop, int surfaceRight, int surfaceBottom) { + if (mShowEntirely.get(child)) { + return false; + } + int childLeft = relativePosition.left; + int childRight = relativePosition.right; + int childTop = relativePosition.top; + int childBottom = relativePosition.bottom; + boolean r = false; + if (getShowMode() == ShowMode.LayDown) { + if ((edge == DragEdge.Right && surfaceRight <= childLeft) || (edge == DragEdge.Left && surfaceLeft >= childRight) + || (edge == DragEdge.Top && surfaceTop >= childBottom) || (edge == DragEdge.Bottom && surfaceBottom <= childTop)) { + r = true; + } + } else if (getShowMode() == ShowMode.PullOut) { + if ((edge == DragEdge.Right && childRight <= getWidth()) || (edge == DragEdge.Left && childLeft >= getPaddingLeft()) + || (edge == DragEdge.Top && childTop >= getPaddingTop()) || (edge == DragEdge.Bottom && childBottom <= getHeight())) { + r = true; + } + } + return r; + } + + protected boolean isViewShowing( + View child, Rect relativePosition, DragEdge availableEdge, int surfaceLeft, int surfaceTop, int surfaceRight, int surfaceBottom) { + int childLeft = relativePosition.left; + int childRight = relativePosition.right; + int childTop = relativePosition.top; + int childBottom = relativePosition.bottom; + if (getShowMode() == ShowMode.LayDown) { + switch (availableEdge) { + case Right: + if (surfaceRight > childLeft && surfaceRight <= childRight) { + return true; + } + break; + case Left: + if (surfaceLeft < childRight && surfaceLeft >= childLeft) { + return true; + } + break; + case Top: + if (surfaceTop >= childTop && surfaceTop < childBottom) { + return true; + } + break; + case Bottom: + if (surfaceBottom > childTop && surfaceBottom <= childBottom) { + return true; + } + break; + default: + break; + } + } else if (getShowMode() == ShowMode.PullOut) { + switch (availableEdge) { + case Right: + if (childLeft <= getWidth() && childRight > getWidth()) { + return true; + } + break; + case Left: + if (childRight >= getPaddingLeft() && childLeft < getPaddingLeft()) { + return true; + } + break; + case Top: + if (childTop < getPaddingTop() && childBottom >= getPaddingTop()) { + return true; + } + break; + case Bottom: + if (childTop < getHeight() && childTop >= getPaddingTop()) { + return true; + } + break; + default: + break; + } + } + return false; + } + + protected Rect getRelativePosition(View child) { + View t = child; + Rect r = new Rect(t.getLeft(), t.getTop(), 0, 0); + while (t.getParent() != null && t != getRootView()) { + t = (View) t.getParent(); + if (t == this) { + break; + } + r.left += t.getLeft(); + r.top += t.getTop(); + } + r.right = r.left + child.getMeasuredWidth(); + r.bottom = r.top + child.getMeasuredHeight(); + return r; + } + + private int mEventCounter = 0; + + protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop, int dx, int dy) { + DragEdge edge = getDragEdge(); + boolean open = true; + if (edge == DragEdge.Left) { + if (dx < 0) { + open = false; + } + } else if (edge == DragEdge.Right) { + if (dx > 0) { + open = false; + } + } else if (edge == DragEdge.Top) { + if (dy < 0) { + open = false; + } + } else if (edge == DragEdge.Bottom) { + if (dy > 0) { + open = false; + } + } + + dispatchSwipeEvent(surfaceLeft, surfaceTop, open); + } + + protected void dispatchSwipeEvent(int surfaceLeft, int surfaceTop, boolean open) { + safeBottomView(); + Status status = getOpenStatus(); + + if (!mSwipeListeners.isEmpty()) { + mEventCounter++; + for (SwipeListener l : mSwipeListeners) { + if (mEventCounter == 1) { + if (open) { + l.onStartOpen(this); + } else { + l.onStartClose(this); + } + } + l.onUpdate(SwipeLayout.this, surfaceLeft - getPaddingLeft(), surfaceTop - getPaddingTop()); + } + + if (status == Status.Close) { + for (SwipeListener l : mSwipeListeners) { + l.onClose(SwipeLayout.this); + } + mEventCounter = 0; + mClickToClose = false; + } + + if (status == Status.Open) { + View currentBottomView = getCurrentBottomView(); + if (currentBottomView != null) { + currentBottomView.setEnabled(true); + } + for (SwipeListener l : mSwipeListeners) { + l.onOpen(SwipeLayout.this); + } + mEventCounter = 0; + mClickToClose = true; + } + } + } + + /** + * prevent bottom view get any touch event. Especially in LayDown mode. + */ + private void safeBottomView() { + Status status = getOpenStatus(); + List bottoms = getBottomViews(); + + if (status == Status.Close) { + for (View bottom : bottoms) { + if (bottom != null && bottom.getVisibility() != INVISIBLE) { + bottom.setVisibility(INVISIBLE); + } + } + } else { + View currentBottomView = getCurrentBottomView(); + if (currentBottomView != null && currentBottomView.getVisibility() != VISIBLE) { + currentBottomView.setVisibility(VISIBLE); + } + } + } + + protected void dispatchRevealEvent(final int surfaceLeft, final int surfaceTop, final int surfaceRight, final int surfaceBottom) { + if (mRevealListeners.isEmpty()) { + return; + } + for (Map.Entry> entry : mRevealListeners.entrySet()) { + View child = entry.getKey(); + Rect rect = getRelativePosition(child); + if (isViewShowing(child, rect, mCurrentDragEdge, surfaceLeft, surfaceTop, surfaceRight, surfaceBottom)) { + mShowEntirely.put(child, false); + int distance = 0; + float fraction = 0f; + if (getShowMode() == ShowMode.LayDown) { + switch (mCurrentDragEdge) { + case Left: + distance = rect.left - surfaceLeft; + fraction = distance / (float) child.getWidth(); + break; + case Right: + distance = rect.right - surfaceRight; + fraction = distance / (float) child.getWidth(); + break; + case Top: + distance = rect.top - surfaceTop; + fraction = distance / (float) child.getHeight(); + break; + case Bottom: + distance = rect.bottom - surfaceBottom; + fraction = distance / (float) child.getHeight(); + break; + default: + break; + } + } else if (getShowMode() == ShowMode.PullOut) { + switch (mCurrentDragEdge) { + case Left: + distance = rect.right - getPaddingLeft(); + fraction = distance / (float) child.getWidth(); + break; + case Right: + distance = rect.left - getWidth(); + fraction = distance / (float) child.getWidth(); + break; + case Top: + distance = rect.bottom - getPaddingTop(); + fraction = distance / (float) child.getHeight(); + break; + case Bottom: + distance = rect.top - getHeight(); + fraction = distance / (float) child.getHeight(); + break; + default: + break; + } + } + + for (OnRevealListener l : entry.getValue()) { + l.onReveal(child, mCurrentDragEdge, Math.abs(fraction), distance); + if (Math.abs(fraction) == 1) { + mShowEntirely.put(child, true); + } + } + } + + if (isViewTotallyFirstShowed(child, rect, mCurrentDragEdge, surfaceLeft, surfaceTop, surfaceRight, surfaceBottom)) { + mShowEntirely.put(child, true); + for (OnRevealListener l : entry.getValue()) { + if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) { + l.onReveal(child, mCurrentDragEdge, 1, child.getWidth()); + } else { + l.onReveal(child, mCurrentDragEdge, 1, child.getHeight()); + } + } + } + } + } + + @Override + public void computeScroll() { + super.computeScroll(); + if (mDragHelper.continueSettling(true)) { + ViewCompat.postInvalidateOnAnimation(this); + } + } + + /** + * {@link android.view.View.OnLayoutChangeListener} added in API 11. I need + * to support it from API 8. + */ + public interface OnLayout { + void onLayout(SwipeLayout v); + } + + private List mOnLayoutListeners; + + public void addOnLayoutListener(OnLayout l) { + if (mOnLayoutListeners == null) { + mOnLayoutListeners = new ArrayList(); + } + mOnLayoutListeners.add(l); + } + + public void removeOnLayoutListener(OnLayout l) { + if (mOnLayoutListeners != null) { + mOnLayoutListeners.remove(l); + } + } + + public void clearDragEdge() { + mDragEdges.clear(); + } + + public void setDrag(DragEdge dragEdge, int childId) { + clearDragEdge(); + addDrag(dragEdge, childId); + } + + public void setDrag(DragEdge dragEdge, View child) { + clearDragEdge(); + addDrag(dragEdge, child); + } + + public void addDrag(DragEdge dragEdge, int childId) { + addDrag(dragEdge, findViewById(childId), null); + } + + public void addDrag(DragEdge dragEdge, View child) { + addDrag(dragEdge, child, null); + } + + public void addDrag(DragEdge dragEdge, View child, ViewGroup.LayoutParams params) { + if (child == null) { + return; + } + + if (params == null) { + params = generateDefaultLayoutParams(); + } + if (!checkLayoutParams(params)) { + params = generateLayoutParams(params); + } + int gravity = -1; + switch (dragEdge) { + case Left: + gravity = Gravity.LEFT; + break; + case Right: + gravity = Gravity.RIGHT; + break; + case Top: + gravity = Gravity.TOP; + break; + case Bottom: + gravity = Gravity.BOTTOM; + break; + default: + break; + } + if (params instanceof LayoutParams) { + ((LayoutParams) params).gravity = gravity; + } + addView(child, 0, params); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child == null) { + return; + } + int gravity = Gravity.NO_GRAVITY; + try { + gravity = (Integer) params.getClass().getField("gravity").get(params); + } catch (Exception e) { + e.printStackTrace(); + } + + if (gravity > 0) { + gravity = GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(this)); + + if ((gravity & Gravity.LEFT) == Gravity.LEFT) { + mDragEdges.put(DragEdge.Left, child); + } + if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) { + mDragEdges.put(DragEdge.Right, child); + } + if ((gravity & Gravity.TOP) == Gravity.TOP) { + mDragEdges.put(DragEdge.Top, child); + } + if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) { + mDragEdges.put(DragEdge.Bottom, child); + } + } else { + for (Map.Entry entry : mDragEdges.entrySet()) { + if (entry.getValue() == null) { + // means used the drag_edge attr, the no gravity child should be use set + mDragEdges.put(entry.getKey(), child); + break; + } + } + } + if (child.getParent() == this) { + return; + } + super.addView(child, index, params); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + + updateBottomViews(); + + if (mOnLayoutListeners != null) { + for (int i = 0; i < mOnLayoutListeners.size(); i++) { + mOnLayoutListeners.get(i).onLayout(this); + } + } + } + + void layoutPullOut() { + View surfaceView = getSurfaceView(); + Rect surfaceRect = mViewBoundCache.get(surfaceView); + if (surfaceRect == null) { + surfaceRect = computeSurfaceLayoutArea(false); + } + if (surfaceView != null) { + surfaceView.layout(surfaceRect.left, surfaceRect.top, surfaceRect.right, surfaceRect.bottom); + bringChildToFront(surfaceView); + } + View currentBottomView = getCurrentBottomView(); + Rect bottomViewRect = mViewBoundCache.get(currentBottomView); + if (bottomViewRect == null) { + bottomViewRect = computeBottomLayoutAreaViaSurface(ShowMode.PullOut, surfaceRect); + } + if (currentBottomView != null) { + currentBottomView.layout(bottomViewRect.left, bottomViewRect.top, bottomViewRect.right, bottomViewRect.bottom); + } + } + + void layoutLayDown() { + View surfaceView = getSurfaceView(); + Rect surfaceRect = mViewBoundCache.get(surfaceView); + if (surfaceRect == null) { + surfaceRect = computeSurfaceLayoutArea(false); + } + if (surfaceView != null) { + surfaceView.layout(surfaceRect.left, surfaceRect.top, surfaceRect.right, surfaceRect.bottom); + bringChildToFront(surfaceView); + } + View currentBottomView = getCurrentBottomView(); + Rect bottomViewRect = mViewBoundCache.get(currentBottomView); + if (bottomViewRect == null) { + bottomViewRect = computeBottomLayoutAreaViaSurface(ShowMode.LayDown, surfaceRect); + } + if (currentBottomView != null) { + currentBottomView.layout(bottomViewRect.left, bottomViewRect.top, bottomViewRect.right, bottomViewRect.bottom); + } + } + + private boolean mIsBeingDragged; + + private void checkCanDrag(MotionEvent ev) { + if (mIsBeingDragged) { + return; + } + if (getOpenStatus() == Status.Middle) { + mIsBeingDragged = true; + return; + } + Status status = getOpenStatus(); + float distanceX = ev.getRawX() - sX; + float distanceY = ev.getRawY() - sY; + float angle = Math.abs(distanceY / distanceX); + angle = (float) Math.toDegrees(Math.atan(angle)); + if (getOpenStatus() == Status.Close) { + DragEdge dragEdge; + if (angle < 45) { + if (distanceX > 0 && isLeftSwipeEnabled()) { + dragEdge = DragEdge.Left; + } else if (distanceX < 0 && isRightSwipeEnabled()) { + dragEdge = DragEdge.Right; + } else { + return; + } + + } else { + if (distanceY > 0 && isTopSwipeEnabled()) { + dragEdge = DragEdge.Top; + } else if (distanceY < 0 && isBottomSwipeEnabled()) { + dragEdge = DragEdge.Bottom; + } else { + return; + } + } + setCurrentDragEdge(dragEdge); + } + + boolean doNothing = isDoNothing(status, distanceX, distanceY, angle); + mIsBeingDragged = !doNothing; + } + + private boolean isDoNothing(Status status, float distanceX, float distanceY, float angle) { + boolean doNothing = false; + if (mCurrentDragEdge == DragEdge.Right) { + boolean suitable = (status == Status.Open && distanceX > mTouchSlop) || (status == Status.Close && distanceX < -mTouchSlop); + suitable = suitable || (status == Status.Middle); + + if (angle > 30 || !suitable) { + doNothing = true; + } + } + + if (mCurrentDragEdge == DragEdge.Left) { + boolean suitable = (status == Status.Open && distanceX < -mTouchSlop) || (status == Status.Close && distanceX > mTouchSlop); + suitable = suitable || status == Status.Middle; + + if (angle > 30 || !suitable) { + doNothing = true; + } + } + + if (mCurrentDragEdge == DragEdge.Top) { + boolean suitable = (status == Status.Open && distanceY < -mTouchSlop) || (status == Status.Close && distanceY > mTouchSlop); + suitable = suitable || status == Status.Middle; + + if (angle < 60 || !suitable) { + doNothing = true; + } + } + + if (mCurrentDragEdge == DragEdge.Bottom) { + boolean suitable = (status == Status.Open && distanceY > mTouchSlop) || (status == Status.Close && distanceY < -mTouchSlop); + suitable = suitable || status == Status.Middle; + + if (angle < 60 || !suitable) { + doNothing = true; + } + } + return doNothing; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + if (!isSwipeEnabled()) { + return false; + } + if (mClickToClose && getOpenStatus() == Status.Open && isTouchOnSurface(ev)) { + return true; + } + for (SwipeDenier denier : mSwipeDeniers) { + if (denier != null && denier.shouldDenySwipe(ev)) { + return false; + } + } + + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mDragHelper.processTouchEvent(ev); + mIsBeingDragged = false; + sX = ev.getRawX(); + sY = ev.getRawY(); + // if the swipe is in middle state(scrolling), should intercept the touch + if (getOpenStatus() == Status.Middle) { + mIsBeingDragged = true; + } + break; + case MotionEvent.ACTION_MOVE: + boolean beforeCheck = mIsBeingDragged; + checkCanDrag(ev); + if (mIsBeingDragged) { + ViewParent parent = getParent(); + if (parent != null) { + parent.requestDisallowInterceptTouchEvent(true); + } + } + if (!beforeCheck && mIsBeingDragged) { + // let children has one chance to catch the touch, and request the swipe not intercept + // useful when swipeLayout wrap a swipeLayout or other gestural layout + return false; + } + break; + + case MotionEvent.ACTION_CANCEL: + mIsBeingDragged = false; + mDragHelper.processTouchEvent(ev); + break; + case MotionEvent.ACTION_UP: + mIsBeingDragged = false; + mDragHelper.processTouchEvent(ev); + break; + default: // handle other action, such as ACTION_POINTER_DOWN/UP + mDragHelper.processTouchEvent(ev); + } + return mIsBeingDragged; + } + + private float sX = -1; + private float sY = -1; + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!isSwipeEnabled()) { + return super.onTouchEvent(event); + } + + int action = event.getActionMasked(); + gestureDetector.onTouchEvent(event); + + switch (action) { + case MotionEvent.ACTION_DOWN: + mDragHelper.processTouchEvent(event); + sX = event.getRawX(); + sY = event.getRawY(); + checkCanDrag(event); + if (mIsBeingDragged) { + getParent().requestDisallowInterceptTouchEvent(true); + mDragHelper.processTouchEvent(event); + } + break; + case MotionEvent.ACTION_MOVE: { + // the drag state and the direction are already judged at onInterceptTouchEvent + checkCanDrag(event); + if (mIsBeingDragged) { + getParent().requestDisallowInterceptTouchEvent(true); + mDragHelper.processTouchEvent(event); + } + break; + } + case MotionEvent.ACTION_UP: + mIsBeingDragged = false; + mDragHelper.processTouchEvent(event); + break; + case MotionEvent.ACTION_CANCEL: + mIsBeingDragged = false; + mDragHelper.processTouchEvent(event); + break; + + default: // handle other action, such as ACTION_POINTER_DOWN/UP + mDragHelper.processTouchEvent(event); + } + + return super.onTouchEvent(event) || mIsBeingDragged || action == MotionEvent.ACTION_DOWN; + } + + public boolean isClickToClose() { + return mClickToClose; + } + + public void setClickToClose(boolean mClickToClose) { + this.mClickToClose = mClickToClose; + } + + public void setSwipeEnabled(boolean enabled) { + mSwipeEnabled = enabled; + } + + public boolean isSwipeEnabled() { + return mSwipeEnabled; + } + + public boolean isLeftSwipeEnabled() { + View bottomView = mDragEdges.get(DragEdge.Left); + return bottomView != null && bottomView.getParent() == this && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Left.ordinal()]; + } + + public void setLeftSwipeEnabled(boolean leftSwipeEnabled) { + this.mSwipesEnabled[DragEdge.Left.ordinal()] = leftSwipeEnabled; + } + + public boolean isRightSwipeEnabled() { + View bottomView = mDragEdges.get(DragEdge.Right); + return bottomView != null && bottomView.getParent() == this && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Right.ordinal()]; + } + + public void setRightSwipeEnabled(boolean rightSwipeEnabled) { + this.mSwipesEnabled[DragEdge.Right.ordinal()] = rightSwipeEnabled; + } + + public boolean isTopSwipeEnabled() { + View bottomView = mDragEdges.get(DragEdge.Top); + return bottomView != null && bottomView.getParent() == this && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Top.ordinal()]; + } + + public void setTopSwipeEnabled(boolean topSwipeEnabled) { + this.mSwipesEnabled[DragEdge.Top.ordinal()] = topSwipeEnabled; + } + + public boolean isBottomSwipeEnabled() { + View bottomView = mDragEdges.get(DragEdge.Bottom); + return bottomView != null && bottomView.getParent() == this && bottomView != getSurfaceView() && mSwipesEnabled[DragEdge.Bottom.ordinal()]; + } + + public void setBottomSwipeEnabled(boolean bottomSwipeEnabled) { + this.mSwipesEnabled[DragEdge.Bottom.ordinal()] = bottomSwipeEnabled; + } + + /*** + * Returns the percentage of revealing at which the view below should the view finish opening + * if it was already open before dragging + * + * @returns The percentage of view revealed to trigger, default value is 0.25 + */ + public float getWillOpenPercentAfterOpen() { + return mWillOpenPercentAfterOpen; + } + + /*** + * Allows to stablish at what percentage of revealing the view below should the view finish opening + * if it was already open before dragging + * + * @param willOpenPercentAfterOpen The percentage of view revealed to trigger, default value is 0.25 + */ + public void setWillOpenPercentAfterOpen(float willOpenPercentAfterOpen) { + this.mWillOpenPercentAfterOpen = willOpenPercentAfterOpen; + } + + /*** + * Returns the percentage of revealing at which the view below should the view finish opening + * if it was already closed before dragging + * + * @returns The percentage of view revealed to trigger, default value is 0.25 + */ + public float getWillOpenPercentAfterClose() { + return mWillOpenPercentAfterClose; + } + + /*** + * Allows to stablish at what percentage of revealing the view below should the view finish opening + * if it was already closed before dragging + * + * @param willOpenPercentAfterClose The percentage of view revealed to trigger, default value is 0.75 + */ + public void setWillOpenPercentAfterClose(float willOpenPercentAfterClose) { + this.mWillOpenPercentAfterClose = willOpenPercentAfterClose; + } + + private boolean insideAdapterView() { + return getAdapterView() != null; + } + + private AdapterView getAdapterView() { + ViewParent t = getParent(); + if (t instanceof AdapterView) { + return (AdapterView) t; + } + return null; + } + + public void performAdapterViewItemClick() { + if (getOpenStatus() != Status.Close) { + return; + } + ViewParent t = getParent(); + if (t instanceof AdapterView) { + AdapterView view = (AdapterView) t; + int p = view.getPositionForView(SwipeLayout.this); + if (p != AdapterView.INVALID_POSITION) { + view.performItemClick(view.getChildAt(p - view.getFirstVisiblePosition()), p, view.getAdapter().getItemId(p)); + } + } + } + + private boolean performAdapterViewItemLongClick() { + if (getOpenStatus() != Status.Close) { + return false; + } + ViewParent t = getParent(); + if (t instanceof AdapterView) { + AdapterView view = (AdapterView) t; + int p = view.getPositionForView(SwipeLayout.this); + if (p == AdapterView.INVALID_POSITION) { + return false; + } + long vId = view.getItemIdAtPosition(p); + boolean handled = false; + try { + Method m = AbsListView.class.getDeclaredMethod("performLongPress", View.class, int.class, long.class); + m.setAccessible(true); + handled = (boolean) m.invoke(view, SwipeLayout.this, p, vId); + + } catch (Exception e) { + e.printStackTrace(); + + if (view.getOnItemLongClickListener() != null) { + handled = view.getOnItemLongClickListener().onItemLongClick(view, SwipeLayout.this, p, vId); + } + if (handled) { + view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } + } + return handled; + } + return false; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (insideAdapterView()) { + if (clickListener == null) { + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + performAdapterViewItemClick(); + } + }); + } + if (longClickListener == null) { + setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + performAdapterViewItemLongClick(); + return true; + } + }); + } + } + } + + OnClickListener clickListener; + + @Override + public void setOnClickListener(OnClickListener l) { + super.setOnClickListener(l); + clickListener = l; + } + + OnLongClickListener longClickListener; + + @Override + public void setOnLongClickListener(OnLongClickListener l) { + super.setOnLongClickListener(l); + longClickListener = l; + } + + private Rect hitSurfaceRect; + + private boolean isTouchOnSurface(MotionEvent ev) { + View surfaceView = getSurfaceView(); + if (surfaceView == null) { + return false; + } + if (hitSurfaceRect == null) { + hitSurfaceRect = new Rect(); + } + surfaceView.getHitRect(hitSurfaceRect); + return hitSurfaceRect.contains((int) ev.getX(), (int) ev.getY()); + } + + private GestureDetector gestureDetector = new GestureDetector(getContext(), new SwipeDetector()); + + class SwipeDetector extends GestureDetector.SimpleOnGestureListener { + @Override + public boolean onSingleTapUp(MotionEvent e) { + if (isTouchOnSurface(e)) { + if (mClickToClose && getOpenStatus() != Status.Close) { + close(); + } else { + if (mDoubleClickListener != null) { + mDoubleClickListener.onClick(); + } + } + } + return super.onSingleTapUp(e); + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + if (mDoubleClickListener != null) { + View target; + View bottom = getCurrentBottomView(); + View surface = getSurfaceView(); + if (bottom != null && e.getX() > bottom.getLeft() && e.getX() < bottom.getRight() && e.getY() > bottom.getTop() + && e.getY() < bottom.getBottom()) { + target = bottom; + } else { + target = surface; + } + mDoubleClickListener.onDoubleClick(SwipeLayout.this, target == surface); + } + return true; + } + } + + /** + * set the drag distance, it will force set the bottom view's width or + * height via this value. + * + * @param max max distance in dp unit + */ + public void setDragDistance(int max) { + if (max < 0) { + max = 0; + } + mDragDistance = dp2px(max); + requestLayout(); + } + + /** + * There are 2 diffirent show mode. + * {@link com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.ShowMode}.PullOut and + * {@link com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.ShowMode}.LayDown. + * + * @param mode + */ + public void setShowMode(ShowMode mode) { + mShowMode = mode; + requestLayout(); + } + + public DragEdge getDragEdge() { + return mCurrentDragEdge; + } + + public int getDragDistance() { + return mDragDistance; + } + + public ShowMode getShowMode() { + return mShowMode; + } + + /** + * return null if there is no surface view(no children) + */ + public View getSurfaceView() { + if (getChildCount() == 0) { + return null; + } + return getChildAt(getChildCount() - 1); + } + + /** + * return null if there is no bottom view + */ + public View getCurrentBottomView() { + List bottoms = getBottomViews(); + if (mCurrentDragEdge.ordinal() < bottoms.size()) { + return bottoms.get(mCurrentDragEdge.ordinal()); + } + return null; + } + + /** + * @return all bottomViews: left, top, right, bottom (may null if the edge is not set) + */ + public List getBottomViews() { + ArrayList bottoms = new ArrayList(); + for (DragEdge dragEdge : DragEdge.values()) { + bottoms.add(mDragEdges.get(dragEdge)); + } + return bottoms; + } + + public enum Status { Middle, Open, Close } + + /** + * get the open status. + * + * @return {@link com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.Status} Open , Close or + * Middle. + */ + public Status getOpenStatus() { + View surfaceView = getSurfaceView(); + if (surfaceView == null) { + return Status.Close; + } + int surfaceLeft = surfaceView.getLeft(); + int surfaceTop = surfaceView.getTop(); + if (surfaceLeft == getPaddingLeft() && surfaceTop == getPaddingTop()) { + return Status.Close; + } + + if (surfaceLeft == (getPaddingLeft() - mDragDistance) || surfaceLeft == (getPaddingLeft() + mDragDistance) + || surfaceTop == (getPaddingTop() - mDragDistance) || surfaceTop == (getPaddingTop() + mDragDistance)) { + return Status.Open; + } + + return Status.Middle; + } + + /** + * Process the surface release event. + * + * @param xvel xVelocity + * @param yvel yVelocity + * @param isCloseBeforeDragged the open state before drag + */ + protected void processHandRelease(float xvel, float yvel, boolean isCloseBeforeDragged) { + float minVelocity = mDragHelper.getMinVelocity(); + View surfaceView = getSurfaceView(); + DragEdge currentDragEdge = mCurrentDragEdge; + if (currentDragEdge == null || surfaceView == null) { + return; + } + float willOpenPercent = (isCloseBeforeDragged ? mWillOpenPercentAfterClose : mWillOpenPercentAfterOpen); + if (currentDragEdge == DragEdge.Left) { + if (xvel > minVelocity) { + open(); + } else if (xvel < -minVelocity) { + close(); + } else { + float openPercent = 1f * getSurfaceView().getLeft() / mDragDistance; + if (openPercent > willOpenPercent) { + open(); + } else { + close(); + } + } + } else if (currentDragEdge == DragEdge.Right) { + if (xvel > minVelocity) { + close(); + } else if (xvel < -minVelocity) { + open(); + } else { + float openPercent = 1f * (-getSurfaceView().getLeft()) / mDragDistance; + if (openPercent > willOpenPercent) { + open(); + } else { + close(); + } + } + } else if (currentDragEdge == DragEdge.Top) { + if (yvel > minVelocity) { + open(); + } else if (yvel < -minVelocity) { + close(); + } else { + float openPercent = 1f * getSurfaceView().getTop() / mDragDistance; + if (openPercent > willOpenPercent) { + open(); + } else { + close(); + } + } + } else if (currentDragEdge == DragEdge.Bottom) { + if (yvel > minVelocity) { + close(); + } else if (yvel < -minVelocity) { + open(); + } else { + float openPercent = 1f * (-getSurfaceView().getTop()) / mDragDistance; + if (openPercent > willOpenPercent) { + open(); + } else { + close(); + } + } + } + } + + /** + * smoothly open surface. + */ + public void open() { + open(true, true); + } + + public void open(boolean smooth) { + open(smooth, true); + } + + public void open(boolean smooth, boolean notify) { + View surface = getSurfaceView(); + View bottom = getCurrentBottomView(); + if (surface == null) { + return; + } + int dx; + int dy; + Rect rect = computeSurfaceLayoutArea(true); + if (smooth) { + mDragHelper.smoothSlideViewTo(surface, rect.left, rect.top); + } else { + dx = rect.left - surface.getLeft(); + dy = rect.top - surface.getTop(); + surface.layout(rect.left, rect.top, rect.right, rect.bottom); + if (getShowMode() == ShowMode.PullOut) { + Rect bRect = computeBottomLayoutAreaViaSurface(ShowMode.PullOut, rect); + if (bottom != null) { + bottom.layout(bRect.left, bRect.top, bRect.right, bRect.bottom); + } + } + if (notify) { + dispatchRevealEvent(rect.left, rect.top, rect.right, rect.bottom); + dispatchSwipeEvent(rect.left, rect.top, dx, dy); + } else { + safeBottomView(); + } + } + invalidate(); + } + + public void open(DragEdge edge) { + setCurrentDragEdge(edge); + open(true, true); + } + + public void open(boolean smooth, DragEdge edge) { + setCurrentDragEdge(edge); + open(smooth, true); + } + + public void open(boolean smooth, boolean notify, DragEdge edge) { + setCurrentDragEdge(edge); + open(smooth, notify); + } + + /** + * smoothly close surface. + */ + public void close() { + close(true, true); + } + + public void close(boolean smooth) { + close(smooth, true); + } + + /** + * close surface + * + * @param smooth smoothly or not. + * @param notify if notify all the listeners. + */ + public void close(boolean smooth, boolean notify) { + View surface = getSurfaceView(); + if (surface == null) { + return; + } + int dx; + int dy; + if (smooth) { + mDragHelper.smoothSlideViewTo(getSurfaceView(), getPaddingLeft(), getPaddingTop()); + } else { + Rect rect = computeSurfaceLayoutArea(false); + dx = rect.left - surface.getLeft(); + dy = rect.top - surface.getTop(); + surface.layout(rect.left, rect.top, rect.right, rect.bottom); + if (notify) { + dispatchRevealEvent(rect.left, rect.top, rect.right, rect.bottom); + dispatchSwipeEvent(rect.left, rect.top, dx, dy); + } else { + safeBottomView(); + } + } + invalidate(); + } + + public void toggle() { + toggle(true); + } + + public void toggle(boolean smooth) { + if (getOpenStatus() == Status.Open) { + close(smooth); + } else if (getOpenStatus() == Status.Close) { + open(smooth); + } + } + + /** + * a helper function to compute the Rect area that surface will hold in. + * + * @param open open status or close status. + */ + private Rect computeSurfaceLayoutArea(boolean open) { + int l = getPaddingLeft(); + int t = getPaddingTop(); + if (open) { + if (mCurrentDragEdge == DragEdge.Left) { + l = getPaddingLeft() + mDragDistance; + } else if (mCurrentDragEdge == DragEdge.Right) { + l = getPaddingLeft() - mDragDistance; + } else if (mCurrentDragEdge == DragEdge.Top) { + t = getPaddingTop() + mDragDistance; + } else { + t = getPaddingTop() - mDragDistance; + } + } + return new Rect(l, t, l + getMeasuredWidth(), t + getMeasuredHeight()); + } + + private Rect computeBottomLayoutAreaViaSurface(ShowMode mode, Rect surfaceArea) { + Rect rect = surfaceArea; + View bottomView = getCurrentBottomView(); + + int bl = rect.left; + int bt = rect.top; + int br = rect.right; + int bb = rect.bottom; + if (mode == ShowMode.PullOut) { + if (mCurrentDragEdge == DragEdge.Left) { + bl = rect.left - mDragDistance; + } else if (mCurrentDragEdge == DragEdge.Right) { + bl = rect.right; + } else if (mCurrentDragEdge == DragEdge.Top) { + bt = rect.top - mDragDistance; + } else { + bt = rect.bottom; + } + + if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) { + bb = rect.bottom; + br = bl + (bottomView == null ? 0 : bottomView.getMeasuredWidth()); + } else { + bb = bt + (bottomView == null ? 0 : bottomView.getMeasuredHeight()); + br = rect.right; + } + } else if (mode == ShowMode.LayDown) { + if (mCurrentDragEdge == DragEdge.Left) { + br = bl + mDragDistance; + } else if (mCurrentDragEdge == DragEdge.Right) { + bl = br - mDragDistance; + } else if (mCurrentDragEdge == DragEdge.Top) { + bb = bt + mDragDistance; + } else { + bt = bb - mDragDistance; + } + } + return new Rect(bl, bt, br, bb); + } + + private Rect computeBottomLayDown(DragEdge dragEdge) { + int bl = getPaddingLeft(); + int bt = getPaddingTop(); + int br; + int bb; + if (dragEdge == DragEdge.Right) { + bl = getMeasuredWidth() - mDragDistance; + } else if (dragEdge == DragEdge.Bottom) { + bt = getMeasuredHeight() - mDragDistance; + } + if (dragEdge == DragEdge.Left || dragEdge == DragEdge.Right) { + br = bl + mDragDistance; + bb = bt + getMeasuredHeight(); + } else { + br = bl + getMeasuredWidth(); + bb = bt + mDragDistance; + } + return new Rect(bl, bt, br, bb); + } + + public void setOnDoubleClickListener(DoubleClickListener doubleClickListener) { + mDoubleClickListener = doubleClickListener; + } + + public interface DoubleClickListener { + void onDoubleClick(SwipeLayout layout, boolean surface); + + void onClick(); + } + + private int dp2px(float dp) { + return (int) (dp * getContext().getResources().getDisplayMetrics().density + 0.5f); + } + + /** + * Deprecated, use {@link #setDrag(com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.DragEdge, android.view.View)} + */ + @Deprecated + public void setDragEdge(DragEdge dragEdge) { + clearDragEdge(); + if (getChildCount() >= 2) { + mDragEdges.put(dragEdge, getChildAt(getChildCount() - 2)); + } + setCurrentDragEdge(dragEdge); + } + + public void onViewRemoved(View child) { + for (Map.Entry entry : new HashMap(mDragEdges).entrySet()) { + if (entry.getValue() == child) { + mDragEdges.remove(entry.getKey()); + } + } + } + + public Map getDragEdgeMap() { + return mDragEdges; + } + + /** + * Deprecated, use {@link #getDragEdgeMap()} + */ + @Deprecated + public List getDragEdges() { + return new ArrayList(mDragEdges.keySet()); + } + + /** + * Deprecated, use {@link #setDrag(com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.DragEdge, android.view.View)} + */ + @Deprecated + public void setDragEdges(List dragEdges) { + clearDragEdge(); + for (int i = 0, size = Math.min(dragEdges.size(), getChildCount() - 1); i < size; i++) { + DragEdge dragEdge = dragEdges.get(i); + mDragEdges.put(dragEdge, getChildAt(i)); + } + if (dragEdges.size() == 0 || dragEdges.contains(DefaultDragEdge)) { + setCurrentDragEdge(DefaultDragEdge); + } else { + setCurrentDragEdge(dragEdges.get(0)); + } + } + + /** + * Deprecated, use {@link #addDrag(com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.DragEdge, android.view.View)} + */ + @Deprecated + public void setDragEdges(DragEdge... mDragEdges) { + clearDragEdge(); + setDragEdges(Arrays.asList(mDragEdges)); + } + + /** + * Deprecated, use {@link #addDrag(com.tencent.qcloud.tuikit.timcommon.component.swipe.SwipeLayout.DragEdge, android.view.View)} + * When using multiple drag edges it's a good idea to pass the ids of the views that + * you're using for the left, right, top bottom views (-1 if you're not using a particular view) + */ + @Deprecated + public void setBottomViewIds(int leftId, int rightId, int topId, int bottomId) { + addDrag(DragEdge.Left, findViewById(leftId)); + addDrag(DragEdge.Right, findViewById(rightId)); + addDrag(DragEdge.Top, findViewById(topId)); + addDrag(DragEdge.Bottom, findViewById(bottomId)); + } + + private float getCurrentOffset() { + if (mCurrentDragEdge == null) { + return 0; + } + return mEdgeSwipesOffset[mCurrentDragEdge.ordinal()]; + } + + private void setCurrentDragEdge(DragEdge dragEdge) { + mCurrentDragEdge = dragEdge; + updateBottomViews(); + } + + private void updateBottomViews() { + View currentBottomView = getCurrentBottomView(); + if (currentBottomView != null) { + if (mCurrentDragEdge == DragEdge.Left || mCurrentDragEdge == DragEdge.Right) { + mDragDistance = currentBottomView.getMeasuredWidth() - dp2px(getCurrentOffset()); + } else { + mDragDistance = currentBottomView.getMeasuredHeight() - dp2px(getCurrentOffset()); + } + } + + if (mShowMode == ShowMode.PullOut) { + layoutPullOut(); + } else if (mShowMode == ShowMode.LayDown) { + layoutLayDown(); + } + + safeBottomView(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/IPlayer.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/IPlayer.java new file mode 100644 index 0000000..5430794 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/IPlayer.java @@ -0,0 +1,74 @@ +package com.tencent.qcloud.tuikit.timcommon.component.videoview; + +import android.content.Context; +import android.net.Uri; +import android.view.Surface; +import android.view.SurfaceHolder; + +import java.io.IOException; + +public interface IPlayer { + void setOnPreparedListener(final OnPreparedListener l); + + void setOnErrorListener(final OnErrorListener l); + + void setOnCompletionListener(final OnCompletionListener l); + + void setOnVideoSizeChangedListener(final OnVideoSizeChangedListener l); + + void setOnSeekCompleteListener(final OnSeekCompleteListener l); + + void setOnInfoListener(final OnInfoListener l); + + void setDisplay(SurfaceHolder sh); + + void setSurface(Surface sh); + + void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException; + + void prepareAsync(); + + void release(); + + void start(); + + void stop(); + + void pause(); + + boolean isPlaying(); + + int getVideoWidth(); + + int getVideoHeight(); + + void seekTo(int progress); + + int getCurrentPosition(); + + int getDuration(); + + interface OnPreparedListener { + void onPrepared(IPlayer mp); + } + + interface OnErrorListener { + boolean onError(IPlayer mp, int what, int extra); + } + + interface OnCompletionListener { + void onCompletion(IPlayer mp); + } + + interface OnVideoSizeChangedListener { + void onVideoSizeChanged(IPlayer mp, int width, int height); + } + + interface OnInfoListener { + void onInfo(IPlayer mp, int what, int extra); + } + + interface OnSeekCompleteListener { + void onSeekComplete(IPlayer mp); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/MediaPlayerProxy.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/MediaPlayerProxy.java new file mode 100644 index 0000000..2791348 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/MediaPlayerProxy.java @@ -0,0 +1,120 @@ +package com.tencent.qcloud.tuikit.timcommon.component.videoview; + +import android.content.Context; +import android.net.Uri; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; + +import java.io.IOException; + +public class MediaPlayerProxy implements IPlayer { + private static final String TAG = MediaPlayerProxy.class.getSimpleName(); + + private IPlayer mMediaPlayer; + + public MediaPlayerProxy() { + mMediaPlayer = new SystemMediaPlayerWrapper(); + Log.i(TAG, "use mMediaPlayer: " + mMediaPlayer); + } + + @Override + public void setOnPreparedListener(final OnPreparedListener l) { + mMediaPlayer.setOnPreparedListener(l); + } + + @Override + public void setOnErrorListener(final OnErrorListener l) { + mMediaPlayer.setOnErrorListener(l); + } + + @Override + public void setOnCompletionListener(final OnCompletionListener l) { + mMediaPlayer.setOnCompletionListener(l); + } + + @Override + public void setOnVideoSizeChangedListener(final OnVideoSizeChangedListener l) { + mMediaPlayer.setOnVideoSizeChangedListener(l); + } + + @Override + public void setOnSeekCompleteListener(OnSeekCompleteListener l) { + mMediaPlayer.setOnSeekCompleteListener(l); + } + + @Override + public void setOnInfoListener(final OnInfoListener l) { + mMediaPlayer.setOnInfoListener(l); + } + + @Override + public void setDisplay(SurfaceHolder sh) { + mMediaPlayer.setDisplay(sh); + } + + @Override + public void setSurface(Surface sh) { + mMediaPlayer.setSurface(sh); + } + + @Override + public void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { + mMediaPlayer.setDataSource(context, uri); + } + + @Override + public void prepareAsync() { + mMediaPlayer.prepareAsync(); + } + + @Override + public void release() { + mMediaPlayer.release(); + } + + @Override + public void start() { + mMediaPlayer.start(); + } + + @Override + public void stop() { + mMediaPlayer.stop(); + } + + @Override + public void pause() { + mMediaPlayer.pause(); + } + + @Override + public boolean isPlaying() { + return mMediaPlayer.isPlaying(); + } + + @Override + public int getVideoWidth() { + return mMediaPlayer.getVideoWidth(); + } + + @Override + public int getVideoHeight() { + return mMediaPlayer.getVideoHeight(); + } + + @Override + public void seekTo(int progress) { + mMediaPlayer.seekTo(progress); + } + + @Override + public int getCurrentPosition() { + return mMediaPlayer.getCurrentPosition(); + } + + @Override + public int getDuration() { + return mMediaPlayer.getDuration(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/SystemMediaPlayerWrapper.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/SystemMediaPlayerWrapper.java new file mode 100644 index 0000000..936b4d5 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/SystemMediaPlayerWrapper.java @@ -0,0 +1,153 @@ +package com.tencent.qcloud.tuikit.timcommon.component.videoview; + +import android.content.Context; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Build; +import android.view.Surface; +import android.view.SurfaceHolder; + +import java.io.IOException; + +public class SystemMediaPlayerWrapper implements IPlayer { + private MediaPlayer mMediaPlayer; + + public SystemMediaPlayerWrapper() { + mMediaPlayer = new MediaPlayer(); + } + + @Override + public void setOnPreparedListener(final OnPreparedListener l) { + mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mp) { + l.onPrepared(SystemMediaPlayerWrapper.this); + } + }); + } + + @Override + public void setOnErrorListener(final OnErrorListener l) { + mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { + @Override + public boolean onError(MediaPlayer mp, int what, int extra) { + return l.onError(SystemMediaPlayerWrapper.this, what, extra); + } + }); + } + + @Override + public void setOnCompletionListener(final OnCompletionListener l) { + mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mp) { + l.onCompletion(SystemMediaPlayerWrapper.this); + } + }); + } + + @Override + public void setOnSeekCompleteListener(final OnSeekCompleteListener l) { + mMediaPlayer.setOnSeekCompleteListener(new MediaPlayer.OnSeekCompleteListener() { + @Override + public void onSeekComplete(MediaPlayer mediaPlayer) { + l.onSeekComplete(SystemMediaPlayerWrapper.this); + } + }); + } + + @Override + public void setOnVideoSizeChangedListener(final OnVideoSizeChangedListener l) { + mMediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() { + @Override + public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { + l.onVideoSizeChanged(SystemMediaPlayerWrapper.this, width, height); + } + }); + } + + @Override + public void setOnInfoListener(final OnInfoListener l) { + mMediaPlayer.setOnInfoListener(new MediaPlayer.OnInfoListener() { + @Override + public boolean onInfo(MediaPlayer mp, int what, int extra) { + l.onInfo(SystemMediaPlayerWrapper.this, what, extra); + return false; + } + }); + } + + @Override + public void setDisplay(SurfaceHolder sh) { + mMediaPlayer.setDisplay(sh); + } + + @Override + public void setSurface(Surface sh) { + mMediaPlayer.setSurface(sh); + } + + @Override + public void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { + mMediaPlayer.setDataSource(context, uri); + } + + @Override + public void prepareAsync() { + mMediaPlayer.prepareAsync(); + } + + @Override + public void release() { + mMediaPlayer.release(); + } + + @Override + public void start() { + mMediaPlayer.start(); + } + + @Override + public void stop() { + mMediaPlayer.stop(); + } + + @Override + public void pause() { + mMediaPlayer.pause(); + } + + @Override + public boolean isPlaying() { + return mMediaPlayer.isPlaying(); + } + + @Override + public int getVideoWidth() { + return mMediaPlayer.getVideoWidth(); + } + + @Override + public int getVideoHeight() { + return mMediaPlayer.getVideoHeight(); + } + + @Override + public void seekTo(int progress) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + mMediaPlayer.seekTo(progress, MediaPlayer.SEEK_CLOSEST); + } else { + mMediaPlayer.seekTo(progress); + } + } + + @Override + public int getCurrentPosition() { + return mMediaPlayer.getCurrentPosition(); + } + + @Override + public int getDuration() { + return mMediaPlayer.getDuration(); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/VideoGestureScaleAttacher.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/VideoGestureScaleAttacher.java new file mode 100644 index 0000000..429d25b --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/VideoGestureScaleAttacher.java @@ -0,0 +1,441 @@ +package com.tencent.qcloud.tuikit.timcommon.component.videoview; + +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.TextureView; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewParent; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.Interpolator; +import android.widget.ImageView; +import android.widget.OverScroller; +import com.tencent.qcloud.tuikit.timcommon.util.ThreadUtils; + +public class VideoGestureScaleAttacher { + private static final float EDGE_DRAG_EVENT_INTERCEPT_THRESHOLD = 50f; + private static final int EDGE_NONE = -1; + private static final int EDGE_LEFT = 0; + private static final int EDGE_RIGHT = 1; + private static final int EDGE_BOTH = 2; + private static final float DEFAULT_MAX_SCALE = 3.0f; + private static final float DEFAULT_MID_SCALE = 1.75f; + private static final float DEFAULT_MIN_SCALE = 1.0f; + private static final int DEFAULT_ZOOM_DURATION = 200; + private ScaleGestureDetector scaleGestureDetector; + private float mTouchSlop; + private float mMinimumVelocity; + private VelocityTracker mVelocityTracker; + private boolean mIsDragging; + private float mLastTouchX; + private float mLastTouchY; + private OnScaleListener internalScaleListener; + private VideoView view; + private float minScale = DEFAULT_MIN_SCALE; + private float middleScale = DEFAULT_MID_SCALE; + private float maxScale = DEFAULT_MAX_SCALE; + private int scrollEdge; + private final Matrix transferMatrix = new Matrix(); + private final RectF rectF = new RectF(); + private final float[] mMatrixValues = new float[9]; + private ImageView.ScaleType scaleType = ImageView.ScaleType.FIT_CENTER; + private final Interpolator interpolator = new AccelerateDecelerateInterpolator(); + + private int zoomDuration = DEFAULT_ZOOM_DURATION; + + private FlingRunnable currentFlingRunnable; + + private VideoGestureScaleAttacher() {} + + public static void attach(VideoView view) { + VideoGestureScaleAttacher attacher = new VideoGestureScaleAttacher(); + attacher.view = view; + if (view == null || view.getContext() == null) { + return; + } + Context context = view.getContext(); + final ViewConfiguration configuration = ViewConfiguration.get(context); + attacher.mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); + attacher.mTouchSlop = configuration.getScaledTouchSlop(); + attacher.internalScaleListener = new OnScaleListener() { + @Override + public boolean onScale(ScaleGestureDetector detector) { + float scaleFactor = detector.getScaleFactor(); + if (Float.isNaN(scaleFactor) || Float.isInfinite(scaleFactor)) { + return false; + } + float focusX = detector.getFocusX(); + float focusY = detector.getFocusY(); + return attacher.scale(scaleFactor, focusX, focusY); + } + + @Override + public void onFling(float startX, float startY, float velocityX, float velocityY) { + if (attacher.scaleGestureDetector.isInProgress()) { + return; + } + attacher.currentFlingRunnable = attacher.new FlingRunnable(attacher.view); + attacher.currentFlingRunnable.fling(attacher.getViewWidth(), attacher.getViewHeight(), (int) velocityX, (int) velocityY); + ThreadUtils.runOnUiThread(attacher.currentFlingRunnable); + } + + @Override + public void onDrag(float dx, float dy) { + if (attacher.scaleGestureDetector.isInProgress()) { + return; + } + attacher.transferMatrix.postTranslate(dx, dy); + attacher.invalidateView(); + attacher.checkMatrixBounds(); + if (attacher.scrollEdge == EDGE_BOTH && Math.abs(dx) >= EDGE_DRAG_EVENT_INTERCEPT_THRESHOLD + || (attacher.scrollEdge == EDGE_LEFT && dx >= EDGE_DRAG_EVENT_INTERCEPT_THRESHOLD) + || (attacher.scrollEdge == EDGE_RIGHT && dx <= -EDGE_DRAG_EVENT_INTERCEPT_THRESHOLD)) { + ViewParent viewParent = view.getParent(); + if (viewParent != null) { + viewParent.requestDisallowInterceptTouchEvent(false); + } + } else { + ViewParent viewParent = view.getParent(); + if (viewParent != null) { + viewParent.requestDisallowInterceptTouchEvent(true); + } + } + } + }; + attacher.scaleGestureDetector = new ScaleGestureDetector(context, attacher.internalScaleListener); + + view.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + attacher.scaleGestureDetector.onTouchEvent(event); + attacher.processTouchEvent(event); + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: { + attacher.cancelFling(); + ViewParent viewParent = view.getParent(); + if (viewParent != null) { + viewParent.requestDisallowInterceptTouchEvent(true); + } + break; + } + case MotionEvent.ACTION_CANCEL: + onActionCancel(); + break; + case MotionEvent.ACTION_UP: { + onActionCancel(); + break; + } + default: + break; + } + return true; + } + + private void onActionCancel() { + if (attacher.getScale() < attacher.minScale) { + RectF rect = attacher.getDisplayRect(); + view.post(attacher.new AnimatedZoomRunnable(attacher.getScale(), attacher.minScale, rect.centerX(), rect.centerY())); + } else if (attacher.getScale() > attacher.maxScale) { + RectF rect = attacher.getDisplayRect(); + view.post(attacher.new AnimatedZoomRunnable(attacher.getScale(), attacher.maxScale, rect.centerX(), rect.centerY())); + } + } + }); + } + + public void cancelFling() { + if (currentFlingRunnable != null) { + currentFlingRunnable.cancelFling(); + currentFlingRunnable = null; + } + } + + private boolean scale(float scaleFactor, float focusX, float focusY) { + if ((getScale() <= maxScale || scaleFactor < 1f) && (getScale() >= minScale || scaleFactor > 1f)) { + transferMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY); + invalidateView(); + checkMatrixBounds(); + return true; + } + return false; + } + + private void invalidateView() { + Matrix baseMatrix = view.getBaseMatrix(); + Matrix matrix = new Matrix(); + matrix.setConcat(baseMatrix, transferMatrix); + view.setTransform(matrix); + view.invalidate(); + } + + private boolean processTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + + mVelocityTracker = VelocityTracker.obtain(); + if (null != mVelocityTracker) { + mVelocityTracker.addMovement(ev); + } + + mLastTouchX = ev.getX(); + mLastTouchY = ev.getY(); + mIsDragging = false; + + break; + case MotionEvent.ACTION_MOVE: + final float x = ev.getX(); + final float y = ev.getY(); + final float dx = x - mLastTouchX; + final float 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) { + internalScaleListener.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 = ev.getX(); + mLastTouchY = ev.getY(); + + // Compute velocity within the last 1000ms + mVelocityTracker.addMovement(ev); + mVelocityTracker.computeCurrentVelocity(1000); + + final float vX = mVelocityTracker.getXVelocity(); + final float vY = mVelocityTracker.getYVelocity(); + + // If the velocity is greater than minVelocity, call + // listener + if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) { + internalScaleListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY); + } + } + } + + // Recycle Velocity Tracker + if (null != mVelocityTracker) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + break; + default: + break; + } + return true; + } + + private RectF getDisplayRect() { + rectF.set(0, 0, view.getWidth(), view.getHeight()); + Matrix matrix = new Matrix(); + view.getTransform(matrix); + matrix.mapRect(rectF); + return rectF; + } + + public float getScale() { + return (float) Math.sqrt((Math.pow(getValue(transferMatrix, Matrix.MSCALE_X), 2) + Math.pow(getValue(transferMatrix, Matrix.MSCALE_Y), 2)) / 2); + } + + private void checkMatrixBounds() { + final RectF rect = getDisplayRect(); + final float height = rect.height(); + final float width = rect.width(); + float deltaX = 0; + float deltaY = 0; + final int viewHeight = getViewHeight(); + if (height <= viewHeight) { + switch (scaleType) { + 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 = getViewWidth(); + if (width <= viewWidth) { + switch (scaleType) { + case FIT_START: + deltaX = -rect.left; + break; + case FIT_END: + deltaX = viewWidth - width - rect.left; + break; + default: + deltaX = (viewWidth - width) / 2 - rect.left; + break; + } + scrollEdge = EDGE_BOTH; + } else if (rect.left > 0) { + scrollEdge = EDGE_LEFT; + deltaX = -rect.left; + } else if (rect.right < viewWidth) { + deltaX = viewWidth - rect.right; + scrollEdge = EDGE_RIGHT; + } else { + scrollEdge = EDGE_NONE; + } + // Finally actually translate the matrix + transferMatrix.postTranslate(deltaX, deltaY); + invalidateView(); + } + + private int getViewWidth() { + return view.getWidth() - view.getPaddingLeft() - view.getPaddingRight(); + } + + private int getViewHeight() { + return view.getHeight() - view.getPaddingTop() - view.getPaddingBottom(); + } + + /** + * 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 returned value + */ + private float getValue(Matrix matrix, int whichValue) { + matrix.getValues(mMatrixValues); + return mMatrixValues[whichValue]; + } + + private class AnimatedZoomRunnable implements Runnable { + private final float focalX; + private final float focalY; + private final long startTime; + private final float zoomStart; + private final float zoomEnd; + + public AnimatedZoomRunnable(final float currentZoom, final float targetZoom, final float focalX, final float focalY) { + this.focalX = focalX; + this.focalY = focalY; + startTime = System.currentTimeMillis(); + zoomStart = currentZoom; + zoomEnd = targetZoom; + } + + @Override + public void run() { + float t = interpolate(); + float scale = zoomStart + t * (zoomEnd - zoomStart); + float deltaScale = scale / getScale(); + scale(deltaScale, focalX, focalY); + // We haven't hit our target scale yet, so post ourselves again + if (t < 1f) { + view.postOnAnimation(this); + } + } + + private float interpolate() { + float t = 1f * (System.currentTimeMillis() - startTime) / zoomDuration; + t = Math.min(1f, t); + t = interpolator.getInterpolation(t); + return t; + } + } + + public class FlingRunnable implements Runnable { + private final OverScroller mScroller; + private int mCurrentX; + private int mCurrentY; + private TextureView view; + + public FlingRunnable(TextureView view) { + mScroller = new OverScroller(view.getContext()); + this.view = view; + } + + public void cancelFling() { + mScroller.forceFinished(true); + } + + public void fling(int viewWidth, int viewHeight, int velocityX, int velocityY) { + final RectF rect = getDisplayRect(); + if (rect == null) { + return; + } + final int startX = Math.round(-rect.left); + final int minX; + final int maxX; + final int minY; + final int 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 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() { + if (mScroller.isFinished()) { + return; // remaining post that should not be handled + } + if (mScroller.computeScrollOffset()) { + final int newX = mScroller.getCurrX(); + final int newY = mScroller.getCurrY(); + transferMatrix.postTranslate(mCurrentX - newX, mCurrentY - newY); + invalidateView(); + checkMatrixBounds(); + mCurrentX = newX; + mCurrentY = newY; + // Post On animation + view.postOnAnimation(this); + } + } + } + + public static class OnScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + public void onFling(float startX, float startY, float velocityX, float velocityY) {} + + public void onDrag(float dx, float dy) {} + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/VideoView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/VideoView.java new file mode 100644 index 0000000..3c1eed8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/component/videoview/VideoView.java @@ -0,0 +1,324 @@ +package com.tencent.qcloud.tuikit.timcommon.component.videoview; + +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.SurfaceTexture; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.TextureView; + +import androidx.annotation.Nullable; + +public class VideoView extends TextureView { + private static final String TAG = VideoView.class.getSimpleName(); + + public static final int STATE_ERROR = -1; + public static final int STATE_IDLE = 0; + public static final int STATE_PREPARING = 1; + public static final int STATE_PREPARED = 2; + public static final int STATE_PLAYING = 3; + public static final int STATE_PAUSED = 4; + public static final int STATE_PLAYBACK_COMPLETED = 5; + public static final int STATE_STOPPED = 6; + + private int mCurrentState = STATE_IDLE; + + private Context mContext; + private Surface mSurface; + private MediaPlayerProxy mMediaPlayer; + + private Uri mUri; + private int mVideoRotationDegree; + private Matrix baseMatrix = new Matrix(); + private IPlayer.OnPreparedListener mOutOnPreparedListener; + private IPlayer.OnErrorListener mOutOnErrorListener; + private IPlayer.OnCompletionListener mOutOnCompletionListener; + private IPlayer.OnSeekCompleteListener mOnSeekCompleteListener; + private IPlayer.OnPreparedListener mOnPreparedListener = new IPlayer.OnPreparedListener() { + public void onPrepared(IPlayer mp) { + mCurrentState = STATE_PREPARED; + // Video fit center + float videoHeight = mp.getVideoHeight(); + float videoWidth = mp.getVideoWidth(); + float viewHeight = getHeight() - getPaddingBottom() - getPaddingTop(); + float viewWidth = getWidth() - getPaddingLeft() - getPaddingRight(); + + float finalVideoHeight = viewHeight; + float finalVideoWidth = viewHeight * videoWidth / videoHeight; + + if (finalVideoWidth > viewWidth) { + finalVideoWidth = viewWidth; + finalVideoHeight = viewWidth * videoHeight / videoWidth; + } + + float scaleX = finalVideoWidth / viewWidth; + float scaleY = finalVideoHeight / viewHeight; + float dx = (viewWidth - finalVideoWidth) / 2; + float dy = (viewHeight - finalVideoHeight) / 2; + Matrix matrix = new Matrix(); + matrix.postScale(scaleX, scaleY); + matrix.postTranslate(dx, dy); + baseMatrix.set(matrix); + setTransform(matrix); + invalidate(); + + Log.i(TAG, "onPrepared mVideoWidth: " + videoWidth + " mVideoHeight: " + videoHeight + " mVideoRotationDegree: " + mVideoRotationDegree); + if (mOutOnPreparedListener != null) { + mOutOnPreparedListener.onPrepared(mp); + } + } + }; + + private IPlayer.OnErrorListener mOnErrorListener = new IPlayer.OnErrorListener() { + public boolean onError(IPlayer mp, int what, int extra) { + Log.w(TAG, "onError: what/extra: " + what + "/" + extra); + mCurrentState = STATE_ERROR; + stopMedia(); + if (mOutOnErrorListener != null) { + mOutOnErrorListener.onError(mp, what, extra); + } + return true; + } + }; + private IPlayer.OnInfoListener mOnInfoListener = new IPlayer.OnInfoListener() { + public void onInfo(IPlayer mp, int what, int extra) { + Log.w(TAG, "onInfo: what/extra: " + what + "/" + extra); + if (what == 10001) { // IJK: MEDIA_INFO_VIDEO_ROTATION_CHANGED + mVideoRotationDegree = extra; + setRotation(mVideoRotationDegree); + requestLayout(); + } + } + }; + private IPlayer.OnCompletionListener mOnCompletionListener = new IPlayer.OnCompletionListener() { + public void onCompletion(IPlayer mp) { + Log.i(TAG, "onCompletion"); + mCurrentState = STATE_PLAYBACK_COMPLETED; + if (mOutOnCompletionListener != null) { + mOutOnCompletionListener.onCompletion(mp); + } + } + }; + private IPlayer.OnVideoSizeChangedListener mOnVideoSizeChangedListener = new IPlayer.OnVideoSizeChangedListener() { + @Override + public void onVideoSizeChanged(IPlayer mp, int width, int height) { + // TUIChatLog.i(TAG, "onVideoSizeChanged width: " + width + " height: " + height); + } + }; + + private IPlayer.OnSeekCompleteListener onSeekCompleteListener = new IPlayer.OnSeekCompleteListener() { + @Override + public void onSeekComplete(IPlayer mp) { + if (mOnSeekCompleteListener != null) { + mOnSeekCompleteListener.onSeekComplete(mp); + } + } + }; + private SurfaceTextureListener mSurfaceTextureListener = new SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + Log.i(TAG, "onSurfaceTextureAvailable"); + mSurface = new Surface(surface); + openVideo(); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + Log.i(TAG, "onSurfaceTextureSizeChanged"); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + Log.i(TAG, "onSurfaceTextureDestroyed"); + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + // TUIChatLog.i(TAG,"onSurfaceTextureUpdated"); + } + }; + + public VideoView(Context context) { + super(context); + initVideoView(context); + } + + public VideoView(Context context, AttributeSet attrs) { + super(context, attrs); + initVideoView(context); + } + + public VideoView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initVideoView(context); + } + + private void initVideoView(Context context) { + Log.i(TAG, "initVideoView"); + mContext = context; + setSurfaceTextureListener(mSurfaceTextureListener); + mCurrentState = STATE_IDLE; + + VideoGestureScaleAttacher.attach(this); + } + + public Matrix getBaseMatrix() { + return baseMatrix; + } + + public void setOnPreparedListener(IPlayer.OnPreparedListener l) { + mOutOnPreparedListener = l; + } + + public void setOnSeekCompleteListener(IPlayer.OnSeekCompleteListener l) { + mOnSeekCompleteListener = l; + } + + public void setOnErrorListener(IPlayer.OnErrorListener l) { + mOutOnErrorListener = l; + } + + public void setOnCompletionListener(IPlayer.OnCompletionListener l) { + mOutOnCompletionListener = l; + } + + public void setVideoURI(Uri uri) { + mUri = uri; + openVideo(); + } + + public void resetVideo() { + openVideo(); + } + + private void openVideo() { + if (mUri == null) { + Log.e(TAG, "openVideo: mUri is null "); + return; + } + Log.i(TAG, "openVideo: mUri: " + mUri.getPath() + " mSurface: " + mSurface); + if (mSurface == null) { + Log.e(TAG, "openVideo: mSurface is null "); + return; + } + + stopMedia(); + try { + mMediaPlayer = new MediaPlayerProxy(); + mMediaPlayer.setOnPreparedListener(mOnPreparedListener); + mMediaPlayer.setOnCompletionListener(mOnCompletionListener); + mMediaPlayer.setOnErrorListener(mOnErrorListener); + mMediaPlayer.setOnInfoListener(mOnInfoListener); + mMediaPlayer.setOnVideoSizeChangedListener(mOnVideoSizeChangedListener); + mMediaPlayer.setOnSeekCompleteListener(onSeekCompleteListener); + mMediaPlayer.setSurface(mSurface); + mMediaPlayer.setDataSource(getContext(), mUri); + mMediaPlayer.prepareAsync(); + mCurrentState = STATE_PREPARING; + } catch (Exception ex) { + Log.w(TAG, "ex = " + ex.getMessage()); + mCurrentState = STATE_ERROR; + } + } + + public boolean start() { + Log.i(TAG, "start mCurrentState:" + mCurrentState); + if (mMediaPlayer != null) { + mMediaPlayer.start(); + mCurrentState = STATE_PLAYING; + } + return true; + } + + public boolean stop() { + Log.i(TAG, "stop mCurrentState:" + mCurrentState); + stopMedia(); + return true; + } + + public boolean pause() { + Log.i(TAG, "pause mCurrentState:" + mCurrentState); + if (mMediaPlayer != null) { + mMediaPlayer.pause(); + mCurrentState = STATE_PAUSED; + } + return true; + } + + public void stopMedia() { + if (mMediaPlayer != null) { + mMediaPlayer.stop(); + mMediaPlayer.release(); + mMediaPlayer = null; + mCurrentState = STATE_IDLE; + } + } + + public int getCurrentState() { + return mCurrentState; + } + + public boolean isPlaying() { + if (mMediaPlayer != null) { + return mMediaPlayer.isPlaying(); + } + return false; + } + + public void seekTo(int progress) { + if (mMediaPlayer != null) { + mMediaPlayer.seekTo(progress); + } + } + + public boolean isPrepared() { + if (mUri == null) { + Log.e(TAG, "isPrepared: mUri is null "); + return false; + } + Log.i(TAG, "isPrepared: mUri: " + mUri.getPath() + " mSurface: " + mSurface); + if (mSurface == null) { + Log.e(TAG, "isPrepared: mSurface is null "); + return false; + } + + return true; + } + + public int getCurrentPosition() { + if (mMediaPlayer != null) { + return mMediaPlayer.getCurrentPosition(); + } + return 0; + } + + public int getDuration() { + if (mMediaPlayer != null) { + return mMediaPlayer.getDuration(); + } + return 0; + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + stopMedia(); + } + + @Override + public void setBackgroundDrawable(Drawable background) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && background != null) { + super.setBackgroundDrawable(background); + } + } + + @Override + public void setOnClickListener(@Nullable OnClickListener l) { + super.setOnClickListener(l); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/config/classicui/TUIConfigClassic.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/config/classicui/TUIConfigClassic.java new file mode 100644 index 0000000..5c68325 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/config/classicui/TUIConfigClassic.java @@ -0,0 +1,336 @@ +package com.tencent.qcloud.tuikit.timcommon.config.classicui; + +import static com.tencent.qcloud.tuikit.timcommon.util.TUIUtil.newDrawable; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; + +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuicore.util.ToastUtil; +import com.tencent.qcloud.tuikit.timcommon.component.highlight.HighlightPresenter; + +public class TUIConfigClassic { + private TUIConfigClassic() {} + + private static final class TUIConfigClassicHolder { + private static final TUIConfigClassic INSTANCE = new TUIConfigClassic(); + } + + private static TUIConfigClassic getInstance() { + return TUIConfigClassicHolder.INSTANCE; + } + + public static final int UNDEFINED = -1; + // message bubble + private boolean enableMessageBubbleStyle = true; + private Drawable sendBubbleBackground; + private Drawable receiveBubbleBackground; + private Drawable sendErrorBubbleBackground; + private Drawable receiveErrorBubbleBackground; + + // message style + private Drawable chatTimeBubble; + private int chatTimeFontSize = UNDEFINED; + private int chatTimeFontColor = UNDEFINED; + private Drawable defaultAvatarImage; + private int avatarRadius = UNDEFINED; + private int avatarSize = UNDEFINED; + private int receiveNickNameVisibility = UNDEFINED; + private int receiveNickNameFontSize = UNDEFINED; + private int receiveNickNameColor = UNDEFINED; + + /** + * Set the default app directory.The default dir is "file". + * @param appDir + */ + public static void setDefaultAppDir(String appDir) { + TUIConfig.setDefaultAppDir(appDir); + TUIConfig.initPath(); + } + + /** + * Set whether show the toast prompt built into TUIKit.The default value is true. + * @param enableToast + */ + public static void enableToast(boolean enableToast) { + ToastUtil.setEnableToast(enableToast); + } + + /** + * Set whether to enable language switching.The default value is false. + * @param enableLanguageSwitch + */ + public static void enableLanguageSwitch(boolean enableLanguageSwitch) { + TUIThemeManager.setEnableLanguageSwitch(enableLanguageSwitch); + } + + /** + * Switch the language of TUIKit. + * The currently supported languages are "en", "zh", and "ar". + * @param context + * @param targetLanguage + */ + public static void switchLanguageToTarget(Context context, String targetLanguage) { + TUIThemeManager.getInstance().changeLanguage(context, targetLanguage); + } + + /** + * Switch theme to target. + * The currently supported themes are THEME_LIGHT, THEME_LIVELY, and THEME_SERIOUS. + * @param context + * @param themeID + */ + public static void switchThemeToTarget(Context context, int themeID) { + TUIThemeManager.getInstance().changeTheme(context, themeID); + } + + /** + * Set whether to enable message bubble style.The default value is true. + * @param enable + */ + public static void setEnableMessageBubbleStyle(boolean enable) { + getInstance().enableMessageBubbleStyle = enable; + } + + /** + * Get whether to enable message bubble style. + * @return true is enable, false is not + */ + public static boolean isEnableMessageBubbleStyle() { + return getInstance().enableMessageBubbleStyle; + } + + /** + * Set the background of the send message bubble. + * @param drawable + */ + public static void setSendBubbleBackground(Drawable drawable) { + getInstance().sendBubbleBackground = drawable; + } + + /** + * Get the background of the send message bubble. + * @return the background + */ + public static Drawable getSendBubbleBackground() { + return newDrawable(getInstance().sendBubbleBackground); + } + + /** + * Set the background of the receive message bubble. + * @param drawable + */ + public static void setReceiveBubbleBackground(Drawable drawable) { + getInstance().receiveBubbleBackground = drawable; + } + + /** + * Get the background of the receive message bubble. + * @return the background + */ + public static Drawable getReceiveBubbleBackground() { + return newDrawable(getInstance().receiveBubbleBackground); + } + + /** + * Set the background of the receive error message bubble. + * @param receiveErrorBubbleBackground + */ + public static void setReceiveErrorBubbleBackground(Drawable receiveErrorBubbleBackground) { + getInstance().receiveErrorBubbleBackground = receiveErrorBubbleBackground; + } + + /** + * Get the background of the receive error message bubble. + * @return the background + */ + public static Drawable getReceiveErrorBubbleBackground() { + return newDrawable(getInstance().receiveErrorBubbleBackground); + } + + /** + * Set the background of the send error message bubble. + * @param sendErrorBubbleBackground + */ + public static void setSendErrorBubbleBackground(Drawable sendErrorBubbleBackground) { + getInstance().sendErrorBubbleBackground = sendErrorBubbleBackground; + } + + /** + * Get the background of the send error message bubble. + * @return the background + */ + public static Drawable getSendErrorBubbleBackground() { + return newDrawable(getInstance().sendErrorBubbleBackground); + } + + /** + * Set the light color of the message bubble in highlight status.. + * @param color + */ + public static void setBubbleHighlightLightColor(int color) { + HighlightPresenter.setHighlightLightColor(color); + } + + /** + * Set the dark color of the message bubble in highlight status.. + * @param color + */ + public static void setBubbleHighlightDarkColor(int color) { + HighlightPresenter.setHighlightDarkColor(color); + } + + /** + * Set the chat time bubble. + * @param drawable + */ + public static void setChatTimeBubble(Drawable drawable) { + getInstance().chatTimeBubble = drawable; + } + + /** + * Get the chat time bubble. + * @return + */ + public static Drawable getChatTimeBubble() { + return newDrawable(getInstance().chatTimeBubble); + } + + /** + * Set the font size of the chat time text. + * @param size + */ + public static void setChatTimeFontSize(int size) { + getInstance().chatTimeFontSize = size; + } + + /** + * Get the font size of the chat time text. + * @return + */ + public static int getChatTimeFontSize() { + return getInstance().chatTimeFontSize; + } + + /** + * Set the font color of the chat time text. + * @param color + */ + public static void setChatTimeFontColor(int color) { + getInstance().chatTimeFontColor = color; + } + + /** + * Get the font color of the chat time text. + * @return + */ + public static int getChatTimeFontColor() { + return getInstance().chatTimeFontColor; + } + + /** + * Set the default avatar image. + * @param drawable + */ + public static void setDefaultAvatarImage(Drawable drawable) { + getInstance().defaultAvatarImage = drawable; + } + + /** + * Get the default avatar image. + * @return the default avatar image + */ + public static Drawable getDefaultAvatarImage() { + return newDrawable(getInstance().defaultAvatarImage); + } + + /** + * Set the radius of the avatar in the message list. + * @param radius + */ + public static void setMessageListAvatarRadius(int radius) { + getInstance().avatarRadius = radius; + } + + /** + * Get the radius of the avatar in the message list. + * @return + */ + public static int getMessageListAvatarRadius() { + return getInstance().avatarRadius; + } + + /** + * Set whether to enable the grid avatar with the group chat.The default value is true. + * @param enableGroupGridAvatar + */ + public static void setEnableGroupGridAvatar(boolean enableGroupGridAvatar) { + TUIConfig.setEnableGroupGridAvatar(enableGroupGridAvatar); + } + + /** + * Set the avatar size in the message list. + * @param size + */ + public static void setMessageListAvatarSize(int size) { + getInstance().avatarSize = size; + } + + /** + * Get the avatar size in the message list. + * @return + */ + public static int getMessageListAvatarSize() { + return getInstance().avatarSize; + } + + /** + * Set whether to hide the nickname of the received message. + * @param hideReceiveNickName + */ + public static void setHideReceiveNickName(boolean hideReceiveNickName) { + getInstance().receiveNickNameVisibility = hideReceiveNickName ? View.GONE : View.VISIBLE; + } + + /** + * Get the visibility of the nickname of the received message. + * @return + */ + public static int getReceiveNickNameVisibility() { + return getInstance().receiveNickNameVisibility; + } + + /** + * Set the font size of the nickname of the received message. + * @param size + */ + public static void setReceiveNickNameFontSize(int size) { + getInstance().receiveNickNameFontSize = size; + } + + /** + * Get the font size of the nickname of the received message. + * @return + */ + public static int getReceiveNickNameFontSize() { + return getInstance().receiveNickNameFontSize; + } + + /** + * Set the font color of the nickname of the received message. + * @param color + */ + public static void setReceiveNickNameColor(int color) { + getInstance().receiveNickNameColor = color; + } + + /** + * Get the font color of the nickname of the received message. + * @return + */ + public static int getReceiveNickNameColor() { + return getInstance().receiveNickNameColor; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/config/minimalistui/TUIConfigMinimalist.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/config/minimalistui/TUIConfigMinimalist.java new file mode 100644 index 0000000..d47dc8f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/config/minimalistui/TUIConfigMinimalist.java @@ -0,0 +1,273 @@ +package com.tencent.qcloud.tuikit.timcommon.config.minimalistui; + +import static com.tencent.qcloud.tuikit.timcommon.util.TUIUtil.newDrawable; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuicore.util.ToastUtil; +import com.tencent.qcloud.tuikit.timcommon.component.highlight.HighlightPresenter; + +public class TUIConfigMinimalist { + private TUIConfigMinimalist() {} + + private static final class TUIConfigMinimalistHolder { + private static final TUIConfigMinimalist INSTANCE = new TUIConfigMinimalist(); + } + + private static TUIConfigMinimalist getInstance() { + return TUIConfigMinimalistHolder.INSTANCE; + } + + public static final int UNDEFINED = -1; + // message bubble + private boolean enableMessageBubbleStyle = true; + private Drawable sendBubbleBackground; + private Drawable sendLastBubbleBackground; + private Drawable receiveBubbleBackground; + private Drawable receiveLastBubbleBackground; + + // message style + private Drawable chatTimeBubble; + private int chatTimeFontSize = UNDEFINED; + private int chatTimeFontColor = UNDEFINED; + private Drawable defaultAvatarImage; + private int avatarRadius = UNDEFINED; + private int avatarSize = UNDEFINED; + + /** + * Set the default app directory.The default dir is "file". + * @param appDir + */ + public static void setDefaultAppDir(String appDir) { + TUIConfig.setDefaultAppDir(appDir); + TUIConfig.initPath(); + } + + /** + * Set whether show the toast prompt built into TUIKit.The default value is true. + * @param enableToast + */ + public static void enableToast(boolean enableToast) { + ToastUtil.setEnableToast(enableToast); + } + + /** + * Set whether to enable language switching.The default value is false. + * @param enableLanguageSwitch + */ + public static void enableLanguageSwitch(boolean enableLanguageSwitch) { + TUIThemeManager.setEnableLanguageSwitch(enableLanguageSwitch); + } + + /** + * Switch the language of TUIKit. + * The currently supported languages are "en", "zh", and "ar". + * @param context + * @param targetLanguage + */ + public static void switchLanguageToTarget(Context context, String targetLanguage) { + TUIThemeManager.getInstance().changeLanguage(context, targetLanguage); + } + + /** + * Set whether to enable message bubble style.The default value is true. + * @param enable + */ + public static void setEnableMessageBubbleStyle(boolean enable) { + getInstance().enableMessageBubbleStyle = enable; + } + + /** + * Get whether to enable message bubble style. + * @return true is enable, false is not + */ + public static boolean isEnableMessageBubbleStyle() { + return getInstance().enableMessageBubbleStyle; + } + + /** + * Set the background of the send message's bubble in consecutive messages. + * @param drawable + */ + public static void setSendBubbleBackground(Drawable drawable) { + getInstance().sendBubbleBackground = drawable; + } + + /** + * Get the background of the send message's bubble in consecutive messages. + * @return the background + */ + public static Drawable getSendBubbleBackground() { + return newDrawable(getInstance().sendBubbleBackground); + } + + /** + * Set the background of the receive message's bubble in consecutive messages. + * @param drawable + */ + public static void setReceiveBubbleBackground(Drawable drawable) { + getInstance().receiveBubbleBackground = drawable; + } + + /** + * Get the background of the receive message's bubble in consecutive messages. + * @return the background + */ + public static Drawable getReceiveBubbleBackground() { + return newDrawable(getInstance().receiveBubbleBackground); + } + + /** + * Set the background image of the last sent message's bubble in consecutive messages. + * @param drawable + */ + public static void setSendLastBubbleBackground(Drawable drawable) { + getInstance().sendLastBubbleBackground = drawable; + } + + /** + * Get the background image of the last sent message's bubble in consecutive messages. + * @return drawable + */ + public static Drawable getSendLastBubbleBackground() { + return newDrawable(getInstance().sendLastBubbleBackground); + } + + /** + * Set the background image of the last received message's bubble in consecutive messages. + * @param drawable + */ + public static void setReceiveLastBubbleBackground(Drawable drawable) { + getInstance().receiveLastBubbleBackground = drawable; + } + + /** + * Get the background image of the last received message's bubble in consecutive messages. + * @return drawable + */ + public static Drawable getReceiveLastBubbleBackground() { + return newDrawable(getInstance().receiveLastBubbleBackground); + } + + /** + * Set the light color of the message bubble in highlight status.. + * @param color + */ + public static void setBubbleHighlightLightColor(int color) { + HighlightPresenter.setHighlightLightColor(color); + } + + /** + * Set the dark color of the message bubble in highlight status.. + * @param color + */ + public static void setBubbleHighlightDarkColor(int color) { + HighlightPresenter.setHighlightDarkColor(color); + } + + /** + * Set the chat time bubble. + * @param drawable + */ + public static void setChatTimeBubble(Drawable drawable) { + getInstance().chatTimeBubble = drawable; + } + + /** + * Get the chat time bubble. + * @return + */ + public static Drawable getChatTimeBubble() { + return newDrawable(getInstance().chatTimeBubble); + } + + /** + * Set the font size of the chat time text. + * @param size + */ + public static void setChatTimeFontSize(int size) { + getInstance().chatTimeFontSize = size; + } + + /** + * Get the font size of the chat time text. + * @return + */ + public static int getChatTimeFontSize() { + return getInstance().chatTimeFontSize; + } + + /** + * Set the font color of the chat time text. + * @param color + */ + public static void setChatTimeFontColor(int color) { + getInstance().chatTimeFontColor = color; + } + + /** + * Get the font color of the chat time text. + * @return + */ + public static int getChatTimeFontColor() { + return getInstance().chatTimeFontColor; + } + + /** + * Set the default avatar image. + * @param drawable + */ + public static void setDefaultAvatarImage(Drawable drawable) { + getInstance().defaultAvatarImage = drawable; + } + + /** + * Get the default avatar image. + * @return the default avatar image + */ + public static Drawable getDefaultAvatarImage() { + return newDrawable(getInstance().defaultAvatarImage); + } + + /** + * Set the radius of the avatar in the message list. + * @param radius + */ + public static void setMessageListAvatarRadius(int radius) { + getInstance().avatarRadius = radius; + } + + /** + * Get the radius of the avatar in the message list. + * @return + */ + public static int getMessageListAvatarRadius() { + return getInstance().avatarRadius; + } + + /** + * Set whether to enable the grid avatar with the group chat.The default value is true. + * @param enableGroupGridAvatar + */ + public static void setEnableGroupGridAvatar(boolean enableGroupGridAvatar) { + TUIConfig.setEnableGroupGridAvatar(enableGroupGridAvatar); + } + + /** + * Set the avatar size in the message list. + * @param size + */ + public static void setMessageListAvatarSize(int size) { + getInstance().avatarSize = size; + } + + /** + * Get the avatar size in the message list. + * @return + */ + public static int getMessageListAvatarSize() { + return getInstance().avatarSize; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/ChatInputMoreListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/ChatInputMoreListener.java new file mode 100644 index 0000000..46ad8ff --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/ChatInputMoreListener.java @@ -0,0 +1,10 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; +import com.tencent.qcloud.tuikit.timcommon.component.interfaces.IUIKitCallback; + +public abstract class ChatInputMoreListener { + public String sendMessage(TUIMessageBean msg, IUIKitCallback callback) { + return null; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/HighlightListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/HighlightListener.java new file mode 100644 index 0000000..938b36d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/HighlightListener.java @@ -0,0 +1,10 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +public interface HighlightListener { + + void onHighlightStart(); + + void onHighlightEnd(); + + void onHighlightUpdate(int color); +} \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/ICommonMessageAdapter.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/ICommonMessageAdapter.java new file mode 100644 index 0000000..afee786 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/ICommonMessageAdapter.java @@ -0,0 +1,15 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; + +public interface ICommonMessageAdapter { + TUIMessageBean getItem(int position); + + TUIMessageBean getFirstMessageBean(); + + TUIMessageBean getLastMessageBean(); + + void onItemRefresh(TUIMessageBean messageBean); + + UserFaceUrlCache getUserFaceUrlCache(); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/IMessageProperties.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/IMessageProperties.java new file mode 100644 index 0000000..ff83942 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/IMessageProperties.java @@ -0,0 +1,295 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +import android.graphics.drawable.Drawable; + +public interface IMessageProperties { + + /** + * + * Get default avatar + * + * @return + */ + int getAvatar(); + + /** + * + * Set the default avatar, the default is the same as the avatar on the left and right + * + * @param resId + */ + void setAvatar(int resId); + + /** + * + * Get avatar rounded corners + * + * @return + */ + int getAvatarRadius(); + + /** + * + * Set avatar rounded corners + * + * @param radius + */ + void setAvatarRadius(int radius); + + /** + * + * Get avatar size + * + * @return + */ + int[] getAvatarSize(); + + /** + * + * Set avatar size + * + * @param size + */ + void setAvatarSize(int[] size); + + /** + * + * + * Get nickname text size + * + * @return + */ + int getNameFontSize(); + + /** + * + * Set nickname text size + * + * @param size + */ + void setNameFontSize(int size); + + /** + * + * Get nickname text color + * + * @return + */ + int getNameFontColor(); + + /** + * + * Set nickname text color + * + * @param color + */ + void setNameFontColor(int color); + + /** + * + * Get the display status of the nickname on the left + * + * @return + */ + int getLeftNameVisibility(); + + /** + * + * Set the display status of the nickname on the left + * + * @param visibility + */ + void setLeftNameVisibility(int visibility); + + /** + * + * Get the display status of the nickname on the right + * + * @return + */ + int getRightNameVisibility(); + + /** + * + * Set the display status of the nickname on the right + * + * @param visibility + */ + void setRightNameVisibility(int visibility); + + /** + * + * Get the background of the chat bubble on the right + * + * @return + */ + Drawable getRightBubble(); + + /** + * + * Set the background of the chat bubble on the right + * + * @param drawable + */ + void setRightBubble(Drawable drawable); + + /** + * + * Get the background of the left chat bubble + * + * @return + */ + Drawable getLeftBubble(); + + /** + * + * Set the background of the left chat bubble + * + * @param drawable + */ + void setLeftBubble(Drawable drawable); + + /** + * + * Get chat content font size + * + * @return + */ + int getChatContextFontSize(); + + /** + * + * Set chat content font size + * + * @param size + */ + void setChatContextFontSize(int size); + + /** + * + * Get the font color of the chat content on the right + * + * @return + */ + int getRightChatContentFontColor(); + + /** + * + * Set the font color of the chat content on the right + * + * @param color + */ + void setRightChatContentFontColor(int color); + + /** + * + * Get the font color of the chat content on the left + * + * @return + */ + int getLeftChatContentFontColor(); + + /** + * + * Set the font color of the chat content on the left + * + * @param color + */ + void setLeftChatContentFontColor(int color); + + /** + * + * Get the context of the chat time + * + * @return + */ + Drawable getChatTimeBubble(); + + /** + * + * Set the context of the chat time + * + * @param drawable + */ + void setChatTimeBubble(Drawable drawable); + + /** + * + * Get the text size of the chat time + * + * @return + */ + int getChatTimeFontSize(); + + /** + * + * Set the text size of the chat time + * + * @param size + */ + void setChatTimeFontSize(int size); + + /** + * + * Get the font color of chat time + * + * @return + */ + int getChatTimeFontColor(); + + /** + * + * Set the font color of chat time + * + * @param color + */ + void setChatTimeFontColor(int color); + + /** + * + * Get context for chat alerts + * + * @return + */ + Drawable getTipsMessageBubble(); + + /** + * + * Set context for chat alerts + * + * @param drawable + */ + void setTipsMessageBubble(Drawable drawable); + + /** + * + * Get the text size of the chat prompt message + * + * @return + */ + int getTipsMessageFontSize(); + + /** + * + * Set the text size of the chat prompt message + * + * @param size + */ + void setTipsMessageFontSize(int size); + + /** + * + * Get the text color of the chat prompt message + * + * @return + */ + int getTipsMessageFontColor(); + + /** + * + * Set the text color of the chat prompt message + * + * @param color + */ + void setTipsMessageFontColor(int color); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnChatPopActionClickListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnChatPopActionClickListener.java new file mode 100644 index 0000000..868c716 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnChatPopActionClickListener.java @@ -0,0 +1,25 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; + +public abstract class OnChatPopActionClickListener { + public void onCopyClick(TUIMessageBean msg) {} + + public void onSendMessageClick(TUIMessageBean msg, boolean retry) {} + + public void onDeleteMessageClick(TUIMessageBean msg) {} + + public void onRevokeMessageClick(TUIMessageBean msg) {} + + public void onMultiSelectMessageClick(TUIMessageBean msg) {} + + public void onForwardMessageClick(TUIMessageBean msg) {} + + public void onReplyMessageClick(TUIMessageBean msg) {} + + public void onQuoteMessageClick(TUIMessageBean msg) {} + + public void onInfoMessageClick(TUIMessageBean msg) {} + + public void onSpeakerModeSwitchClick(TUIMessageBean msg) {} +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnFaceInputListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnFaceInputListener.java new file mode 100644 index 0000000..a907254 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnFaceInputListener.java @@ -0,0 +1,14 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +import com.tencent.qcloud.tuikit.timcommon.bean.ChatFace; + +public interface OnFaceInputListener { + + void onEmojiClicked(String emojiKey); + + void onDeleteClicked(); + + void onSendClicked(); + + void onFaceClicked(ChatFace chatFace); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnItemClickListener.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnItemClickListener.java new file mode 100644 index 0000000..74a207e --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/OnItemClickListener.java @@ -0,0 +1,29 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +import android.view.View; + +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; + +public abstract class OnItemClickListener { + public void onMessageLongClick(View view, TUIMessageBean messageBean) {} + + public void onMessageClick(View view, TUIMessageBean messageBean) {} + + public void onUserIconClick(View view, TUIMessageBean messageBean) {} + + public void onUserIconLongClick(View view, TUIMessageBean messageBean) {} + + public void onReEditRevokeMessage(View view, TUIMessageBean messageBean) {} + + public void onRecallClick(View view, TUIMessageBean messageBean) {} + + public void onReplyMessageClick(View view, TUIMessageBean messageBean) {} + + public void onReplyDetailClick(TUIMessageBean messageBean) {} + + public void onSendFailBtnClick(View view, TUIMessageBean messageBean) {} + + public void onTextSelected(View view, int position, TUIMessageBean messageBean) {} + + public void onMessageReadStatusClick(View view, TUIMessageBean messageBean) {} +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/UserFaceUrlCache.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/UserFaceUrlCache.java new file mode 100644 index 0000000..568c54f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/interfaces/UserFaceUrlCache.java @@ -0,0 +1,8 @@ +package com.tencent.qcloud.tuikit.timcommon.interfaces; + +public interface UserFaceUrlCache { + + String getCachedFaceUrl(String userID); + + void pushFaceUrl(String userID, String faceUrl); +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageBaseHolder.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageBaseHolder.java new file mode 100644 index 0000000..0fcd381 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageBaseHolder.java @@ -0,0 +1,240 @@ +package com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message; + +import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.widget.CheckBox; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; +import androidx.recyclerview.widget.RecyclerView; +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; +import com.tencent.qcloud.tuikit.timcommon.component.highlight.HighlightPresenter; +import com.tencent.qcloud.tuikit.timcommon.config.minimalistui.TUIConfigMinimalist; +import com.tencent.qcloud.tuikit.timcommon.interfaces.HighlightListener; +import com.tencent.qcloud.tuikit.timcommon.interfaces.ICommonMessageAdapter; +import com.tencent.qcloud.tuikit.timcommon.interfaces.OnItemClickListener; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +public abstract class MessageBaseHolder extends RecyclerView.ViewHolder { + public ICommonMessageAdapter mAdapter; + protected OnItemClickListener onItemClickListener; + + public FrameLayout msgContentFrame; + public LinearLayout msgArea; + public LinearLayout msgAreaAndReply; + public FrameLayout reactionArea; + public CheckBox mMutiSelectCheckBox; + public View mContentLayout; + + public TextView chatTimeText; + + protected boolean floatMode = false; + + protected boolean isLayoutOnStart = true; + private HighlightListener highlightListener; + protected TUIMessageBean currentMessageBean; + + public MessageBaseHolder(View itemView) { + super(itemView); + msgContentFrame = itemView.findViewById(R.id.msg_content_fl); + reactionArea = itemView.findViewById(R.id.message_reaction_area); + msgArea = itemView.findViewById(R.id.msg_area); + msgAreaAndReply = itemView.findViewById(R.id.msg_area_and_reply); + mMutiSelectCheckBox = itemView.findViewById(R.id.select_checkbox); + chatTimeText = itemView.findViewById(R.id.message_top_time_tv); + mContentLayout = itemView.findViewById(R.id.message_content_layout); + initVariableLayout(); + } + + public abstract int getVariableLayout(); + + private void setVariableLayout(int resId) { + if (msgContentFrame.getChildCount() == 0) { + View.inflate(itemView.getContext(), resId, msgContentFrame); + } + } + + private void initVariableLayout() { + if (getVariableLayout() != 0) { + setVariableLayout(getVariableLayout()); + } + } + + public void setAdapter(ICommonMessageAdapter adapter) { + mAdapter = adapter; + } + + public void setOnItemClickListener(OnItemClickListener listener) { + this.onItemClickListener = listener; + } + + public OnItemClickListener getOnItemClickListener() { + return this.onItemClickListener; + } + + public void layoutViews(final TUIMessageBean msg, final int position) { + currentMessageBean = msg; + registerHighlightListener(msg.getId()); + + setChatTimeStyle(); + + if (position > 1) { + TUIMessageBean last = mAdapter.getItem(position - 1); + if (last != null) { + if (msg.getMessageTime() - last.getMessageTime() >= 5 * 60) { + chatTimeText.setVisibility(View.VISIBLE); + chatTimeText.setText(getTimeFormatText(new Date(msg.getMessageTime() * 1000))); + } else { + chatTimeText.setVisibility(View.GONE); + } + } + } else { + chatTimeText.setVisibility(View.VISIBLE); + chatTimeText.setText(getTimeFormatText(new Date(msg.getMessageTime() * 1000))); + } + } + + private void setChatTimeStyle() { + Drawable chatTimeBubble = TUIConfigMinimalist.getChatTimeBubble(); + if (chatTimeBubble != null) { + chatTimeText.setBackground(chatTimeBubble); + } + int chatTimeFontColor = TUIConfigMinimalist.getChatTimeFontColor(); + if (chatTimeFontColor != TUIConfigMinimalist.UNDEFINED) { + chatTimeText.setTextColor(chatTimeFontColor); + } + int chatTimeFontSize = TUIConfigMinimalist.getChatTimeFontSize(); + if (chatTimeFontSize != TUIConfigMinimalist.UNDEFINED) { + chatTimeText.setTextSize(chatTimeFontSize); + } + } + + private void registerHighlightListener(String msgID) { + if (highlightListener == null) { + highlightListener = new HighlightListener() { + @Override + public void onHighlightStart() {} + + @Override + public void onHighlightEnd() { + clearHighLightBackground(); + } + + @Override + public void onHighlightUpdate(int color) { + setHighLightBackground(color); + } + }; + } + HighlightPresenter.registerHighlightListener(msgID, highlightListener); + } + + public void onRecycled() { + if (currentMessageBean != null) { + HighlightPresenter.unregisterHighlightListener(currentMessageBean.getId()); + } + } + + public static String getTimeFormatText(Date date) { + if (date == null) { + return ""; + } + Context context = TUIConfig.getAppContext(); + Locale locale; + if (context == null) { + locale = Locale.getDefault(); + } else { + locale = TUIThemeManager.getInstance().getLocale(context); + } + String timeText; + Calendar dayCalendar = Calendar.getInstance(); + dayCalendar.set(Calendar.HOUR_OF_DAY, 0); + dayCalendar.set(Calendar.MINUTE, 0); + dayCalendar.set(Calendar.SECOND, 0); + dayCalendar.set(Calendar.MILLISECOND, 0); + Calendar weekCalendar = Calendar.getInstance(); + weekCalendar.setFirstDayOfWeek(Calendar.SUNDAY); + weekCalendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + weekCalendar.set(Calendar.HOUR_OF_DAY, 0); + weekCalendar.set(Calendar.MINUTE, 0); + weekCalendar.set(Calendar.SECOND, 0); + weekCalendar.set(Calendar.MILLISECOND, 0); + Calendar yearCalendar = Calendar.getInstance(); + yearCalendar.set(Calendar.DAY_OF_YEAR, 1); + yearCalendar.set(Calendar.HOUR_OF_DAY, 0); + yearCalendar.set(Calendar.MINUTE, 0); + yearCalendar.set(Calendar.SECOND, 0); + yearCalendar.set(Calendar.MILLISECOND, 0); + long dayStartTimeInMillis = dayCalendar.getTimeInMillis(); + long weekStartTimeInMillis = weekCalendar.getTimeInMillis(); + long yearStartTimeInMillis = yearCalendar.getTimeInMillis(); + long outTimeMillis = date.getTime(); + if (outTimeMillis < yearStartTimeInMillis) { + timeText = String.format(Locale.US, "%1$tY/%1$tm/%1$td", date); + } else if (outTimeMillis < weekStartTimeInMillis) { + timeText = String.format(Locale.US, "%1$tm/%1$td", date); + } else if (outTimeMillis < dayStartTimeInMillis) { + timeText = String.format(locale, "%tA", date); + } else { + timeText = context.getResources().getString(R.string.chat_time_today); + } + return timeText; + } + + public void setFloatMode(boolean floatMode) { + this.floatMode = floatMode; + } + + protected boolean isShowAvatar(TUIMessageBean messageBean) { + return false; + } + + public void setMessageBubbleZeroPadding() { + if (msgArea == null) { + return; + } + msgArea.setPaddingRelative(0, 0, 0, 0); + } + + public void setMessageBubbleBackground(int resID) { + if (msgArea == null) { + return; + } + msgArea.setBackgroundResource(resID); + } + + public void setMessageBubbleBackground(Drawable drawable) { + if (msgArea == null) { + return; + } + msgArea.setBackground(drawable); + } + + public Drawable getMessageBubbleBackground() { + if (msgArea == null) { + return null; + } + return msgArea.getBackground(); + } + + public void setHighLightBackground(int color) { + Drawable drawable = getMessageBubbleBackground(); + if (drawable != null) { + drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + } + + public void clearHighLightBackground() { + Drawable drawable = getMessageBubbleBackground(); + if (drawable != null) { + drawable.setColorFilter(null); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageContentHolder.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageContentHolder.java new file mode 100644 index 0000000..f6b138a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageContentHolder.java @@ -0,0 +1,735 @@ +package com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message; + +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.RecyclerView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.RoundedCorners; +import com.tencent.imsdk.v2.V2TIMManager; +import com.tencent.imsdk.v2.V2TIMMessage; +import com.tencent.imsdk.v2.V2TIMUserFullInfo; +import com.tencent.imsdk.v2.V2TIMValueCallback; +import com.tencent.qcloud.tuicore.TUIConstants; +import com.tencent.qcloud.tuicore.TUICore; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.bean.MessageRepliesBean; +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; +import com.tencent.qcloud.tuikit.timcommon.component.UnreadCountTextView; +import com.tencent.qcloud.tuikit.timcommon.config.minimalistui.TUIConfigMinimalist; +import com.tencent.qcloud.tuikit.timcommon.util.DateTimeUtil; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; +import com.tencent.qcloud.tuikit.timcommon.util.TUIUtil; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class MessageContentHolder extends MessageBaseHolder { + protected static final int READ_STATUS_UNREAD = 1; + protected static final int READ_STATUS_PART_READ = 2; + protected static final int READ_STATUS_ALL_READ = 3; + protected static final int READ_STATUS_HIDE = 4; + protected static final int READ_STATUS_SENDING = 5; + + public ImageView leftUserIcon; + public ImageView rightUserIcon; + public TextView usernameText; + public LinearLayout msgContentLinear; + public ImageView messageStatusImage; + public ImageView fileStatusImage; + public UnreadCountTextView unreadAudioText; + public LinearLayout extraInfoArea; + protected FrameLayout bottomContentFrameLayout; + + public boolean isForwardMode = false; + public boolean isMessageDetailMode = false; + public boolean isMultiSelectMode = false; + public boolean isOptimize = true; + public boolean isShowSelfAvatar = false; + protected boolean isShowAvatar = true; + + protected TimeInLineTextLayout timeInLineTextLayout; + protected MinimalistMessageLayout rootLayout; + protected ReplyPreviewView replyPreviewView; + + private List mDataSource = new ArrayList<>(); + + // Whether to display the bottom content. The merged-forwarded message details activity does not display the bottom content. + protected boolean isNeedShowBottom = true; + protected boolean isShowRead = false; + private Fragment fragment; + private RecyclerView recyclerView; + + public MessageContentHolder(View itemView) { + super(itemView); + rootLayout = (MinimalistMessageLayout) itemView; + leftUserIcon = itemView.findViewById(R.id.left_user_icon_view); + rightUserIcon = itemView.findViewById(R.id.right_user_icon_view); + usernameText = itemView.findViewById(R.id.user_name_tv); + msgContentLinear = itemView.findViewById(R.id.msg_content_ll); + messageStatusImage = itemView.findViewById(R.id.message_status_iv); + fileStatusImage = itemView.findViewById(R.id.file_status_iv); + unreadAudioText = itemView.findViewById(R.id.unread_audio_text); + replyPreviewView = itemView.findViewById(R.id.msg_reply_preview); + extraInfoArea = itemView.findViewById(R.id.extra_info_area); + bottomContentFrameLayout = itemView.findViewById(R.id.bottom_content_fl); + } + + public void setFragment(Fragment fragment) { + this.fragment = fragment; + } + + public void setRecyclerView(RecyclerView recyclerView) { + this.recyclerView = recyclerView; + } + + public void setDataSource(List dataSource) { + if (dataSource == null || dataSource.isEmpty()) { + mDataSource = null; + } + + List mediaSource = new ArrayList<>(); + for (TUIMessageBean messageBean : dataSource) { + int type = messageBean.getMsgType(); + if (type == V2TIMMessage.V2TIM_ELEM_TYPE_IMAGE || type == V2TIMMessage.V2TIM_ELEM_TYPE_VIDEO) { + mediaSource.add(messageBean); + } + } + mDataSource = mediaSource; + } + + public List getDataSource() { + return mDataSource; + } + + @Override + public void layoutViews(final TUIMessageBean msg, final int position) { + Context context = itemView.getContext(); + if (TUIUtil.isActivityDestroyed(context)) { + return; + } + + super.layoutViews(msg, position); + setLayoutAlignment(msg); + setIsShowAvatar(msg, position); + setMessageGravity(); + setUserNameText(msg); + setMessageBubbleBackground(); + setMessageStatusImage(msg); + setMessageTimeVisibility(); + setAvatarVisibility(); + setEventListener(msg); + + msgContentLinear.setVisibility(View.VISIBLE); + + if (!isForwardMode && !isMessageDetailMode) { + setTimeInLineStatus(msg); + setShowReadStatusClickListener(msg); + } + if (timeInLineTextLayout != null && timeInLineTextLayout.getTextView() != null) { + if (isMultiSelectMode) { + timeInLineTextLayout.getTextView().setActivated(false); + } else { + timeInLineTextLayout.getTextView().setActivated(true); + } + } + + if (timeInLineTextLayout != null) { + timeInLineTextLayout.setTimeText(DateTimeUtil.getHMTimeString(new Date(msg.getMessageTime() * 1000))); + } + + extraInfoArea.setVisibility(View.GONE); + setReplyContent(msg); + setReactContent(msg); + if (isNeedShowBottom) { + setBottomContent(msg); + } + setMessageBubbleDefaultPadding(); + if (floatMode) { + itemView.setPaddingRelative(0, 0, 0, 0); + leftUserIcon.setVisibility(View.GONE); + rightUserIcon.setVisibility(View.GONE); + usernameText.setVisibility(View.GONE); + replyPreviewView.setVisibility(View.GONE); + reactionArea.setVisibility(View.GONE); + } + if (isMessageDetailMode) { + replyPreviewView.setVisibility(View.GONE); + } + + optimizePadding(position, msg); + loadAvatar(msg); + layoutVariableViews(msg, position); + } + + private void setEventListener(TUIMessageBean msg) { + if (!isForwardMode && !isMessageDetailMode) { + msgContentFrame.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgArea, msg); + } + return true; + } + }); + + msgArea.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgArea, msg); + } + return true; + } + }); + + leftUserIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (onItemClickListener != null) { + onItemClickListener.onUserIconClick(view, msg); + } + } + }); + leftUserIcon.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + if (onItemClickListener != null) { + onItemClickListener.onUserIconLongClick(view, msg); + } + return true; + } + }); + rightUserIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (onItemClickListener != null) { + onItemClickListener.onUserIconClick(view, msg); + } + } + }); + } + + if (msg.getStatus() != TUIMessageBean.MSG_STATUS_SEND_FAIL) { + msgContentFrame.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageClick(msgContentFrame, msg); + } + } + }); + } + } + + private void setMessageTimeVisibility() { + if (isForwardMode || floatMode) { + chatTimeText.setVisibility(View.GONE); + } + } + + private void setLayoutAlignment(TUIMessageBean msg) { + if (isForwardMode || isMessageDetailMode) { + isLayoutOnStart = true; + } else { + if (msg.isSelf()) { + isLayoutOnStart = false; + } else { + isLayoutOnStart = true; + } + } + + if (isForwardMode || isMessageDetailMode) { + rootLayout.setIsStart(true); + msgContentLinear.removeView(msgAreaAndReply); + msgContentLinear.addView(msgAreaAndReply); + } else { + if (msg.isSelf()) { + msgContentLinear.removeView(msgAreaAndReply); + msgContentLinear.addView(msgAreaAndReply); + } else { + msgContentLinear.removeView(msgAreaAndReply); + msgContentLinear.addView(msgAreaAndReply, 0); + } + } + setGravity(isLayoutOnStart); + rootLayout.setIsStart(isLayoutOnStart); + } + + private void setUserNameText(TUIMessageBean msg) { + if (isForwardMode || isMessageDetailMode) { + usernameText.setVisibility(View.GONE); + } else { + if (msg.isSelf()) { + usernameText.setVisibility(View.GONE); + } else { + if (msg.isGroup()) { + usernameText.setVisibility(View.GONE); + } else { + usernameText.setVisibility(View.GONE); + } + } + } + + usernameText.setText(msg.getUserDisplayName()); + } + + public void setIsShowAvatar(TUIMessageBean msg, int position) { + isShowAvatar = true; + if (mAdapter == null) { + return; + } + if (isMessageDetailMode || !isOptimize) { + return; + } + TUIMessageBean nextMessage = mAdapter.getItem(position + 1); + if (nextMessage != null) { + if (TextUtils.equals(msg.getSender(), nextMessage.getSender())) { + boolean longPeriod = nextMessage.getMessageTime() - msg.getMessageTime() >= 5 * 60; + if (!isShowAvatar(nextMessage) && nextMessage.getStatus() != TUIMessageBean.MSG_STATUS_REVOKE && !longPeriod) { + isShowAvatar = false; + } + } + } + } + + public void setMessageBubbleBackground() { + if (!TUIConfigMinimalist.isEnableMessageBubbleStyle()) { + setMessageBubbleBackground(null); + return; + } + Drawable sendBubble = TUIConfigMinimalist.getSendBubbleBackground(); + Drawable receiveBubble = TUIConfigMinimalist.getReceiveBubbleBackground(); + Drawable sendLastBubble = TUIConfigMinimalist.getSendLastBubbleBackground(); + Drawable receiveLastBubble = TUIConfigMinimalist.getReceiveLastBubbleBackground(); + if (isShowAvatar) { + if (isLayoutOnStart) { + if (receiveLastBubble != null) { + setMessageBubbleBackground(receiveLastBubble); + } else { + setMessageBubbleBackground(R.drawable.chat_message_popup_stroke_border_left); + } + } else { + if (sendLastBubble != null) { + setMessageBubbleBackground(sendLastBubble); + } else { + setMessageBubbleBackground(R.drawable.chat_message_popup_fill_border_right); + } + } + } else { + if (isLayoutOnStart) { + if (receiveBubble != null) { + setMessageBubbleBackground(receiveBubble); + } else { + setMessageBubbleBackground(R.drawable.chat_message_popup_stroke_border); + } + } else { + if (sendBubble != null) { + setMessageBubbleBackground(sendBubble); + } else { + setMessageBubbleBackground(R.drawable.chat_message_popup_fill_border); + } + } + } + } + + public void setMessageStatusImage(TUIMessageBean msg) { + if (isForwardMode || isMessageDetailMode || floatMode) { + messageStatusImage.setVisibility(View.GONE); + } else { + if (msg.hasRiskContent()) { + messageStatusImage.setVisibility(View.VISIBLE); + } else if (msg.getStatus() == TUIMessageBean.MSG_STATUS_SEND_FAIL) { + messageStatusImage.setVisibility(View.VISIBLE); + messageStatusImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onSendFailBtnClick(messageStatusImage, msg); + } + } + }); + } else { + messageStatusImage.setVisibility(View.GONE); + } + } + } + + public void setBottomContent(TUIMessageBean msg) { + HashMap param = new HashMap<>(); + param.put(TUIConstants.TUIChat.MESSAGE_BEAN, msg); + param.put(TUIConstants.TUIChat.CHAT_RECYCLER_VIEW, recyclerView); + param.put(TUIConstants.TUIChat.FRAGMENT, fragment); + + TUICore.raiseExtension(TUIConstants.TUIChat.Extension.MessageBottom.MINIMALIST_EXTENSION_ID, bottomContentFrameLayout, param); + } + + private void loadAvatar(TUIMessageBean msg) { + Drawable drawable = TUIConfigMinimalist.getDefaultAvatarImage(); + if (drawable != null) { + setupAvatar(drawable); + return; + } + + if (msg.isUseMsgReceiverAvatar() && mAdapter != null) { + String cachedFaceUrl = mAdapter.getUserFaceUrlCache().getCachedFaceUrl(msg.getSender()); + if (cachedFaceUrl == null) { + List idList = new ArrayList<>(); + idList.add(msg.getSender()); + V2TIMManager.getInstance().getUsersInfo(idList, new V2TIMValueCallback>() { + @Override + public void onSuccess(List v2TIMUserFullInfos) { + if (v2TIMUserFullInfos == null || v2TIMUserFullInfos.isEmpty()) { + return; + } + V2TIMUserFullInfo userInfo = v2TIMUserFullInfos.get(0); + String faceUrl = userInfo.getFaceUrl(); + if (TextUtils.isEmpty(userInfo.getFaceUrl())) { + faceUrl = ""; + } + mAdapter.getUserFaceUrlCache().pushFaceUrl(userInfo.getUserID(), faceUrl); + mAdapter.onItemRefresh(msg); + } + + @Override + public void onError(int code, String desc) { + setupAvatar(""); + } + }); + } else { + setupAvatar(cachedFaceUrl); + } + } else { + setupAvatar(msg.getFaceUrl()); + } + } + + private void setupAvatar(Object faceUrl) { + int avatarSize = TUIConfigMinimalist.getMessageListAvatarSize(); + if (avatarSize == TUIConfigMinimalist.UNDEFINED) { + avatarSize = ScreenUtil.dip2px(32); + } + ViewGroup.LayoutParams params = leftUserIcon.getLayoutParams(); + params.width = avatarSize; + if (leftUserIcon.getVisibility() == View.INVISIBLE) { + params.height = 1; + } else { + params.height = avatarSize; + } + leftUserIcon.setLayoutParams(params); + + params = rightUserIcon.getLayoutParams(); + params.width = avatarSize; + if (rightUserIcon.getVisibility() == View.INVISIBLE) { + params.height = 1; + } else { + params.height = avatarSize; + } + rightUserIcon.setLayoutParams(params); + + int radius = ScreenUtil.dip2px(100); + if (TUIConfigMinimalist.getMessageListAvatarRadius() != TUIConfigMinimalist.UNDEFINED) { + radius = TUIConfigMinimalist.getMessageListAvatarRadius(); + } + ImageView renderedView; + if (isLayoutOnStart) { + renderedView = leftUserIcon; + } else { + renderedView = rightUserIcon; + } + + Glide.with(itemView.getContext()).clear(renderedView); + int defaultIconResId = TUIThemeManager.getAttrResId(leftUserIcon.getContext(), com.tencent.qcloud.tuikit.timcommon.R.attr.core_default_user_icon); + RequestBuilder placeholderRequest = Glide.with(itemView.getContext()) + .load(defaultIconResId) + .transform(new RoundedCorners(radius)) + .diskCacheStrategy(DiskCacheStrategy.ALL); + RequestBuilder errorRequestBuilder = Glide.with(itemView.getContext()) + .load(defaultIconResId) + .transform(new RoundedCorners(radius)) + .diskCacheStrategy(DiskCacheStrategy.ALL); + Glide.with(itemView.getContext()) + .load(faceUrl) + .thumbnail(placeholderRequest) + .transform(new RoundedCorners(radius)) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .skipMemoryCache(false) + .dontAnimate() + .error(errorRequestBuilder) + .into(renderedView); + } + + private void setAvatarVisibility() { + if (isShowAvatar) { + if (isLayoutOnStart) { + leftUserIcon.setVisibility(View.VISIBLE); + rightUserIcon.setVisibility(View.GONE); + } else { + leftUserIcon.setVisibility(View.GONE); + if (isShowSelfAvatar) { + rightUserIcon.setVisibility(View.VISIBLE); + } else { + rightUserIcon.setVisibility(View.GONE); + } + } + } else { + leftUserIcon.setVisibility(View.INVISIBLE); + if (isShowSelfAvatar) { + rightUserIcon.setVisibility(View.INVISIBLE); + } else { + rightUserIcon.setVisibility(View.GONE); + } + } + } + + private void optimizePadding(int position, TUIMessageBean messageBean) { + if (mAdapter == null) { + return; + } + if (isMessageDetailMode || !isOptimize) { + return; + } + + TUIMessageBean nextMessage = mAdapter.getItem(position + 1); + int horizontalPadding = ScreenUtil.dip2px(16); + int verticalPadding = ScreenUtil.dip2px(25f); + if (!isShowAvatar) { + horizontalPadding = ScreenUtil.dip2px(16); + verticalPadding = ScreenUtil.dip2px(2); + } + if (nextMessage != null) { + rootLayout.setPaddingRelative(horizontalPadding, 0, horizontalPadding, verticalPadding); + } else { + rootLayout.setPaddingRelative(horizontalPadding, 0, horizontalPadding, ScreenUtil.dip2px(5)); + } + optimizeMessageContent(isShowAvatar); + } + + protected void optimizeMessageContent(boolean isShowAvatar) { + } + + private void setMessageGravity() { + if (isLayoutOnStart) { + msgContentLinear.setGravity(Gravity.START | Gravity.BOTTOM); + extraInfoArea.setGravity(Gravity.START); + } else { + msgContentLinear.setGravity(Gravity.END | Gravity.BOTTOM); + extraInfoArea.setGravity(Gravity.END); + } + } + + private void setTimeInLineStatus(TUIMessageBean msg) { + if (isShowRead) { + if (msg.isSelf()) { + if (TUIMessageBean.MSG_STATUS_SEND_SUCCESS == msg.getStatus()) { + if (!msg.isNeedReadReceipt()) { + setReadStatus(READ_STATUS_HIDE); + } else { + processReadStatus(msg); + } + } else if (TUIMessageBean.MSG_STATUS_SENDING == msg.getStatus()) { + setReadStatus(READ_STATUS_SENDING); + } else { + setReadStatus(READ_STATUS_HIDE); + } + } else { + setReadStatus(READ_STATUS_HIDE); + } + } + } + + protected void setOnTimeInLineTextClickListener(TUIMessageBean messageBean) { + timeInLineTextLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageClick(msgArea, messageBean); + } + } + }); + timeInLineTextLayout.getTextView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageClick(msgArea, messageBean); + } + } + }); + timeInLineTextLayout.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgArea, messageBean); + } + return true; + } + }); + timeInLineTextLayout.getTextView().setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgArea, messageBean); + } + return true; + } + }); + } + + protected void setReadStatus(int readStatus) { + if (timeInLineTextLayout != null) { + int statusIconResID = 0; + switch (readStatus) { + case READ_STATUS_UNREAD: { + statusIconResID = R.drawable.chat_minimalist_message_status_send_no_read; + break; + } + case READ_STATUS_PART_READ: { + statusIconResID = R.drawable.chat_minimalist_message_status_send_part_read; + break; + } + case READ_STATUS_ALL_READ: { + statusIconResID = R.drawable.chat_minimalist_message_status_send_all_read; + break; + } + case READ_STATUS_SENDING: { + statusIconResID = R.drawable.chat_minimalist_status_loading_anim; + break; + } + default: { + } + } + timeInLineTextLayout.setStatusIcon(statusIconResID); + } + } + + protected void setMessageBubbleDefaultPadding() { + // after setting background, the padding will be reset + int paddingHorizontal = itemView.getResources().getDimensionPixelSize(R.dimen.chat_minimalist_message_area_padding_left_right); + int paddingVertical = itemView.getResources().getDimensionPixelSize(R.dimen.chat_minimalist_message_area_padding_top_bottom); + msgArea.setPaddingRelative(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical); + } + + protected void setGravity(boolean isStart) { + int gravity = isStart ? Gravity.START : Gravity.END; + msgAreaAndReply.setGravity(gravity); + ViewGroup.LayoutParams layoutParams = msgContentFrame.getLayoutParams(); + if (layoutParams instanceof FrameLayout.LayoutParams) { + ((FrameLayout.LayoutParams) layoutParams).gravity = gravity; + } else if (layoutParams instanceof LinearLayout.LayoutParams) { + ((LinearLayout.LayoutParams) layoutParams).gravity = gravity; + } + msgContentFrame.setLayoutParams(layoutParams); + } + + private void setReplyContent(TUIMessageBean messageBean) { + MessageRepliesBean messageRepliesBean = messageBean.getMessageRepliesBean(); + if (messageRepliesBean != null && messageRepliesBean.getRepliesSize() > 0) { + extraInfoArea.setVisibility(View.VISIBLE); + replyPreviewView.setVisibility(View.VISIBLE); + replyPreviewView.setMessageRepliesBean(messageRepliesBean); + replyPreviewView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onReplyDetailClick(messageBean); + } + } + }); + } else { + replyPreviewView.setVisibility(View.GONE); + } + } + + private void setReactContent(TUIMessageBean messageBean) { + Map param = new HashMap<>(); + param.put(TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.MESSAGE, messageBean); + param.put(TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.VIEW_TYPE, + TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.VIEW_TYPE_MINIMALIST); + TUICore.raiseExtension(TUIConstants.TUIChat.Extension.MessageReactPreviewExtension.EXTENSION_ID, reactionArea, param); + } + + private void processReadStatus(TUIMessageBean msg) { + if (msg.isGroup()) { + if (msg.isAllRead()) { + setReadStatus(READ_STATUS_ALL_READ); + } else if (msg.isUnread()) { + setReadStatus(READ_STATUS_UNREAD); + } else { + long readCount = msg.getReadCount(); + if (readCount > 0) { + setReadStatus(READ_STATUS_PART_READ); + } + } + } else { + if (msg.isPeerRead()) { + setReadStatus(READ_STATUS_ALL_READ); + } else { + setReadStatus(READ_STATUS_UNREAD); + } + } + } + + private void setShowReadStatusClickListener(TUIMessageBean messageBean) { + if (timeInLineTextLayout != null) { + timeInLineTextLayout.setOnStatusAreaClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageReadStatusClick(v, messageBean); + } + } + }); + timeInLineTextLayout.setOnStatusAreaLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgArea, messageBean); + } + return true; + } + }); + + timeInLineTextLayout.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (onItemClickListener != null) { + onItemClickListener.onMessageLongClick(msgArea, messageBean); + } + return true; + } + }); + } + } + + public abstract void layoutVariableViews(final TUIMessageBean msg, final int position); + + public void onRecycled() { + super.onRecycled(); + } + + public void setNeedShowBottom(boolean needShowBottom) { + isNeedShowBottom = needShowBottom; + } + + public void setShowRead(boolean showRead) { + isShowRead = showRead; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageStatusTimeView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageStatusTimeView.java new file mode 100644 index 0000000..dcbb310 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MessageStatusTimeView.java @@ -0,0 +1,62 @@ +package com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message; + +import android.content.Context; +import android.graphics.drawable.Animatable; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; + +public class MessageStatusTimeView extends FrameLayout { + private TextView timeView; + private ImageView statusIconView; + + public MessageStatusTimeView(@NonNull Context context) { + super(context); + init(context, null); + } + + public MessageStatusTimeView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public MessageStatusTimeView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + LayoutInflater.from(context).inflate(R.layout.chat_minimalist_text_status_layout, this); + statusIconView = findViewById(R.id.status_icon); + timeView = findViewById(R.id.time); + } + + public void setStatusIcon(int resID) { + if (resID == 0) { + statusIconView.setVisibility(GONE); + } else { + statusIconView.setBackgroundResource(resID); + Drawable drawable = statusIconView.getBackground(); + if (drawable instanceof Animatable) { + ((Animatable) drawable).start(); + } + statusIconView.setVisibility(VISIBLE); + } + } + + public void setTimeText(CharSequence charSequence) { + timeView.setText(charSequence); + } + + public void setTimeColor(int color) { + timeView.setTextColor(color); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MinimalistMessageLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MinimalistMessageLayout.java new file mode 100644 index 0000000..0ad18b3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/MinimalistMessageLayout.java @@ -0,0 +1,219 @@ +package com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Rect; +import android.os.Build; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.constraintlayout.widget.ConstraintLayout; + +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.LayoutUtil; + +public class MinimalistMessageLayout extends ConstraintLayout { + private View msgArea; + private View quoteArea; + private View bottomArea; + private View replyArea; + + private boolean isStart = false; + private Paint paint; + private Path quotePath; + private Path bottomPath; + private Path replyPath; + + private Rect msgAreaRect; + private Rect bottomRect; + private Rect quoteRect; + private Rect replyRect; + private float strokeWidth; + + private boolean isRTL = false; + + public MinimalistMessageLayout(Context context) { + super(context); + init(); + } + + public MinimalistMessageLayout(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + public MinimalistMessageLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public MinimalistMessageLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + private void init() { + setWillNotDraw(false); + isRTL = LayoutUtil.isRTL(); + quotePath = new Path(); + bottomPath = new Path(); + replyPath = new Path(); + msgAreaRect = new Rect(); + bottomRect = new Rect(); + quoteRect = new Rect(); + replyRect = new Rect(); + paint = new Paint(); + strokeWidth = getResources().getDimension(R.dimen.chat_minimalist_message_quato_line_width); + paint.setStrokeWidth(strokeWidth); + paint.setStyle(Paint.Style.STROKE); + paint.setAntiAlias(true); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + msgArea = findViewById(R.id.msg_area); + bottomArea = findViewById(R.id.bottom_content_fl); + quoteArea = findViewById(R.id.quote_content_fl); + replyArea = findViewById(R.id.msg_reply_preview); + drawLine(canvas); + } + + public void setIsStart(boolean isStart) { + this.isStart = isStart; + } + + private Rect getChildRectInParent(View child) { + int[] location = new int[2]; + getLocationInWindow(location); + + Rect rect = new Rect(); + int[] childLocation = new int[2]; + child.getLocationInWindow(childLocation); + int left = childLocation[0] - location[0]; + int top = childLocation[1] - location[1]; + int right = left + child.getWidth(); + int bottom = top + child.getHeight(); + rect.set(left, top, right, bottom); + return rect; + } + + private void drawLine(Canvas canvas) { + canvas.drawColor(0x00FFFFFF); + if (msgArea.getVisibility() == VISIBLE) { + msgAreaRect = getChildRectInParent(msgArea); + float msgX; + if (isStart) { + if (isRTL) { + paint.setColor(getResources().getColor(R.color.chat_minimalist_right_message_bubble_color)); + msgX = msgAreaRect.right - strokeWidth / 2; + } else { + paint.setColor(getResources().getColor(R.color.chat_minimalist_left_message_bubble_color)); + msgX = msgAreaRect.left + strokeWidth / 2; + } + } else { + if (isRTL) { + paint.setColor(getResources().getColor(R.color.chat_minimalist_left_message_bubble_color)); + msgX = msgAreaRect.left + strokeWidth / 2; + } else { + paint.setColor(getResources().getColor(R.color.chat_minimalist_right_message_bubble_color)); + msgX = msgAreaRect.right - strokeWidth / 2; + } + } + float msgCenterY; + msgCenterY = msgAreaRect.top + msgAreaRect.height() * 1.0f / 2; + drawBottomArea(canvas, msgX, msgCenterY); + drawQuoteArea(canvas, msgX, msgCenterY); + drawReplyArea(canvas, msgX, msgCenterY); + } + } + + private void drawReplyArea(Canvas canvas, float msgX, float msgCenterY) { + if (replyArea.getVisibility() == VISIBLE) { + float replyX; + float replyCenterY; + replyRect = getChildRectInParent(replyArea); + if (isStart) { + if (isRTL) { + replyX = replyRect.right; + } else { + replyX = replyRect.left; + } + } else { + if (isRTL) { + replyX = replyRect.left; + } else { + replyX = replyRect.right; + } + } + replyCenterY = replyRect.top + replyRect.height() * 1.0f / 2; + int replyControlRadius = (int) Math.abs(replyX - msgX); + replyPath.reset(); + replyPath.moveTo(msgX, msgCenterY); + replyPath.quadTo(msgX, replyCenterY - replyControlRadius, msgX, replyCenterY - replyControlRadius); + replyPath.quadTo(msgX, replyCenterY, replyX, replyCenterY); + canvas.drawPath(replyPath, paint); + } + } + + private void drawQuoteArea(Canvas canvas, float msgX, float msgCenterY) { + if (quoteArea.getVisibility() == VISIBLE) { + float quoteX; + float quoteCenterY; + quoteRect = getChildRectInParent(quoteArea); + if (isStart) { + if (isRTL) { + quoteX = quoteRect.right; + } else { + quoteX = quoteRect.left; + } + } else { + if (isRTL) { + quoteX = quoteRect.left; + } else { + quoteX = quoteRect.right; + } + } + quoteCenterY = quoteRect.top + quoteRect.height() * 1.0f / 2; + int quoteControlRadius = (int) Math.abs(quoteX - msgX); + quotePath.reset(); + quotePath.moveTo(msgX, msgCenterY); + quotePath.quadTo(msgX, quoteCenterY - quoteControlRadius, msgX, quoteCenterY - quoteControlRadius); + quotePath.quadTo(msgX, quoteCenterY, quoteX, quoteCenterY); + canvas.drawPath(quotePath, paint); + } + } + + private void drawBottomArea(Canvas canvas, float msgX, float msgCenterY) { + if (bottomArea.getVisibility() == VISIBLE) { + float bottomX; + float bottomCenterY; + bottomRect = getChildRectInParent(bottomArea); + if (isStart) { + if (isRTL) { + bottomX = bottomRect.right; + } else { + bottomX = bottomRect.left; + } + } else { + if (isRTL) { + bottomX = bottomRect.left; + } else { + bottomX = bottomRect.right; + } + } + bottomCenterY = bottomRect.top + bottomRect.height() * 1.0f / 2; + int bottomControlRadius = (int) Math.abs(bottomX - msgX); + bottomPath.reset(); + bottomPath.moveTo(msgX, msgCenterY); + bottomPath.quadTo(msgX, bottomCenterY - bottomControlRadius, msgX, bottomCenterY - bottomControlRadius); + bottomPath.quadTo(msgX, bottomCenterY, bottomX, bottomCenterY); + canvas.drawPath(bottomPath, paint); + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/ReplyPreviewView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/ReplyPreviewView.java new file mode 100644 index 0000000..9896e65 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/ReplyPreviewView.java @@ -0,0 +1,168 @@ +package com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.CircleCrop; +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.bean.MessageRepliesBean; +import com.tencent.qcloud.tuikit.timcommon.util.ScreenUtil; +import com.tencent.qcloud.tuikit.timcommon.util.TUIUtil; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +public class ReplyPreviewView extends FrameLayout { + private ImageView firstImg; + private ImageView secondImg; + private ImageView thirdImg; + private TextView replyText; + + private MessageRepliesBean messageRepliesBean; + + public ReplyPreviewView(@NonNull Context context) { + super(context); + init(context); + } + + public ReplyPreviewView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public ReplyPreviewView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + public void setMessageRepliesBean(MessageRepliesBean messageRepliesBean) { + this.messageRepliesBean = messageRepliesBean; + setView(); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + public ReplyPreviewView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context); + } + + private void init(Context context) { + LayoutInflater.from(context).inflate(R.layout.chat_minimalist_reply_preview_layout, this); + firstImg = findViewById(R.id.first_avatar); + secondImg = findViewById(R.id.second_avatar); + thirdImg = findViewById(R.id.third_avatar); + replyText = findViewById(R.id.reply_text); + } + + private void setView() { + if (messageRepliesBean != null && messageRepliesBean.getRepliesSize() > 0) { + setVisibility(VISIBLE); + firstImg.setVisibility(GONE); + secondImg.setVisibility(GONE); + thirdImg.setVisibility(GONE); + replyText.setText(String.format(Locale.US, getResources().getString(R.string.chat_reply_num), messageRepliesBean.getRepliesSize())); + List repliesBeanList = messageRepliesBean.getReplies(); + List iconList = getReplyUserIconLt(repliesBeanList); + if (iconList.isEmpty()) { + return; + } + String secondIconUrl = null; + String thirdIconUrl = null; + + if (iconList.size() > 1) { + secondIconUrl = iconList.get(1); + } + if (iconList.size() > 2) { + thirdIconUrl = iconList.get(2); + } + String firstIconUrl = iconList.get(0); + firstImg.setVisibility(VISIBLE); + loadAvatar(firstImg, firstIconUrl); + if (secondIconUrl != null) { + secondImg.setVisibility(VISIBLE); + loadAvatar(secondImg, secondIconUrl); + } + + if (iconList.size() == 3 && thirdIconUrl != null) { + thirdImg.setVisibility(VISIBLE); + loadAvatar(thirdImg, thirdIconUrl); + } else if (iconList.size() > 3) { + thirdImg.setVisibility(VISIBLE); + loadAvatar(thirdImg, R.drawable.chat_reply_more_icon); + } + } else { + setVisibility(GONE); + } + } + + private List getReplyUserIconLt(List repliesBeanList) { + Set iconUrlSet = new LinkedHashSet<>(); + for (MessageRepliesBean.ReplyBean replyBean : repliesBeanList) { + iconUrlSet.add(replyBean.getSenderFaceUrl()); + if (iconUrlSet.size() >= 3) { + break; + } + } + return new ArrayList<>(iconUrlSet); + } + + private void loadAvatar(ImageView imageView, Object url) { + if (TUIUtil.isActivityDestroyed(getContext())) { + return; + } + + RequestBuilder errorRequestBuilder = Glide.with(getContext()) + .load(com.tencent.qcloud.tuikit.timcommon.R.drawable.core_default_user_icon_light) + .transform(new ReplyRingCircleCrop()); + + Glide.with(getContext()) + .load(url) + .centerCrop() + .transform(new ReplyRingCircleCrop()) + .error(errorRequestBuilder) + .into(imageView); + } + + static class ReplyRingCircleCrop extends CircleCrop { + @Override + protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { + Bitmap outBitmap = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + paint.setColor(Color.WHITE); + Canvas canvas = new Canvas(outBitmap); + int borderWidth = ScreenUtil.dip2px(1); + int innerWidth = outWidth - 2 * borderWidth; + canvas.drawCircle(outWidth / 2, outHeight / 2, innerWidth / 2, paint); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + Rect rect = new Rect(borderWidth, borderWidth, outWidth - borderWidth, outHeight - borderWidth); + int innerHeight = outHeight - 2 * borderWidth; + Bitmap bitmap = super.transform(pool, toTransform, innerWidth, innerHeight); + canvas.drawBitmap(bitmap, null, rect, paint); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); + canvas.drawCircle(outWidth / 2, outHeight / 2, outWidth / 2, paint); + return outBitmap; + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/TUIReplyQuoteView.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/TUIReplyQuoteView.java new file mode 100644 index 0000000..bcb215a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/TUIReplyQuoteView.java @@ -0,0 +1,31 @@ +package com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message; + +import android.content.Context; +import android.view.LayoutInflater; +import android.widget.FrameLayout; + +import com.tencent.qcloud.tuikit.timcommon.bean.TUIReplyQuoteBean; + +public abstract class TUIReplyQuoteView extends FrameLayout { + + public abstract int getLayoutResourceId(); + + public TUIReplyQuoteView(Context context) { + super(context); + int resId = getLayoutResourceId(); + if (resId != 0) { + LayoutInflater.from(context).inflate(resId, this, true); + } + } + + public abstract void onDrawReplyQuote(TUIReplyQuoteBean quoteBean); + + /** + * + * Whether the original message sender is himself, used for different UI displays + * + * @param isSelf + */ + public void setSelf(boolean isSelf) {} + +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/TimeInLineTextLayout.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/TimeInLineTextLayout.java new file mode 100644 index 0000000..81f8c42 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/minimalistui/widget/message/TimeInLineTextLayout.java @@ -0,0 +1,160 @@ +package com.tencent.qcloud.tuikit.timcommon.minimalistui.widget.message; + +import android.content.Context; +import android.graphics.Color; +import android.text.Layout; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuikit.timcommon.R; +import com.tencent.qcloud.tuikit.timcommon.util.LayoutUtil; + +public class TimeInLineTextLayout extends FrameLayout { + private TextView textView; + private MessageStatusTimeView statusArea; + private int lineCount; + private boolean isRTL = false; + private int lastLineWidth = 0; + private boolean lastLineRunRTL = true; + + public TimeInLineTextLayout(@NonNull Context context) { + super(context); + init(context, null); + } + + public TimeInLineTextLayout(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public TimeInLineTextLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + isRTL = LayoutUtil.isRTL(); + textView = new TextView(context, null, R.style.ChatMinimalistMessageTextStyle); + textView.setTextColor(Color.BLACK); + textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textView.getResources().getDimension(R.dimen.chat_minimalist_message_text_size)); + LayoutParams textViewParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + addView(textView, textViewParams); + statusArea = new MessageStatusTimeView(context); + LayoutParams statusAreaParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + addView(statusArea, statusAreaParams); + } + + public void setText(CharSequence charSequence) { + textView.setText(charSequence); + } + + public void setText(int resID) { + textView.setText(resID); + } + + public void setStatusIcon(int resID) { + statusArea.setStatusIcon(resID); + } + + public void setTimeText(CharSequence charSequence) { + statusArea.setTimeText(charSequence); + } + + public void setTimeColor(int color) { + statusArea.setTimeColor(color); + } + + public void setTextColor(int color) { + textView.setTextColor(color); + } + + public void setTextSize(int size) { + textView.setTextSize(size); + } + + public TextView getTextView() { + return textView; + } + + public void setOnStatusAreaClickListener(OnClickListener listener) { + statusArea.setOnClickListener(listener); + } + + public void setOnStatusAreaLongClickListener(OnLongClickListener listener) { + statusArea.setOnLongClickListener(listener); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + int textViewWidth = textView.getMeasuredWidth(); + int textViewHeight = textView.getMeasuredHeight(); + int statusAreaWidth = statusArea.getMeasuredWidth(); + int statusAreaHeight = statusArea.getMeasuredHeight(); + if (isRTL) { + if (lineCount <= 1) { + textView.layout(statusAreaWidth, 0, statusAreaWidth + textViewWidth, textViewHeight); + } else { + textView.layout(0, 0, textViewWidth, textViewHeight); + } + } else { + textView.layout(0, 0, textViewWidth, textViewHeight); + } + + if (isRTL) { + if (lineCount <= 1) { + statusArea.layout(0, bottom - top - statusAreaHeight, statusAreaWidth, bottom - top); + } else { + if (lastLineRunRTL) { + statusArea.layout(0, bottom - top - statusAreaHeight, statusAreaWidth, bottom - top); + } else { + statusArea.layout(right - left - statusAreaWidth, bottom - top - statusAreaHeight, right - left, bottom - top); + } + } + } else { + statusArea.layout(right - left - statusAreaWidth, bottom - top - statusAreaHeight, right - left, bottom - top); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int maxWidth; + int maxHeight; + // measure text view + measureChildren(widthMeasureSpec, heightMeasureSpec); + + int textWidth = textView.getMeasuredWidth(); + int textHeight = textView.getMeasuredHeight(); + lineCount = textView.getLineCount(); + + // get last line's width + Layout layout = textView.getLayout(); + if (layout != null) { + lastLineWidth = (int) layout.getLineWidth(lineCount - 1); + int direction = layout.getParagraphDirection(lineCount - 1); + lastLineRunRTL = direction == Layout.DIR_RIGHT_TO_LEFT; + } + + int statusAreaWidth = statusArea.getMeasuredWidth(); + int statusAreaHeight = statusArea.getMeasuredHeight(); + MarginLayoutParams lp = (MarginLayoutParams) statusArea.getLayoutParams(); + statusAreaWidth += lp.leftMargin + lp.rightMargin; + + int layoutWidth = MeasureSpec.getSize(widthMeasureSpec); + // switch a new line + if (lastLineWidth + statusAreaWidth > layoutWidth) { + maxHeight = textHeight + statusAreaHeight; + lineCount++; + maxWidth = Math.max(textWidth, statusAreaWidth); + } else { + maxHeight = Math.max(textHeight, statusAreaHeight); + maxWidth = Math.max(textWidth, lastLineWidth + statusAreaWidth); + } + + setMeasuredDimension(maxWidth, maxHeight); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ActivityResultResolver.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ActivityResultResolver.java new file mode 100644 index 0000000..d888316 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ActivityResultResolver.java @@ -0,0 +1,296 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.app.Activity; +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; +import android.text.TextUtils; +import android.util.Pair; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultCaller; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContract; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; + +import com.tencent.qcloud.tuicore.ServiceInitializer; +import com.tencent.qcloud.tuicore.TUICore; +import com.tencent.qcloud.tuicore.interfaces.TUIValueCallback; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; + +public class ActivityResultResolver { + private static final String TAG = "ActivityResultResolver"; + public static final String CONTENT_TYPE_ALL = "*/*"; + public static final String CONTENT_TYPE_IMAGE = "image/*"; + public static final String CONTENT_TYPE_VIDEO = "video/*"; + + private static final String CONTENT_KEY_TYPE = "ContentType"; + private static final String URI = "Uri"; + private static final String METHOD = "Method"; + private static final String KEY_DATA = "Data"; + + private static final String METHOD_GET_SINGLE_CONTENT = "MethodGetSingleContent"; + private static final String METHOD_GET_MULTIPLE_CONTENT = "MethodGetMultipleContent"; + private static final String METHOD_TAKE_PICTURE = "MethodTakePicture"; + private static final String METHOD_TAKE_VIDEO = "MethodTakeVideo"; + + private ActivityResultResolver() {} + + public static void getSingleContent(ActivityResultCaller activityResultCaller, @NonNull String type, TUIValueCallback callback) { + getContent(activityResultCaller, new String[] {type}, false, new TUIValueCallback>() { + @Override + public void onSuccess(List list) { + if (list != null && !list.isEmpty()) { + TUIValueCallback.onSuccess(callback, list.get(0)); + } else { + TUIValueCallback.onError(callback, -1, "getSingleContent result list is empty"); + } + } + + @Override + public void onError(int errorCode, String errorMessage) { + TUIValueCallback.onError(callback, errorCode, errorMessage); + } + }); + } + + public static void getSingleContent(ActivityResultCaller activityResultCaller, @NonNull String[] type, TUIValueCallback callback) { + getContent(activityResultCaller, type, false, new TUIValueCallback>() { + @Override + public void onSuccess(List list) { + if (list != null && !list.isEmpty()) { + TUIValueCallback.onSuccess(callback, list.get(0)); + } else { + TUIValueCallback.onError(callback, -1, "getSingleContent result list is empty"); + } + } + + @Override + public void onError(int errorCode, String errorMessage) { + TUIValueCallback.onError(callback, errorCode, errorMessage); + } + }); + } + + public static void getMultipleContent(ActivityResultCaller activityResultCaller, @NonNull String type, TUIValueCallback> callback) { + getContent(activityResultCaller, new String[] {type}, true, callback); + } + + public static void getMultipleContent(ActivityResultCaller activityResultCaller, @NonNull String[] type, TUIValueCallback> callback) { + getContent(activityResultCaller, type, true, callback); + } + + private static void getContent( + ActivityResultCaller activityResultCaller, @NonNull String[] types, boolean isMultiContent, TUIValueCallback> callback) { + Bundle bundle = new Bundle(); + bundle.putStringArray(CONTENT_KEY_TYPE, types); + bundle.putString(METHOD, isMultiContent ? METHOD_GET_MULTIPLE_CONTENT : METHOD_GET_SINGLE_CONTENT); + TUICore.startActivityForResult(activityResultCaller, ActivityResultProxyActivity.class, bundle, new ActivityResultCallback() { + @Override + public void onActivityResult(ActivityResult result) { + if (result.getData() != null) { + TUIValueCallback.onSuccess(callback, (List) result.getData().getSerializableExtra(KEY_DATA)); + } + } + }); + } + + public static void takePicture(ActivityResultCaller activityResultCaller, @NonNull Uri uri, TUIValueCallback callback) { + takePictureVideo(activityResultCaller, uri, true, callback); + } + + public static void takeVideo(ActivityResultCaller activityResultCaller, @NonNull Uri uri, TUIValueCallback callback) { + takePictureVideo(activityResultCaller, uri, false, callback); + } + + private static void takePictureVideo(ActivityResultCaller activityResultCaller, @NonNull Uri uri, boolean isPicture, TUIValueCallback callback) { + Bundle bundle = new Bundle(); + if (isPicture) { + bundle.putString(METHOD, METHOD_TAKE_PICTURE); + } else { + bundle.putString(METHOD, METHOD_TAKE_VIDEO); + } + bundle.putParcelable(URI, uri); + TUICore.startActivityForResult(activityResultCaller, ActivityResultProxyActivity.class, bundle, new ActivityResultCallback() { + @Override + public void onActivityResult(ActivityResult result) { + if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) { + TUIValueCallback.onSuccess(callback, result.getData().getBooleanExtra(KEY_DATA, false)); + } + } + }); + } + + private static class GetContentsContract extends ActivityResultContract, List> { + @NonNull + @Override + public Intent createIntent(@NonNull Context context, @NonNull Pair input) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + String[] type = input.first; + if (type.length == 1) { + intent.setType(type[0]); + } else if (type.length > 1) { + intent.setType(type[0]); + intent.putExtra(Intent.EXTRA_MIME_TYPES, type); + } + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + boolean allowMultiple = input.second; + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple); + return intent; + } + + @NonNull + @Override + public final List parseResult(int resultCode, @Nullable Intent intent) { + if (intent == null || resultCode != Activity.RESULT_OK) { + return Collections.emptyList(); + } else { + List uris = getClipDataUris(intent); + + for (Uri uri : uris) { + try { + ServiceInitializer.getAppContext().getContentResolver().takePersistableUriPermission( + uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } catch (SecurityException e) { + TIMCommonLog.e(TAG, "takePersistableUriPermission failed " + e.getMessage()); + } + } + return uris; + } + } + + @NonNull + static List getClipDataUris(@NonNull Intent intent) { + // Use a LinkedHashSet to maintain any ordering that may be + // present in the ClipData + LinkedHashSet resultSet = new LinkedHashSet<>(); + if (intent.getData() != null) { + resultSet.add(intent.getData()); + } + ClipData clipData = intent.getClipData(); + if (clipData == null && resultSet.isEmpty()) { + return Collections.emptyList(); + } else if (clipData != null) { + for (int i = 0; i < clipData.getItemCount(); i++) { + Uri uri = clipData.getItemAt(i).getUri(); + if (uri != null) { + resultSet.add(uri); + } + } + } + return new ArrayList<>(resultSet); + } + } + + private static class TakePictureVideoContract extends ActivityResultContract, Boolean> { + private boolean isTakePicture; + + @NonNull + @Override + public Intent createIntent(@NonNull Context context, @NonNull Pair input) { + isTakePicture = input.second; + if (isTakePicture) { + return new Intent(MediaStore.ACTION_IMAGE_CAPTURE) + .putExtra(MediaStore.EXTRA_OUTPUT, input.first) + .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } else { + return new Intent(MediaStore.ACTION_VIDEO_CAPTURE) + .putExtra(MediaStore.EXTRA_OUTPUT, input.first) + .addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + } + } + + @NonNull + @Override + public final Boolean parseResult(int resultCode, @Nullable Intent intent) { + if (isTakePicture) { + return resultCode == Activity.RESULT_OK; + } else { + return intent != null && resultCode == Activity.RESULT_OK; + } + } + } + + public static class ActivityResultProxyActivity extends FragmentActivity { + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + String method = intent.getStringExtra(METHOD); + if (TextUtils.equals(method, METHOD_GET_SINGLE_CONTENT)) { + getSingleContent(intent); + } else if (TextUtils.equals(method, METHOD_GET_MULTIPLE_CONTENT)) { + getMultipleContent(intent); + } else if (TextUtils.equals(method, METHOD_TAKE_PICTURE)) { + takePicture(intent); + } else if (TextUtils.equals(method, METHOD_TAKE_VIDEO)) { + takeVideo(intent); + } + } + + private void getSingleContent(Intent intent) { + String[] types = intent.getStringArrayExtra(CONTENT_KEY_TYPE); + getContent(types, false); + } + + private void getMultipleContent(Intent intent) { + String[] types = intent.getStringArrayExtra(CONTENT_KEY_TYPE); + getContent(types, true); + } + + private void getContent(String[] types, boolean isMultiple) { + ActivityResultLauncher> launcher = + this.registerForActivityResult(new GetContentsContract(), new ActivityResultCallback>() { + @Override + public void onActivityResult(List result) { + Intent dataIntent = new Intent(); + dataIntent.putExtra(KEY_DATA, new ArrayList<>(result)); + setResult(Activity.RESULT_OK, dataIntent); + finish(); + } + }); + try { + launcher.launch(Pair.create(types, isMultiple)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void takePicture(Intent intent) { + takePictureVideo(intent, true); + } + + private void takeVideo(Intent intent) { + takePictureVideo(intent, false); + } + + private void takePictureVideo(Intent intent, boolean isPicture) { + Uri uri = intent.getParcelableExtra(URI); + ActivityResultLauncher> launcher = + this.registerForActivityResult(new TakePictureVideoContract(), new ActivityResultCallback() { + @Override + public void onActivityResult(Boolean result) { + Intent dataIntent = new Intent(); + dataIntent.putExtra(KEY_DATA, result); + setResult(Activity.RESULT_OK, dataIntent); + finish(); + } + }); + try { + launcher.launch(Pair.create(uri, isPicture)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/DateTimeUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/DateTimeUtil.java new file mode 100644 index 0000000..d65dbe4 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/DateTimeUtil.java @@ -0,0 +1,150 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.content.Context; + +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +public class DateTimeUtil { + private static final long minute = 60 * 1000; + private static final long hour = 60 * minute; + private static final long day = 24 * hour; + private static final long week = 7 * day; + private static final long month = 31 * day; + private static final long year = 12 * month; + + /** + * return format text for time + * you can see https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html + * today:HH:MM + * this week:Sunday, Friday .. + * this year:MM/DD + * before this year:YYYY/MM/DD + * @param date current time + * @return format text + */ + public static String getTimeFormatText(Date date) { + if (date == null) { + return ""; + } + Context context = TUIConfig.getAppContext(); + Locale locale; + if (context == null) { + locale = Locale.getDefault(); + } else { + locale = TUIThemeManager.getInstance().getLocale(context); + } + String timeText; + Calendar dayStartCalendar = Calendar.getInstance(); + dayStartCalendar.set(Calendar.HOUR_OF_DAY, 0); + dayStartCalendar.set(Calendar.MINUTE, 0); + dayStartCalendar.set(Calendar.SECOND, 0); + dayStartCalendar.set(Calendar.MILLISECOND, 0); + Calendar weekStartCalendar = Calendar.getInstance(); + weekStartCalendar.setFirstDayOfWeek(Calendar.SUNDAY); + weekStartCalendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY); + weekStartCalendar.set(Calendar.HOUR_OF_DAY, 0); + weekStartCalendar.set(Calendar.MINUTE, 0); + weekStartCalendar.set(Calendar.SECOND, 0); + weekStartCalendar.set(Calendar.MILLISECOND, 0); + Calendar yearStartCalendar = Calendar.getInstance(); + yearStartCalendar.set(Calendar.DAY_OF_YEAR, 1); + yearStartCalendar.set(Calendar.HOUR_OF_DAY, 0); + yearStartCalendar.set(Calendar.MINUTE, 0); + yearStartCalendar.set(Calendar.SECOND, 0); + yearStartCalendar.set(Calendar.MILLISECOND, 0); + long dayStartTimeInMillis = dayStartCalendar.getTimeInMillis(); + long weekStartTimeInMillis = weekStartCalendar.getTimeInMillis(); + long yearStartTimeInMillis = yearStartCalendar.getTimeInMillis(); + long outTimeMillis = date.getTime(); + if (outTimeMillis < yearStartTimeInMillis) { + timeText = String.format(Locale.US, "%tD", date); + } else if (outTimeMillis < weekStartTimeInMillis) { + timeText = String.format(Locale.US, "%1$tm/%1$td", date); + } else if (outTimeMillis < dayStartTimeInMillis) { + timeText = String.format(locale, "%tA", date); + } else { + timeText = String.format(Locale.US, "%tR", date); + } + return timeText; + } + + /** + * HH:MM + * @param date current time + * @return format text e.g. "12:12" + */ + public static String getHMTimeString(Date date) { + if (date == null) { + return ""; + } + return String.format(Locale.US, "%tR", date); + } + + public static String formatSeconds(long seconds) { + Context context = TUIConfig.getAppContext(); + String timeStr = seconds + context.getString(R.string.date_second_short); + if (seconds > 60) { + long second = seconds % 60; + long min = seconds / 60; + timeStr = min + context.getString(R.string.date_minute_short) + second + context.getString(R.string.date_second_short); + if (min > 60) { + min = (seconds / 60) % 60; + long hour = (seconds / 60) / 60; + timeStr = hour + context.getString(R.string.date_hour_short) + min + context.getString(R.string.date_minute_short) + second + + context.getString(R.string.date_second_short); + if (hour % 24 == 0) { + long day = (((seconds / 60) / 60) / 24); + timeStr = day + context.getString(R.string.date_day_short); + } else if (hour > 24) { + hour = ((seconds / 60) / 60) % 24; + long day = (((seconds / 60) / 60) / 24); + timeStr = day + context.getString(R.string.date_day_short) + hour + context.getString(R.string.date_hour_short) + min + + context.getString(R.string.date_minute_short) + second + context.getString(R.string.date_second_short); + } + } + } + return timeStr; + } + + public static String formatSecondsTo00(int timeSeconds) { + int second = timeSeconds % 60; + int minuteTemp = timeSeconds / 60; + if (minuteTemp > 0) { + int minute = minuteTemp % 60; + int hour = minuteTemp / 60; + if (hour > 0) { + return (hour >= 10 ? (hour + "") : ("0" + hour)) + ":" + (minute >= 10 ? (minute + "") : ("0" + minute)) + ":" + + (second >= 10 ? (second + "") : ("0" + second)); + } else { + return (minute >= 10 ? (minute + "") : ("0" + minute)) + ":" + (second >= 10 ? (second + "") : ("0" + second)); + } + } else { + return "00:" + (second >= 10 ? (second + "") : ("0" + second)); + } + } + + public static long getStringToDate(String dateString, String pattern) { + SimpleDateFormat dateFormat = new SimpleDateFormat(pattern, Locale.US); + Date date = new Date(); + try { + date = dateFormat.parse(dateString); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return date.getTime(); + } + + public static String getTimeStringFromDate(Date date, String pattern) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(pattern, Locale.US); + return simpleDateFormat.format(date); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/FileProvider.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/FileProvider.java new file mode 100644 index 0000000..9e5021c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/FileProvider.java @@ -0,0 +1,3 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +public class FileProvider extends androidx.core.content.FileProvider {} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/FileUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/FileUtil.java new file mode 100644 index 0000000..2bbc922 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/FileUtil.java @@ -0,0 +1,553 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.provider.OpenableColumns; +import android.text.TextUtils; +import android.util.Log; +import android.webkit.MimeTypeMap; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.tencent.qcloud.tuicore.ServiceInitializer; +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuicore.TUILogin; +import com.tencent.qcloud.tuikit.timcommon.R; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; +import java.util.Random; + +public class FileUtil { + public static final String DOCUMENTS_DIR = "documents"; + + public static final String FILE_PROVIDER_AUTH = ".timcommon.fileprovider"; + + public static final int SIZETYPE_B = 1; + public static final int SIZETYPE_KB = 2; + public static final int SIZETYPE_MB = 3; + public static final int SIZETYPE_GB = 4; + + public static boolean deleteFile(String path) { + if (TextUtils.isEmpty(path)) { + return false; + } + boolean result = false; + File file = new File(path); + if (file.exists()) { + result = file.delete(); + } + return result; + } + + public static String getPathFromUri(Uri uri) { + String path = ""; + try { + int sdkVersion = Build.VERSION.SDK_INT; + if (sdkVersion >= 19) { + path = getPathByCopyFile(TUILogin.getAppContext(), uri); + } else { + path = getRealFilePath(uri); + } + } catch (Exception e) { + e.printStackTrace(); + } + if (path == null) { + path = ""; + } + return path; + } + + public static String getRealFilePath(Uri uri) { + if (null == uri) { + return null; + } + final String scheme = uri.getScheme(); + String data = null; + if (scheme == null) { + data = uri.getPath(); + } else if (ContentResolver.SCHEME_FILE.equals(scheme)) { + data = uri.getPath(); + } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) { + Cursor cursor = TUILogin.getAppContext().getContentResolver().query(uri, new String[] {MediaStore.Images.ImageColumns.DATA}, null, null, null); + if (null != cursor) { + if (cursor.moveToFirst()) { + int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA); + if (index > -1) { + data = cursor.getString(index); + } + } + cursor.close(); + } + } + return data; + } + + public static Uri getUriFromPath(String path) { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return FileProvider.getUriForFile( + TUIConfig.getAppContext(), TUIConfig.getAppContext().getApplicationInfo().packageName + FILE_PROVIDER_AUTH, new File(path)); + } else { + return Uri.fromFile(new File(path)); + } + + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * + * Get file path from Uri specially designed for Android4.4 and above + */ + public static String getPath(final Context context, final Uri uri) { + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + // DocumentProvider + if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + // ExternalStorageProvider + if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } else { + return getPathByCopyFile(context, uri); + } + } + // DownloadsProvider + else if (isDownloadsDocument(uri)) { + final String id = DocumentsContract.getDocumentId(uri); + if (id.startsWith("raw:")) { + final String path = id.replaceFirst("raw:", ""); + return path; + } + String[] contentUriPrefixesToTry = + new String[] {"content://downloads/public_downloads", "content://downloads/my_downloads", "content://downloads/all_downloads"}; + + for (String contentUriPrefix : contentUriPrefixesToTry) { + Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), Long.parseLong(id)); + try { + String path = getDataColumn(context, contentUri, null, null); + if (path != null && Build.VERSION.SDK_INT < 29) { + return path; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + // On some android8+ mobile phones, the path cannot be obtained, so the new file name is obtained by copying, and then the file is sent out + return getPathByCopyFile(context, uri); + } + // MediaProvider + else if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + if ("image".equals(type)) { + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + } else if ("video".equals(type)) { + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + } else if ("audio".equals(type)) { + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[] {split[1]}; + + String path = getDataColumn(context, contentUri, selection, selectionArgs); + if (TextUtils.isEmpty(path) || Build.VERSION.SDK_INT >= 29) { + path = getPathByCopyFile(context, uri); + } + return path; + } + } + // MediaStore (and general) + else if ("content".equalsIgnoreCase(uri.getScheme())) { + String path = getDataColumn(context, uri, null, null); + if (TextUtils.isEmpty(path) || Build.VERSION.SDK_INT >= 29) { + + path = getPathByCopyFile(context, uri); + } + return path; + } + // File + else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + + return null; + } + + private static String getPathByCopyFile(Context context, Uri uri) { + String fileName = getFileName(context, uri); + File cacheDir = getDocumentCacheDir(context); + File file = generateFileName(fileName, cacheDir); + String destinationPath = null; + if (file != null) { + destinationPath = file.getAbsolutePath(); + boolean saveSuccess = saveFileFromUri(context, uri, destinationPath); + if (!saveSuccess) { + file.delete(); + return null; + } + } + + return destinationPath; + } + + @Nullable + public static File generateFileName(@Nullable String name, File directory) { + if (name == null) { + return null; + } + + File file = new File(directory, name); + + if (file.exists()) { + String fileName = name; + String extension = ""; + int dotIndex = name.lastIndexOf('.'); + if (dotIndex > 0) { + fileName = name.substring(0, dotIndex); + extension = name.substring(dotIndex); + } + + int index = 0; + + while (file.exists()) { + index++; + name = fileName + '(' + index + ')' + extension; + file = new File(directory, name); + } + } + + try { + if (!file.createNewFile()) { + return null; + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + return file; + } + + public static String getFileName(@NonNull Context context, Uri uri) { + String mimeType = context.getContentResolver().getType(uri); + String filename = null; + + if (mimeType == null) { + filename = getName(uri.toString()); + } else { + Cursor returnCursor = context.getContentResolver().query(uri, null, null, null, null); + if (returnCursor != null) { + int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); + returnCursor.moveToFirst(); + filename = returnCursor.getString(nameIndex); + returnCursor.close(); + } + } + + return filename; + } + + public static String getName(String filePath) { + if (filePath == null) { + return null; + } + int index = filePath.lastIndexOf('/'); + return filePath.substring(index + 1); + } + + public static File getDocumentCacheDir(@NonNull Context context) { + File dir = new File(context.getCacheDir(), DOCUMENTS_DIR); + if (!dir.exists()) { + dir.mkdirs(); + } + + return dir; + } + + private static boolean saveFileFromUri(Context context, Uri uri, String destinationPath) { + InputStream is = null; + BufferedOutputStream bos = null; + try { + is = context.getContentResolver().openInputStream(uri); + bos = new BufferedOutputStream(new FileOutputStream(destinationPath, false)); + byte[] buf = new byte[1024]; + + int actualBytes; + while ((actualBytes = is.read(buf)) != -1) { + bos.write(buf, 0, actualBytes); + } + } catch (IOException e) { + e.printStackTrace(); + return false; + } finally { + try { + if (is != null) { + is.close(); + } + if (bos != null) { + bos.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return true; + } + + /** + * Get the value of the data column for this Uri. This is useful for + * MediaStore Uris, and other file-based ContentProviders. + * + * @param context The context. + * @param uri The Uri to query. + * @param selection (Optional) Filter used in the query. + * @param selectionArgs (Optional) Selection arguments used in the query. + * @return The value of the _data column, which is typically a file path. + */ + public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + Cursor cursor = null; + final String column = "_data"; + final String[] projection = {column}; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return null; + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is ExternalStorageProvider. + */ + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is DownloadsProvider. + */ + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + /** + * @param uri The Uri to check. + * @return Whether the Uri authority is MediaProvider. + */ + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * + * Convert file size to string + * + * @param fileS + * @return + */ + public static String formatFileSize(long fileS) { + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + DecimalFormat df = new DecimalFormat("#.00", symbols); + String fileSizeString = ""; + String wrongSize = "0B"; + if (fileS == 0) { + return wrongSize; + } + if (fileS < 1024) { + fileSizeString = df.format((double) fileS) + "B"; + } else if (fileS < 1048576) { + fileSizeString = df.format((double) fileS / 1024) + "KB"; + } else if (fileS < 1073741824) { + fileSizeString = df.format((double) fileS / 1048576) + "MB"; + } else { + fileSizeString = df.format((double) fileS / 1073741824) + "GB"; + } + return fileSizeString; + } + + + // fix the problem that getFileExtensionFromUrl does not support Chinese + public static String getFileExtensionFromUrl(String url) { + if (!TextUtils.isEmpty(url)) { + int fragment = url.lastIndexOf('#'); + if (fragment > 0) { + url = url.substring(0, fragment); + } + + int query = url.lastIndexOf('?'); + if (query > 0) { + url = url.substring(0, query); + } + + int filenamePos = url.lastIndexOf('/'); + String filename = 0 <= filenamePos ? url.substring(filenamePos + 1) : url; + + // if the filename contains special characters, we don't + // consider it valid for our matching purposes: + + // if (!filename.isEmpty() && Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) + if (!filename.isEmpty()) { + int dotPos = filename.lastIndexOf('.'); + if (0 <= dotPos) { + return filename.substring(dotPos + 1).toLowerCase(); + } + } + } + + return ""; + } + + public static void openFile(String path, String fileName) { + Uri uri = FileProvider.getUriForFile( + TUIConfig.getAppContext(), TUIConfig.getAppContext().getApplicationInfo().packageName + FILE_PROVIDER_AUTH, new File(path)); + if (uri == null) { + Log.e("FileUtil", "openFile failed , uri is null"); + return; + } + String fileExtension; + if (TextUtils.isEmpty(fileName)) { + fileExtension = FileUtil.getFileExtensionFromUrl(path); + } else { + fileExtension = FileUtil.getFileExtensionFromUrl(fileName); + } + String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(uri, mimeType); + try { + Intent chooserIntent = Intent.createChooser(intent, TUIConfig.getAppContext().getString(R.string.open_file_tips)); + chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + TUIConfig.getAppContext().startActivity(chooserIntent); + } catch (Exception e) { + Log.e("FileUtil", "openFile failed , " + e.getMessage()); + } + } + + public static long getFileSize(String path) { + File file = new File(path); + if (file.exists()) { + return file.length(); + } + return 0; + } + + public static String generateImageFilePath() { + String name = System.nanoTime() + "_" + Math.abs(new Random().nextInt()); + return TUIConfig.getImageBaseDir() + name + ".jpg"; + } + + public static String generateExternalStorageImageFilePath() { + File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + TUIConfig.getAppContext().getPackageName() + + TUIConfig.IMAGE_BASE_DIR_SUFFIX); + if (!dir.exists()) { + dir.mkdirs(); + } + return dir.getAbsolutePath() + File.separatorChar + System.nanoTime() + "_" + Math.abs(new Random().nextInt()) + ".jpg"; + } + + public static String generateVideoFilePath() { + String name = System.nanoTime() + "_" + Math.abs(new Random().nextInt()); + return TUIConfig.getVideoBaseDir() + name + ".mp4"; + } + + public static String generateExternalStorageVideoFilePath() { + File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + TUIConfig.getAppContext().getPackageName() + + TUIConfig.VIDEO_BASE_DIR_SUFFIX); + if (!dir.exists()) { + dir.mkdirs(); + } + return dir.getAbsolutePath() + File.separatorChar + System.nanoTime() + "_" + Math.abs(new Random().nextInt()) + ".mp4"; + } + + public static boolean saveBitmap(String path, Bitmap b) { + try { + FileOutputStream fout = new FileOutputStream(path); + BufferedOutputStream bos = new BufferedOutputStream(fout); + b.compress(Bitmap.CompressFormat.JPEG, 100, bos); + bos.flush(); + bos.close(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + public static boolean isFileExists(String path) { + try { + File file = new File(path); + return file.exists() && file.isFile(); + } catch (Exception e) { + return false; + } + } + + public static boolean isDirExists(String path) { + try { + File file = new File(path); + return file.exists() && file.isDirectory(); + } catch (Exception e) { + return false; + } + } + + public static boolean isFileSizeExceedsLimit(Uri data, int maxSize) { + try { + Cursor returnCursor = ServiceInitializer.getAppContext().getContentResolver().query(data, null, null, null, null); + if (returnCursor != null) { + int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE); + returnCursor.moveToFirst(); + int size = returnCursor.getInt(sizeIndex); + if (size > maxSize) { + return true; + } + returnCursor.close(); + return false; + } + return false; + } catch (Exception e) { + return false; + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ImageUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ImageUtil.java new file mode 100644 index 0000000..dd2c375 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ImageUtil.java @@ -0,0 +1,300 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +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.text.TextUtils; + +import com.tencent.imsdk.v2.V2TIMImageElem; +import com.tencent.qcloud.tuicore.TUIConfig; +import com.tencent.qcloud.tuicore.TUILogin; +import com.tencent.qcloud.tuicore.util.SPUtils; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +public class ImageUtil { + public static final String SP_IMAGE = "_conversation_group_face"; + + /** + * @param outFile + * @param bitmap + * @return + */ + public static File storeBitmap(File outFile, Bitmap bitmap) { + if (!outFile.exists() || outFile.isDirectory()) { + outFile.getParentFile().mkdirs(); + } + FileOutputStream fOut = null; + try { + outFile.deleteOnExit(); + outFile.createNewFile(); + fOut = new FileOutputStream(outFile); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); + fOut.flush(); + } catch (IOException e1) { + outFile.deleteOnExit(); + } finally { + if (null != fOut) { + try { + fOut.close(); + } catch (IOException e) { + e.printStackTrace(); + outFile.deleteOnExit(); + } + } + } + return outFile; + } + + /** + * + * Read the rotation angle of the image + */ + public static int getBitmapDegree(String fileName) { + int degree = 0; + try { + ExifInterface exifInterface = new ExifInterface(fileName); + 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; + default: + break; + } + } catch (IOException e) { + e.printStackTrace(); + } + return degree; + } + + /** + * + * + * Rotate the image by an angle + * + * @param bm image to be rotated + * @param degree Rotation angle + * @return rotated image + */ + public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) { + Bitmap returnBm = null; + + Matrix matrix = new Matrix(); + matrix.postRotate(degree); + try { + returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); + } catch (OutOfMemoryError e) { + e.printStackTrace(); + } + if (returnBm == null) { + returnBm = bm; + } + if (bm != returnBm) { + bm.recycle(); + } + return returnBm; + } + + public static int[] getImageSize(String path) { + int[] size = new int[2]; + try { + BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); + onlyBoundsOptions.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, onlyBoundsOptions); + int originalWidth = onlyBoundsOptions.outWidth; + int originalHeight = onlyBoundsOptions.outHeight; + // size[0] = originalWidth; + // size[1] = originalHeight; + + int degree = getBitmapDegree(path); + if (degree == 0) { + size[0] = originalWidth; + size[1] = originalHeight; + } else { + float hh = 800f; + float ww = 480f; + if (degree == 90 || degree == 270) { + hh = 480; + ww = 800; + } + int be = 1; + if (originalWidth > originalHeight && originalWidth > ww) { + be = (int) (originalWidth / ww); + } else if (originalWidth < originalHeight && originalHeight > hh) { + be = (int) (originalHeight / hh); + } + if (be <= 0) { + be = 1; + } + BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); + bitmapOptions.inSampleSize = be; + bitmapOptions.inDither = true; + bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; + Bitmap bitmap = BitmapFactory.decodeFile(path, bitmapOptions); + bitmap = rotateBitmapByDegree(bitmap, degree); + size[0] = bitmap.getWidth(); + size[1] = bitmap.getHeight(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return size; + } + + + // The image file is rotated locally, and the path of the image file after rotation is returned. + public static String getImagePathAfterRotate(final String imagePath) { + try { + Bitmap originBitmap = BitmapFactory.decodeFile(imagePath, null); + int degree = ImageUtil.getBitmapDegree(imagePath); + if (degree == 0) { + return imagePath; + } else { + Bitmap newBitmap = ImageUtil.rotateBitmapByDegree(originBitmap, degree); + String oldName = FileUtil.getName(imagePath); + File newImageFile = FileUtil.generateFileName(oldName, FileUtil.getDocumentCacheDir(TUIConfig.getAppContext())); + if (newImageFile == null) { + return imagePath; + } + ImageUtil.storeBitmap(newImageFile, newBitmap); + newBitmap.recycle(); + return newImageFile.getAbsolutePath(); + } + } catch (Exception e) { + return imagePath; + } + } + + /** + * + * Convert image to circle + * + * @param bitmap Pass in a Bitmap object + * @return + */ + public static Bitmap toRoundBitmap(Bitmap bitmap) { + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + float roundPx; + float left; + float top; + float right; + float bottom; + float dstLeft; + float dstTop; + float dstRight; + float dstBottom; + if (width <= height) { + roundPx = width / 2; + left = 0; + top = 0; + right = width; + bottom = width; + height = width; + dstLeft = 0; + dstTop = 0; + dstRight = width; + dstBottom = width; + } else { + roundPx = height / 2; + float clip = (width - height) / 2; + left = clip; + right = width - clip; + top = 0; + bottom = height; + width = height; + dstLeft = 0; + dstTop = 0; + dstRight = height; + dstBottom = height; + } + + Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(output); + + final int color = 0xff424242; + final Paint paint = new Paint(); + final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom); + final Rect dst = new Rect((int) dstLeft, (int) dstTop, (int) dstRight, (int) dstBottom); + final RectF rectF = new RectF(dst); + + paint.setAntiAlias(true); + + canvas.drawARGB(0, 0, 0, 0); + paint.setColor(color); + + // canvas.drawRoundRect(rectF, roundPx, roundPx, paint); + canvas.drawCircle(roundPx, roundPx, roundPx, paint); + + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + canvas.drawBitmap(bitmap, src, dst, paint); + + return output; + } + + public static Bitmap zoomImg(Bitmap bm, int targetWidth, int targetHeight) { + int srcWidth = bm.getWidth(); + int srcHeight = bm.getHeight(); + float widthScale = targetWidth * 1.0f / srcWidth; + float heightScale = targetHeight * 1.0f / srcHeight; + Matrix matrix = new Matrix(); + matrix.postScale(widthScale, heightScale, 0, 0); + Bitmap bmpRet = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.RGB_565); + Canvas canvas = new Canvas(bmpRet); + Paint paint = new Paint(); + canvas.drawBitmap(bm, matrix, paint); + return bmpRet; + } + + /** + * + * Get the image file path based on the image UUID and type + * @param uuid + * @param imageType V2TIMImageElem.V2TIM_IMAGE_TYPE_THUMB , V2TIMImageElem.V2TIM_IMAGE_TYPE_ORIGIN , + * V2TIMImageElem.V2TIM_IMAGE_TYPE_LARGE + * @return path + */ + public static String generateImagePath(String uuid, int imageType) { + String imageTypePreStr; + if (imageType == V2TIMImageElem.V2TIM_IMAGE_TYPE_THUMB) { + imageTypePreStr = "thumb_"; + } else if (imageType == V2TIMImageElem.V2TIM_IMAGE_TYPE_ORIGIN) { + imageTypePreStr = "origin_"; + } else if (imageType == V2TIMImageElem.V2TIM_IMAGE_TYPE_LARGE) { + imageTypePreStr = "large_"; + } else { + imageTypePreStr = "other_"; + } + return TUIConfig.getImageDownloadDir() + imageTypePreStr + uuid; + } + + public static String getGroupConversationAvatar(String conversationID) { + SPUtils spUtils = SPUtils.getInstance(TUILogin.getSdkAppId() + SP_IMAGE); + final String savedIcon = spUtils.getString(conversationID, ""); + if (!TextUtils.isEmpty(savedIcon) && new File(savedIcon).isFile() && new File(savedIcon).exists()) { + return savedIcon; + } + return ""; + } + + public static void setGroupConversationAvatar(String conversationId, String url) { + SPUtils spUtils = SPUtils.getInstance(TUILogin.getSdkAppId() + SP_IMAGE); + spUtils.put(conversationId, url); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/LayoutUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/LayoutUtil.java new file mode 100644 index 0000000..2e4ff0d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/LayoutUtil.java @@ -0,0 +1,17 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.content.Context; +import android.content.res.Configuration; +import android.view.View; + +import com.tencent.qcloud.tuicore.ServiceInitializer; + +public class LayoutUtil { + + public static boolean isRTL() { + Context context = ServiceInitializer.getAppContext(); + Configuration configuration = context.getResources().getConfiguration(); + int layoutDirection = configuration.getLayoutDirection(); + return layoutDirection == View.LAYOUT_DIRECTION_RTL; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/MessageBuilder.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/MessageBuilder.java new file mode 100644 index 0000000..266cfd5 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/MessageBuilder.java @@ -0,0 +1,34 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.text.TextUtils; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; +import java.util.HashMap; + +public class MessageBuilder { + private static final String TAG = MessageBuilder.class.getSimpleName(); + + public static void mergeCloudCustomData(TUIMessageBean messageBean, String key, Object data) { + if (messageBean == null || messageBean.getV2TIMMessage() == null) { + return; + } + String cloudCustomData = messageBean.getV2TIMMessage().getCloudCustomData(); + Gson gson = new Gson(); + HashMap hashMap = null; + if (TextUtils.isEmpty(cloudCustomData)) { + hashMap = new HashMap(); + } else { + try { + hashMap = gson.fromJson(cloudCustomData, HashMap.class); + } catch (JsonSyntaxException e) { + TIMCommonLog.e(TAG, " mergeCloudCustomData error " + e.getMessage()); + } + } + if (hashMap != null) { + hashMap.put(key, data); + cloudCustomData = gson.toJson(hashMap); + } + messageBean.getV2TIMMessage().setCloudCustomData(cloudCustomData); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/MessageParser.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/MessageParser.java new file mode 100644 index 0000000..fce73e3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/MessageParser.java @@ -0,0 +1,72 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.text.TextUtils; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import com.tencent.imsdk.v2.V2TIMMessage; +import com.tencent.qcloud.tuikit.timcommon.bean.MessageFeature; +import com.tencent.qcloud.tuikit.timcommon.bean.MessageRepliesBean; +import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean; +import java.util.HashMap; +import java.util.Map; + +public class MessageParser { + private static final String TAG = MessageParser.class.getSimpleName(); + + public static MessageRepliesBean parseMessageReplies(TUIMessageBean messageBean) { + V2TIMMessage message = messageBean.getV2TIMMessage(); + String cloudCustomData = message.getCloudCustomData(); + + try { + Gson gson = new Gson(); + HashMap hashMap = gson.fromJson(cloudCustomData, HashMap.class); + if (hashMap != null) { + Object repliesContentObj = hashMap.get(TIMCommonConstants.MESSAGE_REPLIES_KEY); + MessageRepliesBean repliesBean = null; + if (repliesContentObj instanceof Map) { + repliesBean = gson.fromJson(gson.toJson(repliesContentObj), MessageRepliesBean.class); + } + if (repliesBean != null) { + if (repliesBean.getVersion() > MessageRepliesBean.VERSION) { + return null; + } + return repliesBean; + } + } + } catch (JsonSyntaxException e) { + TIMCommonLog.e(TAG, " getCustomJsonMap error "); + } + return null; + } + + public static MessageFeature isSupportTyping(TUIMessageBean messageBean) { + if (messageBean.getV2TIMMessage() == null) { + return null; + } + String cloudCustomData = messageBean.getV2TIMMessage().getCloudCustomData(); + if (TextUtils.isEmpty(cloudCustomData)) { + return null; + } + try { + Gson gson = new Gson(); + HashMap featureHashMap = gson.fromJson(cloudCustomData, HashMap.class); + if (featureHashMap != null) { + Object featureContentObj = featureHashMap.get(TIMCommonConstants.MESSAGE_FEATURE_KEY); + MessageFeature messageFeature = null; + if (featureContentObj instanceof Map) { + messageFeature = gson.fromJson(gson.toJson(featureContentObj), MessageFeature.class); + } + if (messageFeature != null) { + if (messageFeature.getVersion() > MessageFeature.VERSION) { + return null; + } + + return messageFeature; + } + } + } catch (JsonSyntaxException e) { + TIMCommonLog.e(TAG, " isSupportTyping exception e = " + e); + } + return null; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/PopWindowUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/PopWindowUtil.java new file mode 100644 index 0000000..4f517a5 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/PopWindowUtil.java @@ -0,0 +1,19 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.graphics.drawable.ColorDrawable; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; +import android.widget.PopupWindow; + +public class PopWindowUtil { + + public static PopupWindow popupWindow(View windowView, View parent, int x, int y) { + PopupWindow popup = new PopupWindow(windowView, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT); + popup.setOutsideTouchable(true); + popup.setFocusable(true); + popup.setBackgroundDrawable(new ColorDrawable(0xAEEEEE00)); + popup.showAtLocation(windowView, Gravity.CENTER | Gravity.TOP, x, y); + return popup; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ScreenUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ScreenUtil.java new file mode 100644 index 0000000..08567d9 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ScreenUtil.java @@ -0,0 +1,39 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.WindowManager; +import com.tencent.qcloud.tuicore.TUIConfig; + +public class ScreenUtil { + private static final String TAG = ScreenUtil.class.getSimpleName(); + + public static int getScreenHeight(Context context) { + DisplayMetrics metric = new DisplayMetrics(); + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getMetrics(metric); + return metric.heightPixels; + } + + public static int getScreenWidth(Context context) { + DisplayMetrics metric = new DisplayMetrics(); + WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getMetrics(metric); + return metric.widthPixels; + } + + public static int getPxByDp(float dp) { + float scale = TUIConfig.getAppContext().getResources().getDisplayMetrics().density; + return (int) (dp * scale + 0.5f); + } + + public static int dip2px(float dpValue) { + final float scale = TUIConfig.getAppContext().getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + public static float dp2px(float dpValue, DisplayMetrics displayMetrics) { + return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, displayMetrics); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/SoftKeyBoardUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/SoftKeyBoardUtil.java new file mode 100644 index 0000000..d41cd87 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/SoftKeyBoardUtil.java @@ -0,0 +1,59 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.content.Context; +import android.graphics.Rect; +import android.os.IBinder; +import android.util.DisplayMetrics; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import com.tencent.qcloud.tuicore.TUIConfig; + +public class SoftKeyBoardUtil { + public static void hideKeyBoard(IBinder token) { + InputMethodManager imm = (InputMethodManager) TUIConfig.getAppContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.hideSoftInputFromWindow(token, 0); + } + } + + public static void hideKeyBoard(Window window) { + InputMethodManager imm = (InputMethodManager) TUIConfig.getAppContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + if (isSoftInputShown(window)) { + imm.toggleSoftInput(0, 0); + } + } + } + + public static void showKeyBoard(Window window) { + InputMethodManager imm = (InputMethodManager) TUIConfig.getAppContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + if (!isSoftInputShown(window)) { + imm.toggleSoftInput(0, 0); + } + } + } + + private static boolean isSoftInputShown(Window window) { + View decorView = window.getDecorView(); + int screenHeight = decorView.getHeight(); + Rect rect = new Rect(); + decorView.getWindowVisibleDisplayFrame(rect); + return screenHeight - rect.bottom - getNavigateBarHeight(window.getWindowManager()) >= 0; + } + + private static int getNavigateBarHeight(WindowManager windowManager) { + DisplayMetrics metrics = new DisplayMetrics(); + windowManager.getDefaultDisplay().getMetrics(metrics); + int usableHeight = metrics.heightPixels; + windowManager.getDefaultDisplay().getRealMetrics(metrics); + int realHeight = metrics.heightPixels; + if (realHeight > usableHeight) { + return realHeight - usableHeight; + } else { + return 0; + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonConstants.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonConstants.java new file mode 100644 index 0000000..937a2de --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonConstants.java @@ -0,0 +1,11 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +public class TIMCommonConstants { + public static final String MESSAGE_REPLY_KEY = "messageReply"; + public static final String MESSAGE_REPLIES_KEY = "messageReplies"; + public static final String MESSAGE_REACT_KEY = "messageReact"; + public static final String MESSAGE_FEATURE_KEY = "messageFeature"; + public static final String CHAT_SETTINGS_SP_NAME = "chat_settings_sp"; + public static final String CHAT_REPLY_GUIDE_SHOW_SP_KEY = "chat_reply_guide_show"; + +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonLog.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonLog.java new file mode 100644 index 0000000..3b642aa --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonLog.java @@ -0,0 +1,77 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import com.tencent.imsdk.common.IMLog; + +public class TIMCommonLog extends IMLog { + private static final String PRE = "TIMCommon-"; + + private static String mixTag(String tag) { + return PRE + tag; + } + + /** + * + * print INFO level log + * + * @param strTag TAG + * @param strInfo + */ + public static void v(String strTag, String strInfo) { + IMLog.v(mixTag(strTag), strInfo); + } + + /** + * + * print DEBUG level log + * + * @param strTag TAG + * @param strInfo + */ + public static void d(String strTag, String strInfo) { + IMLog.d(mixTag(strTag), strInfo); + } + + /** + * + * print INFO level log + * + * @param strTag TAG + * @param strInfo + */ + public static void i(String strTag, String strInfo) { + IMLog.i(mixTag(strTag), strInfo); + } + + /** + * + * print WARN level log + * + * @param strTag TAG + * @param strInfo + */ + public static void w(String strTag, String strInfo) { + IMLog.w(mixTag(strTag), strInfo); + } + + /** + * + * print WARN level log + * + * @param strTag TAG + * @param strInfo + */ + public static void w(String strTag, String strInfo, Throwable e) { + IMLog.w(mixTag(strTag), strInfo + e.getMessage()); + } + + /** + * + * print ERROR level log + * + * @param strTag TAG + * @param strInfo + */ + public static void e(String strTag, String strInfo) { + IMLog.e(mixTag(strTag), strInfo); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonUtil.java new file mode 100644 index 0000000..5b46030 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TIMCommonUtil.java @@ -0,0 +1,17 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import static com.tencent.qcloud.tuicore.TUIConstants.TUIConversation.CONVERSATION_C2C_PREFIX; +import static com.tencent.qcloud.tuicore.TUIConstants.TUIConversation.CONVERSATION_GROUP_PREFIX; + +public class TIMCommonUtil { + + public static String getConversationIdByID(String chatID, boolean isGroup) { + String conversationIdPrefix = isGroup ? CONVERSATION_GROUP_PREFIX : CONVERSATION_C2C_PREFIX; + return conversationIdPrefix + chatID; + } + + + public static boolean isChatbot(String chatID) { + return chatID.startsWith("@RBT#"); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TUIUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TUIUtil.java new file mode 100644 index 0000000..d4b680c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TUIUtil.java @@ -0,0 +1,82 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.text.TextUtils; +import com.tencent.imsdk.v2.V2TIMManager; +import com.tencent.qcloud.tuicore.TUIThemeManager; +import com.tencent.qcloud.tuikit.timcommon.R; +import java.lang.reflect.Method; + +public class TUIUtil { + private static String currentProcessName = ""; + + public static String getProcessName() { + if (!TextUtils.isEmpty(currentProcessName)) { + return currentProcessName; + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + currentProcessName = Application.getProcessName(); + return currentProcessName; + } + + try { + final Method declaredMethod = Class.forName("android.app.ActivityThread", false, Application.class.getClassLoader()) + .getDeclaredMethod("currentProcessName", (Class[]) new Class[0]); + declaredMethod.setAccessible(true); + final Object invoke = declaredMethod.invoke(null, new Object[0]); + if (invoke instanceof String) { + currentProcessName = (String) invoke; + } + } catch (Throwable e) { + e.printStackTrace(); + } + + return currentProcessName; + } + + public static int getDefaultGroupIconResIDByGroupType(Context context, String groupType) { + if (context == null || TextUtils.isEmpty(groupType)) { + return R.drawable.core_default_group_icon_community; + } + if (TextUtils.equals(groupType, V2TIMManager.GROUP_TYPE_WORK)) { + return TUIThemeManager.getAttrResId(context, R.attr.core_default_group_icon_work); + } else if (TextUtils.equals(groupType, V2TIMManager.GROUP_TYPE_MEETING)) { + return TUIThemeManager.getAttrResId(context, R.attr.core_default_group_icon_meeting); + } else if (TextUtils.equals(groupType, V2TIMManager.GROUP_TYPE_PUBLIC)) { + return TUIThemeManager.getAttrResId(context, R.attr.core_default_group_icon_public); + } else if (TextUtils.equals(groupType, V2TIMManager.GROUP_TYPE_COMMUNITY)) { + return TUIThemeManager.getAttrResId(context, R.attr.core_default_group_icon_community); + } + return R.drawable.core_default_group_icon_community; + } + + public static Drawable newDrawable(Drawable drawable) { + if (drawable == null) { + return null; + } + Drawable.ConstantState state = drawable.getConstantState(); + if (state != null) { + return state.newDrawable().mutate(); + } + return drawable.mutate(); + } + + public static String identityHashCode(Object object) { + return System.identityHashCode(object) + ""; + } + + + public static boolean isActivityDestroyed(Context context) { + if (context instanceof Activity) { + if (((Activity) context).isFinishing() || ((Activity) context).isDestroyed()) { + return true; + } + } + return false; + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TextUtil.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TextUtil.java new file mode 100644 index 0000000..8b7763e --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/TextUtil.java @@ -0,0 +1,155 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.Region; +import android.text.Layout; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextPaint; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; +import android.text.util.Linkify; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.tencent.qcloud.tuicore.TUIThemeManager; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public class TextUtil { + + public static final Pattern PHONE_NUMBER_PATTERN = + Pattern.compile("(\\+?(\\d{1,4}[-\\s]?)?)?(\\(?\\d+\\)?[-\\s]?)?[\\d\\s-]{5,14}"); + + public static void linkifyUrls(TextView textView) { + Linkify.addLinks(textView, Linkify.WEB_URLS | Linkify.EMAIL_ADDRESSES); + Linkify.addLinks(textView, PHONE_NUMBER_PATTERN, "tel:"); + SpannableString spannableString = new SpannableString(textView.getText()); + + URLSpan[] urlSpans = spannableString.getSpans(0, spannableString.length(), URLSpan.class); + int urlColor = 0xFF6495ED; + if (TUIThemeManager.getInstance().getCurrentTheme() != TUIThemeManager.THEME_LIGHT) { + urlColor = 0xFF87CEFA; + } + if (urlSpans != null) { + for (URLSpan span : urlSpans) { + int start = spannableString.getSpanStart(span); + int end = spannableString.getSpanEnd(span); + spannableString.removeSpan(span); + spannableString.setSpan(new TextLinkSpan(span.getURL(), urlColor), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + GestureDetector gestureDetector = new GestureDetector(textView.getContext(), new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(MotionEvent e) { + if (!textView.isActivated()) { + return false; + } + ClickableSpan[] spans = findSpansByLocation(textView, Math.round(e.getX()), Math.round(e.getY())); + if (spans != null && spans.length > 0) { + ClickableSpan span = spans[0]; + span.onClick(textView); + } + return false; + } + }); + textView.setText(spannableString); + textView.setMovementMethod(new LinkMovementMethod() { + @Override + public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) { + gestureDetector.onTouchEvent(event); + return false; + } + }); + } + + public static ClickableSpan[] findSpansByLocation(TextView textView, int x, int y) { + if (textView == null || !(textView.getText() instanceof Spannable)) { + return null; + } + Spannable spannable = (Spannable) textView.getText(); + Layout layout = textView.getLayout(); + int offset = getPreciseOffset(textView, x, y); + ClickableSpan[] spans = spannable.getSpans(offset, offset, ClickableSpan.class); + List result = new ArrayList<>(); + for (ClickableSpan span : spans) { + int spanStart = spannable.getSpanStart(span); + int spanEnd = spannable.getSpanEnd(span); + Path path = new Path(); + layout.getSelectionPath(spanStart, spanEnd, path); + RectF rect = new RectF(); + path.computeBounds(rect, true); + Region region = new Region(); + Region pathClipRegion = new Region((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom); + region.setPath(path, pathClipRegion); + if (region.contains(x, y)) { + result.add(span); + } + } + return result.toArray(new ClickableSpan[] {}); + } + + private static int getPreciseOffset(TextView textView, int x, int y) { + Layout layout = textView.getLayout(); + if (layout != null) { + int topVisibleLine = layout.getLineForVertical(y); + int offset = layout.getOffsetForHorizontal(topVisibleLine, x); + + int offsetX = (int) layout.getPrimaryHorizontal(offset); + + if (offsetX > x) { + return layout.getOffsetToLeftOf(offset); + } else { + return offset; + } + } else { + return -1; + } + } + + public static class TextLinkSpan extends URLSpan { + private final int color; + + public TextLinkSpan(String url, int color) { + super(url); + this.color = color; + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + ds.setColor(color); + } + } + + public static class ForegroundColorClickableSpan extends ClickableSpan { + private final int color; + private final View.OnClickListener listener; + + public ForegroundColorClickableSpan(int color, View.OnClickListener listener) { + super(); + this.color = color; + this.listener = listener; + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + ds.setColor(color); + } + + @Override + public void onClick(@NonNull View widget) { + if (listener != null) { + listener.onClick(widget); + } + } + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ThreadUtils.java b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ThreadUtils.java new file mode 100644 index 0000000..4782eb9 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/java/com/tencent/qcloud/tuikit/timcommon/util/ThreadUtils.java @@ -0,0 +1,38 @@ +package com.tencent.qcloud.tuikit.timcommon.util; + +import android.os.Handler; +import android.os.Looper; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class ThreadUtils { + private static final Handler handler = new Handler(Looper.getMainLooper()); + private static final ExecutorService executors = Executors.newCachedThreadPool(); + + private ThreadUtils() {} + + public static void execute(Runnable runnable) { + executors.execute(runnable); + } + + public static boolean isOnMainThread() { + return Thread.currentThread() == Looper.getMainLooper().getThread(); + } + + public static void runOnUiThread(Runnable runnable) { + if (isOnMainThread()) { + runnable.run(); + } else { + postOnUiThread(runnable); + } + } + + public static boolean postOnUiThread(Runnable runnable) { + return handler.post(runnable); + } + + public static boolean postOnUiThreadDelayed(Runnable runnable, long delayMillis) { + return handler.postDelayed(runnable, delayMillis); + } +} diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-ldrtl/chat_bubble_other_bg_light.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-ldrtl/chat_bubble_other_bg_light.xml new file mode 100644 index 0000000..e68d78c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-ldrtl/chat_bubble_other_bg_light.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-ldrtl/chat_bubble_self_bg_light.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-ldrtl/chat_bubble_self_bg_light.xml new file mode 100644 index 0000000..4803d45 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-ldrtl/chat_bubble_self_bg_light.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_community_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_community_light.png new file mode 100644 index 0000000..67d36c2 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_community_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_meeting_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_meeting_light.png new file mode 100644 index 0000000..f32ec47 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_meeting_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_public_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_public_light.png new file mode 100644 index 0000000..03f988e Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_public_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_work_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_work_light.png new file mode 100644 index 0000000..9002d45 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_group_icon_work_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_user_icon_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_user_icon_light.png new file mode 100644 index 0000000..b384546 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_default_user_icon_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_online_status_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_online_status_light.png new file mode 100644 index 0000000..cf07e72 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_online_status_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_selected_icon_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_selected_icon_light.png new file mode 100644 index 0000000..ae46e89 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_selected_icon_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_title_bar_back_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_title_bar_back_light.png new file mode 100644 index 0000000..83d658d Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable-xxhdpi/core_title_bar_back_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_bubble_other_bg_light.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_bubble_other_bg_light.xml new file mode 100644 index 0000000..ad3338f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_bubble_other_bg_light.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_bubble_self_bg_light.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_bubble_self_bg_light.xml new file mode 100644 index 0000000..493753c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_bubble_self_bg_light.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_reply_icon_light.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_reply_icon_light.png new file mode 100644 index 0000000..19e4b81 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/chat_reply_icon_light.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/core_title_bar_bg_light.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/core_title_bar_bg_light.xml new file mode 100644 index 0000000..7a5193b --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/drawable/core_title_bar_bg_light.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/values/light_colors.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/values/light_colors.xml new file mode 100644 index 0000000..5df62e8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/values/light_colors.xml @@ -0,0 +1,10 @@ + + + #FF000000 + #ECECEC + @color/core_bubble_bg_color_light + #888888 + + #679CE1 + #888888 + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/values/light_styles.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/values/light_styles.xml new file mode 100644 index 0000000..16d8503 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-light/values/light_styles.xml @@ -0,0 +1,28 @@ + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-ldrtl/chat_bubble_other_bg_lively.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-ldrtl/chat_bubble_other_bg_lively.xml new file mode 100644 index 0000000..5c1677a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-ldrtl/chat_bubble_other_bg_lively.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-ldrtl/chat_bubble_self_bg_lively.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-ldrtl/chat_bubble_self_bg_lively.xml new file mode 100644 index 0000000..b77ba54 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-ldrtl/chat_bubble_self_bg_lively.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_community_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_community_lively.png new file mode 100644 index 0000000..028d844 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_community_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_meeting_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_meeting_lively.png new file mode 100644 index 0000000..5480bcb Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_meeting_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_public_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_public_lively.png new file mode 100644 index 0000000..18dd84c Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_public_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_work_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_work_lively.png new file mode 100644 index 0000000..32e2fbf Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_group_icon_work_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_user_icon_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_user_icon_lively.png new file mode 100644 index 0000000..b384546 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_default_user_icon_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_online_status_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_online_status_lively.png new file mode 100644 index 0000000..69b05d8 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_online_status_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_selected_icon_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_selected_icon_lively.png new file mode 100644 index 0000000..c86e1ac Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_selected_icon_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_title_bar_back_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_title_bar_back_lively.png new file mode 100644 index 0000000..2072634 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable-xxhdpi/core_title_bar_back_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_bubble_other_bg_lively.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_bubble_other_bg_lively.xml new file mode 100644 index 0000000..a3cd187 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_bubble_other_bg_lively.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_bubble_self_bg_lively.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_bubble_self_bg_lively.xml new file mode 100644 index 0000000..b757580 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_bubble_self_bg_lively.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_reply_icon_lively.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_reply_icon_lively.png new file mode 100644 index 0000000..5e9158c Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/chat_reply_icon_lively.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/core_title_bar_bg_lively.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/core_title_bar_bg_lively.xml new file mode 100644 index 0000000..3d6ec23 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/drawable/core_title_bar_bg_lively.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/values/lively_colors.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/values/lively_colors.xml new file mode 100644 index 0000000..c22b28d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/values/lively_colors.xml @@ -0,0 +1,10 @@ + + + #FFFFFFFF + #F8F8F9 + @color/core_bubble_bg_color_lively + #FFFFFF + + #ECA08E + #888888 + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/values/lively_styles.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/values/lively_styles.xml new file mode 100644 index 0000000..caf8e02 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-lively/values/lively_styles.xml @@ -0,0 +1,30 @@ + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-ldrtl/chat_bubble_other_bg_serious.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-ldrtl/chat_bubble_other_bg_serious.xml new file mode 100644 index 0000000..26df0f8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-ldrtl/chat_bubble_other_bg_serious.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-ldrtl/chat_bubble_self_bg_serious.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-ldrtl/chat_bubble_self_bg_serious.xml new file mode 100644 index 0000000..b04976a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-ldrtl/chat_bubble_self_bg_serious.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_community_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_community_serious.png new file mode 100644 index 0000000..1a2e268 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_community_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_meeting_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_meeting_serious.png new file mode 100644 index 0000000..76b9e15 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_meeting_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_public_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_public_serious.png new file mode 100644 index 0000000..a4f3e7b Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_public_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_work_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_work_serious.png new file mode 100644 index 0000000..7081399 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_group_icon_work_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_user_icon_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_user_icon_serious.png new file mode 100644 index 0000000..b384546 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_default_user_icon_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_online_status_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_online_status_serious.png new file mode 100644 index 0000000..ec31c4a Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_online_status_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_selected_icon_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_selected_icon_serious.png new file mode 100644 index 0000000..3486026 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_selected_icon_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_title_bar_back_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_title_bar_back_serious.png new file mode 100644 index 0000000..2072634 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable-xxhdpi/core_title_bar_back_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_bubble_other_bg_serious.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_bubble_other_bg_serious.xml new file mode 100644 index 0000000..6676701 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_bubble_other_bg_serious.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_bubble_self_bg_serious.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_bubble_self_bg_serious.xml new file mode 100644 index 0000000..5e21bac --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_bubble_self_bg_serious.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_reply_icon_serious.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_reply_icon_serious.png new file mode 100644 index 0000000..19e4b81 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/chat_reply_icon_serious.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/core_title_bar_bg_serious.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/core_title_bar_bg_serious.xml new file mode 100644 index 0000000..9471fbc --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/drawable/core_title_bar_bg_serious.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/values/serious_colors.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/values/serious_colors.xml new file mode 100644 index 0000000..6c2f1dc --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/values/serious_colors.xml @@ -0,0 +1,9 @@ + + + #FFFFFFFF + #F8F8F9 + @color/core_bubble_bg_color_serious + #FFFFFF + #5695E7 + #888888 + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/values/serious_styles.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/values/serious_styles.xml new file mode 100644 index 0000000..702f917 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res-serious/values/serious_styles.xml @@ -0,0 +1,30 @@ + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/common_bottom_select_sheet_enter.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/common_bottom_select_sheet_enter.xml new file mode 100644 index 0000000..b3d4b5c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/common_bottom_select_sheet_enter.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/common_bottom_select_sheet_exit.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/common_bottom_select_sheet_exit.xml new file mode 100644 index 0000000..802d60a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/common_bottom_select_sheet_exit.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/core_popup_in_anim.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/core_popup_in_anim.xml new file mode 100644 index 0000000..74069b9 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/core_popup_in_anim.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/core_popup_out_anim.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/core_popup_out_anim.xml new file mode 100644 index 0000000..7dd9512 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/anim/core_popup_out_anim.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/color/common_bg_negative_btn.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/color/common_bg_negative_btn.xml new file mode 100644 index 0000000..40cd9cd --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/color/common_bg_negative_btn.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/color/common_bg_positive_btn.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/color/common_bg_positive_btn.xml new file mode 100644 index 0000000..8322df9 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/color/common_bg_positive_btn.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_bubble_other_transparent_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_bubble_other_transparent_bg.xml new file mode 100644 index 0000000..3f74d66 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_bubble_other_transparent_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_bubble_self_transparent_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_bubble_self_transparent_bg.xml new file mode 100644 index 0000000..6c2ccc1 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_bubble_self_transparent_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_fill_border_right.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_fill_border_right.xml new file mode 100644 index 0000000..da1d6b0 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_fill_border_right.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_risk_content_border_left.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_risk_content_border_left.xml new file mode 100644 index 0000000..cb1a181 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_risk_content_border_left.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_risk_content_border_right.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_risk_content_border_right.xml new file mode 100644 index 0000000..1ca644a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_risk_content_border_right.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_stroke_border_left.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_stroke_border_left.xml new file mode 100644 index 0000000..a95f4c0 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_stroke_border_left.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_stroke_border_right.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_stroke_border_right.xml new file mode 100644 index 0000000..b1547a6 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable-ldrtl/chat_message_popup_stroke_border_right.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_bubble_other_transparent_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_bubble_other_transparent_bg.xml new file mode 100644 index 0000000..a29c9ec --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_bubble_other_transparent_bg.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_bubble_self_transparent_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_bubble_self_transparent_bg.xml new file mode 100644 index 0000000..e89b11e --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_bubble_self_transparent_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_checkbox_selector.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_checkbox_selector.xml new file mode 100644 index 0000000..fa67e5c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_checkbox_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_gray_round_rect_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_gray_round_rect_bg.xml new file mode 100644 index 0000000..900bad8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_gray_round_rect_bg.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_bottom_area_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_bottom_area_bg.xml new file mode 100644 index 0000000..198b891 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_bottom_area_bg.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_bottom_area_risk_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_bottom_area_risk_bg.xml new file mode 100644 index 0000000..586bae4 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_bottom_area_risk_bg.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_fill_border.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_fill_border.xml new file mode 100644 index 0000000..b4c5cdf --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_fill_border.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_fill_border_right.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_fill_border_right.xml new file mode 100644 index 0000000..306aac3 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_fill_border_right.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_risk_content_border_left.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_risk_content_border_left.xml new file mode 100644 index 0000000..5a02bc5 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_risk_content_border_left.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_risk_content_border_right.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_risk_content_border_right.xml new file mode 100644 index 0000000..4612a7d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_risk_content_border_right.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border.xml new file mode 100644 index 0000000..9b0dba2 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border_left.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border_left.xml new file mode 100644 index 0000000..b1547a6 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border_left.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border_right.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border_right.xml new file mode 100644 index 0000000..a95f4c0 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_message_popup_stroke_border_right.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading00.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading00.png new file mode 100644 index 0000000..062ce9d Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading00.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading01.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading01.png new file mode 100644 index 0000000..41c1724 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading01.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading02.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading02.png new file mode 100644 index 0000000..23aac51 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading02.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading03.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading03.png new file mode 100644 index 0000000..32cc0f6 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading03.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading04.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading04.png new file mode 100644 index 0000000..ef8899f Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading04.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading05.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading05.png new file mode 100644 index 0000000..4141cdd Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading05.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading06.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading06.png new file mode 100644 index 0000000..9002deb Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading06.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading07.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading07.png new file mode 100644 index 0000000..8443261 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading07.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading08.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading08.png new file mode 100644 index 0000000..7879c4b Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading08.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading09.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading09.png new file mode 100644 index 0000000..89a5fa3 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading09.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading10.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading10.png new file mode 100644 index 0000000..3e7c121 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading10.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading11.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading11.png new file mode 100644 index 0000000..7b0c309 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading11.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading12.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading12.png new file mode 100644 index 0000000..884cdff Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading12.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading13.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading13.png new file mode 100644 index 0000000..917feba Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading13.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading14.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading14.png new file mode 100644 index 0000000..b5f1ad3 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading14.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading15.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading15.png new file mode 100644 index 0000000..8cd3a40 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading15.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading16.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading16.png new file mode 100644 index 0000000..f2cc6ee Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading16.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading17.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading17.png new file mode 100644 index 0000000..e6305cb Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading17.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading18.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading18.png new file mode 100644 index 0000000..06f9008 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading18.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading19.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading19.png new file mode 100644 index 0000000..672181d Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading19.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading20.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading20.png new file mode 100644 index 0000000..902e3e2 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading20.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading21.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading21.png new file mode 100644 index 0000000..afc12d5 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading21.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading22.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading22.png new file mode 100644 index 0000000..ca5d98a Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading22.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading23.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading23.png new file mode 100644 index 0000000..ebc5872 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading23.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading24.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading24.png new file mode 100644 index 0000000..6392044 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading24.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading25.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading25.png new file mode 100644 index 0000000..5830e1c Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading25.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading26.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading26.png new file mode 100644 index 0000000..819db1a Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading26.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading27.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading27.png new file mode 100644 index 0000000..f9653c1 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading27.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading28.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading28.png new file mode 100644 index 0000000..842fef4 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading28.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading29.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading29.png new file mode 100644 index 0000000..9555de6 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading29.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading30.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading30.png new file mode 100644 index 0000000..a6ebf21 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading30.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading31.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading31.png new file mode 100644 index 0000000..281a9e3 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading31.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading32.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading32.png new file mode 100644 index 0000000..0dc299c Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading32.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading33.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading33.png new file mode 100644 index 0000000..7ebe0b1 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading33.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading34.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading34.png new file mode 100644 index 0000000..8c7a556 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading34.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading35.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading35.png new file mode 100644 index 0000000..040edf7 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading35.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading36.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading36.png new file mode 100644 index 0000000..2135b47 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading36.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading37.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading37.png new file mode 100644 index 0000000..f5582bf Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading37.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading38.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading38.png new file mode 100644 index 0000000..e6a366c Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading38.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading39.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading39.png new file mode 100644 index 0000000..bf0cc02 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading39.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading40.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading40.png new file mode 100644 index 0000000..43a4f7e Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading40.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading41.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading41.png new file mode 100644 index 0000000..a0960b5 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading41.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading42.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading42.png new file mode 100644 index 0000000..d52b140 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading42.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading43.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading43.png new file mode 100644 index 0000000..0122042 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading43.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading44.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading44.png new file mode 100644 index 0000000..a7d64da Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_anim_loading44.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_file_download_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_file_download_icon.png new file mode 100644 index 0000000..4c83f96 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_file_download_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_all_read.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_all_read.png new file mode 100644 index 0000000..02d8ea0 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_all_read.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_failed.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_failed.png new file mode 100644 index 0000000..cac29f3 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_failed.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_no_read.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_no_read.png new file mode 100644 index 0000000..3d94f35 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_no_read.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_part_read.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_part_read.png new file mode 100644 index 0000000..ada629c Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_message_status_send_part_read.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_status_loading_anim.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_status_loading_anim.xml new file mode 100644 index 0000000..964ff92 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_minimalist_status_loading_anim.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_react_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_react_bg.xml new file mode 100644 index 0000000..52af45a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_react_bg.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_reply_more_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_reply_more_icon.png new file mode 100644 index 0000000..20c7ce5 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_reply_more_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_unselected_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_unselected_icon.png new file mode 100644 index 0000000..357ddc8 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/chat_unselected_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_arrow_right.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_arrow_right.png new file mode 100644 index 0000000..a3ea33a Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_arrow_right.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_bottom_sheet_border.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_bottom_sheet_border.xml new file mode 100644 index 0000000..7a4ca9f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_bottom_sheet_border.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_check_box_selected.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_check_box_selected.png new file mode 100644 index 0000000..f2d0c79 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_check_box_selected.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_check_box_unselected.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_check_box_unselected.png new file mode 100644 index 0000000..630c712 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_check_box_unselected.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_dialog_react_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_dialog_react_bg.xml new file mode 100644 index 0000000..fa64736 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_dialog_react_bg.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_edit_cursor.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_edit_cursor.xml new file mode 100644 index 0000000..03016a8 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_edit_cursor.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_edit_text_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_edit_text_bg.xml new file mode 100644 index 0000000..68c4f6a --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_edit_text_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_item_pressed_effect_background.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_item_pressed_effect_background.xml new file mode 100644 index 0000000..d64661f --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_item_pressed_effect_background.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_title_bar_home_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_title_bar_home_icon.png new file mode 100644 index 0000000..7897e8b Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_title_bar_home_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_trans_bg.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_trans_bg.png new file mode 100644 index 0000000..113bfca Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/common_trans_bg.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_close_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_close_icon.png new file mode 100644 index 0000000..57a14c2 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_close_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_default_group_icon_community.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_default_group_icon_community.png new file mode 100644 index 0000000..1cc8d44 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_default_group_icon_community.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_delete_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_delete_icon.png new file mode 100644 index 0000000..02c74fe Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_delete_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_icon_offline_status.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_icon_offline_status.png new file mode 100644 index 0000000..f326e14 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_icon_offline_status.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_list_divider.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_list_divider.xml new file mode 100644 index 0000000..59cf049 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_list_divider.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_minimalist_back_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_minimalist_back_icon.png new file mode 100644 index 0000000..257376f Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_minimalist_back_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_bg.xml new file mode 100644 index 0000000..9063d53 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_bg.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_disable_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_disable_bg.xml new file mode 100644 index 0000000..a8b242b --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_disable_bg.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_normal_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_normal_bg.xml new file mode 100644 index 0000000..745901d --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_normal_bg.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_pressed_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_pressed_bg.xml new file mode 100644 index 0000000..f53a321 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_positive_btn_pressed_bg.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_search_icon.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_search_icon.png new file mode 100644 index 0000000..76585b3 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/core_search_icon.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/indicator_point_nomal.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/indicator_point_nomal.png new file mode 100644 index 0000000..3249233 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/indicator_point_nomal.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/indicator_point_select.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/indicator_point_select.png new file mode 100644 index 0000000..3b6aa41 Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/indicator_point_select.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/message_send_fail.png b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/message_send_fail.png new file mode 100644 index 0000000..ede352a Binary files /dev/null and b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/message_send_fail.png differ diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_switch_thumb.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_switch_thumb.xml new file mode 100644 index 0000000..d21a142 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_switch_thumb.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_switch_track.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_switch_track.xml new file mode 100644 index 0000000..3f0c1d4 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_switch_track.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_translation_area_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_translation_area_bg.xml new file mode 100644 index 0000000..64d5036 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/minimalist_translation_area_bg.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/popup_card_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/popup_card_bg.xml new file mode 100644 index 0000000..325f6cd --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/popup_card_bg.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/quote_message_area_bg.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/quote_message_area_bg.xml new file mode 100644 index 0000000..87bd817 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/quote_message_area_bg.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/selected_border.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/selected_border.xml new file mode 100644 index 0000000..dd04892 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/selected_border.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb.xml new file mode 100644 index 0000000..a76dc22 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb_blue.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb_blue.xml new file mode 100644 index 0000000..2fba5a6 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb_blue.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb_gray.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb_gray.xml new file mode 100644 index 0000000..280eaff --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_thumb_gray.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track.xml new file mode 100644 index 0000000..986f41b --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track_blue.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track_blue.xml new file mode 100644 index 0000000..51e0eb2 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track_blue.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track_gray.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track_gray.xml new file mode 100644 index 0000000..ee7467c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/drawable/switch_track_gray.xml @@ -0,0 +1,16 @@ + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/chat_minimalist_reply_preview_layout.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/chat_minimalist_reply_preview_layout.xml new file mode 100644 index 0000000..e98ec46 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/chat_minimalist_reply_preview_layout.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/chat_minimalist_text_status_layout.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/chat_minimalist_text_status_layout.xml new file mode 100644 index 0000000..557a85c --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/chat_minimalist_text_status_layout.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_bottom_select_sheet.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_bottom_select_sheet.xml new file mode 100644 index 0000000..9da8bb0 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_bottom_select_sheet.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_bottom_sheet_item.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_bottom_sheet_item.xml new file mode 100644 index 0000000..94adbc7 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_bottom_sheet_item.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_dialog_view_layout.xml b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_dialog_view_layout.xml new file mode 100644 index 0000000..3448353 --- /dev/null +++ b/CoalMiningKit/TIMCommon/timcommon/src/main/res/layout/common_dialog_view_layout.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + +