初始代码
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# OS X Files
|
||||
.DS_Store
|
||||
**/.DS_Store
|
||||
|
||||
|
||||
# Gradle files
|
||||
*/Android/.gradle/
|
||||
|
||||
# IDEA Files
|
||||
*/Android/.idea/
|
||||
|
||||
*/Android/local.properties
|
||||
@@ -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.
|
||||
@@ -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 群进行技术交流和反馈问题。
|
||||
|
||||

|
||||
@@ -0,0 +1 @@
|
||||
/build/
|
||||
@@ -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'
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application >
|
||||
|
||||
<activity
|
||||
android:name="com.tencent.qcloud.tuikit.timcommon.component.activities.SelectionActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="com.tencent.qcloud.tuikit.timcommon.component.activities.ImageSelectActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.tencent.qcloud.tuikit.timcommon.component.activities.SelectionMinimalistActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
<activity
|
||||
android:name="com.tencent.qcloud.tuikit.timcommon.component.activities.ImageSelectMinimalistActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity android:name="com.tencent.qcloud.tuikit.timcommon.util.ActivityResultResolver$ActivityResultProxyActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:multiprocess="true"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/CoreActivityTranslucent"
|
||||
android:windowSoftInputMode="stateHidden|stateAlwaysHidden"/>
|
||||
|
||||
<provider
|
||||
android:name="com.tencent.qcloud.tuikit.timcommon.util.FileProvider"
|
||||
android:authorities="${applicationId}.timcommon.fileprovider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/file_paths_public"/>
|
||||
</provider>
|
||||
|
||||
</application>
|
||||
</manifest>
|
||||
+61
@@ -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;
|
||||
}
|
||||
}
|
||||
+26
@@ -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();
|
||||
}
|
||||
}
|
||||
+69
@@ -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<? extends ChatFace> 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<? extends ChatFace> faceGroup) {
|
||||
this.faceGroup = faceGroup;
|
||||
}
|
||||
|
||||
public FaceGroup<? extends ChatFace> 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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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;
|
||||
}
|
||||
}
|
||||
+98
@@ -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<T extends ChatFace> {
|
||||
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<String, T> 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<T> 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;
|
||||
}
|
||||
}
|
||||
+15
@@ -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;
|
||||
}
|
||||
}
|
||||
+128
@@ -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;
|
||||
}
|
||||
}
|
||||
+30
@@ -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;
|
||||
}
|
||||
}
|
||||
+55
@@ -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;
|
||||
}
|
||||
}
|
||||
+117
@@ -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<ReplyBean> 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<ReplyBean> getReplies() {
|
||||
return replies;
|
||||
}
|
||||
|
||||
public void setReplies(List<ReplyBean> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+477
@@ -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<String, UserBean> 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<MessageRepliesBean.ReplyBean> 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<String> getAdditionalUserIDList() {
|
||||
Set<String> userIdSet = new HashSet<>();
|
||||
MessageRepliesBean messageRepliesBean = getMessageRepliesBean();
|
||||
if (messageRepliesBean != null && messageRepliesBean.getRepliesSize() > 0) {
|
||||
List<MessageRepliesBean.ReplyBean> 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<? extends TUIReplyQuoteBean> getReplyQuoteBeanClass() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.tencent.qcloud.tuikit.timcommon.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public abstract class TUIReplyQuoteBean<T extends TUIMessageBean> 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;
|
||||
}
|
||||
}
|
||||
+82
@@ -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;
|
||||
}
|
||||
}
|
||||
+181
@@ -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<T extends TUIMessageBean> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+708
@@ -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<T extends TUIMessageBean> extends MessageBaseHolder<T> {
|
||||
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<TUIMessageBean> 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<TUIMessageBean> dataSource) {
|
||||
if (dataSource == null || dataSource.isEmpty()) {
|
||||
mForwardDataSource = null;
|
||||
}
|
||||
|
||||
List<TUIMessageBean> 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<TUIMessageBean> 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<String, Object> 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<String> idList = new ArrayList<>();
|
||||
idList.add(msg.getSender());
|
||||
V2TIMManager.getInstance().getUsersInfo(idList, new V2TIMValueCallback<List<V2TIMUserFullInfo>>() {
|
||||
@Override
|
||||
public void onSuccess(List<V2TIMUserFullInfo> 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<Drawable> placeholderRequest = Glide.with(itemView.getContext())
|
||||
.load(defaultIconResId)
|
||||
.transform(new RoundedCorners(radius))
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL);
|
||||
RequestBuilder<Drawable> 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<String, Object> 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;
|
||||
}
|
||||
}
|
||||
+512
@@ -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<SelectionHelper> 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;
|
||||
}
|
||||
}
|
||||
+31
@@ -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<T extends TUIReplyQuoteBean<?>> 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) {}
|
||||
|
||||
}
|
||||
+93
@@ -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<String> selectList = new ArrayList<>();
|
||||
private Dialog dialog;
|
||||
private ArrayAdapter<String> 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<String> selectList) {
|
||||
this.selectList.clear();
|
||||
this.selectList.addAll(selectList);
|
||||
}
|
||||
|
||||
public void setOnClickListener(BottomSelectSheetOnClickListener onClickListener) {
|
||||
this.onClickListener = onClickListener;
|
||||
}
|
||||
|
||||
public interface BottomSelectSheetOnClickListener {
|
||||
void onSheetClick(int index);
|
||||
}
|
||||
}
|
||||
+34
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -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;
|
||||
}
|
||||
}
|
||||
+126
@@ -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<ImageView> 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();
|
||||
}
|
||||
}
|
||||
+144
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -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);
|
||||
}
|
||||
}
|
||||
+44
@@ -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);
|
||||
}
|
||||
}
|
||||
+159
@@ -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);
|
||||
}
|
||||
}
|
||||
+39
@@ -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);
|
||||
}
|
||||
}
|
||||
+278
@@ -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();
|
||||
}
|
||||
}
|
||||
+126
@@ -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);
|
||||
}
|
||||
}
|
||||
+127
@@ -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);
|
||||
}
|
||||
}
|
||||
+59
@@ -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<SwitchCompat> clazz = SwitchCompat.class;
|
||||
Field mSwitchWidthFiled = clazz.getDeclaredField("mSwitchWidth");
|
||||
mSwitchWidthFiled.setAccessible(true);
|
||||
mSwitchWidthFiled.set(this, customSwitchWidth);
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
+186
@@ -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;
|
||||
}
|
||||
}
|
||||
+84
@@ -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);
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.tencent.qcloud.tuikit.timcommon.component.action;
|
||||
|
||||
public interface PopActionClickListener {
|
||||
void onActionClick(int index, Object data);
|
||||
}
|
||||
+63
@@ -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<PopMenuAction> 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;
|
||||
}
|
||||
}
|
||||
+51
@@ -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;
|
||||
}
|
||||
}
|
||||
+81
@@ -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<PopMenuAction> 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;
|
||||
}
|
||||
}
|
||||
+48
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+450
@@ -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<ImageBean> 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<ImageBean>) 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<File>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> 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<File> 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<ImageGridAdapter.ImageViewHolder> {
|
||||
private int itemWidth;
|
||||
private int itemHeight;
|
||||
|
||||
private List<ImageBean> data;
|
||||
private ImageBean selected;
|
||||
private int placeHolder;
|
||||
private OnItemClickListener onItemClickListener;
|
||||
|
||||
public void setData(List<ImageBean> 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<Object> 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<Object> getGroupGridAvatar() {
|
||||
return groupGridAvatar;
|
||||
}
|
||||
|
||||
public void setGroupGridAvatar(List<Object> groupGridAvatar) {
|
||||
this.groupGridAvatar = groupGridAvatar;
|
||||
}
|
||||
|
||||
public String getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
public void setImageId(String imageId) {
|
||||
this.imageId = imageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
+483
@@ -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<ImageBean> 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<ImageBean>) 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<File>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> 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<File> 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<ImageGridAdapter.ImageViewHolder> {
|
||||
private int itemWidth;
|
||||
private int itemHeight;
|
||||
|
||||
private List<ImageBean> data;
|
||||
private ImageBean selected;
|
||||
private int placeHolder;
|
||||
private OnItemClickListener onItemClickListener;
|
||||
|
||||
public void setData(List<ImageBean> 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<Object> 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<Object> 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<Object> getGroupGridAvatar() {
|
||||
return groupGridAvatar;
|
||||
}
|
||||
|
||||
public void setGroupGridAvatar(List<Object> groupGridAvatar) {
|
||||
this.groupGridAvatar = groupGridAvatar;
|
||||
}
|
||||
|
||||
public String getImageId() {
|
||||
return imageId;
|
||||
}
|
||||
|
||||
public void setImageId(String imageId) {
|
||||
this.imageId = imageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
+241
@@ -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<String> 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<String> 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<SelectAdapter.SelectViewHolder> {
|
||||
int selectedItem = -1;
|
||||
ArrayList<String> data = new ArrayList<>();
|
||||
|
||||
public void setData(ArrayList<String> 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;
|
||||
}
|
||||
}
|
||||
+241
@@ -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<String> 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<String> 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<SelectAdapter.SelectViewHolder> {
|
||||
int selectedItem = -1;
|
||||
ArrayList<String> data = new ArrayList<>();
|
||||
|
||||
public void setData(ArrayList<String> 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;
|
||||
}
|
||||
}
|
||||
+199
@@ -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);
|
||||
}
|
||||
}
|
||||
+329
@@ -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> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+546
@@ -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<String, Emoji> emojiMap = new LinkedHashMap<>();
|
||||
private final Context context;
|
||||
|
||||
private final Map<Integer, FaceGroup<?>> faceGroupMap = new ConcurrentHashMap<>();
|
||||
|
||||
private FaceManager() {
|
||||
context = TIMCommonService.getAppContext();
|
||||
}
|
||||
|
||||
private static FaceManager getInstance() {
|
||||
return FaceManagerHolder.instance;
|
||||
}
|
||||
|
||||
public static ArrayList<Emoji> getEmojiList() {
|
||||
return new ArrayList<>(getInstance().emojiMap.values());
|
||||
}
|
||||
|
||||
public static Map<String, Emoji> 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 <T extends ChatFace> void addFaceGroup(int groupID, FaceGroup<T> faceGroup) {
|
||||
faceGroup.setGroupID(groupID);
|
||||
getInstance().faceGroupMap.put(groupID, faceGroup);
|
||||
if (faceGroup.isEmojiGroup()) {
|
||||
List<T> faces = faceGroup.getFaces();
|
||||
for (T face : faces) {
|
||||
getInstance().emojiMap.put(face.getFaceKey(), (Emoji) face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<FaceGroup> 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<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> 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<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> 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<Bitmap>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Bitmap> target, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Bitmap resource, Object model, Target<Bitmap> 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<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> 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<EmojiData> 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<String> splitEmojiText(String text) {
|
||||
String regex = "\\[(\\S+?)\\]";
|
||||
Pattern p = Pattern.compile(regex);
|
||||
Matcher m = p.matcher(text);
|
||||
ArrayList<EmojiData> 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<String> 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<String> findEmojiKeyListFromText(String text) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
List<String> 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;
|
||||
}
|
||||
}
|
||||
+87
@@ -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<String> emojiList) {
|
||||
getInstance().putCollection(DEFAULT_RECENT_EMOJI_KEY, emojiList);
|
||||
}
|
||||
|
||||
public RecentEmojiManager putCollection(String key, List<String> 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<String> 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<String> collection = null;
|
||||
if (collectionObj instanceof List) {
|
||||
collection = (List<String>) collectionObj;
|
||||
}
|
||||
return collection;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> getCollection() {
|
||||
return getInstance().getCollection(DEFAULT_RECENT_EMOJI_KEY);
|
||||
}
|
||||
|
||||
public static void updateRecentUseEmoji(String emojiKey) {
|
||||
List<String> recentList = getCollection();
|
||||
recentList.remove(emojiKey);
|
||||
recentList.add(0, emojiKey);
|
||||
if (recentList.size() > DEFAULT_RECENT_NUM) {
|
||||
recentList.remove(recentList.size() - 1);
|
||||
}
|
||||
putCollection(recentList);
|
||||
}
|
||||
}
|
||||
+99
@@ -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<Object> imageUrls;
|
||||
int defaultImageResId;
|
||||
Map<Integer, Bitmap> 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<Object> imageUrls, int defaultImageResId) {
|
||||
this.imageUrls = imageUrls;
|
||||
this.defaultImageResId = defaultImageResId;
|
||||
}
|
||||
|
||||
public int getDefaultImageResId() {
|
||||
return defaultImageResId;
|
||||
}
|
||||
|
||||
public void setDefaultImageResId(int defaultImageResId) {
|
||||
this.defaultImageResId = defaultImageResId;
|
||||
}
|
||||
|
||||
public List<Object> getImageUrls() {
|
||||
return imageUrls;
|
||||
}
|
||||
|
||||
public void setImageUrls(List<Object> 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;
|
||||
}
|
||||
}
|
||||
+92
@@ -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<Bitmap> 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();
|
||||
}
|
||||
}
|
||||
+87
@@ -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<Object> 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();
|
||||
}
|
||||
}
|
||||
+12
@@ -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);
|
||||
}
|
||||
+334
@@ -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<Object> 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);
|
||||
}
|
||||
}
|
||||
+65
@@ -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<Object> iconUrls) {
|
||||
mIconView.displayImage(iconUrls).load(null);
|
||||
}
|
||||
}
|
||||
+136
@@ -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<String, WeakReference<HighlightListener>> highlightListenerMap = new HashMap<>();
|
||||
|
||||
private final Map<String, ValueAnimator> 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<HighlightListener> listener = highlightListenerMap.get(highlightID);
|
||||
if (listener != null) {
|
||||
return listener.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+120
@@ -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);
|
||||
}
|
||||
}
|
||||
+19
@@ -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);
|
||||
}
|
||||
+131
@@ -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
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.tencent.qcloud.tuikit.timcommon.component.interfaces;
|
||||
|
||||
public abstract class IUIKitCallback<T> {
|
||||
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 <O> void callbackOnSuccess(IUIKitCallback<O> callback, O data) {
|
||||
if (callback != null) {
|
||||
callback.onSuccess(data);
|
||||
}
|
||||
}
|
||||
|
||||
public static <O> void callbackOnError(IUIKitCallback<O> callback, int errCode, String errMsg, O data) {
|
||||
if (callback != null) {
|
||||
callback.onError(errCode, errMsg, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -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);
|
||||
}
|
||||
}
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
Copyright 2011, 2012 Chris Banes.
|
||||
<p/>
|
||||
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
|
||||
<p/>
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
<p/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
+29
@@ -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);
|
||||
}
|
||||
+18
@@ -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);
|
||||
}
|
||||
+14
@@ -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);
|
||||
}
|
||||
+22
@@ -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);
|
||||
}
|
||||
+17
@@ -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);
|
||||
}
|
||||
+21
@@ -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);
|
||||
}
|
||||
+16
@@ -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);
|
||||
}
|
||||
+16
@@ -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);
|
||||
}
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
Copyright 2011, 2012 Chris Banes.
|
||||
<p>
|
||||
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
|
||||
<p>
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
<p>
|
||||
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);
|
||||
}
|
||||
}
|
||||
+807
@@ -0,0 +1,807 @@
|
||||
/*
|
||||
Copyright 2011, 2012 Chris Banes.
|
||||
<p>
|
||||
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
|
||||
<p>
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
<p>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+39
@@ -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;
|
||||
}
|
||||
}
|
||||
+40
@@ -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;
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package com.tencent.qcloud.tuikit.timcommon.component.swipe;
|
||||
|
||||
public class Attributes {
|
||||
public enum Mode { Single, Multiple }
|
||||
}
|
||||
+85
@@ -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<VH extends RecyclerView.ViewHolder>
|
||||
extends RecyclerView.Adapter<VH> 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<Integer> getOpenItems() {
|
||||
return mItemManger.getOpenItems();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SwipeLayout> 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);
|
||||
}
|
||||
}
|
||||
+21
@@ -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) {}
|
||||
}
|
||||
+9
@@ -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);
|
||||
}
|
||||
+219
@@ -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<Integer> mOpenPositions = new HashSet<Integer>();
|
||||
protected Set<SwipeLayout> mShownLayouts = new HashSet<SwipeLayout>();
|
||||
|
||||
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<Integer> getOpenItems() {
|
||||
if (mode == Attributes.Mode.Multiple) {
|
||||
return new ArrayList<Integer>(mOpenPositions);
|
||||
} else {
|
||||
return Collections.singletonList(mOpenPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SwipeLayout> getOpenLayouts() {
|
||||
return new ArrayList<SwipeLayout>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -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<Integer> getOpenItems();
|
||||
|
||||
List<SwipeLayout> getOpenLayouts();
|
||||
|
||||
void removeShownLayouts(SwipeLayout layout);
|
||||
|
||||
boolean isOpen(int position);
|
||||
|
||||
Attributes.Mode getMode();
|
||||
|
||||
void setMode(Attributes.Mode mode);
|
||||
|
||||
void switchAllSwipeEnable(boolean enable);
|
||||
}
|
||||
+1806
File diff suppressed because it is too large
Load Diff
+74
@@ -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);
|
||||
}
|
||||
}
|
||||
+120
@@ -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();
|
||||
}
|
||||
}
|
||||
+153
@@ -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();
|
||||
}
|
||||
}
|
||||
+441
@@ -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) {}
|
||||
}
|
||||
}
|
||||
+324
@@ -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);
|
||||
}
|
||||
}
|
||||
+336
@@ -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;
|
||||
}
|
||||
}
|
||||
+273
@@ -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;
|
||||
}
|
||||
}
|
||||
+10
@@ -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<TUIMessageBean> callback) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.tencent.qcloud.tuikit.timcommon.interfaces;
|
||||
|
||||
public interface HighlightListener {
|
||||
|
||||
void onHighlightStart();
|
||||
|
||||
void onHighlightEnd();
|
||||
|
||||
void onHighlightUpdate(int color);
|
||||
}
|
||||
+15
@@ -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();
|
||||
}
|
||||
+295
@@ -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);
|
||||
}
|
||||
+25
@@ -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) {}
|
||||
}
|
||||
+14
@@ -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);
|
||||
}
|
||||
+29
@@ -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) {}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user