taomenggo init

This commit is contained in:
guozhen
2024-08-06 10:30:15 +08:00
committed by xuhuixiang
parent 3e7fd07f4f
commit c929efd05e
3007 changed files with 229844 additions and 77 deletions

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
* Use of this source code is governed by a MIT license that can be found in the LICENSE file.
*/
plugins {
id("com.android.library")
kotlin("android")
}
android {
compileSdk = 33
defaultConfig {
minSdk = 21
targetSdk = 33
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
buildConfigField("String", "versionName", "\"9.6.2\"")
}
buildTypes {
getByName("release") {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
buildFeatures {
viewBinding = true
}
sourceSets["main"].res.srcDirs("src/main/res","src/main/res-fun","src/main/res-normal")
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
namespace = "com.netease.yunxin.kit.contactkit.ui"
}
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
api("com.netease.yunxin.kit.chat:chatkit:9.6.2")
api("com.netease.yunxin.kit.common:common-ui:1.2.0")
api("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21")
implementation("androidx.appcompat:appcompat:1.4.2")
implementation("com.google.android.material:material:1.5.0")
implementation("androidx.recyclerview:recyclerview:1.2.1")
implementation("com.github.bumptech.glide:glide:4.13.1")
implementation("com.google.code.gson:gson:2.9.0")
}

View File

21
contactkit-ui/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.kts.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.netease.yunxin.kit.contactkit.ui">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<provider
android:name="com.netease.yunxin.kit.corekit.startup.InitializationProvider"
android:authorities="${applicationId}.xkit-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.netease.yunxin.kit.contactkit.ui.ContactUIService"
android:value="xkit.startup" />
</provider>
<activity
android:name=".normal.contact.ContactActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".normal.addfriend.AddFriendActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".normal.blacklist.BlackListActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".normal.team.TeamListActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".normal.verify.VerifyListActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".normal.userinfo.UserInfoActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".normal.userinfo.CommentActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".normal.selector.ContactSelectorActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".normal.search.GlobalSearchActivity"
android:exported="false"
android:screenOrientation="portrait" />
<!-- fun style activity -->
<activity
android:name=".fun.selector.FunBaseSelectorDataActivity"
android:exported="false"
android:screenOrientation="portrait" />
<activity
android:name=".fun.contact.FunContactActivity"
android:exported="false"
android:screenOrientation="portrait" />
<!-- <activity-->
<!-- android:name=".fun.addfriend.FunAddFriendActivity"-->
<!-- android:exported="false"-->
<!-- android:screenOrientation="portrait" />-->
<activity
android:name=".fun.blacklist.FunBlackListActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".fun.team.FunTeamListActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".fun.verify.FunVerifyListActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".fun.verify.FunVerifyDetailsActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".fun.userinfo.FunUserInfoActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".fun.userinfo.FunCommentActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".fun.search.FunSearchActivity"
android:exported="false"
android:screenOrientation="portrait" />
</application>
</manifest>

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
public class ContactConstant {
public static final String LIB_TAG = "ContactKit-UI";
public static final String USER_INFO_KEY = "UserInfo";
public static final String REQUEST_CONTACT_SELECTOR_AVATAR = "useravatar";
public static final String REQUEST_CONTACT_LAST_FRIENDSBEAN = "lastfiends";
public static final String REQUEST_CONTACT_ALL_FRIENDSBEAN = "allfiends";
public static final String PATH_FUN_CREATE_GROUP_PAGE = "creategroup";
public static final String REQUEST_FUN_CREATE_GROUP_AUTO = "autoinvited";
public static class SearchViewType {
public static final int USER = 1;
public static final int TEAM = 2;
public static final int GROUP = 3;
public static final int TITLE = 4;
}
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
public class ContactKitClient {
private static ContactUIConfig sContactConfig;
public static void setContactUIConfig(ContactUIConfig config) {
sContactConfig = config;
}
public static ContactUIConfig getContactUIConfig() {
return sContactConfig;
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
import android.util.SparseArray;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactClickListener;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactSelectorListener;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import java.util.List;
public class ContactUIConfig {
public static int INT_DEFAULT_NULL = -1;
//title config
public String title;
public Integer titleColor;
public boolean showTitleBar = false;
public boolean showTitleBarRight2Icon = false;
public Integer titleBarRight2Res;
public View.OnClickListener titleBarRight2Click;
public boolean showTitleBarRightIcon = false;
public Integer titleBarRightRes;
public View.OnClickListener titleBarRightClick;
//page
public boolean showHeader = false;
public List<ContactEntranceBean> headerData;
public ContactListViewAttrs contactAttrs;
public SparseArray<IContactClickListener> itemClickListeners = new SparseArray<>();
public SparseArray<IContactSelectorListener> itemSelectorListeners = new SparseArray<>();
public IContactFactory viewHolderFactory;
public IContactViewLayout customLayout;
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
import android.content.Context;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.chatkit.ChatService;
import com.netease.yunxin.kit.contactkit.ui.fun.blacklist.FunBlackListActivity;
import com.netease.yunxin.kit.contactkit.ui.fun.contact.FunContactActivity;
import com.netease.yunxin.kit.contactkit.ui.fun.search.FunSearchActivity;
import com.netease.yunxin.kit.contactkit.ui.fun.team.FunTeamListActivity;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
@Keep
public class ContactUIService extends ChatService {
@NonNull
@Override
public String getServiceName() {
return "ContactUIKit";
}
@NonNull
@Override
public String getVersionName() {
return BuildConfig.versionName;
}
@NonNull
@Override
public ChatService create(@NonNull Context context) {
//normal
// XKitRouter.registerRouter(
// RouterConstant.PATH_CONTACT_SELECTOR_PAGE, FunContactSelectorActivity.class);
// XKitRouter.registerRouter(RouterConstant.PATH_ADD_FRIEND_PAGE, FunAddFriendActivity.class);
// XKitRouter.registerRouter(RouterConstant.PATH_USER_INFO_PAGE, FunUserInfoActivity.class);
XKitRouter.registerRouter(RouterConstant.PATH_MY_TEAM_PAGE, FunTeamListActivity.class);
XKitRouter.registerRouter(RouterConstant.PATH_MY_BLACK_PAGE, FunBlackListActivity.class);
// XKitRouter.registerRouter(RouterConstant.PATH_MY_NOTIFICATION_PAGE, FunVerifyListActivity.class);
XKitRouter.registerRouter(RouterConstant.PATH_CONTACT_PAGE, FunContactActivity.class);
XKitRouter.registerRouter(RouterConstant.PATH_GLOBAL_SEARCH_PAGE, FunSearchActivity.class);
//fun
// XKitRouter.registerRouter(
// RouterConstant.PATH_FUN_CONTACT_SELECTOR_PAGE, FunContactSelectorActivity.class);
// XKitRouter.registerRouter(RouterConstant.PATH_FUN_ADD_FRIEND_PAGE, FunAddFriendActivity.class);
// XKitRouter.registerRouter(RouterConstant.PATH_FUN_USER_INFO_PAGE, FunUserInfoActivity.class);
XKitRouter.registerRouter(RouterConstant.PATH_FUN_MY_TEAM_PAGE, FunTeamListActivity.class);
// XKitRouter.registerRouter(RouterConstant.PATH_FUN_MY_BLACK_PAGE, FunBlackListActivity.class);
// XKitRouter.registerRouter(
// RouterConstant.PATH_FUN_MY_NOTIFICATION_PAGE, FunVerifyListActivity.class);
XKitRouter.registerRouter(RouterConstant.PATH_FUN_CONTACT_PAGE, FunContactActivity.class);
XKitRouter.registerRouter(RouterConstant.PATH_FUN_GLOBAL_SEARCH_PAGE, FunSearchActivity.class);
return this;
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
public class FetchCallbackImpl<T> implements FetchCallback<T> {
String data;
public FetchCallbackImpl(String data) {
this.data = data;
}
@Override
public void onSuccess(@Nullable T param) {}
@Override
public void onFailed(int code) {}
@Override
public void onException(@Nullable Throwable exception) {}
}

View File

@@ -0,0 +1,12 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
import androidx.fragment.app.Fragment;
public abstract class FragmentBuilder {
public abstract Fragment build();
}

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
public interface IContactFactory {
//Adapter获取数据对应的ViewType
int getItemViewType(BaseContactBean data);
//创建ViewHolder
BaseContactViewHolder createViewHolder(@NonNull ViewGroup parent, int viewType);
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
import com.netease.yunxin.kit.contactkit.ui.view.ContactLayout;
public interface IContactViewLayout {
void customizeContactLayout(final ContactLayout layout);
}

View File

@@ -0,0 +1,13 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
/** load listener */
public interface ILoadListener {
boolean hasMore();
void loadMore(Object last);
}

View File

@@ -0,0 +1,45 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
public abstract class BaseListActivity extends BaseActivity {
protected BaseListActivityLayoutBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = BaseListActivityLayoutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
binding.title.setOnBackIconClickListener(v -> onBackPressed());
initView();
initData();
}
protected void configViewHolderFactory() {}
protected abstract void initView();
protected abstract void initData();
public boolean onTouchEvent(MotionEvent event) {
if(null != this.getCurrentFocus()){
/**
* 点击空白位置 隐藏软键盘
*/
InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
return super .onTouchEvent(event);
}
}

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.addfriend;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.chatkit.repo.ContactRepo;
import com.netease.yunxin.kit.common.ui.viewmodel.BaseViewModel;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.ContactConstant;
import com.netease.yunxin.kit.corekit.im.IMKitClient;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AddFriendViewModel extends BaseViewModel {
private static final String TAG = "AddFriendViewModel";
private final MutableLiveData<FetchResult<UserInfo>> resultLiveData = new MutableLiveData<>();
private FetchResult<UserInfo> fetchResult = new FetchResult<>(LoadStatus.Finish);
public MutableLiveData<FetchResult<UserInfo>> getFetchResult() {
return resultLiveData;
}
public void fetchUser(String account) {
ALog.d(LIB_TAG, TAG, "fetchUser:" + account);
List<String> accountList = new ArrayList<>();
accountList.add(account);
fetchResult.setStatus(LoadStatus.Loading);
resultLiveData.postValue(fetchResult);
ContactRepo.fetchUserInfo(
accountList,
new FetchCallback<List<UserInfo>>() {
@Override
public void onSuccess(@Nullable List<UserInfo> param) {
ALog.d(LIB_TAG, TAG, "fetchUser,onSuccess:" + (param == null));
if (param != null && param.size() > 0) {
fetchResult.setStatus(LoadStatus.Success);
fetchResult.setData(param.get(0));
} else {
fetchResult.setData(null);
fetchResult.setStatus(LoadStatus.Success);
}
resultLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "fetchUser,onFailed:" + code);
fetchResult.setError(code, "");
resultLiveData.postValue(fetchResult);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "fetchUser,onException");
fetchResult.setStatus(LoadStatus.Error);
resultLiveData.postValue(fetchResult);
}
});
}
}

View File

@@ -0,0 +1,495 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.addfriend;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.fun.addfriend.FunAddFriendsBottomDialog;
import com.netease.yunxin.kit.contactkit.ui.fun.userinfo.FunContactBottomConfirmDialog;
import com.netease.yunxin.kit.contactkit.ui.model.ContactUserInfoBean;
import com.netease.yunxin.kit.contactkit.ui.userinfo.BaseUserInfoActivity;
import com.netease.yunxin.kit.contactkit.ui.userinfo.UserInfoViewModel;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.corekit.im.IMKitClient;
import com.netease.yunxin.kit.corekit.im.model.FriendVerifyType;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.Objects;
public abstract class BaseAddFriendActivity extends BaseActivity {
protected AddFriendViewModel viewModel;
protected UserInfoViewModel userinfoviewModel;
protected LinearLayout layoutInputID;
private View rootView;
protected EditText etAddFriendAccount;
protected View ivAddFriendBack;
protected View ivFriendClear;
protected View ivFriendAccountClear;
protected View addFriendEmptyLayout;
protected View ivAccountSearch;
protected View ivPhoneSearch;
protected EditText etAddFriendPhone;
protected TextView tvAddFriendID;
protected TextView tvAddFriendPhone;
protected RelativeLayout layoutInputPhone;
protected LinearLayout layoutUserInfo;
protected ContactAvatarView avatarView;
protected TextView tvUserName;
protected TextView tvUserTips;
protected TextView tvUserinfohint;
protected boolean isShowId = true;
protected TextView tvArea;
protected onBaseAddClickListener onBaseAddClickListener;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootView = initViewAndGetRootView(savedInstanceState);
checkViews();
setContentView(rootView);
viewModel = new ViewModelProvider(this).get(AddFriendViewModel.class);
userinfoviewModel = new ViewModelProvider(this).get(UserInfoViewModel.class);
etAddFriendAccount.setOnEditorActionListener(actionListener);
etAddFriendPhone.setOnEditorActionListener(actionListener);
//viewModel.getFetchResult().observe(this, this::loadData);
ivAddFriendBack.setOnClickListener(v -> this.finish());
ivFriendClear.setOnClickListener(v -> etAddFriendPhone.setText(null));
ivFriendAccountClear.setOnClickListener(v -> etAddFriendAccount.setText(null));
ivAccountSearch.setOnClickListener(view -> {
if (onBaseAddClickListener != null && !TextUtils.isEmpty(etAddFriendAccount.getText().toString())) {
onBaseAddClickListener.doSearch(etAddFriendAccount.getText().toString());
}
});
ivPhoneSearch.setOnClickListener(view -> {
if (onBaseAddClickListener != null && !TextUtils.isEmpty(etAddFriendPhone.getText().toString())) {
onBaseAddClickListener.doSearch(etAddFriendPhone.getText().toString());
}
});
etAddFriendAccount.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(s.toString())) {
ivFriendAccountClear.setVisibility(View.GONE);
layoutUserInfo.setVisibility(View.GONE);
showEmptyView(false);
} else {
ivFriendAccountClear.setVisibility(View.VISIBLE);
}
}
});
etAddFriendPhone.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(s.toString())) {
ivFriendClear.setVisibility(View.GONE);
layoutUserInfo.setVisibility(View.GONE);
showEmptyView(false);
} else {
ivFriendClear.setVisibility(View.VISIBLE);
}
}
});
tvAddFriendID.setOnClickListener(view -> {
if (!isShowId) {
setShowSearchType(true);
}
});
tvAddFriendPhone.setOnClickListener(view -> {
if (isShowId) {
setShowSearchType(false);
}
});
// tvArea.setOnClickListener(view -> {
// if (onBaseAddClickListener != null) {
// onBaseAddClickListener.doSearch();
// }
// });
}
public void setOnBaseAddClickListener(BaseAddFriendActivity.onBaseAddClickListener onBaseAddClickListener) {
this.onBaseAddClickListener = onBaseAddClickListener;
}
public void setShowSearchType(boolean showId) {
if (showId) {
layoutInputID.setVisibility(View.VISIBLE);
layoutInputPhone.setVisibility(View.GONE);
addFriendEmptyLayout.setVisibility(View.GONE);
layoutUserInfo.setVisibility(View.GONE);
tvAddFriendID.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_selected_search), null, null, null);
tvAddFriendPhone.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_unselected_search), null, null, null);
} else {
layoutInputID.setVisibility(View.GONE);
layoutInputPhone.setVisibility(View.VISIBLE);
addFriendEmptyLayout.setVisibility(View.GONE);
layoutUserInfo.setVisibility(View.GONE);
tvAddFriendID.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_unselected_search), null, null, null);
tvAddFriendPhone.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.ic_selected_search), null, null, null);
}
isShowId = showId;
}
protected abstract View initViewAndGetRootView(Bundle savedInstanceState);
protected void checkViews() {
Objects.requireNonNull(rootView);
Objects.requireNonNull(etAddFriendAccount);
Objects.requireNonNull(ivAddFriendBack);
Objects.requireNonNull(ivFriendClear);
Objects.requireNonNull(addFriendEmptyLayout);
}
private final EditText.OnEditorActionListener actionListener =
(v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String accountId = v.getEditableText().toString();
if (!TextUtils.isEmpty(accountId)) {
if (onBaseAddClickListener != null) {
onBaseAddClickListener.doSearch(accountId);
}
//viewModel.fetchUser(accountId);
}
}
return false;
};
public void loadDataResult(int result, String msg, UserInfo userInfo, boolean isVerify) {
if (result == 1) {
if (userInfo != null) {
showEmptyView(false);
ColorUtils.loadAvator(userInfo.getAvatar(), avatarView);
// avatarView.setData(userInfo.getAvatar(),
// userInfo.getName() == null ? "" : userInfo.getName());
tvUserName.setText(userInfo.getName());
if (TextUtils.equals(userInfo.getAccount(), IMKitClient.account())) {
tvUserinfohint.setText(getString(R.string.fun_contact_searchresult_text));
tvUserTips.setVisibility(View.VISIBLE);
tvUserTips.setText(getResources().getString(R.string.fun_contact_searchself_txt));
tvUserinfohint.setOnClickListener(view -> {
// XKitRouter.withKey(RouterConstant.PATH_MINE_INFO_PAGE).withContext(this).navigate();
startProfileActivity(userInfo);
});
} else {
userinfoviewModel
.getFriendFetchResult()
.observe(
this,
mapFetchResult -> {
if (mapFetchResult.getLoadStatus() == LoadStatus.Success) {
ContactUserInfoBean userInfoData = mapFetchResult.getData();
if (userInfoData.isFriend) {
tvUserinfohint.setText(getString(R.string.chat));
tvUserTips.setVisibility(View.VISIBLE);
tvUserTips.setText(getResources().getString(R.string.fun_contact_searchfriends_txt));
tvUserinfohint.setOnClickListener(view -> {
XKitRouter.withKey(RouterConstant.PATH_CHAT_P2P_PAGE)
.withParam(RouterConstant.CHAT_ID_KRY, userInfoData.data.getAccount())
.withContext(this)
.navigate();
// XKitRouter.withKey(RouterConstant.PATH_USER_INFO_PAGE)
// .withContext(this)
// .withParam(RouterConstant.KEY_ACCOUNT_ID_KEY, userInfo.getAccount())
// .navigate();
// startProfileActivity(userInfo);
});
} else {
tvUserinfohint.setText(getString(R.string.fun_contact_searchresult_addtext));
tvUserTips.setVisibility(View.GONE);
tvUserinfohint.setOnClickListener(view -> {
showSendMsgDialog(IMKitClient.getUserInfo(), userInfoData, isVerify);
});
}
} else {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
}
}
});
userinfoviewModel.fetchData(userInfo.getAccount());
}
layoutUserInfo.setVisibility(View.VISIBLE);
} else {
layoutUserInfo.setVisibility(View.GONE);
showEmptyView(true);
}
} else {
Toast.makeText(
this, msg, Toast.LENGTH_SHORT)
.show();
}
}
private void loadData(FetchResult<UserInfo> result) {
if (result.getLoadStatus() == LoadStatus.Loading) {
//todo show loading progress
} else if (result.getLoadStatus() == LoadStatus.Success) {
if (result.getData() != null) {
showEmptyView(false);
UserInfo userInfo = result.getData();
avatarView.setData(userInfo.getAvatar(),
userInfo.getName() == null ? "" : userInfo.getName());
tvUserName.setText(userInfo.getName());
if (TextUtils.equals(userInfo.getAccount(), IMKitClient.account())) {
tvUserinfohint.setText("主页");
tvUserTips.setVisibility(View.VISIBLE);
tvUserTips.setText(getResources().getString(R.string.fun_contact_searchself_txt));
tvUserinfohint.setOnClickListener(view -> {
// XKitRouter.withKey(RouterConstant.PATH_MINE_INFO_PAGE).withContext(this).navigate();
startProfileActivity(userInfo);
});
} else {
userinfoviewModel
.getFriendFetchResult()
.observe(
this,
mapFetchResult -> {
if (mapFetchResult.getLoadStatus() == LoadStatus.Success) {
ContactUserInfoBean userInfoData = mapFetchResult.getData();
if (userInfoData.isFriend) {
tvUserinfohint.setText("主页");
tvUserTips.setVisibility(View.VISIBLE);
tvUserTips.setText(getResources().getString(R.string.fun_contact_searchfriends_txt));
tvUserinfohint.setOnClickListener(view -> {
// XKitRouter.withKey(RouterConstant.PATH_USER_INFO_PAGE)
// .withContext(this)
// .withParam(RouterConstant.KEY_ACCOUNT_ID_KEY, userInfo.getAccount())
// .navigate();
startProfileActivity(userInfo);
});
} else {
tvUserinfohint.setText("添加");
tvUserTips.setVisibility(View.GONE);
tvUserinfohint.setOnClickListener(view -> {
showSendMsgDialog(IMKitClient.getUserInfo(), userInfoData, true);
});
}
} else {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
}
}
});
userinfoviewModel.fetchData(userInfo.getAccount());
}
layoutUserInfo.setVisibility(View.VISIBLE);
} else {
layoutUserInfo.setVisibility(View.GONE);
showEmptyView(true);
}
} else if (result.getLoadStatus() == LoadStatus.Error) {
Toast.makeText(
this, getResources().getString(R.string.add_friend_search_error), Toast.LENGTH_SHORT)
.show();
}
}
FunAddFriendsBottomDialog addFriendsBottomDialog;
public void showSendMsgDialog(UserInfo userInfo, ContactUserInfoBean contactUserInfoBean, boolean isVerify) {
addFriendsBottomDialog = new FunAddFriendsBottomDialog(this)
.configUser(contactUserInfoBean.data.getName(), contactUserInfoBean.data.getAvatar());
addFriendsBottomDialog.show(
() -> {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
return;
}
//发送
String account = "";
if (contactUserInfoBean.data == null) {
if (contactUserInfoBean.friendInfo != null) {
account = contactUserInfoBean.friendInfo.getAccount();
}
} else {
account = contactUserInfoBean.data.getAccount();
}
addfriend(account, addFriendsBottomDialog.getVerifyMsg(), isVerify);
finish();
});
}
public void addfriend(String account, String verifymsg, boolean isVerify) {
userinfoviewModel.addFriendVerifyMsg(
account,
verifymsg,
isVerify,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
Toast.makeText(
BaseAddFriendActivity.this,
getResources().getString(R.string.add_friend_operate_success),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onFailed(int code) {
String tips = getResources().getString(R.string.add_friend_operate_fail);
Toast.makeText(
BaseAddFriendActivity.this,
String.format(tips, String.valueOf(code)),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onException(@Nullable Throwable exception) {
String tips = getResources().getString(R.string.add_friend_operate_fail);
Toast.makeText(
BaseAddFriendActivity.this,
String.format(tips, exception.getMessage()),
Toast.LENGTH_SHORT)
.show();
}
});
}
public void addFriendNoVerify(String account, String verifymsg) {
userinfoviewModel.addFriendVerifyMsg(
account,
verifymsg,
true,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
Toast.makeText(
BaseAddFriendActivity.this,
getResources().getString(R.string.add_friend_operate_success),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onFailed(int code) {
String tips = getResources().getString(R.string.add_friend_operate_fail);
Toast.makeText(
BaseAddFriendActivity.this,
String.format(tips, String.valueOf(code)),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onException(@Nullable Throwable exception) {
String tips = getResources().getString(R.string.add_friend_operate_fail);
Toast.makeText(
BaseAddFriendActivity.this,
String.format(tips, exception.getMessage()),
Toast.LENGTH_SHORT)
.show();
}
});
}
private void showEmptyView(boolean show) {
if (show) {
addFriendEmptyLayout.setVisibility(View.VISIBLE);
} else {
addFriendEmptyLayout.setVisibility(View.GONE);
}
}
protected void startProfileActivity(UserInfo userInfo) {
if (userInfo == null) {
return;
}
// if (TextUtils.equals(userInfo.getAccount(), IMKitClient.account())) {
// XKitRouter.withKey(RouterConstant.PATH_MINE_INFO_PAGE).withContext(this)
// .withParam(RouterConstant.KEY_ACCOUNT_ID_KEY, userInfo.getAccount()).navigate();
// } else {
// XKitRouter.withKey(RouterConstant.PATH_USER_INFO_PAGE)
// .withContext(this)
// .withParam("uid", userInfo.getAccount())
//
// .navigate();
// }
XKitRouter.withKey(RouterConstant.PATH_USER_INFO_PAGE)
.withContext(this)
.withParam(RouterConstant.KEY_ACCOUNT_ID_KEY, IMKitClient.account())
.withParam("userId", userInfo.getAccount())
.navigate();
}
public interface onBaseAddClickListener {
void doSearch(String idOrPhone);
// String selectorArea();
}
public boolean onTouchEvent(MotionEvent event) {
if(null != this.getCurrentFocus()){
/**
* 点击空白位置 隐藏软键盘
*/
InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
return super .onTouchEvent(event);
}
}

View File

@@ -0,0 +1,145 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.blacklist;
import static com.netease.yunxin.kit.corekit.im.utils.RouterConstant.REQUEST_CONTACT_SELECTOR_KEY;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.view.View;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.FetchCallbackImpl;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.activity.BaseListActivity;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import java.util.ArrayList;
import java.util.List;
public class BaseBlackListActivity extends BaseListActivity {
protected BlackListViewModel viewModel;
protected ActivityResultLauncher<Intent> blackListLauncher;
private final String TAG = "BaseBlackListActivity";
protected void initView() {
registerResult();
binding.tvTips.setVisibility(View.VISIBLE);
binding.tvTips.setText(R.string.black_list_tips);
configTitle(binding);
configViewHolderFactory();
}
protected void configTitle(BaseListActivityLayoutBinding binding) {
}
private void registerResult() {
blackListLauncher =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
return;
}
ArrayList<String> friends =
result.getData().getStringArrayListExtra(REQUEST_CONTACT_SELECTOR_KEY);
if (friends != null && friends.size() > 0) {
for (String account : friends) {
ALog.d(TAG, "addBlackList", "account:" + account);
viewModel.addBlackOp(
account,
new FetchCallbackImpl<Void>(account) {
@Override
public void onFailed(int code) {
String content =
String.format(
getResources().getString(R.string.add_black_error), account);
Toast.makeText(BaseBlackListActivity.this, content, Toast.LENGTH_SHORT)
.show();
}
@Override
public void onException(@Nullable Throwable exception) {
String content =
String.format(
getResources().getString(R.string.add_black_error), account);
Toast.makeText(BaseBlackListActivity.this, content, Toast.LENGTH_SHORT)
.show();
}
});
}
}
}
});
}
protected void initData() {
viewModel = new ViewModelProvider(this).get(BlackListViewModel.class);
viewModel
.getFetchResult()
.observe(
this,
result -> {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
return;
}
if (result.getLoadStatus() == LoadStatus.Success && result.getData() != null) {
ALog.d(TAG, "FetchResult", "Success:" + result.getData().size());
binding.contactListView.addContactData(result.getData());
}
// LoadStatus.Finish respect observer data changed
if (result.getLoadStatus() == LoadStatus.Finish && result.getData() != null) {
if (result.getType() == FetchResult.FetchType.Add) {
ALog.d(TAG, "FetchResult", "Add:" + result.getData().size());
binding.contactListView.addContactData(result.getData());
} else if (result.getType() == FetchResult.FetchType.Remove) {
ALog.d(TAG, "FetchResult", "Remove:" + result.getData().size());
binding.contactListView.removeContactData(result.getData());
}
}
List<BaseContactBean> list = binding.contactListView.getAdapter().getDataList();
if (list != null && list.size() == 0) {
binding.layoutEmpty.setVisibility(View.VISIBLE);
binding.contactListView.setVisibility(View.GONE);
} else {
binding.layoutEmpty.setVisibility(View.GONE);
binding.contactListView.setVisibility(View.VISIBLE);
}
setBtnBackground(false);
// if(binding.contactListView.getAdapter().getItemCount()==0){
// binding.tvBlackremove.setBackgroundResource(R.drawable.fun_cornor_6d_cccccc);
// }else{
// binding.tvBlackremove.setBackgroundResource(R.drawable.fun_cornor_6d_6b3689);
// }
});
viewModel.fetchBlackList();
}
public void setBtnBackground(boolean isEnable) {
if (isEnable) {
binding.tvBlackremove.setBackgroundTintList(null);
binding.tvBlackremove.setEnabled(true);
} else {
binding.tvBlackremove.setBackgroundTintList(ColorStateList.valueOf(getColor(R.color.color_6d80ff_alpha70)));
binding.tvBlackremove.setEnabled(false);
}
}
}

View File

@@ -0,0 +1,180 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.blacklist;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.chatkit.repo.ContactObserverRepo;
import com.netease.yunxin.kit.chatkit.repo.ContactRepo;
import com.netease.yunxin.kit.common.ui.viewmodel.BaseViewModel;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.model.ContactBlackListBean;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.provider.FriendChangeType;
import com.netease.yunxin.kit.corekit.im.provider.FriendObserver;
import java.util.ArrayList;
import java.util.List;
public class BlackListViewModel extends BaseViewModel {
private static final String TAG = "BlackListViewModel";
private final MutableLiveData<FetchResult<List<ContactBlackListBean>>> resultLiveData =
new MutableLiveData<>();
private final FetchResult<List<ContactBlackListBean>> fetchResult =
new FetchResult<>(LoadStatus.Finish);
private final List<ContactBlackListBean> blackList = new ArrayList<>();
private final FriendObserver friendObserver;
public MutableLiveData<FetchResult<List<ContactBlackListBean>>> getFetchResult() {
return resultLiveData;
}
public BlackListViewModel() {
friendObserver =
(friendChangeType, accountList) -> {
if (friendChangeType == FriendChangeType.RemoveBlack) {
removeBlackData(accountList);
}
if (friendChangeType == FriendChangeType.AddBlack) {
addBlackData(accountList);
}
};
ContactObserverRepo.registerFriendObserver(friendObserver);
}
public void fetchBlackList() {
ALog.d(LIB_TAG, TAG, "fetchBlackList");
fetchResult.setStatus(LoadStatus.Loading);
resultLiveData.postValue(fetchResult);
ContactRepo.getBlackList(
new FetchCallback<List<UserInfo>>() {
@Override
public void onSuccess(@Nullable List<UserInfo> param) {
ALog.d(
LIB_TAG,
TAG,
"fetchBlackList,onSuccess:" + (param == null ? "null" : param.size()));
blackList.clear();
if (param != null && param.size() > 0) {
fetchResult.setStatus(LoadStatus.Success);
for (UserInfo contactInfo : param) {
ContactBlackListBean friendBean = new ContactBlackListBean(contactInfo);
friendBean.friendInfo = ContactRepo.getFriend(contactInfo.getAccount());
blackList.add(friendBean);
}
fetchResult.setData(blackList);
} else {
fetchResult.setData(null);
fetchResult.setStatus(LoadStatus.Success);
}
resultLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "fetchBlackList,onFailed:" + code);
fetchResult.setError(code, "");
resultLiveData.postValue(fetchResult);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "fetchBlackList,onException");
fetchResult.setError(-1, "");
resultLiveData.postValue(fetchResult);
}
});
}
public void addBlackOp(String account, FetchCallback<Void> callback) {
ALog.d(LIB_TAG, TAG, "addBlackOp:" + account);
if (!TextUtils.isEmpty(account)) {
ContactRepo.addBlackList(account, callback);
}
}
public void removeBlackOp(String account, FetchCallback<Void> callback) {
ALog.d(LIB_TAG, TAG, "removeBlackOp:" + account);
ContactRepo.removeBlackList(account, callback);
}
private void removeBlackData(List<String> accountList) {
ALog.d(LIB_TAG, TAG, "removeBlackData:" + (accountList == null ? "null" : accountList.size()));
if (accountList == null || accountList.size() < 1) {
return;
}
List<ContactBlackListBean> delete = new ArrayList<>();
for (String account : accountList) {
for (ContactBlackListBean bean : blackList) {
if (TextUtils.equals(bean.data.getAccount(), account)) {
delete.add(bean);
blackList.remove(bean);
break;
}
}
}
//black list match
if (accountList.size() == delete.size()) {
fetchResult.setFetchType(FetchResult.FetchType.Remove);
fetchResult.setData(delete);
resultLiveData.setValue(fetchResult);
} else {
//black list has error,need to fetch
fetchBlackList();
}
}
private void addBlackData(List<String> accountList) {
ALog.d(LIB_TAG, TAG, "addBlackData:" + (accountList == null ? "null" : accountList.size()));
if (accountList == null || accountList.size() < 1) {
return;
}
List<ContactBlackListBean> add = new ArrayList<>();
ContactRepo.getUserInfo(
accountList,
new FetchCallback<List<UserInfo>>() {
@Override
public void onSuccess(@Nullable List<UserInfo> param) {
ALog.d(
LIB_TAG, TAG, "addBlackData,onSuccess:" + (param == null ? "null" : param.size()));
if (param != null && param.size() > 0) {
for (UserInfo contactInfo : param) {
ContactBlackListBean blackBean = new ContactBlackListBean(contactInfo);
blackBean.friendInfo = ContactRepo.getFriend(contactInfo.getAccount());
add.add(blackBean);
blackList.add(0, blackBean);
}
fetchResult.setFetchType(FetchResult.FetchType.Add);
fetchResult.setData(add);
resultLiveData.setValue(fetchResult);
}
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "addBlackData,onFailed:" + code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "addBlackData,exception");
}
});
}
@Override
protected void onCleared() {
super.onCleared();
ContactObserverRepo.unregisterFriendObserver(friendObserver);
}
}

View File

@@ -0,0 +1,55 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.contact;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.contactkit.ui.R;
import java.util.Objects;
public abstract class BaseContactActivity extends BaseActivity {
private View rootView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootView = initViewAndGetRootView(savedInstanceState);
checkViews();
setContentView(rootView);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager
.beginTransaction()
.add(R.id.contact_fragment_container, getContactFragment())
.commitAllowingStateLoss();
}
protected abstract View initViewAndGetRootView(Bundle savedInstanceState);
protected void checkViews() {
Objects.requireNonNull(rootView);
}
protected BaseContactFragment getContactFragment() {
return null;
}
public boolean onTouchEvent(MotionEvent event) {
if(null != this.getCurrentFocus()){
/**
* 点击空白位置 隐藏软键盘
*/
InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
return super .onTouchEvent(event);
}
}

View File

@@ -0,0 +1,313 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.contact;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import static com.netease.yunxin.kit.corekit.im.utils.RouterConstant.PATH_ADD_FRIEND_PAGE;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.common.ui.fragments.BaseFragment;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.ContactKitClient;
import com.netease.yunxin.kit.contactkit.ui.ContactUIConfig;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.interfaces.ContactActions;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactCallback;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.view.ContactLayout;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/** contact page */
public abstract class BaseContactFragment extends BaseFragment {
private final String TAG = "BaseContactFragment";
protected ContactViewModel viewModel;
private View rootView;
protected ContactLayout contactLayout;
protected View emptyView;
protected ContactUIConfig contactConfig;
protected Observer<FetchResult<List<ContactFriendBean>>> contactObserver;
protected Observer<FetchResult<List<ContactFriendBean>>> userInfoObserver;
protected IContactCallback contactCallback;
protected int headerCount = 0;
public void setContactConfig(ContactUIConfig config) {
contactConfig = config;
}
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = initViewAndGetRootView(inflater, container, savedInstanceState);
checkViews();
viewModel = new ViewModelProvider(this).get(ContactViewModel.class);
if (ContactKitClient.getContactUIConfig() != null && contactConfig == null) {
contactConfig = ContactKitClient.getContactUIConfig();
}
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
return rootView;
}
protected abstract View initViewAndGetRootView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState);
protected void checkViews() {
Objects.requireNonNull(rootView);
Objects.requireNonNull(contactLayout);
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
//data observer
contactObserver =
contactBeansResult -> {
if (contactBeansResult.getLoadStatus() == LoadStatus.Success) {
ALog.d(LIB_TAG, TAG, "contactObserver, Success");
contactLayout
.getContactListView()
.onFriendDataSourceChanged(contactBeansResult.getData());
} else if (contactBeansResult.getLoadStatus() == LoadStatus.Finish) {
if (contactBeansResult.getType() == FetchResult.FetchType.Add
&& contactBeansResult.getData() != null) {
ALog.d(LIB_TAG, TAG, "contactObserver,Add");
contactLayout.getContactListView().addFriendData(contactBeansResult.getData());
} else if (contactBeansResult.getType() == FetchResult.FetchType.Remove
&& contactBeansResult.getData() != null) {
ALog.d(LIB_TAG, TAG, "contactObserver,Remove");
contactLayout.getContactListView().removeFriendData(contactBeansResult.getData());
} else if (contactBeansResult.getType() == FetchResult.FetchType.Update
&& contactBeansResult.getData() != null) {
ALog.d(LIB_TAG, TAG, "contactObserver,Update");
contactLayout.getContactListView().updateFriendData(contactBeansResult.getData());
}
}
if (emptyView != null) {
if (contactLayout.getContactListView().getItemCount() - headerCount <= 0) {
emptyView.setVisibility(View.VISIBLE);
} else {
emptyView.setVisibility(View.GONE);
}
}
};
userInfoObserver =
userInfoResult -> {
if (userInfoResult.getLoadStatus() == LoadStatus.Finish) {
contactLayout.getContactListView().updateFriendData(userInfoResult.getData());
}
};
initView();
viewModel.getContactLiveData().observeForever(contactObserver);
viewModel.getUserInfoLiveData().observeForever(userInfoObserver);
viewModel.fetchContactList();
}
private void initView() {
loadTitle();
initContactAction();
loadConfig();
}
private void initContactAction() {
ContactActions actions = new ContactActions();
//entrance data
// loadHeader(actions);
//contact list
loadDefaultContactAction(actions);
//load contact action
loadContactAction(actions);
}
private void loadContactAction(ContactActions actions) {
if (contactConfig.itemClickListeners.size() > 0) {
for (int index = 0; index < contactConfig.itemClickListeners.size(); index++) {
actions.addContactListener(
contactConfig.itemClickListeners.keyAt(index),
contactConfig.itemClickListeners.valueAt(index));
}
}
if (contactConfig.itemSelectorListeners.size() > 0) {
for (int index = 0; index < contactConfig.itemSelectorListeners.size(); index++) {
actions.addSelectorListener(
contactConfig.itemSelectorListeners.keyAt(index),
contactConfig.itemSelectorListeners.valueAt(index));
}
}
contactLayout.getContactListView().setContactAction(actions);
}
private void loadConfig() {
if (contactConfig != null) {
if (contactConfig.viewHolderFactory != null) {
contactLayout.getContactListView().setViewHolderFactory(contactConfig.viewHolderFactory);
}
if (contactConfig.contactAttrs != null) {
contactLayout.getContactListView().setViewConfig(contactConfig.contactAttrs);
}
if (contactConfig.customLayout != null) {
contactConfig.customLayout.customizeContactLayout(contactLayout);
}
}
}
protected void loadDefaultContactAction(ContactActions actions) {
actions.addContactListener(
IViewTypeConstant.CONTACT_FRIEND,
(position, data) -> {
FriendInfo friendInfo = ((ContactFriendBean) data).data;
XKitRouter.withKey(RouterConstant.PATH_USER_INFO_PAGE)
.withContext(requireContext())
.withParam(RouterConstant.KEY_ACCOUNT_ID_KEY, friendInfo.getAccount())
.navigate();
});
}
public int getUnreadCount() {
return viewModel.getVerifyCount();
}
public void setContactCallback(IContactCallback contactCallback) {
this.contactCallback = contactCallback;
if (viewModel != null && contactCallback != null) {
this.contactCallback.updateUnreadCount(getUnreadCount());
}
}
private void loadHeader(ContactActions actions) {
if (contactConfig.showHeader) {
if (contactConfig.headerData == null) {
List<ContactEntranceBean> entranceBeanList = getContactEntranceList(this.requireContext());
headerCount = entranceBeanList.size();
viewModel.configVerifyBean(configVerifyBean());
for (ContactEntranceBean bean : entranceBeanList) {
contactLayout.getContactListView().addContactData(bean);
}
viewModel
.getContactEntranceLiveData()
.observe(
getViewLifecycleOwner(),
contactEntranceBean -> {
if (contactCallback != null) {
contactCallback.updateUnreadCount(contactEntranceBean.number);
}
contactLayout.getContactListView().updateContactData(contactEntranceBean);
});
actions.addContactListener(
IViewTypeConstant.CONTACT_ACTION_ENTER,
(position, data) -> {
if (!TextUtils.isEmpty(data.router)) {
XKitRouter.withKey(data.router)
.withContext(BaseContactFragment.this.requireContext())
.navigate();
}
});
} else {
contactLayout.getContactListView().addContactData(contactConfig.headerData);
}
}
}
protected void loadTitle() {
if (contactConfig.showTitleBar) {
contactLayout.getTitleBar().setVisibility(View.VISIBLE);
if (contactConfig.titleColor != null) {
contactLayout.getTitleBar().setTitleColor(contactConfig.titleColor);
}
if (contactConfig.title != null) {
contactLayout.getTitleBar().setTitle(contactConfig.title);
} else {
contactLayout.getTitleBar().setTitle(getResources().getString(R.string.contact_title));
}
} else {
contactLayout.getTitleBar().setVisibility(View.GONE);
}
if (contactConfig.showTitleBarRight2Icon) {
contactLayout.getTitleBar().showRight2ImageView(true);
if (contactConfig.titleBarRight2Res != null) {
contactLayout.getTitleBar().setRight2ImageRes(contactConfig.titleBarRight2Res);
}
contactLayout
.getTitleBar()
.setRight2ImageClick(
v -> {
if (contactConfig.titleBarRight2Click != null) {
contactConfig.titleBarRight2Click.onClick(v);
} else {
XKitRouter.withKey(RouterConstant.PATH_GLOBAL_SEARCH_PAGE)
.withContext(requireContext())
.navigate();
}
});
} else {
contactLayout.getTitleBar().showRight2ImageView(false);
}
if (contactConfig.showTitleBarRightIcon) {
contactLayout.getTitleBar().showRightImageView(true);
if (contactConfig.titleBarRightRes != null) {
contactLayout.getTitleBar().setRightImageRes(contactConfig.titleBarRightRes);
}
if (contactConfig.titleBarRightClick != null) {
contactLayout.getTitleBar().setRightImageClick(contactConfig.titleBarRightClick);
} else {
contactLayout
.getTitleBar()
.setRightImageClick(
v ->
XKitRouter.withKey(PATH_ADD_FRIEND_PAGE)
.withContext(requireContext())
.navigate());
}
} else {
contactLayout.getTitleBar().showRightImageView(false);
}
}
@Override
public void onDestroy() {
super.onDestroy();
ALog.d(LIB_TAG, TAG, "onDestroy");
if (viewModel != null) {
viewModel.getContactLiveData().removeObserver(contactObserver);
}
}
protected List<ContactEntranceBean> getContactEntranceList(Context context) {
return Collections.emptyList();
}
protected ContactEntranceBean configVerifyBean() {
return null;
}
}

View File

@@ -0,0 +1,394 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.contact;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import android.content.Context;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.chatkit.repo.ContactObserverRepo;
import com.netease.yunxin.kit.chatkit.repo.ContactRepo;
import com.netease.yunxin.kit.common.ui.utils.ToastX;
import com.netease.yunxin.kit.common.ui.viewmodel.BaseViewModel;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.provider.FriendChangeType;
import com.netease.yunxin.kit.corekit.im.provider.FriendObserver;
import com.netease.yunxin.kit.corekit.im.provider.LoginSyncObserver;
import com.netease.yunxin.kit.corekit.im.provider.SyncStatus;
import com.netease.yunxin.kit.corekit.im.provider.SystemUnreadCountObserver;
import com.netease.yunxin.kit.corekit.im.provider.UserInfoObserver;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import java.util.ArrayList;
import java.util.List;
/**
* contact view model
*/
public class ContactViewModel extends BaseViewModel {
private final String TAG = "ContactViewModel";
private final MutableLiveData<FetchResult<List<ContactFriendBean>>> contactLiveData =
new MutableLiveData<>();
private final FetchResult<List<ContactFriendBean>> fetchResult =
new FetchResult<>(LoadStatus.Finish);
private final List<ContactFriendBean> contactFriendBeanList = new ArrayList<>();
private final MutableLiveData<ContactEntranceBean> contactEntranceLiveData =
new MutableLiveData<>();
private final MutableLiveData<FetchResult<List<ContactFriendBean>>> userInfoLiveData =
new MutableLiveData<>();
private ContactEntranceBean verifyBean;
private int unreadCount = 0;
public ContactViewModel() {
registerObserver();
}
public LiveData<FetchResult<List<ContactFriendBean>>> getContactLiveData() {
return contactLiveData;
}
public LiveData<ContactEntranceBean> getContactEntranceLiveData() {
return contactEntranceLiveData;
}
public MutableLiveData<FetchResult<List<ContactFriendBean>>> getUserInfoLiveData() {
return userInfoLiveData;
}
public void fetchContactList() {
ALog.d(LIB_TAG, TAG, "fetchContactList");
ContactRepo.getContactList(
new FetchCallback<List<FriendInfo>>() {
@Override
public void onSuccess(@Nullable List<FriendInfo> param) {
ALog.d(
LIB_TAG,
TAG,
"fetchContactList,onSuccess:" + (param == null ? "null" : param.size()));
contactFriendBeanList.clear();
if (param != null) {
for (FriendInfo info : param) {
ContactFriendBean bean = new ContactFriendBean(info);
bean.viewType = IViewTypeConstant.CONTACT_FRIEND;
contactFriendBeanList.add(bean);
}
}
fetchResult.setStatus(LoadStatus.Success);
fetchResult.setData(contactFriendBeanList);
contactLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "fetchContactList,onFailed:" + code);
fetchResult.setError(code, "");
contactLiveData.postValue(fetchResult);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "fetchContactList,onException");
fetchResult.setError(-1, "");
contactLiveData.postValue(fetchResult);
}
});
ContactRepo.getNotificationUnreadCount(
new FetchCallback<Integer>() {
@Override
public void onSuccess(@Nullable Integer count) {
ALog.d(LIB_TAG, TAG, "fetchContactList,getNotificationUnreadCount,onSuccess:" + count);
updateVerifyNumber(count);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "fetchContactList,getNotificationUnreadCount,onFailed:" + code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "fetchContactList,getNotificationUnreadCount,onException");
}
});
}
private void registerObserver() {
ContactObserverRepo.registerFriendObserver(friendObserver);
ContactObserverRepo.registerUserInfoObserver(userInfoObserver);
ContactObserverRepo.registerNotificationUnreadCountObserver(unreadCountObserver);
ContactObserverRepo.registerLoginSyncObserver(loginSyncObserver);
}
private final UserInfoObserver userInfoObserver =
userList -> {
ALog.d(LIB_TAG, TAG, "userInfoObserver,userList:" + userList.size());
if (userList.size() > 0) {
FetchResult<List<ContactFriendBean>> result = new FetchResult<>(LoadStatus.Finish);
List<ContactFriendBean> updateBean = new ArrayList<>();
for (ContactFriendBean bean : contactFriendBeanList) {
for (UserInfo userInfo : userList) {
if (TextUtils.equals(userInfo.getAccount(), bean.data.getAccount())) {
bean.data.setUserInfo(userInfo);
updateBean.add(bean);
break;
}
}
}
result.setData(updateBean);
userInfoLiveData.setValue(result);
}
};
private final FriendObserver friendObserver =
(friendChangeType, accountList) -> {
if (friendChangeType == FriendChangeType.Delete
|| friendChangeType == FriendChangeType.AddBlack) {
ALog.d(LIB_TAG, TAG, "friendObserver,Delete||AddBlack:" + accountList.size());
removeFriend(accountList);
return;
}
if (friendChangeType == FriendChangeType.Update) {
ALog.d(LIB_TAG, TAG, "friendObserver,Update:" + accountList.size());
updateFriend(accountList);
return;
}
// get new friend account, add or remove
List<String> addFriendList = new ArrayList<>();
if (friendChangeType == FriendChangeType.RemoveBlack) {
for (String account : accountList) {
if (ContactRepo.isFriend(account)) {
addFriendList.add(account);
}
}
}
if (friendChangeType == FriendChangeType.Add) {
ALog.d(LIB_TAG, TAG, "friendObserver,Add:" + accountList.size());
addFriendList.addAll(accountList);
}
// fetch friend info and update to view
addFriend(addFriendList);
};
private final SystemUnreadCountObserver unreadCountObserver =
(count) -> {
updateVerifyNumber(count);
};
private void updateVerifyNumber(int count) {
ALog.d(LIB_TAG, TAG, "updateVerifyNumber:" + count);
if (verifyBean != null) {
if (count != unreadCount) {
verifyBean.number = count;
unreadCount = count;
contactEntranceLiveData.postValue(verifyBean);
}
} else {
unreadCount = count;
}
}
public int getVerifyCount() {
return unreadCount;
}
private final LoginSyncObserver loginSyncObserver =
(syncStatus) -> {
ALog.d(LIB_TAG, TAG, "loginSyncObserver:" + syncStatus.name());
if (syncStatus == SyncStatus.Complete) {
fetchContactList();
} else if (syncStatus == SyncStatus.BeginSync) {
fetchResult.setStatus(LoadStatus.Loading);
fetchResult.setData(null);
contactLiveData.postValue(fetchResult);
}
};
private void removeFriend(List<String> accountList) {
ALog.d(LIB_TAG, TAG, "removeFriend:" + (accountList == null ? "null" : accountList.size()));
if (accountList == null || accountList.isEmpty()) {
return;
}
List<ContactFriendBean> removeData = new ArrayList<>();
for (String account : accountList) {
for (ContactFriendBean bean : contactFriendBeanList) {
if (TextUtils.equals(account, bean.data.getAccount())) {
contactFriendBeanList.remove(bean);
removeData.add(bean);
ALog.d(LIB_TAG, TAG, "removeFriend:removeData add" + bean.data.getAccount());
break;
}
}
}
ALog.d(
LIB_TAG,
TAG,
"removeFriend:removeData" + (removeData == null ? "null" : removeData.size()));
if (!removeData.isEmpty()) {
FetchResult<List<ContactFriendBean>> removeResult =
new FetchResult<>(FetchResult.FetchType.Remove);
removeResult.setData(removeData);
contactLiveData.setValue(removeResult);
}
}
private void addFriend(List<String> accountList) {
ALog.d(LIB_TAG, TAG, "addFriend:" + (accountList == null ? "null" : accountList.size()));
assert accountList != null;
if (!accountList.isEmpty() && accountList.size() > 0) {
ContactRepo.getFriendListWithUserInfo(
accountList,
new FetchCallback<List<FriendInfo>>() {
@Override
public void onSuccess(@Nullable List<FriendInfo> param) {
setAddLivieData(param);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "addFriend,onFailed:" + code);
ToastX.showShortToast(String.valueOf(code));
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "addFriend,onException");
}
});
}
}
private void setAddLivieData(List<FriendInfo> addFriendList) {
List<ContactFriendBean> addList = new ArrayList<>();
for (FriendInfo friendInfo : addFriendList) {
ContactFriendBean bean = new ContactFriendBean(friendInfo);
bean.viewType = IViewTypeConstant.CONTACT_FRIEND;
if (contactFriendBeanList.contains(bean)) {
continue;
}
contactFriendBeanList.add(bean);
ALog.d(LIB_TAG, TAG, "addFriend,add:" + "id=" + friendInfo.getAccount());
addList.add(bean);
}
FetchResult<List<ContactFriendBean>> addResult = new FetchResult<>(FetchResult.FetchType.Add);
addResult.setData(addList);
contactLiveData.setValue(addResult);
}
private void updateFriend(List<String> accountList) {
ALog.d(LIB_TAG, TAG, "updateFriend:" + (accountList == null ? "null" : accountList.size()));
ContactRepo.getFriendListWithUserInfo(
accountList,
new FetchCallback<List<FriendInfo>>() {
@Override
public void onSuccess(@Nullable List<FriendInfo> param) {
ALog.d(
LIB_TAG, TAG, "updateFriend,onSuccess:" + (param == null ? "null" : param.size()));
boolean result = setUpdateLiveData(param);
//IM SDK 设备登录切换两个账号,都添加统一账号好友并同意,第二个账号收到的是更新好友通知不是添加
//更新的时候,发现如果好友不存在,则走添加的逻辑
if (!result) {
setAddLivieData(param);
}
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "updateFriend,onFailed:" + code);
ToastX.showShortToast(String.valueOf(code));
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "updateFriend,onException");
}
});
}
private boolean setUpdateLiveData(List<FriendInfo> updateList) {
List<ContactFriendBean> updateBean = new ArrayList<>();
for (FriendInfo friendInfo : updateList) {
if (friendInfo == null) {
continue;
}
for (ContactFriendBean bean : contactFriendBeanList) {
if (TextUtils.equals(friendInfo.getAccount(), bean.data.getAccount())) {
bean.data = friendInfo;
updateBean.add(bean);
break;
}
}
}
if (updateBean.size() > 0) {
FetchResult<List<ContactFriendBean>> updateResult =
new FetchResult<>(FetchResult.FetchType.Update);
updateResult.setData(updateBean);
contactLiveData.setValue(updateResult);
return true;
}
return false;
}
void configVerifyBean(ContactEntranceBean verifyBean) {
this.verifyBean = verifyBean;
if (this.verifyBean != null) {
this.verifyBean.number = unreadCount;
}
}
private List<ContactEntranceBean> getContactEntranceList(Context context) {
List<ContactEntranceBean> contactDataList = new ArrayList<>();
//verify message
verifyBean =
new ContactEntranceBean(
R.mipmap.ic_contact_verfiy_msg, context.getString(R.string.contact_list_verify_msg));
verifyBean.number = unreadCount;
verifyBean.router = RouterConstant.PATH_MY_NOTIFICATION_PAGE;
//black list
ContactEntranceBean blackBean =
new ContactEntranceBean(
R.mipmap.ic_contact_black_list, context.getString(R.string.contact_list_black_list));
blackBean.router = RouterConstant.PATH_MY_BLACK_PAGE;
//my group
ContactEntranceBean groupBean =
new ContactEntranceBean(
R.mipmap.ic_contact_my_group, context.getString(R.string.contact_list_my_group));
groupBean.router = RouterConstant.PATH_MY_TEAM_PAGE;
contactDataList.add(verifyBean);
contactDataList.add(blackBean);
contactDataList.add(groupBean);
return contactDataList;
}
public void pullContactView() {
}
@Override
protected void onCleared() {
super.onCleared();
ContactObserverRepo.unregisterFriendObserver(friendObserver);
ContactObserverRepo.unregisterUserInfoObserver(userInfoObserver);
ContactObserverRepo.unregisterNotificationUnreadCountObserver(unreadCountObserver);
}
}

View File

@@ -0,0 +1,149 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.ContactUIConfig;
import com.netease.yunxin.kit.contactkit.ui.FragmentBuilder;
import com.netease.yunxin.kit.contactkit.ui.fun.contact.FunContactFragment;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactClickListener;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactSelectorListener;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import java.util.List;
public class FunContactFragmentBuilder extends FragmentBuilder {
ContactUIConfig contactConfig;
public FunContactFragmentBuilder() {}
@Override
public FunContactFragment build() {
FunContactFragment fragment = new FunContactFragment();
if (contactConfig != null) {
fragment.setContactConfig(contactConfig);
}
return fragment;
}
public void setContactConfig(ContactUIConfig contactConfig) {
this.contactConfig = contactConfig;
}
public FunContactFragmentBuilder setShowTitleBar(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showTitleBar = show;
return this;
}
public FunContactFragmentBuilder setTitle(String title) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.title = title;
return this;
}
public FunContactFragmentBuilder setTitleColor(int color) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleColor = color;
return this;
}
public FunContactFragmentBuilder setShowTitleBarRight2Icon(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showTitleBarRight2Icon = show;
return this;
}
public FunContactFragmentBuilder setSearchIcon(int res) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRight2Res = res;
return this;
}
public FunContactFragmentBuilder setSearchClickListener(View.OnClickListener listener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRight2Click = listener;
return this;
}
public FunContactFragmentBuilder setShowMoreIcon(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showTitleBarRightIcon = show;
return this;
}
public FunContactFragmentBuilder setMoreIcon(int res) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRightRes = res;
return this;
}
public FunContactFragmentBuilder setMoreClickListener(View.OnClickListener listener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRightClick = listener;
return this;
}
public FunContactFragmentBuilder showHeader(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showHeader = show;
return this;
}
public FunContactFragmentBuilder setContactListViewAttar(ContactListViewAttrs attrs) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.contactAttrs = attrs;
return this;
}
public FunContactFragmentBuilder setHeaderData(List<ContactEntranceBean> data) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.headerData = data;
return this;
}
public FunContactFragmentBuilder setContactClickListener(
int type, IContactClickListener contactListener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.itemClickListeners.put(type, contactListener);
return this;
}
public FunContactFragmentBuilder setContactSelection(
int type, IContactSelectorListener contactListener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.itemSelectorListeners.put(type, contactListener);
return this;
}
}

View File

@@ -0,0 +1,106 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.addfriend;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunBottomAddfriendsMessageBinding;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunContactBottomConfirmDialogBinding;
import com.netease.yunxin.kit.contactkit.ui.fun.userinfo.FunContactBottomConfirmDialog;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
public class FunAddFriendsBottomDialog extends Dialog {
private static final String TAG = "FunBottomConfirmDialog";
private Runnable confirmRunnable;
private FunBottomAddfriendsMessageBinding binding;
public FunAddFriendsBottomDialog(@NonNull Activity activity) {
super(activity, R.style.SelectorFriendsBottomDialogTheme);
renderRootView();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Window window = getWindow();
// if (window != null) {
// WindowManager.LayoutParams wlp = window.getAttributes();
//// wlp.gravity = Gravity.BOTTOM;
// wlp.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
// wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
// wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
// window.setAttributes(wlp);
// }
setContentView(binding.getRoot());
binding.editAddfriendsRemarkmsg.requestFocus();
setCanceledOnTouchOutside(true);
setCancelable(true);
setOnDismissListener(dialog -> confirmRunnable = null);
}
public FunAddFriendsBottomDialog configUser(String name, String avatar) {
binding.tvUserName.setText(name);
ColorUtils.loadAvator(avatar,binding.cavUserIcon);
return this;
}
/**
* render page
*/
private void renderRootView() {
binding = FunBottomAddfriendsMessageBinding.inflate(getLayoutInflater());
binding.tvAddfriendsSend.setOnClickListener(
v -> {
if (confirmRunnable != null) {
confirmRunnable.run();
}
dismiss();
});
binding.viewCloseclick.setOnClickListener(v -> dismiss());
}
public String getVerifyMsg() {
return binding.editAddfriendsVerifymsg.getText().toString();
}
public String getRemarksMsg() {
return binding.editAddfriendsRemarkmsg.getText().toString();
}
public void show(Runnable confirmRunnable) {
if (isShowing()) {
return;
}
this.confirmRunnable = confirmRunnable;
try {
super.show();
} catch (Throwable throwable) {
ALog.e(TAG, "show FunBottomConfirmDialog", throwable);
}
}
public void dismiss() {
if (!isShowing()) {
return;
}
try {
super.dismiss();
} catch (Throwable throwable) {
ALog.e(TAG, "dismiss FunBottomConfirmDialog", throwable);
}
}
}

View File

@@ -0,0 +1,177 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.blacklist;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.blacklist.BaseBlackListActivity;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.fun.view.FunContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder.FunBlackListViewHolder;
import com.netease.yunxin.kit.contactkit.ui.model.ContactBlackListBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.ArrayList;
import java.util.List;
@Deprecated
public class FunBlackListActivity extends BaseBlackListActivity {
private FunBlackListViewHolder viewHolder;
private List<ContactBlackListBean> listBlackUserInfo = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
changeStatusBarColor(R.color.color_white);
binding.heimindanTv.setVisibility(View.VISIBLE);
binding.tvBlackremove.setVisibility(View.VISIBLE);
binding.tvBlackremove.setOnClickListener(view -> {
if (listBlackUserInfo.size() > 0) {
if (!NetworkUtils.isConnected()) {
Toast.makeText(
FunBlackListActivity.this,
R.string.contact_network_error_tip,
Toast.LENGTH_SHORT)
.show();
return;
}
for (ContactBlackListBean data : listBlackUserInfo) {
remove(data);
}
}
});
}
public void remove(ContactBlackListBean data) {
viewModel.removeBlackOp(
data.data.getAccount(),
new FetchCallback<Void>() {
@Override
public void onException(@Nullable Throwable exception) {
Toast.makeText(
FunBlackListActivity.this,
getText(R.string.remove_black_fail),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onFailed(int code) {
Toast.makeText(
FunBlackListActivity.this,
getText(R.string.remove_black_fail),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onSuccess(@Nullable Void param) {
binding.contactListView.removeContactData(data);
}
});
}
protected void configTitle(BaseListActivityLayoutBinding binding) {
binding
.title
.setTitle(R.string.black_list)
// .setActionImg(R.mipmap.ic_title_bar_more)
.setActionListener(
v -> {
XKitRouter.withKey(RouterConstant.PATH_FUN_CONTACT_SELECTOR_PAGE)
.withContext(this)
.navigate(blackListLauncher);
});
binding.title.getTitleTextView().setTextSize(17);
binding.title.getTitleTextView().setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
binding.title.setBackgroundResource(R.color.color_white);
}
public void setBlackRemoveBg() {
if (listBlackUserInfo.size() > 0) {
binding.tvBlackremove.setBackgroundResource(R.drawable.fun_cornor_12d_fe6881);
} else {
binding.tvBlackremove.setBackgroundResource(R.drawable.fun_cornor_6d_cccccc);
}
}
@Override
protected void configViewHolderFactory() {
binding.contactListView.setViewHolderFactory(
new FunContactViewHolderFactory() {
@Override
protected BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType) {
if (viewType == IViewTypeConstant.CONTACT_BLACK_LIST) {
viewHolder = new FunBlackListViewHolder(view);
viewHolder.setItemCheckedListener((data, isCheck) -> {
if (isCheck) {
listBlackUserInfo.add(data);
} else {
listBlackUserInfo.remove(data);
}
setBlackRemoveBg();
});
// viewHolder.setRelieveListener(
// data -> {
// if (!NetworkUtils.isConnected()) {
// Toast.makeText(
// FunBlackListActivity.this,
// R.string.contact_network_error_tip,
// Toast.LENGTH_SHORT)
// .show();
// return;
// }
// viewModel.removeBlackOp(
// data.data.getAccount(),
// new FetchCallback<Void>() {
//
// @Override
// public void onException(@Nullable Throwable exception) {
// Toast.makeText(
// FunBlackListActivity.this,
// getText(R.string.remove_black_fail),
// Toast.LENGTH_SHORT)
// .show();
// }
//
// @Override
// public void onFailed(int code) {
// Toast.makeText(
// FunBlackListActivity.this,
// getText(R.string.remove_black_fail),
// Toast.LENGTH_SHORT)
// .show();
// }
//
// @Override
// public void onSuccess(@Nullable Void param) {
// binding.contactListView.removeContactData(data);
// }
// });
// });
return viewHolder;
}
return null;
}
});
}
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.contact;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.contact.BaseContactActivity;
import com.netease.yunxin.kit.contactkit.ui.contact.BaseContactFragment;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunContactActivityBinding;
public class FunContactActivity extends BaseContactActivity {
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
FunContactActivityBinding viewBinding =
FunContactActivityBinding.inflate(LayoutInflater.from(this));
return viewBinding.getRoot();
}
protected BaseContactFragment getContactFragment() {
return new FunContactFragment();
}
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.contact;
import android.view.ViewGroup;
import com.netease.yunxin.kit.contactkit.ui.fun.view.FunContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
public class FunContactDefaultFactory extends FunContactViewHolderFactory {
@Override
protected BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType) {
return null;
}
}

View File

@@ -0,0 +1,210 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.contact;
import static com.netease.yunxin.kit.corekit.im.utils.RouterConstant.PATH_FUN_ADD_FRIEND_PAGE;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.contact.BaseContactFragment;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunContactFragmentBinding;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunContactTopSearchViewBinding;
import com.netease.yunxin.kit.contactkit.ui.interfaces.ContactActions;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.ArrayList;
import java.util.List;
/**
* contact page
*/
public class FunContactFragment extends BaseContactFragment {
private final String TAG = "FunContactFragment";
private FunContactFragmentBinding viewBinding;
private ContactEntranceBean verifyBean;
@Override
protected View initViewAndGetRootView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
viewBinding = FunContactFragmentBinding.inflate(inflater, container, false);
contactLayout = viewBinding.contactLayout;
contactLayout
.getContactListView()
.getDecoration()
.setTitleAlignBottom(true)
.setShowTagOff(false)
.setIndexDecorationBg(getResources().getColor(R.color.title_transfer))
.setColorTitleBottomLine(getResources().getColor(R.color.title_transfer));
// loadconfig();
FunContactDefaultFactory funContactDefaultFactory = new FunContactDefaultFactory();
funContactDefaultFactory.setCenter(true);
contactLayout.getContactListView().setViewHolderFactory(funContactDefaultFactory);
contactLayout
.getContactListView()
.configIndexTextBGColor(getResources().getColor(R.color.color_58be6b));
contactLayout.getDivideLineForTitle().setVisibility(View.GONE);
// addTopView(inflater, contactLayout.getBodyTopLayout());
emptyView = viewBinding.emptyLayout;
return viewBinding.getRoot();
}
private void loadconfig() {
ContactListViewAttrs attrs = new ContactListViewAttrs();
attrs.setShowIndexBar(true);
attrs.setShowPinTop(true);
contactLayout.getContactListView().setViewConfig(attrs);
}
private void addTopView(LayoutInflater inflater, ViewGroup topGroup) {
topGroup.setBackgroundResource(R.color.color_ededed);
FunContactTopSearchViewBinding topSearchViewBinding =
FunContactTopSearchViewBinding.inflate(inflater, topGroup, true);
topSearchViewBinding
.getRoot()
.setOnClickListener(
v -> {
if (contactConfig.titleBarRight2Click != null) {
contactConfig.titleBarRight2Click.onClick(v);
} else {
XKitRouter.withKey(RouterConstant.PATH_FUN_GLOBAL_SEARCH_PAGE)
.withContext(requireContext())
.navigate();
}
});
}
@Override
protected void loadDefaultContactAction(ContactActions actions) {
actions.addContactListener(
IViewTypeConstant.CONTACT_FRIEND,
(position, data) -> {
FriendInfo friendInfo = ((ContactFriendBean) data).data;
XKitRouter.withKey(RouterConstant.PATH_FUN_CHAT_P2P_PAGE)
.withParam(RouterConstant.CHAT_ID_KRY, friendInfo.getAccount())
.withContext(requireActivity())
.navigate();
// XKitRouter.withKey(RouterConstant.PATH_FUN_USER_INFO_PAGE)
// .withContext(requireContext())
// .withParam(RouterConstant.KEY_ACCOUNT_ID_KEY, friendInfo.getAccount())
// .navigate();
});
}
protected void loadTitle() {
if (contactConfig.showTitleBar) {
viewBinding.contactLayout.getTitleBar().setTitleBgRes(R.color.color_ededed);
viewBinding.contactLayout.getTitleBar().getTitleTextView().setVisibility(View.GONE);
viewBinding
.contactLayout
.getTitleBar()
.getCenterTitleTextView()
.setTextColor(getResources().getColor(R.color.title_color));
viewBinding.contactLayout.getTitleBar().getCenterTitleTextView().setTextSize(17);
viewBinding.contactLayout.getTitleBar().setVisibility(View.VISIBLE);
if (contactConfig.titleColor != null) {
viewBinding
.contactLayout
.getTitleBar()
.getCenterTitleTextView()
.setTextColor(contactConfig.titleColor);
}
if (contactConfig.title != null) {
viewBinding
.contactLayout
.getTitleBar()
.getCenterTitleTextView()
.setText(contactConfig.title);
} else {
viewBinding
.contactLayout
.getTitleBar()
.getCenterTitleTextView()
.setText(getResources().getString(R.string.contact_title));
}
} else {
viewBinding.contactLayout.getTitleBar().setVisibility(View.GONE);
}
viewBinding.contactLayout.getTitleBar().showRight2ImageView(false);
if (contactConfig.showTitleBarRightIcon) {
viewBinding.contactLayout.getTitleBar().showRightImageView(true);
if (contactConfig.titleBarRightRes != null) {
viewBinding.contactLayout.getTitleBar().setRightImageRes(contactConfig.titleBarRightRes);
} else {
viewBinding
.contactLayout
.getTitleBar()
.setRightImageRes(R.drawable.fun_ic_contact_add_friend);
}
if (contactConfig.titleBarRightClick != null) {
viewBinding
.contactLayout
.getTitleBar()
.setRightImageClick(contactConfig.titleBarRightClick);
} else {
viewBinding
.contactLayout
.getTitleBar()
.setRightImageClick(
v ->
XKitRouter.withKey(PATH_FUN_ADD_FRIEND_PAGE)
.withContext(requireContext())
.navigate());
}
} else {
viewBinding.contactLayout.getTitleBar().showRightImageView(false);
}
}
@Override
protected List<ContactEntranceBean> getContactEntranceList(Context context) {
List<ContactEntranceBean> contactDataList = new ArrayList<>();
//verify message
verifyBean =
new ContactEntranceBean(
R.mipmap.fun_ic_contact_verfiy_msg,
context.getString(R.string.contact_list_verify_msg));
verifyBean.router = RouterConstant.PATH_FUN_MY_NOTIFICATION_PAGE;
verifyBean.showRightArrow = false;
//black list
ContactEntranceBean blackBean =
new ContactEntranceBean(
R.mipmap.fun_ic_contact_black_list,
context.getString(R.string.contact_list_black_list));
blackBean.router = RouterConstant.PATH_FUN_MY_BLACK_PAGE;
blackBean.showRightArrow = false;
//my group
ContactEntranceBean groupBean =
new ContactEntranceBean(
R.mipmap.fun_ic_contact_my_group, context.getString(R.string.contact_list_my_group));
groupBean.router = RouterConstant.PATH_FUN_MY_TEAM_PAGE;
groupBean.showRightArrow = false;
contactDataList.add(verifyBean);
contactDataList.add(blackBean);
contactDataList.add(groupBean);
return contactDataList;
}
@Override
protected ContactEntranceBean configVerifyBean() {
return verifyBean;
}
}

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.search;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.yunxin.kit.common.utils.SizeUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunSearchActivityBinding;
import com.netease.yunxin.kit.contactkit.ui.search.page.BaseSearchActivity;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
public class FunSearchActivity extends BaseSearchActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setStatusBarColor(getResources().getColor(R.color.color_fcfcfc));
getWindow().setNavigationBarColor(getResources().getColor(R.color.color_white));
// changeStatusBarColor(R.color.fun_contact_page_bg_color);
}
@Override
protected View initViewAndGetRootView() {
FunSearchActivityBinding viewBinding = FunSearchActivityBinding.inflate(getLayoutInflater());
searchRv = viewBinding.rvSearch;
clearView = viewBinding.ivClear;
emptyView = viewBinding.emptyLl;
searchEditText = viewBinding.etSearch;
backView = viewBinding.funTitleBar.getBackImageView();
searchRv.addItemDecoration(getItemDecoration());
return viewBinding.getRoot();
}
@Override
protected void bindView() {
super.bindView();
searchAdapter.setViewHolderFactory(new FunSearchViewHolderFactory());
routerFriend = RouterConstant.PATH_FUN_CHAT_P2P_PAGE;
routerTeam = RouterConstant.PATH_FUN_CHAT_TEAM_PAGE;
}
public RecyclerView.ItemDecoration getItemDecoration() {
return new RecyclerView.ItemDecoration() {
final int topPadding = SizeUtils.dp2px(0.5f);
final int indent = SizeUtils.dp2px(16);
@Override
public void onDrawOver(
@NonNull Canvas canvas, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
int left = parent.getPaddingLeft() + indent;
int right = parent.getWidth() - parent.getPaddingRight();
int childCount = parent.getChildCount();
for (int i = 0; i < childCount - 1; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int top = child.getBottom() + params.bottomMargin;
int bottom = top + topPadding;
Paint paint = new Paint();
paint.setColor(Color.LTGRAY);
canvas.drawRect(left, top, right, bottom, paint);
}
}
};
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.search;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.common.ui.viewholder.IViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.ContactConstant;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunSearchTitleViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunSearchUserViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.fun.search.viewholder.FunSearchFriendViewHolder;
import com.netease.yunxin.kit.contactkit.ui.fun.search.viewholder.FunSearchTeamViewHolder;
import com.netease.yunxin.kit.contactkit.ui.fun.search.viewholder.FunTitleViewHolder;
/** search result list view holder factory */
public class FunSearchViewHolderFactory implements IViewHolderFactory {
@Override
public BaseViewHolder createViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ContactConstant.SearchViewType.TITLE) { //title view
return new FunTitleViewHolder(
FunSearchTitleViewHolderBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false));
} else if (viewType == ContactConstant.SearchViewType.USER) { // friend view
return new FunSearchFriendViewHolder(
FunSearchUserViewHolderBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false));
} else if (viewType == ContactConstant.SearchViewType.TEAM) { //team view
return new FunSearchTeamViewHolder(
FunSearchUserViewHolderBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false));
}
return null;
}
}

View File

@@ -0,0 +1,104 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.search.viewholder;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import com.netease.nimlib.sdk.search.model.RecordHitInfo;
import com.netease.yunxin.kit.chatkit.model.FriendSearchInfo;
import com.netease.yunxin.kit.chatkit.model.HitType;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunSearchUserViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.SearchFriendBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
public class FunSearchFriendViewHolder extends BaseViewHolder<SearchFriendBean> {
private FriendSearchInfo friendInfo;
private FunSearchUserViewHolderBinding viewBinding;
public FunSearchFriendViewHolder(@NonNull View itemView) {
super(itemView);
}
public FunSearchFriendViewHolder(@NonNull FunSearchUserViewHolderBinding viewBinding) {
this(viewBinding.getRoot());
this.viewBinding = viewBinding;
}
@Override
public void onBindData(SearchFriendBean data, int position) {
friendInfo = data.friendSearchInfo;
if (friendInfo != null) {
ColorUtils.loadAvator(friendInfo.getFriendInfo().getAvatar(),viewBinding.cavUserIcon);
// viewBinding.cavUserIcon.setData(
// friendInfo.getFriendInfo().getAvatar(),
// friendInfo.getFriendInfo().getName(),
// AvatarColor.avatarColor(friendInfo.getFriendInfo().getAccount()));
if (friendInfo.getHitType() == HitType.Alias) {
viewBinding.nickTv.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAlias(), friendInfo.getHitInfo()));
viewBinding.nameTv.setVisibility(View.GONE);
} else if (friendInfo.getHitType() == HitType.UserName) {
if (!TextUtils.isEmpty(friendInfo.getFriendInfo().getAlias())) {
viewBinding.nickTv.setText(friendInfo.getFriendInfo().getAlias());
viewBinding.nickTv.setVisibility(View.VISIBLE);
viewBinding.nameTv.setText(
getSelectSpanText(
friendInfo.getFriendInfo().getUserInfo().getName(), friendInfo.getHitInfo()));
viewBinding.nameTv.setVisibility(View.VISIBLE);
} else {
viewBinding.nickTv.setText(
getSelectSpanText(
friendInfo.getFriendInfo().getUserInfo().getName(), friendInfo.getHitInfo()));
viewBinding.nickTv.setVisibility(View.VISIBLE);
viewBinding.nameTv.setVisibility(View.GONE);
}
} else {
viewBinding.nickTv.setVisibility(View.VISIBLE);
if (!TextUtils.isEmpty(friendInfo.getFriendInfo().getAlias())) {
viewBinding.nickTv.setText(friendInfo.getFriendInfo().getAlias());
viewBinding.nameTv.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAccount(), friendInfo.getHitInfo()));
viewBinding.nameTv.setVisibility(View.VISIBLE);
} else if (!TextUtils.isEmpty(friendInfo.getFriendInfo().getUserInfo().getName())) {
viewBinding.nickTv.setText(friendInfo.getFriendInfo().getUserInfo().getName());
viewBinding.nameTv.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAccount(), friendInfo.getHitInfo()));
viewBinding.nameTv.setVisibility(View.VISIBLE);
} else {
viewBinding.nickTv.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAccount(), friendInfo.getHitInfo()));
viewBinding.nameTv.setVisibility(View.GONE);
}
}
viewBinding.getRoot().setOnClickListener(v -> itemListener.onClick(v, data, position));
;
}
}
private SpannableString getSelectSpanText(String text, RecordHitInfo hitInfo) {
SpannableString spannable = new SpannableString(text);
if (hitInfo != null) {
spannable.setSpan(
new ForegroundColorSpan(
viewBinding
.getRoot()
.getContext()
.getResources()
.getColor(R.color.fun_contact_search_hit_text_color)),
hitInfo.start,
hitInfo.end,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
return spannable;
}
}

View File

@@ -0,0 +1,75 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.search.viewholder;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import com.netease.nimlib.sdk.search.model.RecordHitInfo;
import com.netease.yunxin.kit.chatkit.model.TeamSearchInfo;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunSearchUserViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.SearchTeamBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
public class FunSearchTeamViewHolder extends BaseViewHolder<SearchTeamBean> {
private FunSearchUserViewHolderBinding viewBinding;
private TeamSearchInfo searchInfo;
public FunSearchTeamViewHolder(@NonNull View itemView) {
super(itemView);
}
public FunSearchTeamViewHolder(@NonNull FunSearchUserViewHolderBinding viewBinding) {
this(viewBinding.getRoot());
this.viewBinding = viewBinding;
}
@Override
public void onBindData(SearchTeamBean data, int position) {
if (data != null) {
searchInfo = data.teamSearchInfo;
ColorUtils.loadGroupAvator(searchInfo.getTeam().getIcon(), viewBinding.cavUserIcon);
// if(TextUtils.isEmpty(searchInfo.getTeam().getIcon())){
// viewBinding.cavUserIcon.setData(R.mipmap.ic_group_defaulthead, searchInfo.getTeam().getName());
// }else{
// viewBinding.cavUserIcon.setData(
// searchInfo.getTeam().getIcon(),
// searchInfo.getTeam().getName(),
// AvatarColor.avatarColor(searchInfo.getTeam().getId()));
// }
viewBinding.nickTv.setText(
getSelectSpanText(searchInfo.getTeam().getName(), searchInfo.getHitInfo()));
viewBinding.nickTv.setVisibility(View.VISIBLE);
viewBinding.nameTv.setVisibility(View.GONE);
viewBinding.getRoot().setOnClickListener(v -> itemListener.onClick(v, data, position));
}
}
private SpannableString getSelectSpanText(String text, RecordHitInfo hitInfo) {
SpannableString spannable = new SpannableString(text);
if (hitInfo != null) {
spannable.setSpan(
new ForegroundColorSpan(
viewBinding
.getRoot()
.getContext()
.getResources()
.getColor(R.color.fun_contact_search_hit_text_color)),
hitInfo.start,
hitInfo.end,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
return spannable;
}
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.search.viewholder;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunSearchTitleViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.SearchTitleBean;
public class FunTitleViewHolder extends BaseViewHolder<SearchTitleBean> {
private FunSearchTitleViewHolderBinding viewBinding;
public FunTitleViewHolder(@NonNull View itemView) {
super(itemView);
}
public FunTitleViewHolder(@NonNull FunSearchTitleViewHolderBinding viewBinding) {
this(viewBinding.getRoot());
this.viewBinding = viewBinding;
}
@Override
public void onBindData(SearchTitleBean data, int position) {
if (TextUtils.isEmpty(data.title)) {
viewBinding.tvTitle.setText(data.titleRes);
} else {
viewBinding.tvTitle.setText(data.title);
}
}
}

View File

@@ -0,0 +1,135 @@
package com.netease.yunxin.kit.contactkit.ui.fun.selector;
import static com.netease.nim.highavailable.HighAvailableObject.getContext;
import android.view.MotionEvent;
import android.view.inputmethod.InputMethodManager;
import androidx.lifecycle.ViewModelProvider;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.RequestCallbackWrapper;
import com.netease.nimlib.sdk.msg.MsgService;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.RecentContact;
import com.netease.yunxin.kit.chatkit.model.ConversationInfo;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.contact.ContactViewModel;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.search.SearchViewModel;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import java.util.ArrayList;
import java.util.List;
//选择好友数据Activity
public abstract class FunBaseSelectorDataActivity extends BaseActivity {
protected List<UserInfo> listmemebers = new ArrayList<>();
protected ContactViewModel viewModel;
protected SearchViewModel searchViewModel;
private List<ContactFriendBean> accountList;
private List<ContactFriendBean> lastlist;
public FunContactSelectorDialog contactSelectorDialog;
//获取好友列表,最近联系,搜索好友
public void initData() {
viewModel = new ViewModelProvider(this).get(ContactViewModel.class);
searchViewModel = new ViewModelProvider(this).get(SearchViewModel.class);
viewModel
.getContactLiveData()
.observe(
this,
contactBeansResult -> {
if (contactBeansResult.getLoadStatus() == LoadStatus.Success) {
//好友列表
accountList = contactBeansResult.getData();
}
});
viewModel.fetchContactList();
List<RecentContact> recentContact = NIMClient.getService(MsgService.class).queryRecentContactsBlock();
if (recentContact != null && recentContact.size() > 0) {
lastlist = convertConvacationList(recentContact);
}
// conversationViewModel = new ViewModelProvider(this).get(ConversationViewModel.class);
// conversationViewModel
// .getQueryLiveData()
// .observe(
// this,
// result -> {
// if (result.getLoadStatus() == LoadStatus.Success) {
// lastlist = convertConvacationList(result.getData());
// }
//
// });
// conversationViewModel.fetchConversation();
searchViewModel.getQueryLiveData().observe(this, result -> {
if (result.getLoadStatus() == LoadStatus.Success) {
if (result.getData() != null && result.getData().size() > 0) {
if (contactSelectorDialog != null) {
contactSelectorDialog.showSearchResult(result.getData());
}
}
}
});
}
public abstract List<ContactFriendBean> getLovelist();
public List<ContactFriendBean> convertConvacationList(List<RecentContact> list) {
List<ContactFriendBean> contactFriendBeans = new ArrayList<>();
for (RecentContact recentContact : list) {
if (recentContact.getSessionType() == SessionTypeEnum.P2P) {
ConversationInfo conversationInfo = new ConversationInfo(recentContact);
FriendInfo friendInfo = conversationInfo.getFriendInfo();
if(friendInfo!=null){
friendInfo.setUserInfo(conversationInfo.getUserInfo());
ContactFriendBean item = new ContactFriendBean(friendInfo);
contactFriendBeans.add(item);
}
}
}
return contactFriendBeans;
}
public FunContactSelectorDialog showAddFriendsDialog(List<String> selectorlistkey, FunContactSelectorDialog.onActionClickListener onActionClickListener) {
contactSelectorDialog = new FunContactSelectorDialog(this)
.setSelector(selectorlistkey)
.setTeamShow(false)
.setActionTitle(getResources().getString(R.string.invite_sure))
.setAccountList(accountList)
.setLastList(lastlist)
.setLoveList(getLovelist())
.setActionListener(onActionClickListener);
contactSelectorDialog.show();
return contactSelectorDialog;
}
public boolean onTouchEvent(MotionEvent event) {
if (null != this.getCurrentFocus()) {
/**
* 点击空白位置 隐藏软键盘
*/
InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
return super.onTouchEvent(event);
}
}

View File

@@ -0,0 +1,137 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.selector;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.common.ui.utils.ToastUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunContactSelectorSettingActivityBinding;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunFriendSelectedViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.selector.BaseSelectedListAdapter;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.corekit.im.IMKitClient;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
// 成员列表
public class FunContactMembersAdapter extends RecyclerView.Adapter<FunContactMembersAdapter.FunContactMembersViewHolder> {
protected List<UserInfo> members;
private Context mContext;
public FunContactMembersAdapter(Context context) {
members = new ArrayList<>();
this.mContext = context;
}
protected FunContactMembersAdapter.ItemClickListener itemClickListener;
public void setItemClickListener(FunContactMembersAdapter.ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public void setData(List<UserInfo> list) {
members.addAll(list);
notifyDataSetChanged();
}
@NonNull
@Override
public FunContactMembersViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new FunContactMembersViewHolder(FunFriendSelectedViewHolderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
}
@Override
public void onBindViewHolder(@NonNull FunContactMembersViewHolder holder, int position) {
UserInfo userInfo = members.get(position);
if (position == 0) {
holder.binding.avatarView.setData(R.drawable.ic_creategroup_add, mContext.getResources().getString(R.string.fun_addmembers_txt));
holder.binding.icClean.setVisibility(View.GONE);
holder.binding.tvName.setTextColor(mContext.getResources().getColor(R.color.color_6d94ff));
holder.binding.tvName.setText(mContext.getResources().getString(R.string.fun_addmembers_txt));
} else {
holder.binding.tvName.setTextColor(mContext.getResources().getColor(R.color.title_color));
String avaterUrl = userInfo.getAvatar();
// if (TextUtils.isEmpty(avaterUrl)) {
// holder.binding.avatarView.setData(R.mipmap.default_head_img, userInfo.getName(), 0);
// } else {
// holder.binding.avatarView.setData(userInfo.getAvatar(), userInfo.getName(), AvatarColor.avatarColor(userInfo.getAccount()));
// }
ColorUtils.loadAvator(avaterUrl, holder.binding.avatarView);
holder.binding.icClean.setVisibility(userInfo.getAccount().equals(IMKitClient.account()) ? View.GONE : View.VISIBLE);
holder.binding.tvName.setText(userInfo.getName());
}
holder.binding.getRoot().setOnClickListener(view -> {
if (itemClickListener != null) {
if (position == 0) {
itemClickListener.onAddClick();
}
if (position > 1) {
if (getItemCount() == 3) {
Toast.makeText(view.getContext(), mContext.getResources().getString(R.string.fun_addmemberstoast_txt), Toast.LENGTH_SHORT).show();
return;
}
removeFriend(userInfo);
itemClickListener.onItemClick(userInfo);
}
}
});
}
public void removeFriend(UserInfo userInfo) {
int pos = members.indexOf(userInfo);
if (pos >= 0) {
members.remove(userInfo);
notifyItemRemoved(pos);
}
}
public void addFriends(int size, List<UserInfo> userInfos) {
members.addAll(userInfos);
notifyItemRangeInserted(size, userInfos.size());
}
@Override
public int getItemCount() {
return members.size();
}
public interface ItemClickListener {
void onItemClick(UserInfo item);
void onAddClick();
}
protected class FunContactMembersViewHolder extends RecyclerView.ViewHolder {
FunFriendSelectedViewHolderBinding binding;
public FunContactMembersViewHolder(@NonNull ViewBinding binding) {
super(binding.getRoot());
this.binding = (FunFriendSelectedViewHolderBinding) binding;
}
}
}

View File

@@ -0,0 +1,504 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.selector;
import static com.netease.yunxin.kit.contactkit.ui.selector.BaseContactSelectorActivity.DEFAULT_MAX_SELECT_COUNT;
import android.app.Activity;
import android.app.Dialog;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunContactSelectorDialogLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.fun.contact.FunContactDefaultFactory;
import com.netease.yunxin.kit.contactkit.ui.interfaces.ContactActions;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.model.SearchFriendBean;
import com.netease.yunxin.kit.contactkit.ui.selector.BaseSelectedListAdapter;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListView;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class FunContactSelectorDialog extends Dialog {
private static final String TAG = "FunBottomConfirmDialog";
private FunContactSelectorDialogLayoutBinding binding;
protected RecyclerView rvSelected;
protected BaseSelectedListAdapter<?> selectedListAdapter;
private List<String> accountSelector;
protected ContactListView contactListView;
protected ContactListView lastContactView;
protected ContactListView loveContactView;
protected ContactListView searchContactView;
protected List<ContactFriendBean> accountList; //好友列表
protected List<ContactFriendBean> lastlist; //最近联系
protected List<ContactFriendBean> lovelist; //特别关心
protected onActionClickListener onActionClickListener;
protected onTeamQrClickListener onTeamQrClickListener;
protected onSelectorItemClickListener onSelectorItemClickListener;
private boolean isTeamShow = false;
private Drawable insertDrawable;
private String actionTitle;
public FunContactSelectorDialog(@NonNull Activity activity) {
super(activity, R.style.SelectorFriendsBottomDialogTheme);
renderRootView();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (insertDrawable == null) {
binding.getRoot().setBackground(getContext().getResources().getDrawable(R.drawable.fun_insert_topdialog));
} else {
binding.getRoot().setBackground(insertDrawable);
}
Window window = getWindow();
if (window != null) {
window.setGravity(Gravity.TOP);
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
wlp.height = WindowManager.LayoutParams.MATCH_PARENT;
window.setAttributes(wlp);
}
setContentView(binding.getRoot());
initTitleView();
contactListView = binding.contactListView;
lastContactView = binding.contactLast;
searchContactView = binding.contactSearchlistView;
loveContactView = binding.contactListViewMylove;
rvSelected = binding.rvSelected;
setViewHolderFactory(contactListView);
setViewHolderFactory(lastContactView);
setViewHolderFactory(searchContactView);
setViewHolderFactory(loveContactView);
binding.tvContactmember.setText(String.format(getContext().getResources().getString(R.string.fun_contactselector_members), accountList.size()));
if (lastlist.size() > 0) {
binding.tvContactlast.setVisibility(View.VISIBLE);
binding.tvContactlast.setText(String.format(getContext().getResources().getString(R.string.fun_contactselector_last), lastlist.size()));
lastContactView.setVisibility(View.VISIBLE);
}
if (lovelist.size() > 0) {
binding.tvContactspecialgz.setVisibility(View.VISIBLE);
binding.tvContactspecialgz.setText(String.format(getContext().getResources().getString(R.string.fun_contactselector_mylove), lovelist.size()));
loveContactView.setVisibility(View.VISIBLE);
}
setBooleanCanSelector(accountList, contactListView);
setBooleanCanSelector(lastlist, lastContactView);
setBooleanCanSelector(lovelist, loveContactView);
isTeamShow = getTeamShow();
if (isTeamShow) {
binding.lineTop.setVisibility(View.GONE);
binding.llyTeamsetting.setVisibility(View.VISIBLE);
}
LinearLayoutManager layoutManager =
new LinearLayoutManager(getContext(), RecyclerView.HORIZONTAL, false);
rvSelected.setLayoutManager(layoutManager);
selectedListAdapter = new FunSelectedListAdapter();
selectedListAdapter.setItemClickListener(
item -> {
item.setSelected(false);
item.setCannotSelected(false);
contactListView.updateContactData(item);
lastContactView.updateContactData(item);
searchContactView.updateContactData(item);
loveContactView.updateContactData(item);
updateActionTitle();
});
rvSelected.setAdapter(selectedListAdapter);
setOnActionListener();
setSearchView();
setCanceledOnTouchOutside(true);
setCancelable(true);
}
private void initTitleView() {
if (!TextUtils.isEmpty(actionTitle)) {
binding.title.setActionText(actionTitle);
// actionTitle = getContext().getResources().getString(R.string.invite_sure);
}
}
public void setSearchView() {
binding.editSelectorFriends.setOnEditorActionListener((textView, actionId, keyEvent) ->
{
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String search = textView.getEditableText().toString();
//搜索好友
if (onActionClickListener != null) {
onActionClickListener.search(search);
}
}
return false;
});
binding.editSelectorFriends.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (TextUtils.isEmpty(binding.editSelectorFriends.getText().toString())) {
binding.icCleanEdit.setVisibility(View.GONE);
binding.layoutContactlist.setVisibility(View.VISIBLE);
binding.layoutSearchlist.setVisibility(View.GONE);
} else {
binding.icCleanEdit.setVisibility(View.VISIBLE);
}
}
});
binding.icCleanEdit.setOnClickListener(view -> binding.editSelectorFriends.setText(""));
}
public void showSearchResult(List<BaseBean> result) {
binding.layoutSearchlist.setVisibility(View.VISIBLE);
binding.layoutContactlist.setVisibility(View.GONE);
for (ContactFriendBean bean : selectedListAdapter.getSelectedFriends()) {
accountSelector.add(bean.data.getAccount());
}
List<ContactFriendBean> listsearch = convertSearchList(result);
setBooleanCanSelector(listsearch, searchContactView);
}
public List<ContactFriendBean> convertSearchList(List<BaseBean> list) {
List<ContactFriendBean> contactFriendBeans = new ArrayList<>();
LinkedList<ContactFriendBean> selected = selectedListAdapter.getSelectedFriends();
for (BaseBean bean : list) {
if (bean instanceof SearchFriendBean) {
SearchFriendBean searchFriendBean = (SearchFriendBean) bean;
FriendInfo friendInfo = searchFriendBean.friendSearchInfo.getFriendInfo();
ContactFriendBean item = new ContactFriendBean(friendInfo);
if (selected.size() > 0 && selected.contains(item)) {
contactFriendBeans.add(selected.get(selected.indexOf(item)));
} else {
contactFriendBeans.add(item);
}
}
}
return contactFriendBeans;
}
public void setViewHolderFactory(ContactListView contactView) {
contactView
.getDecoration()
.setTitleAlignBottom(true)
.setShowTagOff(false)
.setIndexDecorationBg(getContext().getResources().getColor(R.color.title_transfer))
.setColorTitleBottomLine(getContext().getResources().getColor(R.color.title_transfer));
FunContactDefaultFactory funContactDefaultFactory = new FunContactDefaultFactory();
funContactDefaultFactory.setCenter(true);
contactView.setViewHolderFactory(funContactDefaultFactory);
}
//选中listener
public void setOnActionListener() {
ContactActions actions = new ContactActions();
actions.addSelectorListener(
IViewTypeConstant.CONTACT_FRIEND,
(selector, data) -> {
if (selector) {
if (selectedListAdapter.getItemCount() >= DEFAULT_MAX_SELECT_COUNT) {
Toast.makeText(
getContext(),
getContext().getString(
R.string.contact_selector_max_count, String.valueOf(DEFAULT_MAX_SELECT_COUNT)),
Toast.LENGTH_LONG)
.show();
((ContactFriendBean) data).setSelected(false);
contactListView.updateContactData(data);
lastContactView.updateContactData(data);
searchContactView.updateContactData(data);
loveContactView.updateContactData(data);
} else {
((ContactFriendBean) data).setSelected(true);
contactListView.updateContactData(data);
lastContactView.updateContactData(data);
searchContactView.updateContactData(data);
loveContactView.updateContactData(data);
selectedListAdapter.addFriend((ContactFriendBean) data);
}
} else {
((ContactFriendBean) data).setSelected(false);
selectedListAdapter.removeFriend((ContactFriendBean) data);
contactListView.updateContactData(data);
lastContactView.updateContactData(data);
loveContactView.updateContactData(data);
searchContactView.updateContactData(data);
}
updateActionTitle();
if (onSelectorItemClickListener != null) {
onSelectorItemClickListener.selector(((ContactFriendBean) data).data.getAccount());
}
});
contactListView.setContactAction(actions);
lastContactView.setContactAction(actions);
searchContactView.setContactAction(actions);
loveContactView.setContactAction(actions);
}
public void updateActionTitle() {
if (!TextUtils.isEmpty(actionTitle)) {
String value = actionTitle;
if (selectedListAdapter.getItemCount() > 0) {
value = actionTitle + "(" + selectedListAdapter.getItemCount() + ")";
}
binding.title.setActionText(value);
}
}
//设置是否能选择
public void setBooleanCanSelector(List<ContactFriendBean> list, ContactListView contactListView) {
if (list.size() > 0) {
for (ContactFriendBean bean : list) {
bean.setCannotSelected(false);
bean.setSelected(false);
if (accountSelector.contains(bean.data.getAccount())) {
bean.setCannotSelected(true);
}
}
contactListView.onFriendDataSourceChanged(list);
}
}
public FunContactSelectorDialog setSelector(List<String> selector) {
if (selector == null) {
accountSelector = new ArrayList<>();
} else {
this.accountSelector = selector;
}
return this;
}
public boolean getTeamShow() {
return isTeamShow;
}
public FunContactSelectorDialog setTeamShow(boolean teamShow) {
isTeamShow = teamShow;
return this;
}
public FunContactSelectorDialog setInsertDrawable(Drawable drawable) {
this.insertDrawable = drawable;
return this;
}
public FunContactSelectorDialog setActionTitle(String actionText) {
this.actionTitle = actionText;
return this;
}
public FunContactSelectorDialog setAccountList(List<ContactFriendBean> paramList) {
if (paramList == null) {
accountList = new ArrayList<>();
} else {
this.accountList = paramList;
}
return this;
}
public FunContactSelectorDialog setLastList(List<ContactFriendBean> paramLastList) {
if (paramLastList == null) {
lastlist = new ArrayList<>();
} else {
this.lastlist = paramLastList;
}
return this;
}
public FunContactSelectorDialog setLoveList(List<ContactFriendBean> paramLastList) {
if (paramLastList == null) {
lovelist = new ArrayList<>();
} else {
this.lovelist = paramLastList;
}
return this;
}
public FunContactSelectorDialog setActionListener(onActionClickListener actionListener) {
this.onActionClickListener = actionListener;
return this;
}
public FunContactSelectorDialog setTeamQrClickListener(onTeamQrClickListener teamQrClickListener) {
this.onTeamQrClickListener = teamQrClickListener;
return this;
}
public FunContactSelectorDialog setOnSelectorItemClickListener(onSelectorItemClickListener itemClickListener) {
this.onSelectorItemClickListener = itemClickListener;
return this;
}
/**
* render page
*/
private void renderRootView() {
binding = FunContactSelectorDialogLayoutBinding.inflate(getLayoutInflater());
binding.title.getBackImageView().setImageResource(R.drawable.ic_dialog_close);
binding.title.setOnBackIconClickListener(
v -> {
if (onSelectorItemClickListener != null) {
onSelectorItemClickListener.close();
}
dismiss();
});
binding.title.setTitle(R.string.select)
.setActionTextColor(getContext().getResources().getColor(R.color.color_6877fe))
.setActionListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//邀请
if (onActionClickListener != null) {
int newselectedsize = selectedListAdapter.getSelectedFriends().size();
if (newselectedsize == 0) {
Toast.makeText(
getContext(),
getContext().getString(
R.string.select_empty_tips),
Toast.LENGTH_LONG)
.show();
return;
}
if (newselectedsize + accountSelector.size() > DEFAULT_MAX_SELECT_COUNT) {
Toast.makeText(
getContext(),
getContext().getString(
R.string.contact_selector_max_count, String.valueOf(DEFAULT_MAX_SELECT_COUNT)),
Toast.LENGTH_LONG)
.show();
return;
}
onActionClickListener.invited(getSelectedAccount(), getSelectedName(), getSelectedAvatar());
}
}
});
binding.tvTeamqr.setOnClickListener(view -> {
if (onTeamQrClickListener != null) {
onTeamQrClickListener.showTeamQR();
}
});
binding.tvTeamurl.setOnClickListener(view -> {
if (onTeamQrClickListener != null) {
onTeamQrClickListener.showTeamUrl();
}
});
}
protected ArrayList<String> getSelectedAccount() {
ArrayList<String> result = new ArrayList<>();
for (ContactFriendBean bean : selectedListAdapter.getSelectedFriends()) {
result.add(bean.data.getAccount());
}
return result;
}
protected ArrayList<String> getSelectedName() {
ArrayList<String> result = new ArrayList<>();
for (ContactFriendBean bean : selectedListAdapter.getSelectedFriends()) {
if (bean.data.getUserInfo() == null) {
continue;
}
String name = bean.data.getUserInfo().getName();
result.add(TextUtils.isEmpty(name) ? bean.data.getAccount() : name);
}
return result;
}
protected ArrayList<String> getSelectedAvatar() {
ArrayList<String> result = new ArrayList<>();
for (ContactFriendBean bean : selectedListAdapter.getSelectedFriends()) {
if (bean.data.getUserInfo() == null) {
continue;
}
String avatar = bean.data.getUserInfo().getAvatar();
result.add(TextUtils.isEmpty(avatar) ? "" : avatar);
}
return result;
}
public void dismiss() {
if (!isShowing()) {
return;
}
try {
super.dismiss();
} catch (Throwable throwable) {
ALog.e(TAG, "dismiss FunBottomConfirmDialog", throwable);
}
}
public interface onActionClickListener {
void invited(List<String> listaccount, List<String> listname, List<String> listavat);
void search(String text);
}
public interface onTeamQrClickListener {
void showTeamQR();
void showTeamUrl();
}
public interface onSelectorItemClickListener {
void close();
void selector(String userId);
}
}

View File

@@ -0,0 +1,48 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.selector;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunFriendSelectedViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.selector.BaseSelectedListAdapter;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
public class FunSelectedListAdapter
extends BaseSelectedListAdapter<FunFriendSelectedViewHolderBinding> {
@Override
protected FunFriendSelectedViewHolderBinding provideViewBinding(
@NonNull ViewGroup parent, int viewType) {
return FunFriendSelectedViewHolderBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
protected void handleBindViewHolder(
SelectedViewHolder<FunFriendSelectedViewHolderBinding> holder,
ContactFriendBean bean,
FriendInfo friendData) {
String friendAvaterUrl = friendData.getAvatar();
ColorUtils.loadAvator(friendAvaterUrl,holder.binding.avatarView);
holder.binding.tvName.setText(friendData.getName());
holder.itemView.setOnClickListener(
v -> {
removeFriend(bean);
if (itemClickListener != null) {
itemClickListener.onItemClick(bean);
}
});
}
}

View File

@@ -0,0 +1,71 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.team;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.fun.view.FunContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder.FunTeamListViewHolder;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.team.BaseTeamListActivity;
import com.netease.yunxin.kit.contactkit.ui.team.TeamListViewModel;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
public class FunTeamListActivity extends BaseTeamListActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
changeStatusBarColor(R.color.color_white);
}
protected void configTitle(BaseListActivityLayoutBinding binding) {
super.configTitle(binding);
binding.title.getTitleTextView().setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
// binding.title.getTitleTextView().setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
binding.title.setBackgroundResource(R.color.color_white);
}
protected void configRoutePath(TeamListViewModel viewModel) {
viewModel.configRoutePath(RouterConstant.PATH_FUN_CHAT_TEAM_PAGE);
}
@Override
protected void configViewHolderFactory() {
binding.contactListView.setViewHolderFactory(
new FunContactViewHolderFactory() {
@Override
protected BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType) {
if (viewType == IViewTypeConstant.CONTACT_TEAM_LIST) {
FunTeamListViewHolder viewHolder = new FunTeamListViewHolder(view);
viewHolder.setItemClickListener(
data -> {
if (isSelector) {
Intent result = new Intent();
result.putExtra(RouterConstant.KEY_TEAM_ID, data.data.getId());
setResult(RESULT_OK, result);
finish();
} else {
XKitRouter.withKey(data.router)
.withParam(RouterConstant.CHAT_KRY, data.data)
.withContext(FunTeamListActivity.this)
.navigate();
}
});
return viewHolder;
}
return null;
}
});
}
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.userinfo;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.common.ui.widgets.BackTitleBar;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunCommentActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.userinfo.BaseCommentActivity;
public class FunCommentActivity extends BaseCommentActivity {
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
FunCommentActivityLayoutBinding binding =
FunCommentActivityLayoutBinding.inflate(getLayoutInflater());
titleBar = binding.title;
titleBar.setActionTextColor(getResources().getColor(R.color.color_58be6b));
edtComment = binding.edtComment;
return binding.getRoot();
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
changeStatusBarColor(R.color.color_ededed);
super.onCreate(savedInstanceState);
}
protected void configTitle(BackTitleBar titleBar) {
super.configTitle(titleBar);
titleBar.getTitleTextView().setTextSize(17);
titleBar.getTitleTextView().setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
}
}

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.userinfo;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunContactBottomConfirmDialogBinding;
public class FunContactBottomConfirmDialog extends Dialog {
private static final String TAG = "FunBottomConfirmDialog";
private Runnable confirmRunnable;
private FunContactBottomConfirmDialogBinding binding;
public FunContactBottomConfirmDialog(@NonNull Activity activity) {
super(activity, R.style.SelectorFriendsBottomDialogTheme);
renderRootView();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Window window = getWindow();
if (window != null) {
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.gravity = Gravity.BOTTOM;
wlp.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(wlp);
}
setContentView(binding.getRoot());
setCanceledOnTouchOutside(true);
setCancelable(true);
setOnDismissListener(dialog -> confirmRunnable = null);
}
/** render page */
private void renderRootView() {
binding = FunContactBottomConfirmDialogBinding.inflate(getLayoutInflater());
binding.tvDelete.setOnClickListener(
v -> {
if (confirmRunnable != null) {
confirmRunnable.run();
}
dismiss();
});
binding.tvCancel.setOnClickListener(v -> dismiss());
}
public FunContactBottomConfirmDialog configTip(String tip) {
binding.tvTip.setText(tip);
return this;
}
public void show(Runnable confirmRunnable) {
if (isShowing()) {
return;
}
this.confirmRunnable = confirmRunnable;
try {
super.show();
} catch (Throwable throwable) {
ALog.e(TAG, "show FunBottomConfirmDialog", throwable);
}
}
public void dismiss() {
if (!isShowing()) {
return;
}
try {
super.dismiss();
} catch (Throwable throwable) {
ALog.e(TAG, "dismiss FunBottomConfirmDialog", throwable);
}
}
}

View File

@@ -0,0 +1,64 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.userinfo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.common.utils.SizeUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunUserInfoActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.userinfo.BaseUserInfoActivity;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
public class FunUserInfoActivity extends BaseUserInfoActivity {
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
FunUserInfoActivityLayoutBinding binding =
FunUserInfoActivityLayoutBinding.inflate(getLayoutInflater());
contactInfoView = binding.contactUser;
contactInfoView.configSCBlackSwitch(
R.drawable.fun_switch_track_selector, R.drawable.fun_switch_thumb_selector);
contactInfoView.configAvatarCorner(SizeUtils.dp2px(4));
contactInfoView.configChatBtnColor(getResources().getColor(R.color.color_525c8c));
contactInfoView.configDivideLineColor(getResources().getColor(R.color.color_ededed));
contactInfoView.configRootBgColor(getResources().getColor(R.color.color_ededed));
contactInfoView.configSignatureMaxLines(2);
titleBar = binding.title;
return binding.getRoot();
}
@Override
protected Class<? extends Activity> getCommentActivity() {
return FunCommentActivity.class;
}
protected void showDeleteConfirmDialog() {
new FunContactBottomConfirmDialog(this)
.configTip(
String.format(getString(R.string.delete_contact_account), userInfoData.getName()))
.show(
() -> {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
return;
}
viewModel.deleteFriend(userInfoData.data.getAccount());
finish();
});
}
protected void goChat() {
XKitRouter.withKey(RouterConstant.PATH_FUN_CHAT_P2P_PAGE)
.withParam(RouterConstant.CHAT_ID_KRY, userInfoData.data.getAccount())
.withContext(FunUserInfoActivity.this)
.navigate();
finish();
}
}

View File

@@ -0,0 +1,105 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.verify;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.contactkit.ui.ILoadListener;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunVerifyDetailsActivityBinding;
import com.netease.yunxin.kit.contactkit.ui.fun.view.FunContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder.FunVerifyInfoViewHolder;
import com.netease.yunxin.kit.contactkit.ui.model.ContactVerifyInfoBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.verify.BaseVerifyListActivity;
import com.netease.yunxin.kit.contactkit.ui.verify.VerifyViewModel;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.FriendNotify;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoStatus;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoType;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
/**
* 好友申请详情信息
*/
public class FunVerifyDetailsActivity extends BaseActivity {
FunVerifyDetailsActivityBinding binding;
protected VerifyViewModel viewModel;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = FunVerifyDetailsActivityBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
viewModel = new ViewModelProvider(this).get(VerifyViewModel.class);
configTitleBar();
ContactVerifyInfoBean bean = (ContactVerifyInfoBean) getIntent().getSerializableExtra("bean");
if (bean != null) {
UserInfo info = bean.data.getFromUserInfo();
assert info != null;
ColorUtils.loadAvator(info.getAvatar(),binding.avatarView);
binding.tvName.setText(info.getName());
FriendNotify notifyAttach = (FriendNotify) bean.data.getAttachObject();
assert notifyAttach != null;
binding.tvVerifymsg.setText(notifyAttach.getMsg());
}
binding.tvAgree.setOnClickListener(view -> {
viewModel.agree(
bean,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
setResultBack(0, 0);
}
@Override
public void onFailed(int code) {
setResultBack(-1, code);
}
@Override
public void onException(@Nullable Throwable exception) {
setResultBack(-2, 0);
}
});
});
}
public void setResultBack(int resultStatus, int errorCode) {
Intent intent = new Intent();
intent.putExtra("resultStatus", resultStatus);
intent.putExtra("errorCode", errorCode);
setResult(RESULT_OK, intent);
finish();
}
public void configTitleBar() {
binding.titleBarView.setOnBackIconClickListener(view -> onBackPressed());
binding.titleBarView.getTitleTextView().setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
}
}

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.view;
import android.view.ViewGroup;
import com.netease.yunxin.kit.contactkit.ui.IContactFactory;
import com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder.FunContactViewHolder;
import com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder.FunEntranceViewHolder;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
/**
* viewHolder factory
*/
public abstract class FunContactViewHolderFactory implements IContactFactory {
@Override
public int getItemViewType(BaseContactBean data) {
return data.viewType;
}
public BaseContactViewHolder createViewHolder(ViewGroup view, int viewType) {
if (viewType == IViewTypeConstant.CONTACT_FRIEND) {
FunContactViewHolder funContactViewHolder = new FunContactViewHolder(view);
funContactViewHolder.setCenter(isCenter);
return funContactViewHolder;
} else if (viewType == IViewTypeConstant.CONTACT_ACTION_ENTER) {
return new FunEntranceViewHolder(view);
} else if (viewType >= IViewTypeConstant.CUSTOM_START) {
return getCustomViewHolder(view, viewType);
}
return null;
}
private boolean isCenter = false;
public void setCenter(boolean isCenterShow) {
this.isCenter = isCenterShow;
}
protected abstract BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType);
}

View File

@@ -0,0 +1,105 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.nim.highavailable.LogUtils;
import com.netease.yunxin.kit.common.utils.SizeUtils;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunBlackListViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactBlackListBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import java.util.ArrayList;
import java.util.List;
public class FunBlackListViewHolder extends BaseContactViewHolder {
private final int cornerSize = SizeUtils.dp2px(4);
private RelieveListener relieveListener;
private ItemCheckedListener checkedListener;
private FunBlackListViewHolderBinding binding;
public FunBlackListViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = FunBlackListViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
ContactBlackListBean blackListBean = ((ContactBlackListBean) bean);
UserInfo friendData = ((ContactBlackListBean) bean).data;
binding.tvName.setText(blackListBean.getName());
binding.rbSelector.setChecked(false);
// binding.avatarView.setCornerRadius(cornerSize);
ColorUtils.loadAvator(friendData.getAvatar(),binding.avatarView);
// binding.avatarView.setData(
// friendData.getAvatar(),
// blackListBean.getAvatarName(),
// ColorUtils.avatarColor(friendData.getAccount()));
binding.tvRelieve.setOnClickListener(
v -> {
if (relieveListener != null) {
relieveListener.onUserRelieve((ContactBlackListBean) bean);
}
});
binding.rootView.setOnClickListener(view -> {
binding.rbSelector.setChecked(!binding.rbSelector.isChecked());
if (checkedListener != null) {
checkedListener.onChecked((ContactBlackListBean) bean, binding.rbSelector.isChecked());
}
});
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
public void setRelieveListener(RelieveListener relieveListener) {
this.relieveListener = relieveListener;
}
public void setItemCheckedListener(ItemCheckedListener checkedListener) {
this.checkedListener = checkedListener;
}
public interface RelieveListener {
void onUserRelieve(ContactBlackListBean data);
}
public interface ItemCheckedListener {
void onChecked(ContactBlackListBean data, boolean isCheck);
}
}

View File

@@ -0,0 +1,199 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder;
import static android.provider.Settings.System.getString;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.nim.highavailable.LogUtils;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.msg.MsgService;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.RecentContact;
import com.netease.yunxin.kit.common.utils.SizeUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunFriendContactViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.utils.GsonUtils;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import org.json.JSONException;
import org.json.JSONObject;
public class FunContactViewHolder extends BaseContactViewHolder {
FunFriendContactViewHolderBinding binding;
private final int cornerSize = SizeUtils.dp2px(4);
public FunContactViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = FunFriendContactViewHolderBinding.inflate(layoutInflater, container, true);
}
private boolean isCenter = false;
public void setCenter(boolean center) {
isCenter = center;
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
FriendInfo friendInfo = ((ContactFriendBean) bean).data;
LogUtils.i("结构是啥:", "结构是啥:" + GsonUtils.beanToJSONString(bean));
LogUtils.i("结构是啥:", "结构是啥1:" + GsonUtils.beanToJSONString(attrs));
if (friendInfo != null) {
String nickName = friendInfo.getName();
String avatarName = friendInfo.getAvatarName();
String conversationText = ((ContactFriendBean) bean).getConversationText();
binding.tvName.setText(nickName);
binding.messageLayout.setVisibility(View.VISIBLE);
binding.tvTime.setVisibility(View.VISIBLE);
binding.tvTime.setText(((ContactFriendBean) bean).getConversationTime());
if (isCenter) {
binding.messageLayout.setVisibility(View.GONE);
binding.tvTime.setVisibility(View.GONE);
binding.unreadTv.setVisibility(View.GONE);
}
binding.tvHint.setText(binding.tvHint.getContext().getResources().getString(R.string.nmhmylg_txt));
if (!TextUtils.isEmpty(conversationText)) {
binding.tvHint.setText(conversationText);
if (friendInfo.getUserInfo() != null) {
binding.tvName.setText(friendInfo.getUserInfo().getName());
}
}
String avaterUrl = friendInfo.getAvatar();
if (TextUtils.isEmpty(avaterUrl)) {
binding.avatarView.setData(R.mipmap.default_head_img
, avatarName, 0);
} else {
binding.avatarView.setData(
friendInfo.getAvatar(), avatarName, ColorUtils.avatarColor(friendInfo.getAccount()));
}
if (attrs.getShowSelector()) {
binding.layoutSelector.setVisibility(View.VISIBLE);
if (((ContactFriendBean) bean).isCannotSelected()) {
binding.rbSelector.setVisibility(View.GONE);
binding.ivSelector.setVisibility(View.VISIBLE);
} else {
binding.ivSelector.setVisibility(View.GONE);
binding.rbSelector.setChecked(((ContactFriendBean) bean).isSelected());
binding.rbSelector.setVisibility(View.VISIBLE);
binding.rootView.setOnClickListener(
v -> {
boolean newStatue = !binding.rbSelector.isChecked();
((ContactFriendBean) bean).setSelected(newStatue);
binding.rbSelector.setChecked(newStatue);
if (actions != null && actions.getSelectorListener(bean.viewType) != null) {
actions.getSelectorListener(bean.viewType).onSelector(newStatue, bean);
}
});
}
} else {
binding.rbSelector.setVisibility(View.GONE);
binding.rootView.setOnClickListener(
v -> {
if (actions != null && actions.getContactListener(bean.viewType) != null) {
actions.getContactListener(bean.viewType).onClick(position, bean);
}
});
}
RecentContact recentContact = NIMClient.getService(MsgService.class).queryRecentContact(friendInfo.getAccount(), SessionTypeEnum.P2P);
if (recentContact != null) {
// if (recentContact.getUnreadCount() > 0) {
// int count = recentContact.getUnreadCount();
// String content;
// if (count >= 100) {
// content = "99+";
// } else {
// content = String.valueOf(count);
// }
// binding.unreadTv.setText(content);
// binding.unreadTv.setVisibility(View.VISIBLE);
// } else {
// binding.unreadTv.setVisibility(View.GONE);
// }
binding.tvHint.setText(recentContact.getContent());
if (recentContact.getContent().equals("[自定义消息]")) {
String bean2 = GsonUtils.beanToJSONString(recentContact.getAttachment());
try {
JSONObject jsonObject = new JSONObject(bean2);
switch ((int) jsonObject.get("type")) {
case 1002:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.voom_message_txt));
break;
case 1003:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.gerenminp_txt));
break;
case 1004:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.zhuanzhang_chat_txt));
break;
case 1005:
case 1006:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.hongbao_chat_txt));
break;
case 1007:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.albums_chat_txt));
break;
case 1008:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.helppay_chat_txt));
break;
case 1009:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.inviteGroup_chat_txt));
break;
default:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.custom_chat_txt));
}
} catch (JSONException e) {
}
}
} else {
binding.unreadTv.setVisibility(View.GONE);
}
// loadConfig(attrs);
}
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
}

View File

@@ -0,0 +1,82 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunEntranceContactViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
public class FunEntranceViewHolder extends BaseContactViewHolder {
FunEntranceContactViewHolderBinding binding;
private static final int maxNumber = 99;
public FunEntranceViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = FunEntranceContactViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
ContactEntranceBean entranceBean = (ContactEntranceBean) bean;
if (entranceBean.showRightArrow) {
binding.ivArrow.setVisibility(View.VISIBLE);
} else {
binding.ivArrow.setVisibility(View.GONE);
}
if (entranceBean.number > 0) {
binding.tvNumber.setVisibility(View.VISIBLE);
String numberValue = String.valueOf(entranceBean.number);
if (entranceBean.number > maxNumber) {
numberValue = context.getResources().getString(R.string.verify_max_count_text);
}
binding.tvNumber.setText(numberValue);
} else {
binding.tvNumber.setVisibility(View.GONE);
}
Glide.with(context).load(entranceBean.icon).centerCrop().into(binding.ivIcon);
binding.tvTitle.setText(entranceBean.title);
binding.rootView.setOnClickListener(
v -> {
if (actions != null && actions.getContactListener(bean.viewType) != null) {
actions.getContactListener(bean.viewType).onClick(position, bean);
}
});
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvTitle.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvTitle.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.flIvIcon.setRadius(attrs.getAvatarCornerRadius());
}
}
}

View File

@@ -0,0 +1,158 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder;
import static android.provider.Settings.System.getString;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.msg.MsgService;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.RecentContact;
import com.netease.nimlib.sdk.team.model.Team;
import com.netease.yunxin.kit.common.utils.SizeUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunTeamListViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactTeamBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.utils.GsonUtils;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import org.json.JSONException;
import org.json.JSONObject;
public class FunTeamListViewHolder extends BaseContactViewHolder {
// private final int cornerSize = SizeUtils.dp2px(4);
private itemClickListener itemClickListener;
private FunTeamListViewHolderBinding binding;
public FunTeamListViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = FunTeamListViewHolderBinding.inflate(layoutInflater, container, true);
}
private boolean isCenter = false;
public void setCenter(boolean center) {
isCenter = center;
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
Team teamInfo = ((ContactTeamBean) bean).data;
if (teamInfo != null) {
binding.tvName.setText(teamInfo.getName());
String value = ((ContactTeamBean) bean).getContent();
String time = ((ContactTeamBean) bean).getTime();
binding.tvHint.setText(TextUtils.isEmpty(value) ? binding.tvHint.getContext().getResources().getString(R.string.nmhmylg_txt) : value);
binding.tvTime.setText(TextUtils.isEmpty(time) ? "" : time);
// binding.avatarView.setCornerRadius(cornerSize);
binding.messageLayout.setVisibility(View.VISIBLE);
binding.tvTime.setVisibility(View.VISIBLE);
if (isCenter) {
binding.messageLayout.setVisibility(View.GONE);
binding.tvTime.setVisibility(View.GONE);
}
ColorUtils.loadGroupAvator(teamInfo.getIcon(),binding.avatarView);
binding.rootView.setOnClickListener(
v -> {
if (itemClickListener != null) {
itemClickListener.onClick((ContactTeamBean) bean);
}
});
RecentContact recentContact = NIMClient.getService(MsgService.class).queryRecentContact(teamInfo.getId(), SessionTypeEnum.Team);
if (recentContact != null) {
// if (recentContact.getUnreadCount() > 0) {
// int count = recentContact.getUnreadCount();
// String content;
// if (count >= 100) {
// content = "99+";
// } else {
// content = String.valueOf(count);
// }
// binding.unreadTv.setText(content);
// binding.unreadTv.setVisibility(View.VISIBLE);
// } else {
// binding.unreadTv.setVisibility(View.GONE);
// }
binding.tvHint.setText(recentContact.getContent());
if (recentContact.getContent().equals("[自定义消息]")) {
String bean2 = GsonUtils.beanToJSONString(recentContact.getAttachment());
try {
JSONObject jsonObject = new JSONObject(bean2);
switch ((int) jsonObject.get("type")) {
case 1002:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.voom_message_txt));
break;
case 1003:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.gerenminp_txt));
break;
case 1004:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.zhuanzhang_chat_txt));
break;
case 1005:
case 1006:
binding.tvHint.setText(binding.unreadTv.getResources().getString(R.string.hongbao_chat_txt));
break;
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}else{
binding.unreadTv.setVisibility(View.GONE);
}
loadConfig(attrs);
}
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
public void setItemClickListener(FunTeamListViewHolder.itemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public interface itemClickListener {
void onClick(ContactTeamBean data);
}
}

View File

@@ -0,0 +1,218 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.fun.view.viewholder;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.nim.highavailable.LogUtils;
import com.netease.nimlib.sdk.friend.model.AddFriendNotify;
import com.netease.yunxin.kit.chatkit.repo.ContactRepo;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.common.utils.SizeUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.FunVerifyListViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactVerifyInfoBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.model.FriendNotify;
import com.netease.yunxin.kit.corekit.im.model.FriendVerifyType;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfo;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoStatus;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoType;
public class FunVerifyInfoViewHolder extends BaseContactViewHolder {
private final int cornerSize = SizeUtils.dp2px(4);
private VerifyListener verifyListener;
private FunVerifyListViewHolderBinding binding;
public FunVerifyInfoViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = FunVerifyListViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
ContactVerifyInfoBean infoBean = ((ContactVerifyInfoBean) bean);
SystemMessageInfo info = infoBean.data;
String name = info.getFromUserName();
String targetName = info.getTargetName();
// String avatar =
// info.getTargetTeam() != null
// ? info.getTargetTeam().getIcon()
// : info.getFromUserInfo().getAvatar();
if(!TextUtils.isEmpty(info.getFromAccount())){
FriendInfo friendInfo = ContactRepo.getFriend(info.getFromAccount());
if (friendInfo != null && !TextUtils.isEmpty(friendInfo.getAlias())) {
name = friendInfo.getAlias();
}
}
binding.tvName.setText(name);
// binding.avatarView.setCornerRadius(cornerSize);
if (info.getTargetTeam() == null) {
ColorUtils.loadAvator(info.getFromUserInfo().getAvatar(), binding.avatarView);
} else {
ColorUtils.loadGroupAvator(info.getTargetTeam().getIcon(), binding.avatarView);
}
// binding.avatarView.setData(avatar, name, AvatarColor.avatarColor(info.getFromAccount()));
int unreadCount = infoBean.getUnreadCount();
if (unreadCount > 1) {
binding.unreadTv.setVisibility(View.VISIBLE);
if (unreadCount > 99) {
binding.unreadTv.setText(context.getString(R.string.verify_max_count_text));
} else {
binding.unreadTv.setText(String.valueOf(unreadCount));
}
} else {
binding.unreadTv.setVisibility(View.GONE);
}
if (infoBean.data.getUnread()) {
binding.rootView.setBackgroundColor(context.getResources().getColor(R.color.color_ededef));
} else {
binding.rootView.setBackgroundColor(context.getResources().getColor(R.color.color_white));
}
switch (info.getInfoStatus()) {
case Init:
binding.llyVerifyResult.setVisibility(View.GONE);
binding.llyVerify.setVisibility(View.VISIBLE);
binding.tvAccept.setOnClickListener(
v -> {
if (verifyListener != null) {
verifyListener.onAccept((ContactVerifyInfoBean) bean);
binding.tvAccept.setEnabled(false);
}
});
binding.tvReject.setOnClickListener(
v -> {
if (verifyListener != null) {
verifyListener.onReject((ContactVerifyInfoBean) bean);
binding.tvReject.setEnabled(false);
}
});
binding.rootView.setOnClickListener(view -> {
if (verifyListener != null) {
if (info.getAttachObject() instanceof FriendNotify) {
FriendNotify notifyAttach = (FriendNotify) info.getAttachObject();
if (notifyAttach.getType() == FriendVerifyType.VerifyRequest && info.getInfoStatus() == SystemMessageInfoStatus.Init) {
verifyListener.onDetails((ContactVerifyInfoBean) bean);
}
}
}
});
break;
case Passed:
showResult(context.getString(R.string.contact_verify_agreed), true);
break;
case Declined:
showResult(context.getString(R.string.contact_verify_rejected), false);
break;
case Ignored:
case Expired:
showResult(context.getString(R.string.contact_verify_expired), false);
break;
}
switch (info.getInfoType()) {
case ApplyJoinTeam:
binding.tvAction.setText(
String.format(context.getString(R.string.apply_to_join_group), targetName));
break;
case RejectTeamApply:
binding.tvAction.setText(
String.format(context.getString(R.string.apply_to_join_group), targetName));
showResult(context.getString(R.string.contact_verify_agreed), false);
break;
case TeamInvite:
binding.tvAction.setText(
String.format(context.getString(R.string.invited_to_join_group), targetName));
break;
case DeclineTeamInvite:
binding.tvAction.setText(
String.format(context.getString(R.string.invited_to_join_group), targetName));
showResult(context.getString(R.string.contact_verify_agreed), false);
break;
case AddFriend:
if (info.getAttachObject() != null && info.getAttachObject() instanceof FriendNotify) {
FriendNotify notifyAttach = (FriendNotify) info.getAttachObject();
if (notifyAttach.getType() == FriendVerifyType.AgreeAdd) {
binding.tvAction.setText(R.string.accept_your_friend_apply);
showResult(null, true);
} else if (notifyAttach.getType() == FriendVerifyType.DirectAdd) {
binding.tvAction.setText(R.string.direct_add_your_friend_apply);
showResult(null, true);
} else if (notifyAttach.getType() == FriendVerifyType.RejectAdd) {
binding.tvAction.setText(R.string.reject_your_friend_apply);
showResult(null, true);
} else if (notifyAttach.getType() == FriendVerifyType.VerifyRequest) {
binding.tvAction.setText(R.string.friend_apply);
}
} else {
binding.tvAction.setText(R.string.friend_apply);
}
break;
case Undefined:
break;
}
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
private void showResult(String content, boolean agreeIcon) {
if (!TextUtils.isEmpty(content)) {
binding.tvVerifyResult.setText(content);
binding.ivVerifyResult.setImageResource(
agreeIcon ? R.mipmap.ic_agree_status : R.mipmap.ic_reject_status);
binding.llyVerifyResult.setVisibility(View.VISIBLE);
} else {
binding.llyVerifyResult.setVisibility(View.GONE);
}
binding.llyVerify.setVisibility(View.GONE);
}
public void setVerifyListener(VerifyListener verifyListener) {
this.verifyListener = verifyListener;
}
public interface VerifyListener {
void onAccept(ContactVerifyInfoBean bean);
void onReject(ContactVerifyInfoBean bean);
void onDetails(ContactVerifyInfoBean bean);
}
}

View File

@@ -0,0 +1,394 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.indexbar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.indexbar.bean.IndexPinyinBean;
import com.netease.yunxin.kit.contactkit.ui.indexbar.helper.IIndexBarDataHelper;
import com.netease.yunxin.kit.contactkit.ui.indexbar.helper.IndexBarDataHelperImpl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/** index bar in the right of contact */
public class IndexBar extends View {
public IndexBar(Context context) {
this(context, null);
}
public IndexBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IndexBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
public static String[] INDEX_STRING = {
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"T", "U", "V", "W", "X", "Y", "Z", "#"
};
// whether need get index from dataif only ABCshow A B C Tag only
private boolean isNeedRealIndex;
private List<String> mIndexDataList;
//width and height for view
private int mWidth, mHeight;
//height for item
private int mGapHeight;
//pressed index
int pressI = -1;
private Paint mPaint;
//paint for background
private Paint backgroundPaint;
private int mPressedBackground;
@ColorInt private int textColor;
@ColorInt private int textColorPressed;
private IIndexBarDataHelper mDataHelper;
//this TextView will be set outsideit will show selected tag
private TextView mPressedShowTextView;
private boolean isSourceDataAlreadySorted;
private List<? extends IndexPinyinBean> mSourceData;
private LinearLayoutManager mLayoutManager;
private int mHeaderViewCount = 0;
private onIndexPressedListener mOnIndexPressedListener;
public int getHeaderViewCount() {
return mHeaderViewCount;
}
public IndexBar setHeaderViewCount(int headerViewCount) {
mHeaderViewCount = headerViewCount;
return this;
}
public boolean isSourceDataAlreadySorted() {
return isSourceDataAlreadySorted;
}
public IndexBar setSourceDataAlreadySorted(boolean sourceDataAlreadySorted) {
isSourceDataAlreadySorted = sourceDataAlreadySorted;
return this;
}
public IIndexBarDataHelper getDataHelper() {
return mDataHelper;
}
public IndexBar setDataHelper(IIndexBarDataHelper dataHelper) {
mDataHelper = dataHelper;
return this;
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
int textSize =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
8,
getResources().getDisplayMetrics()); //default text size
textColor = getContext().getResources().getColor(R.color.color_14131b);
textColorPressed = getContext().getResources().getColor(R.color.color_ffffff);
mPressedBackground = Color.TRANSPARENT; //default background color
TypedArray typedArray =
context.getTheme().obtainStyledAttributes(attrs, R.styleable.IndexBar, defStyleAttr, 0);
int n = typedArray.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = typedArray.getIndex(i);
if (attr == R.styleable.IndexBar_indexBarTextSize) {
textSize = typedArray.getDimensionPixelSize(attr, textSize);
} else if (attr == R.styleable.IndexBar_indexBarPressBackground) {
mPressedBackground = typedArray.getColor(attr, mPressedBackground);
} else if (attr == R.styleable.IndexBar_indexBarTextColor) {
textColor = typedArray.getColor(attr, textColor);
} else if (attr == R.styleable.IndexBar_indexBarTextColorPress) {
textColorPressed = typedArray.getColor(attr, textColorPressed);
}
}
typedArray.recycle();
initIndexData();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(textSize);
mPaint.setColor(textColor);
backgroundPaint = new Paint();
backgroundPaint.setAntiAlias(true);
backgroundPaint.setColor(context.getResources().getColor(R.color.color_537ff4));
//set default index pressed listener
setOnIndexPressedListener(
new onIndexPressedListener() {
@Override
public void onIndexPressed(int index, String text) {
//show hintTextView
if (mPressedShowTextView != null) {
mPressedShowTextView.setVisibility(View.VISIBLE);
mPressedShowTextView.setText(text);
}
//scroll recycleView
if (mLayoutManager != null) {
int position = getPosByTag(text);
if (position != -1) {
mLayoutManager.scrollToPositionWithOffset(position, 0);
}
}
}
@Override
public void onMotionEventEnd() {
//hide hintTextView
if (mPressedShowTextView != null) {
mPressedShowTextView.setVisibility(View.GONE);
}
}
});
mDataHelper = new IndexBarDataHelperImpl();
}
public void configIndexTextBGColor(int color) {
backgroundPaint.setColor(color);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//get Mode and Size
int wMode = MeasureSpec.getMode(widthMeasureSpec);
int wSize = MeasureSpec.getSize(widthMeasureSpec);
int hMode = MeasureSpec.getMode(heightMeasureSpec);
int hSize = MeasureSpec.getSize(heightMeasureSpec);
int measureWidth = 0, measureHeight = 0;
//rect for index
Rect indexBounds = new Rect();
String index; //index tag
for (int i = 0; i < mIndexDataList.size(); i++) {
index = mIndexDataList.get(i);
mPaint.getTextBounds(
index, 0, index.length(), indexBounds); //get width and height for text rect
measureWidth = Math.max(indexBounds.width(), measureWidth); //get max width for index
measureHeight = Math.max(indexBounds.height(), measureHeight); //get max height for index
}
measureHeight *= mIndexDataList.size(); //total index
switch (wMode) {
case MeasureSpec.EXACTLY:
measureWidth = wSize;
break;
case MeasureSpec.AT_MOST:
measureWidth = Math.min(measureWidth, wSize);
break;
case MeasureSpec.UNSPECIFIED:
break;
}
switch (hMode) {
case MeasureSpec.EXACTLY:
measureHeight = hSize;
break;
case MeasureSpec.AT_MOST:
measureHeight = Math.min(measureHeight, hSize);
break;
case MeasureSpec.UNSPECIFIED:
break;
}
setMeasuredDimension(measureWidth, measureHeight);
}
@Override
protected void onDraw(Canvas canvas) {
int t = getPaddingTop();
String index; //index to draw
for (int i = 0; i < mIndexDataList.size(); i++) {
//draw selected index background
if (i == pressI) {
canvas.drawCircle(
(float) mWidth / 2,
t + mGapHeight * (i + 0.5f),
(float) Math.min(mWidth, mGapHeight) / 2,
backgroundPaint);
mPaint.setColor(textColorPressed);
} else {
mPaint.setColor(textColor);
}
index = mIndexDataList.get(i);
Paint.FontMetrics fontMetrics =
mPaint.getFontMetrics(); //get FontMetrics of painty for drawText mean text baseLine
int baseline =
(int)
((mGapHeight - fontMetrics.bottom - fontMetrics.top)
/ 2); //get every index rect,baseline on vertical central
canvas.drawText(
index,
(float) mWidth / 2 - mPaint.measureText(index) / 2,
t + mGapHeight * i + baseline,
mPaint);
}
}
@Override
public boolean performClick() {
return super.performClick();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
setBackgroundColor(mPressedBackground);
case MotionEvent.ACTION_MOVE:
float y = event.getY();
pressI = (int) ((y - getPaddingTop()) / mGapHeight);
if (pressI < 0) {
pressI = 0;
} else if (pressI >= mIndexDataList.size()) {
pressI = mIndexDataList.size() - 1;
}
if (null != mOnIndexPressedListener && pressI > -1 && pressI < mIndexDataList.size()) {
mOnIndexPressedListener.onIndexPressed(pressI, mIndexDataList.get(pressI));
invalidate();
}
break;
case MotionEvent.ACTION_UP:
performClick();
case MotionEvent.ACTION_CANCEL:
default:
setBackgroundResource(android.R.color.transparent); //set transparent when up
if (null != mOnIndexPressedListener) {
mOnIndexPressedListener.onMotionEventEnd();
}
pressI = -1;
invalidate();
break;
}
return true;
}
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
super.onSizeChanged(w, h, oldW, oldH);
mWidth = w;
mHeight = h;
if (null == mIndexDataList || mIndexDataList.isEmpty()) {
return;
}
computeGapHeight();
}
public onIndexPressedListener getOnIndexPressedListener() {
return mOnIndexPressedListener;
}
public void setOnIndexPressedListener(onIndexPressedListener mOnIndexPressedListener) {
this.mOnIndexPressedListener = mOnIndexPressedListener;
}
public IndexBar setPressedShowTextView(TextView mPressedShowTextView) {
this.mPressedShowTextView = mPressedShowTextView;
return this;
}
public IndexBar setLayoutManager(LinearLayoutManager mLayoutManager) {
this.mLayoutManager = mLayoutManager;
return this;
}
/** must been invoked before {@link #setSourceData(List)} */
public IndexBar setNeedRealIndex(boolean needRealIndex) {
isNeedRealIndex = needRealIndex;
initIndexData();
return this;
}
private void initIndexData() {
if (isNeedRealIndex) {
mIndexDataList = new ArrayList<>();
} else {
mIndexDataList = Arrays.asList(INDEX_STRING);
}
}
public IndexBar setSourceData(List<? extends IndexPinyinBean> mSourceDatas) {
this.mSourceData = mSourceDatas;
initSourceData();
return this;
}
/** init source data and get index data */
private void initSourceData() {
if (null == mSourceData || mSourceData.isEmpty()) {
return;
}
if (!isSourceDataAlreadySorted) {
//sort sourceData
mDataHelper.sortSourceData(mSourceData);
} else {
mDataHelper.convert(mSourceData);
mDataHelper.fillIndexTag(mSourceData);
}
if (isNeedRealIndex) {
mDataHelper.getSortedIndexData(mSourceData, mIndexDataList);
computeGapHeight();
}
}
/** invoked when 1 data changed 2 size changed get gapHeight */
private void computeGapHeight() {
mGapHeight = (mHeight - getPaddingTop() - getPaddingBottom()) / mIndexDataList.size();
}
private int getPosByTag(String tag) {
if (null == mSourceData || mSourceData.isEmpty()) {
return -1;
}
if (TextUtils.isEmpty(tag)) {
return -1;
}
for (int i = 0; i < mSourceData.size(); i++) {
if (tag.equals(mSourceData.get(i).getIndexTag())) {
return i + getHeaderViewCount();
}
}
return -1;
}
/** on index pressed listener */
public interface onIndexPressedListener {
void onIndexPressed(int index, String text);
void onMotionEventEnd(); //UP CANCEL
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.indexbar.bean;
import com.netease.yunxin.kit.contactkit.ui.indexbar.suspension.ISuspension;
import java.io.Serializable;
/** base bean for index */
public abstract class IndexBean implements ISuspension, Serializable {
private String indexTag; //tag of index show in the top
public String getIndexTag() {
return indexTag;
}
public IndexBean setIndexTag(String baseIndexTag) {
this.indexTag = baseIndexTag;
return this;
}
@Override
public String getTag() {
return indexTag;
}
@Override
public boolean isShowDivision() {
return true;
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.indexbar.bean;
/** index pinyin bean */
public abstract class IndexPinyinBean extends IndexBean {
private String indexPinyin;
public String getIndexPinyin() {
return indexPinyin;
}
public IndexPinyinBean setIndexPinyin(String indexPinyin) {
this.indexPinyin = indexPinyin;
return this;
}
//is need to translate as pinyin
public boolean isNeedToPinyin() {
return false;
}
//get target String
public abstract String getTarget();
@Override
public boolean isSuspension() {
return false;
}
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.indexbar.helper;
import com.netease.yunxin.kit.contactkit.ui.indexbar.bean.IndexPinyinBean;
import java.util.List;
/**
* helper for indexBar and data 1 translate Chinese characters to pinyin 2 fill indexTag 3 sort
* source data 4 source data after sort->indexBar source data
*/
public interface IIndexBarDataHelper {
//translate Chinese characters to pinyin
IIndexBarDataHelper convert(List<? extends IndexPinyinBean> data);
//pinyin->tag
IIndexBarDataHelper fillIndexTag(List<? extends IndexPinyinBean> data);
//sort source data
IIndexBarDataHelper sortSourceData(List<? extends IndexPinyinBean> data);
//sort the data for indexBar,call after sortSourceData
IIndexBarDataHelper getSortedIndexData(
List<? extends IndexPinyinBean> sourceData, List<String> data);
}

View File

@@ -0,0 +1,120 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.indexbar.helper;
import android.text.TextUtils;
import com.github.promeg.pinyinhelper.Pinyin;
import com.netease.yunxin.kit.contactkit.ui.indexbar.bean.IndexPinyinBean;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class IndexBarDataHelperImpl implements IIndexBarDataHelper {
/** if need Chinese Character -> Pinyin */
@Override
public IIndexBarDataHelper convert(List<? extends IndexPinyinBean> data) {
if (null == data || data.isEmpty()) {
return this;
}
int size = data.size();
for (int i = 0; i < size; i++) {
IndexPinyinBean indexPinyinBean = data.get(i);
StringBuilder pySb = new StringBuilder();
if (indexPinyinBean.isNeedToPinyin()) {
String target = indexPinyinBean.getTarget();
for (int i1 = 0; i1 < target.length(); i1++) {
pySb.append(Pinyin.toPinyin(target.charAt(i1)).toUpperCase());
}
indexPinyinBean.setIndexPinyin(pySb.toString()); //设置城市名全拼音
}
}
return this;
}
/** 如果需要取出,则 取出首字母->tag,或者特殊字母 "#". 否则,用户已经实现设置好 */
@Override
public IIndexBarDataHelper fillIndexTag(List<? extends IndexPinyinBean> dataList) {
if (null == dataList || dataList.isEmpty()) {
return this;
}
int size = dataList.size();
for (int i = 0; i < size; i++) {
IndexPinyinBean indexPinyinBean = dataList.get(i);
if (indexPinyinBean.isNeedToPinyin()) {
//以下代码设置城市拼音首字母
if (TextUtils.isEmpty(indexPinyinBean.getIndexPinyin())) {
indexPinyinBean.setIndexTag("#");
continue;
}
String tagString = indexPinyinBean.getIndexPinyin().substring(0, 1);
if (tagString.matches("[A-Z]")) { //如果是A-Z字母开头
indexPinyinBean.setIndexTag(tagString);
} else { //特殊字母这里统一用#处理
indexPinyinBean.setIndexTag("#");
}
}
}
return this;
}
@Override
public IIndexBarDataHelper sortSourceData(List<? extends IndexPinyinBean> data) {
if (null == data || data.isEmpty()) {
return this;
}
convert(data);
fillIndexTag(data);
//对数据源进行排序
Collections.sort(
data,
(Comparator<IndexPinyinBean>)
(lhs, rhs) -> {
if (!lhs.isNeedToPinyin()) {
return 0;
} else if (!rhs.isNeedToPinyin()) {
return 0;
} else if (lhs.getIndexTag().equals("#") && !rhs.getIndexTag().equals("#")) {
return 1;
} else if (!lhs.getIndexTag().equals("#") && rhs.getIndexTag().equals("#")) {
return -1;
} else {
String lhsIndexTag = lhs.getIndexTag();
String rhsIndexTag = rhs.getIndexTag();
if (lhsIndexTag.equals("#") && rhsIndexTag.equals("#")) {
String lhsFirst = lhs.getIndexPinyin().substring(0, 1);
String rhsFirst = rhs.getIndexPinyin().substring(0, 1);
if (TextUtils.isDigitsOnly(lhsFirst) && !TextUtils.isDigitsOnly(rhsFirst)) {
return -1;
} else if (!TextUtils.isDigitsOnly(lhsFirst)
&& TextUtils.isDigitsOnly(rhsFirst)) {
return 1;
} else {
return lhs.getIndexPinyin().compareTo(rhs.getIndexPinyin());
}
} else {
return lhs.getIndexPinyin().compareTo(rhs.getIndexPinyin());
}
}
});
return this;
}
@Override
public IIndexBarDataHelper getSortedIndexData(
List<? extends IndexPinyinBean> sourceData, List<String> data) {
if (null == sourceData || sourceData.isEmpty()) {
return this;
}
int size = sourceData.size();
String baseIndexTag;
for (int i = 0; i < size; i++) {
baseIndexTag = sourceData.get(i).getIndexTag();
if (!data.contains(baseIndexTag)) {
data.add(baseIndexTag);
}
}
return this;
}
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.indexbar.suspension;
/** suspension interface */
public interface ISuspension {
boolean isSuspension();
boolean isShowDivision();
String getTag();
}

View File

@@ -0,0 +1,298 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.indexbar.suspension;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.yunxin.kit.contactkit.ui.R;
import java.util.List;
/** custom decoration for Suspension */
public class SuspensionDecoration extends RecyclerView.ItemDecoration {
private static final int DEFAULT_COLOR_TITLE_BOTTOM_LINE_COLOR_RES = R.color.color_dbe0e8;
private static final int DEFAULT_INDEX_DECORATION_BG_RES = R.color.color_eff1f4;
private int colorTitleBg;
private int colorTitleBottomLine;
private int colorTitleFont;
private int indexDecorationBg;
private int mTitleFontSize;
private int bottomLineHeight;
private List<? extends ISuspension> mData;
private final Paint mPaint;
private final Rect mBounds; //rect for restore text for measure
private int mTitleHeight;
private int mHeaderViewCount = 0;
private float paddingLeft;
private boolean showTagOffset = true;
private boolean isFirstTagDraw = true;
private boolean titleAlignBottom = false;
private final int indexDecorationHeight;
public SuspensionDecoration(Context context, List<? extends ISuspension> datas) {
super();
mData = datas;
mPaint = new Paint();
mBounds = new Rect();
colorTitleBg = context.getResources().getColor(R.color.color_ffffff);
colorTitleBottomLine =
context.getResources().getColor(DEFAULT_COLOR_TITLE_BOTTOM_LINE_COLOR_RES);
colorTitleFont = context.getResources().getColor(R.color.color_b3b7bc);
indexDecorationBg = context.getResources().getColor(DEFAULT_INDEX_DECORATION_BG_RES);
mTitleHeight =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 33f, context.getResources().getDisplayMetrics());
indexDecorationHeight =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 6f, context.getResources().getDisplayMetrics());
mTitleFontSize =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 14f, context.getResources().getDisplayMetrics());
bottomLineHeight =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 1f, context.getResources().getDisplayMetrics());
mPaint.setTextSize(mTitleFontSize);
mPaint.setAntiAlias(true);
}
public SuspensionDecoration setTitleHeight(int mTitleHeight) {
this.mTitleHeight = mTitleHeight;
return this;
}
public SuspensionDecoration setBottomLineHeight(int bottomLineHeight) {
this.bottomLineHeight = bottomLineHeight;
return this;
}
public SuspensionDecoration setIndexDecorationBg(int indexDecorationBg) {
this.indexDecorationBg = indexDecorationBg;
return this;
}
public SuspensionDecoration setColorTitleBg(int colorTitleBg) {
this.colorTitleBg = colorTitleBg;
return this;
}
public SuspensionDecoration setColorTitleFont(int colorTitleFont) {
this.colorTitleFont = colorTitleFont;
return this;
}
public SuspensionDecoration setTitleFontSize(int mTitleFontSize) {
this.mTitleFontSize = mTitleFontSize;
mPaint.setTextSize(mTitleFontSize);
return this;
}
public SuspensionDecoration setColorTitleBottomLine(int colorTitleBottomLine) {
this.colorTitleBottomLine = colorTitleBottomLine;
return this;
}
public SuspensionDecoration setPaddingLeft(float paddingLeft) {
this.paddingLeft = paddingLeft;
return this;
}
public SuspensionDecoration setTitleAlignBottom(boolean align) {
this.titleAlignBottom = align;
return this;
}
public SuspensionDecoration setData(List<? extends ISuspension> mData) {
this.mData = mData;
return this;
}
public SuspensionDecoration setShowTagOff(boolean show) {
this.showTagOffset = show;
return this;
}
public int getHeaderViewCount() {
return mHeaderViewCount;
}
public SuspensionDecoration setHeaderViewCount(int headerViewCount) {
mHeaderViewCount = headerViewCount;
return this;
}
@Override
public void onDraw(
@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
isFirstTagDraw = true;
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int position = params.getViewLayoutPosition();
position -= getHeaderViewCount();
if (mData == null
|| mData.isEmpty()
|| position > mData.size() - 1
|| position < 0
|| !mData.get(position).isShowDivision()) {
continue;
}
if (position == 0) { //if 0 must have title
drawTitleArea(c, left, right, child, params, position);
} else {
if (null != mData.get(position).getTag()
&& !mData.get(position).getTag().equals(mData.get(position - 1).getTag())) {
//not empty and different with pre
drawTitleArea(c, left, right, child, params, position);
}
}
}
}
/** draw title area */
private void drawTitleArea(
Canvas c,
int left,
int right,
View child,
RecyclerView.LayoutParams params,
int position) { //最先调用,绘制在最下层
if (isFirstTagDraw) {
mPaint.setColor(indexDecorationBg);
c.drawRect(
left,
child.getTop() - params.topMargin - mTitleHeight - indexDecorationHeight,
right,
child.getTop() - params.topMargin - mTitleHeight,
mPaint);
}
isFirstTagDraw = false;
mPaint.setColor(colorTitleBg);
c.drawRect(
left,
child.getTop() - params.topMargin - mTitleHeight,
right,
child.getTop() - params.topMargin,
mPaint);
mPaint.setColor(colorTitleBottomLine);
c.drawRect(
left + paddingLeft,
child.getTop() - params.topMargin - bottomLineHeight,
right,
child.getTop() - params.topMargin,
mPaint);
mPaint.setColor(colorTitleFont);
mPaint.getTextBounds(
mData.get(position).getTag(), 0, mData.get(position).getTag().length(), mBounds);
c.drawText(
mData.get(position).getTag(),
child.getPaddingLeft() + paddingLeft,
child.getTop()
- params.topMargin
- (titleAlignBottom ? 0 : ((float) mTitleHeight / 2 - (float) mBounds.height() / 2)),
mPaint);
}
@Override
public void onDrawOver(
@NonNull Canvas c, final RecyclerView parent, @NonNull RecyclerView.State state) {
if (parent.getLayoutManager() == null) {
return;
}
int pos = ((LinearLayoutManager) (parent.getLayoutManager())).findFirstVisibleItemPosition();
pos -= getHeaderViewCount();
if (mData == null
|| mData.isEmpty()
|| pos > mData.size() - 1
|| pos < 0
|| !mData.get(pos).isSuspension()) {
return;
}
String tag = mData.get(pos).getTag();
RecyclerView.ViewHolder viewHolder =
parent.findViewHolderForLayoutPosition(pos + getHeaderViewCount());
if (viewHolder == null) {
return;
}
View child = viewHolder.itemView;
boolean flag = false; //flag for canvas have moved
if ((pos + 1) < mData.size()) {
if (null != tag && !tag.equals(mData.get(pos + 1).getTag())) {
if (child.getHeight() + child.getTop() < mTitleHeight) {
c.save();
flag = true;
c.translate(0, child.getHeight() + child.getTop() - mTitleHeight);
}
}
}
mPaint.setColor(colorTitleBg);
c.drawRect(
parent.getPaddingLeft(),
parent.getPaddingTop(),
parent.getRight() - parent.getPaddingRight(),
parent.getPaddingTop() + mTitleHeight,
mPaint);
mPaint.setColor(colorTitleFont);
mPaint.getTextBounds(tag, 0, tag == null ? 0 : tag.length(), mBounds);
c.drawText(
tag,
child.getPaddingLeft() + 40,
parent.getPaddingTop()
+ mTitleHeight
- (titleAlignBottom ? 0 : ((float) mTitleHeight / 2 - (float) mBounds.height() / 2)),
mPaint);
if (flag) c.restore();
}
@Override
public void getItemOffsets(
@NonNull Rect outRect,
@NonNull View view,
@NonNull RecyclerView parent,
@NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewLayoutPosition();
position -= getHeaderViewCount();
if (mData == null || mData.isEmpty() || position > mData.size() - 1) {
return;
}
if (position >= 0) {
ISuspension titleCategoryInterface = mData.get(position);
if (titleCategoryInterface.isShowDivision()) {
if (position == 0) {
outRect.set(0, mTitleHeight + (showTagOffset ? indexDecorationHeight : 0), 0, 0);
} else {
if (null != titleCategoryInterface.getTag()
&& !titleCategoryInterface.getTag().equals(mData.get(position - 1).getTag())) {
outRect.set(0, mTitleHeight + (showTagOffset ? indexDecorationHeight : 0), 0, 0);
}
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.interfaces;
import android.util.SparseArray;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListView;
/** Actions for default ViewHolder in {@link ContactListView} */
public class ContactActions {
private final SparseArray<IContactClickListener> itemClickListeners;
private final SparseArray<IContactSelectorListener> itemSelectorListeners;
public ContactActions() {
itemClickListeners = new SparseArray<>();
itemSelectorListeners = new SparseArray<>();
}
public void addSelectorListener(int viewType, IContactSelectorListener selectorListener) {
itemSelectorListeners.put(viewType, selectorListener);
}
public void addContactListener(int viewType, IContactClickListener contactListener) {
itemClickListeners.put(viewType, contactListener);
}
public IContactClickListener getContactListener(int viewType) {
return itemClickListeners.get(viewType);
}
public IContactSelectorListener getSelectorListener(int viewType) {
return itemSelectorListeners.get(viewType);
}
}

View File

@@ -0,0 +1,10 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.interfaces;
public interface IContactCallback {
void updateUnreadCount(int count);
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.interfaces;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
/** Action listener */
public interface IContactClickListener {
/** on item click */
void onClick(int position, BaseContactBean data);
}

View File

@@ -0,0 +1,90 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.interfaces;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactTeamBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListView;
import java.util.List;
/**
* when data have changedyou can user this interface post data to {@link ContactListView}
*/
public interface IContactDataChanged {
/**
* Contacts friend data have changed
*/
void onFriendDataSourceChanged(List<ContactFriendBean> contactItemBeanList);
/**
* add one friend data
*
* @param friend friend data,this data will been sort
*/
void addFriendData(List<ContactFriendBean> friend);
void insertFrindData(int index, ContactFriendBean friendBean);
void updateGroupData(ContactTeamBean teamBeans);
/**
* remove one friend data
*
* @param friend friend data,data will been sort
*/
void removeFriendData(List<ContactFriendBean> friend);
/**
* update one friend data
*
* @param friend friend data,data will been sort
*/
void updateFriendData(List<ContactFriendBean> friend);
/**
* update data which have been added
*
* @param data any data instanceof BaseContactBean except Friend
*/
void updateContactData(BaseContactBean data);
/**
* update data as list(remove all this type data and add new data)
*
* @param viewType the viewType been update
* @param data any data instanceof BaseContactBean except Friends
*/
void updateContactData(int viewType, List<? extends BaseContactBean> data);
/**
* add a custom data
*/
void addContactData(BaseContactBean contactData);
/**
* add custom data as list
*/
void addContactData(List<? extends BaseContactBean> contactData);
/**
* remove custom data
*/
void removeContactData(BaseContactBean contactData);
/**
* remove custom as list
*/
void removeContactData(List<? extends BaseContactBean> contactData);
/**
* clear data
*/
void clearContactData();
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.interfaces;
import com.netease.yunxin.kit.contactkit.ui.IContactFactory;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.adapter.ContactAdapter;
public interface IContactListView {
//******************interaction*************
/**
* set action for Contact
*
* @param contactActions actions
*/
void setContactAction(ContactActions contactActions);
//******************ui display*************
/** get adapter in ContactListView */
ContactAdapter getAdapter();
/**
* if you want add your owner viewHolder,you should set your owner factory here
*
* @param viewHolderFactory your owner ContactViewHolderFactory
*/
void setViewHolderFactory(IContactFactory viewHolderFactory);
/**
* set config for ContactListView
*
* @param attrs config
*/
void setViewConfig(ContactListViewAttrs attrs);
}

View File

@@ -0,0 +1,14 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.interfaces;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
/** Action listener */
public interface IContactSelectorListener {
/** on item click */
void onSelector(boolean selector, BaseContactBean data);
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.interfaces;
import androidx.annotation.ColorInt;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListView;
/** attrs for {@link ContactListView} */
public interface IContactViewAttrs {
/** set textColor for Name */
void setTitleColor(@ColorInt int color);
/**
* show indexBar or not
*
* @param show show if true
*/
void showIndexBar(boolean show);
/**
* show selector
*
* @param show show selector if true
*/
void showSelector(boolean show);
}

View File

@@ -0,0 +1,38 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import com.netease.nimlib.sdk.msg.attachment.MsgAttachment;
import com.netease.yunxin.kit.contactkit.ui.indexbar.bean.IndexPinyinBean;
public abstract class BaseContactBean extends IndexPinyinBean {
/** viewType {@link IViewTypeConstant} for data */
public int viewType;
/** router for item click */
public String router;
/** start as 0, weight bigger means item will show higher 0 for Friends Data */
public int weight;
public MsgAttachment attachment;
public MsgAttachment getAttachment() {
return attachment;
}
public void setAttachment(MsgAttachment attachment) {
this.attachment = attachment;
}
public interface ContactBeanWeight {
/** base weight,for friend item */
int BASE_WEIGHT = 0;
/** weight for entrance */
int ENTRANCE_WEIGHT = 50;
}
}

View File

@@ -0,0 +1,55 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import android.text.TextUtils;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
/**
* data bean for friend
*/
public class ContactBlackListBean extends BaseContactBean {
public UserInfo data;
public FriendInfo friendInfo;
public ContactBlackListBean(UserInfo data) {
this.data = data;
viewType = IViewTypeConstant.CONTACT_BLACK_LIST;
}
public String getName() {
if (friendInfo != null && !TextUtils.isEmpty(friendInfo.getAlias())) {
return friendInfo.getAlias();
}
if (!TextUtils.isEmpty(data.getName())) {
return data.getName();
}
return data.getAccount();
}
public String getAvatarName() {
if (!TextUtils.isEmpty(data.getName())) {
return data.getName();
}
return data.getAccount();
}
@Override
public boolean isShowDivision() {
return false;
}
@Override
public String getTarget() {
return data.getName();
}
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import java.util.Objects;
/** Contact data for default ViewHolder */
public class ContactEntranceBean extends BaseContactBean {
public @DrawableRes int icon;
public int number;
public boolean showRightArrow = true;
public String title;
public ContactEntranceBean(@DrawableRes int icon, @NonNull String title) {
this.icon = icon;
this.title = title;
weight = ContactBeanWeight.ENTRANCE_WEIGHT;
viewType = IViewTypeConstant.CONTACT_ACTION_ENTER;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContactEntranceBean that = (ContactEntranceBean) o;
return icon == that.icon && Objects.equals(title, that.title);
}
@Override
public int hashCode() {
return Objects.hash(icon, title);
}
@Override
public boolean isShowDivision() {
return false;
}
@Override
public String getTarget() {
return null;
}
}

View File

@@ -0,0 +1,130 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import com.netease.nimlib.sdk.msg.attachment.MsgAttachment;
import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum;
import com.netease.yunxin.kit.chatkit.model.ConversationInfo;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import java.util.Objects;
/**
* data bean for friend
*/
public class ContactFriendBean extends BaseContactBean {
public FriendInfo data;
public String conversationText;
public String conversationTime;
public MsgTypeEnum msgTypeEnum;
public MsgAttachment msgAttachment;
// public ConversationInfo conversationInfo;
public long time = 0;
public MsgAttachment getMsgAttachment() {
return msgAttachment;
}
public void setMsgAttachment(MsgAttachment msgAttachment) {
this.msgAttachment = msgAttachment;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public MsgTypeEnum getMsgTypeEnum() {
return msgTypeEnum;
}
public void setMsgTypeEnum(MsgTypeEnum msgTypeEnum) {
this.msgTypeEnum = msgTypeEnum;
}
// public ConversationInfo getConversationInfo() {
// return conversationInfo;
// }
//
// public void setConversationInfo(ConversationInfo conversationInfo) {
// this.conversationInfo = conversationInfo;
// }
public boolean isSelected;
public boolean cannotSelected;
public String getConversationText() {
return conversationText;
}
public void setConversationText(String conversationText) {
this.conversationText = conversationText;
}
public String getConversationTime() {
return conversationTime;
}
public void setConversationTime(String conversationTime) {
this.conversationTime = conversationTime;
}
public ContactFriendBean(FriendInfo data) {
this.data = data;
weight = ContactBeanWeight.BASE_WEIGHT;
viewType = IViewTypeConstant.CONTACT_FRIEND;
}
public boolean isSelected() {
return isSelected;
}
public boolean isCannotSelected() {
return cannotSelected;
}
public void setCannotSelected(boolean cannotSelected) {
this.cannotSelected = cannotSelected;
}
public void setSelected(boolean selected) {
isSelected = selected;
}
@Override
public boolean isNeedToPinyin() {
return true;
}
@Override
public boolean isShowDivision() {
return true;
}
@Override
public String getTarget() {
return data.getName();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContactFriendBean that = (ContactFriendBean) o;
return this.data.equals(that.data);
}
@Override
public int hashCode() {
return Objects.hash(data);
}
}

View File

@@ -0,0 +1,109 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import com.netease.nimlib.sdk.msg.attachment.MsgAttachment;
import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum;
import com.netease.nimlib.sdk.team.model.Team;
import com.netease.yunxin.kit.chatkit.model.ConversationInfo;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
/**
* Contact data for team
*/
public class ContactTeamBean extends BaseContactBean {
public String content;
public Team data;
public long conversationTime;
public String time;
public MsgTypeEnum msgTypeEnum;
public MsgAttachment msgAttachment;
public String fromNick;
public String fromAccount;
public String getFromNick() {
return fromNick;
}
public void setFromNick(String fromNick) {
this.fromNick = fromNick;
}
public String getFromAccount() {
return fromAccount;
}
public void setFromAccount(String fromAccount) {
this.fromAccount = fromAccount;
}
public MsgTypeEnum getMsgTypeEnum() {
return msgTypeEnum;
}
public void setMsgTypeEnum(MsgTypeEnum msgTypeEnum) {
this.msgTypeEnum = msgTypeEnum;
}
public MsgAttachment getMsgAttachment() {
return msgAttachment;
}
public void setMsgAttachment(MsgAttachment msgAttachment) {
this.msgAttachment = msgAttachment;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
// public ConversationInfo getConversationInfo() {
// return conversationInfo;
// }
//
// public void setConversationInfo(ConversationInfo conversationInfo) {
// this.conversationInfo = conversationInfo;
// }
public long getConversationTime() {
return conversationTime;
}
public void setConversationTime(long conversationTime) {
this.conversationTime = conversationTime;
}
public ContactTeamBean(Team data) {
this.data = data;
viewType = IViewTypeConstant.CONTACT_TEAM_LIST;
this.router = RouterConstant.PATH_CHAT_TEAM_PAGE;
}
@Override
public boolean isShowDivision() {
return false;
}
@Override
public String getTarget() {
return null;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import android.text.TextUtils;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
/** profile page user info */
public class ContactUserInfoBean {
public UserInfo data;
public FriendInfo friendInfo;
public boolean isFriend;
public boolean isBlack;
public boolean messageNotify = true;
public ContactUserInfoBean(UserInfo user) {
this.data = user;
}
public String getName() {
String name = data.getUserInfoName();
if (friendInfo != null && !TextUtils.isEmpty(friendInfo.getAlias())) {
name = friendInfo.getName();
}
return name;
}
}

View File

@@ -0,0 +1,128 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import android.text.TextUtils;
import com.netease.nimlib.sdk.friend.model.AddFriendNotify;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfo;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoStatus;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoType;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/** Contact data for verify data */
public class ContactVerifyInfoBean extends BaseContactBean {
public SystemMessageInfo data;
public List<SystemMessageInfo> messageList = new ArrayList<>();
public ContactVerifyInfoBean(SystemMessageInfo data) {
this.data = data;
messageList.add(data);
viewType = IViewTypeConstant.CONTACT_VERIFY_INFO;
}
public ContactVerifyInfoBean(SystemMessageInfo data, List<SystemMessageInfo> list) {
this.data = data;
messageList.clear();
messageList.add(data);
if (list != null) {
messageList.addAll(list);
}
viewType = IViewTypeConstant.CONTACT_VERIFY_INFO;
}
public int getUnreadCount() {
int count = 0;
for (SystemMessageInfo messageInfo : messageList) {
if (messageInfo.getUnread()) {
count++;
}
}
return count;
}
public void clearUnreadCount() {
for (SystemMessageInfo messageInfo : messageList) {
if (messageInfo.getUnread()) {
messageInfo.setUnread(false);
}
}
}
public void updateStatus(SystemMessageInfoStatus status) {
for (SystemMessageInfo messageInfo : messageList) {
messageInfo.setInfoStatus(status);
messageInfo.setUnread(false);
}
}
public boolean pushMessageIfSame(SystemMessageInfo messageInfo) {
if (isSameMessage(messageInfo)) {
if (data.getTime() < messageInfo.getTime()) {
data = messageInfo;
}
messageList.add(messageInfo);
return true;
}
return false;
}
@Override
public boolean isShowDivision() {
return false;
}
@Override
public String getTarget() {
return null;
}
public boolean isSameMessage(SystemMessageInfo messageInfo) {
if (messageInfo == null) {
return false;
}
if (data.getInfoType() == messageInfo.getInfoType()
&& TextUtils.equals(data.getFromAccount(), messageInfo.getFromAccount())
&& TextUtils.equals(data.getTargetId(), messageInfo.getTargetId())
&& data.getInfoStatus() == messageInfo.getInfoStatus()) {
if (messageInfo.getInfoType() == SystemMessageInfoType.AddFriend
&& messageInfo.getAttachObject() instanceof AddFriendNotify) {
if (!(data.getAttachObject() instanceof AddFriendNotify)) {
return false;
}
AddFriendNotify notify = (AddFriendNotify) data.getAttachObject();
AddFriendNotify notifyInfo = (AddFriendNotify) messageInfo.getAttachObject();
if (notify.getEvent() == notifyInfo.getEvent()
&& TextUtils.equals(notify.getAccount(), notifyInfo.getAccount())) {
return true;
} else {
return false;
}
}
return true;
}
return false;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ContactVerifyInfoBean)) return false;
ContactVerifyInfoBean infoBean = (ContactVerifyInfoBean) o;
return isSameMessage(infoBean.data);
}
@Override
public int hashCode() {
return Objects.hash(data);
}
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
/** constants view type 1~9 for default ViewHolder */
public interface IViewTypeConstant {
/** friend */
int CONTACT_FRIEND = 1;
/** entrance */
int CONTACT_ACTION_ENTER = 2;
/** custom view type start with this */
int CUSTOM_START = 10;
/** blackList */
int CONTACT_BLACK_LIST = 11;
/** verify info */
int CONTACT_VERIFY_INFO = 12;
/** group list */
int CONTACT_TEAM_LIST = 13;
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import com.netease.yunxin.kit.chatkit.model.FriendSearchInfo;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.contactkit.ui.ContactConstant;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import java.util.Objects;
public class SearchFriendBean extends BaseBean {
private static final String TAG = "SearchFriendBean";
public FriendSearchInfo friendSearchInfo;
public SearchFriendBean(FriendSearchInfo searchInfo, String router) {
this.friendSearchInfo = searchInfo;
this.viewType = ContactConstant.SearchViewType.USER;
this.router = router;
this.paramKey = RouterConstant.CHAT_KRY;
this.param = searchInfo.getFriendInfo().getUserInfo();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SearchFriendBean)) return false;
SearchFriendBean that = (SearchFriendBean) o;
return Objects.equals(
friendSearchInfo.getFriendInfo().getAccount(),
that.friendSearchInfo.getFriendInfo().getAccount());
}
@Override
public int hashCode() {
return Objects.hash(friendSearchInfo.getFriendInfo().getAccount());
}
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import com.netease.yunxin.kit.chatkit.model.TeamSearchInfo;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.contactkit.ui.ContactConstant;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import java.util.Objects;
public class SearchTeamBean extends BaseBean {
private static final String TAG = "SearchTeamBean";
public TeamSearchInfo teamSearchInfo;
public SearchTeamBean(TeamSearchInfo searchInfo, String router) {
this.teamSearchInfo = searchInfo;
this.viewType = ContactConstant.SearchViewType.TEAM;
this.router = router;
this.paramKey = RouterConstant.CHAT_KRY;
this.param = searchInfo.getTeam();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SearchTeamBean)) return false;
SearchTeamBean that = (SearchTeamBean) o;
return Objects.equals(teamSearchInfo.getTeam().getId(), that.teamSearchInfo.getTeam().getId());
}
@Override
public int hashCode() {
return Objects.hash(teamSearchInfo.getTeam().getId());
}
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.model;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.contactkit.ui.ContactConstant;
public class SearchTitleBean extends BaseBean {
public String title;
public int titleRes;
public SearchTitleBean(String tt) {
this.title = tt;
this.viewType = ContactConstant.SearchViewType.TITLE;
}
public SearchTitleBean(int res) {
this.titleRes = res;
this.viewType = ContactConstant.SearchViewType.TITLE;
}
}

View File

@@ -0,0 +1,149 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.ContactUIConfig;
import com.netease.yunxin.kit.contactkit.ui.FragmentBuilder;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactClickListener;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactSelectorListener;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.normal.contact.ContactFragment;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import java.util.List;
public class ContactFragmentBuilder extends FragmentBuilder {
ContactUIConfig contactConfig;
public ContactFragmentBuilder() {}
@Override
public ContactFragment build() {
ContactFragment fragment = new ContactFragment();
if (contactConfig != null) {
fragment.setContactConfig(contactConfig);
}
return fragment;
}
public void setContactConfig(ContactUIConfig contactConfig) {
this.contactConfig = contactConfig;
}
public ContactFragmentBuilder setShowTitleBar(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showTitleBar = show;
return this;
}
public ContactFragmentBuilder setTitle(String title) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.title = title;
return this;
}
public ContactFragmentBuilder setTitleColor(int color) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleColor = color;
return this;
}
public ContactFragmentBuilder setShowTitleBarRight2Icon(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showTitleBarRight2Icon = show;
return this;
}
public ContactFragmentBuilder setSearchIcon(int res) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRight2Res = res;
return this;
}
public ContactFragmentBuilder setSearchClickListener(View.OnClickListener listener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRight2Click = listener;
return this;
}
public ContactFragmentBuilder setShowMoreIcon(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showTitleBarRightIcon = show;
return this;
}
public ContactFragmentBuilder setMoreIcon(int res) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRightRes = res;
return this;
}
public ContactFragmentBuilder setMoreClickListener(View.OnClickListener listener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.titleBarRightClick = listener;
return this;
}
public ContactFragmentBuilder showHeader(boolean show) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.showHeader = show;
return this;
}
public ContactFragmentBuilder setContactListViewAttar(ContactListViewAttrs attrs) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.contactAttrs = attrs;
return this;
}
public ContactFragmentBuilder setHeaderData(List<ContactEntranceBean> data) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.headerData = data;
return this;
}
public ContactFragmentBuilder setContactClickListener(
int type, IContactClickListener contactListener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.itemClickListeners.put(type, contactListener);
return this;
}
public ContactFragmentBuilder setContactSelection(
int type, IContactSelectorListener contactListener) {
if (contactConfig == null) {
contactConfig = new ContactUIConfig();
}
contactConfig.itemSelectorListeners.put(type, contactListener);
return this;
}
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.addfriend;
import android.os.Bundle;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.addfriend.BaseAddFriendActivity;
import com.netease.yunxin.kit.contactkit.ui.databinding.AddFriendActivityBinding;
public class AddFriendActivity extends BaseAddFriendActivity {
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
AddFriendActivityBinding viewBinding = AddFriendActivityBinding.inflate(getLayoutInflater());
etAddFriendAccount = viewBinding.etAddFriendAccount;
ivAddFriendBack = viewBinding.ivAddFriendBack;
ivFriendClear = viewBinding.ivFriendClear;
addFriendEmptyLayout = viewBinding.addFriendEmptyLayout;
return viewBinding.getRoot();
}
}

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.blacklist;
import android.content.Intent;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.blacklist.BaseBlackListActivity;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.normal.selector.ContactSelectorActivity;
import com.netease.yunxin.kit.contactkit.ui.normal.view.ContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder.BlackListViewHolder;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
public class BlackListActivity extends BaseBlackListActivity {
@Override
protected void configViewHolderFactory() {
binding.contactListView.setViewHolderFactory(
new ContactViewHolderFactory() {
@Override
protected BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType) {
if (viewType == IViewTypeConstant.CONTACT_BLACK_LIST) {
BlackListViewHolder viewHolder = new BlackListViewHolder(view);
viewHolder.setRelieveListener(
data -> {
if (!NetworkUtils.isConnected()) {
Toast.makeText(
BlackListActivity.this,
R.string.contact_network_error_tip,
Toast.LENGTH_SHORT)
.show();
return;
}
viewModel.removeBlackOp(
data.data.getAccount(),
new FetchCallback<Void>() {
@Override
public void onException(@Nullable Throwable exception) {
Toast.makeText(
BlackListActivity.this,
getText(R.string.remove_black_fail),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onFailed(int code) {
Toast.makeText(
BlackListActivity.this,
getText(R.string.remove_black_fail),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onSuccess(@Nullable Void param) {
binding.contactListView.removeContactData(data);
}
});
});
return viewHolder;
}
return null;
}
});
}
protected void configTitle(BaseListActivityLayoutBinding binding) {
binding
.title
.setTitle(R.string.black_list)
.setActionImg(R.mipmap.ic_title_bar_more)
.setActionListener(
v -> {
Intent intent = new Intent(this, ContactSelectorActivity.class);
blackListLauncher.launch(intent);
});
}
}

View File

@@ -0,0 +1,25 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.contact;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.contact.BaseContactActivity;
import com.netease.yunxin.kit.contactkit.ui.contact.BaseContactFragment;
import com.netease.yunxin.kit.contactkit.ui.databinding.ContactActivityBinding;
public class ContactActivity extends BaseContactActivity {
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
ContactActivityBinding viewBinding = ContactActivityBinding.inflate(LayoutInflater.from(this));
return viewBinding.getRoot();
}
protected BaseContactFragment getContactFragment() {
return new ContactFragment();
}
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.contact;
import android.view.ViewGroup;
import com.netease.yunxin.kit.contactkit.ui.normal.view.ContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
public class ContactDefaultFactory extends ContactViewHolderFactory {
@Override
protected BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType) {
return null;
}
}

View File

@@ -0,0 +1,143 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.contact;
import static com.netease.yunxin.kit.corekit.im.utils.RouterConstant.PATH_ADD_FRIEND_PAGE;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.contact.BaseContactFragment;
import com.netease.yunxin.kit.contactkit.ui.databinding.ContactFragmentBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.ArrayList;
import java.util.List;
/** contact page */
public class ContactFragment extends BaseContactFragment {
private final String TAG = "ContactFragment";
private ContactFragmentBinding viewBinding;
private ContactEntranceBean verifyBean;
@Override
protected View initViewAndGetRootView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
viewBinding = ContactFragmentBinding.inflate(inflater, container, false);
contactLayout = viewBinding.contactLayout;
contactLayout
.getContactListView()
.getDecoration()
.setTitleAlignBottom(false)
.setIndexDecorationBg(getResources().getColor(R.color.color_eff1f4))
.setColorTitleBottomLine(getResources().getColor(R.color.color_dbe0e8));
contactLayout.getContactListView().setViewHolderFactory(new ContactDefaultFactory());
emptyView = viewBinding.emptyLayout;
return viewBinding.getRoot();
}
protected void loadTitle() {
if (contactConfig.showTitleBar) {
viewBinding.contactLayout.getTitleBar().setVisibility(View.VISIBLE);
if (contactConfig.titleColor != null) {
viewBinding.contactLayout.getTitleBar().setTitleColor(contactConfig.titleColor);
}
if (contactConfig.title != null) {
viewBinding.contactLayout.getTitleBar().setTitle(contactConfig.title);
} else {
viewBinding
.contactLayout
.getTitleBar()
.setTitle(getResources().getString(R.string.contact_title));
}
} else {
viewBinding.contactLayout.getTitleBar().setVisibility(View.GONE);
}
if (contactConfig.showTitleBarRight2Icon) {
viewBinding.contactLayout.getTitleBar().showRight2ImageView(true);
if (contactConfig.titleBarRight2Res != null) {
viewBinding.contactLayout.getTitleBar().setRight2ImageRes(contactConfig.titleBarRight2Res);
}
viewBinding
.contactLayout
.getTitleBar()
.setRight2ImageClick(
v -> {
if (contactConfig.titleBarRight2Click != null) {
contactConfig.titleBarRight2Click.onClick(v);
} else {
XKitRouter.withKey(RouterConstant.PATH_GLOBAL_SEARCH_PAGE)
.withContext(requireContext())
.navigate();
}
});
} else {
viewBinding.contactLayout.getTitleBar().showRight2ImageView(false);
}
if (contactConfig.showTitleBarRightIcon) {
viewBinding.contactLayout.getTitleBar().showRightImageView(true);
if (contactConfig.titleBarRightRes != null) {
viewBinding.contactLayout.getTitleBar().setRightImageRes(contactConfig.titleBarRightRes);
}
if (contactConfig.titleBarRightClick != null) {
viewBinding
.contactLayout
.getTitleBar()
.setRightImageClick(contactConfig.titleBarRightClick);
} else {
viewBinding
.contactLayout
.getTitleBar()
.setRightImageClick(
v ->
XKitRouter.withKey(PATH_ADD_FRIEND_PAGE)
.withContext(requireContext())
.navigate());
}
} else {
viewBinding.contactLayout.getTitleBar().showRightImageView(false);
}
}
@Override
protected List<ContactEntranceBean> getContactEntranceList(Context context) {
List<ContactEntranceBean> contactDataList = new ArrayList<>();
//verify message
verifyBean =
new ContactEntranceBean(
R.mipmap.ic_contact_verfiy_msg, context.getString(R.string.contact_list_verify_msg));
verifyBean.router = RouterConstant.PATH_MY_NOTIFICATION_PAGE;
//black list
ContactEntranceBean blackBean =
new ContactEntranceBean(
R.mipmap.ic_contact_black_list, context.getString(R.string.contact_list_black_list));
blackBean.router = RouterConstant.PATH_MY_BLACK_PAGE;
//my group
ContactEntranceBean groupBean =
new ContactEntranceBean(
R.mipmap.ic_contact_my_group, context.getString(R.string.contact_list_my_group));
groupBean.router = RouterConstant.PATH_MY_TEAM_PAGE;
contactDataList.add(verifyBean);
contactDataList.add(blackBean);
contactDataList.add(groupBean);
return contactDataList;
}
@Override
protected ContactEntranceBean configVerifyBean() {
return verifyBean;
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.search;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.databinding.GlobalSearchActivityBinding;
import com.netease.yunxin.kit.contactkit.ui.search.page.BaseSearchActivity;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
/** search your friend or team */
public class GlobalSearchActivity extends BaseSearchActivity {
protected View initViewAndGetRootView() {
GlobalSearchActivityBinding viewBinding =
GlobalSearchActivityBinding.inflate(getLayoutInflater());
searchRv = viewBinding.rvSearch;
clearView = viewBinding.ivClear;
emptyView = viewBinding.emptyLl;
searchEditText = viewBinding.etSearch;
backView = viewBinding.globalTitleBar.getBackImageView();
return viewBinding.getRoot();
}
@Override
protected void bindView() {
super.bindView();
searchAdapter.setViewHolderFactory(new SearchViewHolderFactory());
routerFriend = RouterConstant.PATH_CHAT_P2P_PAGE;
routerTeam = RouterConstant.PATH_CHAT_TEAM_PAGE;
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.search;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.common.ui.viewholder.IViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.ContactConstant;
import com.netease.yunxin.kit.contactkit.ui.databinding.SearchTitleItemLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.databinding.SearchUserItemLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.normal.search.viewholder.FriendViewHolder;
import com.netease.yunxin.kit.contactkit.ui.normal.search.viewholder.TeamViewHolder;
import com.netease.yunxin.kit.contactkit.ui.normal.search.viewholder.TitleViewHolder;
/** search result list view holder factory */
public class SearchViewHolderFactory implements IViewHolderFactory {
@Override
public BaseViewHolder createViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ContactConstant.SearchViewType.TITLE) { //title view
return new TitleViewHolder(
SearchTitleItemLayoutBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false));
} else if (viewType == ContactConstant.SearchViewType.USER) { // friend view
return new FriendViewHolder(
SearchUserItemLayoutBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false));
} else if (viewType == ContactConstant.SearchViewType.TEAM) { //team view
return new TeamViewHolder(
SearchUserItemLayoutBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false));
}
return null;
}
}

View File

@@ -0,0 +1,98 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.search.viewholder;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import com.netease.nimlib.sdk.search.model.RecordHitInfo;
import com.netease.yunxin.kit.chatkit.model.FriendSearchInfo;
import com.netease.yunxin.kit.chatkit.model.HitType;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.SearchUserItemLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.SearchFriendBean;
public class FriendViewHolder extends BaseViewHolder<SearchFriendBean> {
private FriendSearchInfo friendInfo;
private SearchUserItemLayoutBinding viewBinding;
public FriendViewHolder(@NonNull View itemView) {
super(itemView);
}
public FriendViewHolder(@NonNull SearchUserItemLayoutBinding viewBinding) {
this(viewBinding.getRoot());
this.viewBinding = viewBinding;
}
@Override
public void onBindData(SearchFriendBean data, int position) {
friendInfo = data.friendSearchInfo;
if (friendInfo != null) {
viewBinding.cavUserIcon.setData(
friendInfo.getFriendInfo().getAvatar(),
friendInfo.getFriendInfo().getName(),
AvatarColor.avatarColor(friendInfo.getFriendInfo().getAccount()));
if (friendInfo.getHitType() == HitType.Alias) {
viewBinding.tvNickName.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAlias(), friendInfo.getHitInfo()));
viewBinding.tvName.setVisibility(View.GONE);
} else if (friendInfo.getHitType() == HitType.UserName) {
if (!TextUtils.isEmpty(friendInfo.getFriendInfo().getAlias())) {
viewBinding.tvNickName.setText(friendInfo.getFriendInfo().getAlias());
viewBinding.tvNickName.setVisibility(View.VISIBLE);
viewBinding.tvName.setText(
getSelectSpanText(
friendInfo.getFriendInfo().getUserInfo().getName(), friendInfo.getHitInfo()));
viewBinding.tvName.setVisibility(View.VISIBLE);
} else {
viewBinding.tvNickName.setText(
getSelectSpanText(
friendInfo.getFriendInfo().getUserInfo().getName(), friendInfo.getHitInfo()));
viewBinding.tvNickName.setVisibility(View.VISIBLE);
viewBinding.tvName.setVisibility(View.GONE);
}
} else {
viewBinding.tvNickName.setVisibility(View.VISIBLE);
if (!TextUtils.isEmpty(friendInfo.getFriendInfo().getAlias())) {
viewBinding.tvNickName.setText(friendInfo.getFriendInfo().getAlias());
viewBinding.tvName.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAccount(), friendInfo.getHitInfo()));
viewBinding.tvName.setVisibility(View.VISIBLE);
} else if (!TextUtils.isEmpty(friendInfo.getFriendInfo().getUserInfo().getName())) {
viewBinding.tvNickName.setText(friendInfo.getFriendInfo().getUserInfo().getName());
viewBinding.tvName.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAccount(), friendInfo.getHitInfo()));
viewBinding.tvName.setVisibility(View.VISIBLE);
} else {
viewBinding.tvNickName.setText(
getSelectSpanText(friendInfo.getFriendInfo().getAccount(), friendInfo.getHitInfo()));
viewBinding.tvName.setVisibility(View.GONE);
}
}
viewBinding.getRoot().setOnClickListener(v -> itemListener.onClick(v, data, position));
;
}
}
private SpannableString getSelectSpanText(String text, RecordHitInfo hitInfo) {
SpannableString spannable = new SpannableString(text);
if (hitInfo != null) {
spannable.setSpan(
new ForegroundColorSpan(
viewBinding.getRoot().getContext().getResources().getColor(R.color.color_337eff)),
hitInfo.start,
hitInfo.end,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
return spannable;
}
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.search.viewholder;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.view.View;
import androidx.annotation.NonNull;
import com.netease.nimlib.sdk.search.model.RecordHitInfo;
import com.netease.yunxin.kit.chatkit.model.TeamSearchInfo;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.SearchUserItemLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.SearchTeamBean;
public class TeamViewHolder extends BaseViewHolder<SearchTeamBean> {
private SearchUserItemLayoutBinding viewBinding;
private TeamSearchInfo searchInfo;
public TeamViewHolder(@NonNull View itemView) {
super(itemView);
}
public TeamViewHolder(@NonNull SearchUserItemLayoutBinding viewBinding) {
this(viewBinding.getRoot());
this.viewBinding = viewBinding;
}
@Override
public void onBindData(SearchTeamBean data, int position) {
if (data != null) {
searchInfo = data.teamSearchInfo;
viewBinding.cavUserIcon.setData(
searchInfo.getTeam().getIcon(),
searchInfo.getTeam().getName(),
AvatarColor.avatarColor(searchInfo.getTeam().getId()));
viewBinding.tvNickName.setText(
getSelectSpanText(searchInfo.getTeam().getName(), searchInfo.getHitInfo()));
viewBinding.tvNickName.setVisibility(View.VISIBLE);
viewBinding.tvName.setVisibility(View.GONE);
viewBinding.getRoot().setOnClickListener(v -> itemListener.onClick(v, data, position));
}
}
private SpannableString getSelectSpanText(String text, RecordHitInfo hitInfo) {
SpannableString spannable = new SpannableString(text);
if (hitInfo != null) {
spannable.setSpan(
new ForegroundColorSpan(
viewBinding.getRoot().getContext().getResources().getColor(R.color.color_337eff)),
hitInfo.start,
hitInfo.end,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
return spannable;
}
}

View File

@@ -0,0 +1,35 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.search.viewholder;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.contactkit.ui.databinding.SearchTitleItemLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.SearchTitleBean;
public class TitleViewHolder extends BaseViewHolder<SearchTitleBean> {
private SearchTitleItemLayoutBinding viewBinding;
public TitleViewHolder(@NonNull View itemView) {
super(itemView);
}
public TitleViewHolder(@NonNull SearchTitleItemLayoutBinding viewBinding) {
this(viewBinding.getRoot());
this.viewBinding = viewBinding;
}
@Override
public void onBindData(SearchTitleBean data, int position) {
if (TextUtils.isEmpty(data.title)) {
viewBinding.tvTitle.setText(data.titleRes);
} else {
viewBinding.tvTitle.setText(data.title);
}
}
}

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.selector;
import android.os.Bundle;
import android.view.View;
import androidx.viewbinding.ViewBinding;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.ContactSelectorActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.normal.contact.ContactDefaultFactory;
import com.netease.yunxin.kit.contactkit.ui.selector.BaseContactSelectorActivity;
import com.netease.yunxin.kit.contactkit.ui.selector.BaseSelectedListAdapter;
import java.util.List;
public class ContactSelectorActivity extends BaseContactSelectorActivity {
protected ContactSelectorActivityLayoutBinding binding;
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
binding = ContactSelectorActivityLayoutBinding.inflate(getLayoutInflater());
contactListView = binding.contactListView;
contactListView
.getDecoration()
.setTitleAlignBottom(false)
.setIndexDecorationBg(getResources().getColor(R.color.color_eff1f4))
.setColorTitleBottomLine(getResources().getColor(R.color.color_dbe0e8));
contactListView.setViewHolderFactory(new ContactDefaultFactory());
emptyGroup = binding.emptyLayout;
rvSelected = binding.rvSelected;
titleBar = binding.title;
return binding.getRoot();
}
protected BaseSelectedListAdapter<? extends ViewBinding> getSelectedListAdapter() {
return new SelectedListAdapter();
}
@Override
protected List<ContactFriendBean> getFollowList() {
return null;
}
}

View File

@@ -0,0 +1,42 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.selector;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.contactkit.ui.databinding.FriendSelectedViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.selector.BaseSelectedListAdapter;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
public class SelectedListAdapter extends BaseSelectedListAdapter<FriendSelectedViewHolderBinding> {
@Override
protected FriendSelectedViewHolderBinding provideViewBinding(
@NonNull ViewGroup parent, int viewType) {
return FriendSelectedViewHolderBinding.inflate(
LayoutInflater.from(parent.getContext()), parent, false);
}
@Override
protected void handleBindViewHolder(
SelectedViewHolder<FriendSelectedViewHolderBinding> holder,
ContactFriendBean bean,
FriendInfo friendData) {
holder.binding.avatarView.setData(
friendData.getAvatar(),
friendData.getName(),
AvatarColor.avatarColor(friendData.getAccount()));
holder.itemView.setOnClickListener(
v -> {
removeFriend(bean);
if (itemClickListener != null) {
itemClickListener.onItemClick(bean);
}
});
}
}

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.team;
import android.content.Intent;
import android.view.ViewGroup;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.normal.view.ContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder.TeamListViewHolder;
import com.netease.yunxin.kit.contactkit.ui.team.BaseTeamListActivity;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
public class TeamListActivity extends BaseTeamListActivity {
@Override
protected void configViewHolderFactory() {
binding.contactListView.setViewHolderFactory(
new ContactViewHolderFactory() {
@Override
protected BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType) {
if (viewType == IViewTypeConstant.CONTACT_TEAM_LIST) {
TeamListViewHolder viewHolder = new TeamListViewHolder(view);
viewHolder.setItemClickListener(
data -> {
if (isSelector) {
Intent result = new Intent();
result.putExtra(RouterConstant.KEY_TEAM_ID, data.data.getId());
setResult(RESULT_OK, result);
finish();
} else {
XKitRouter.withKey(data.router)
.withParam(RouterConstant.CHAT_KRY, data.data)
.withContext(TeamListActivity.this)
.navigate();
}
});
return viewHolder;
}
return null;
}
});
}
}

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.userinfo;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.CommentActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.userinfo.BaseCommentActivity;
public class CommentActivity extends BaseCommentActivity {
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
CommentActivityLayoutBinding binding =
CommentActivityLayoutBinding.inflate(getLayoutInflater());
titleBar = binding.title;
titleBar.setActionTextColor(getResources().getColor(R.color.color_337eff));
edtComment = binding.edtComment;
return binding.getRoot();
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
changeStatusBarColor(R.color.color_eff1f4);
super.onCreate(savedInstanceState);
}
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.userinfo;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import com.netease.yunxin.kit.contactkit.ui.databinding.UserInfoActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.userinfo.BaseUserInfoActivity;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
public class UserInfoActivity extends BaseUserInfoActivity {
@Override
protected View initViewAndGetRootView(Bundle savedInstanceState) {
UserInfoActivityLayoutBinding binding =
UserInfoActivityLayoutBinding.inflate(getLayoutInflater());
contactInfoView = binding.contactUser;
contactInfoView.configSignatureMaxLines(2);
titleBar = binding.title;
return binding.getRoot();
}
@Override
protected Class<? extends Activity> getCommentActivity() {
return CommentActivity.class;
}
protected void goChat() {
XKitRouter.withKey(RouterConstant.PATH_CHAT_P2P_PAGE)
.withParam(RouterConstant.CHAT_ID_KRY, userInfoData.data.getAccount())
.withContext(this)
.navigate();
finish();
}
}

View File

@@ -0,0 +1,119 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.verify;
import android.view.ViewGroup;
import androidx.annotation.Nullable;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.yunxin.kit.contactkit.ui.ILoadListener;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.model.ContactVerifyInfoBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.normal.view.ContactViewHolderFactory;
import com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder.VerifyInfoViewHolder;
import com.netease.yunxin.kit.contactkit.ui.verify.BaseVerifyListActivity;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoStatus;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoType;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
public class VerifyListActivity extends BaseVerifyListActivity implements ILoadListener {
@Override
protected void initView() {
super.initView();
binding.title.setBackgroundColor(getResources().getColor(R.color.color_white));
}
@Override
protected void configViewHolderFactory() {
binding.contactListView.setViewHolderFactory(
new ContactViewHolderFactory() {
@Override
protected BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType) {
if (viewType == IViewTypeConstant.CONTACT_VERIFY_INFO) {
VerifyInfoViewHolder viewHolder = new VerifyInfoViewHolder(view);
viewHolder.setVerifyListener(
new VerifyInfoViewHolder.VerifyListener() {
@Override
public void onAccept(ContactVerifyInfoBean bean) {
viewModel.agree(
bean,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
viewModel.setVerifyStatus(bean, SystemMessageInfoStatus.Passed);
binding.contactListView.updateContactData(bean);
if (bean.data.getInfoType() == SystemMessageInfoType.AddFriend) {
XKitRouter.withKey(RouterConstant.PATH_CHAT_SEND_TEXT_ACTION)
.withContext(VerifyListActivity.this)
.withParam(
RouterConstant.KEY_SESSION_ID, bean.data.getFromAccount())
.withParam(
RouterConstant.KEY_SESSION_TYPE,
SessionTypeEnum.P2P.getValue())
.withParam(
RouterConstant.KEY_MESSAGE_CONTENT,
getResources()
.getString(R.string.verify_agree_message_text))
.navigate();
}
}
@Override
public void onFailed(int code) {
if (code == error_duplicate) {
viewModel.setVerifyStatus(bean, SystemMessageInfoStatus.Passed);
binding.contactListView.updateContactData(bean);
}
toastResult(true, bean.data.getInfoType(), code);
}
@Override
public void onException(@Nullable Throwable exception) {
toastResult(true, bean.data.getInfoType(), 0);
}
});
}
@Override
public void onReject(ContactVerifyInfoBean bean) {
viewModel.disagree(
bean,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
viewModel.setVerifyStatus(bean, SystemMessageInfoStatus.Declined);
binding.contactListView.updateContactData(bean);
}
@Override
public void onFailed(int code) {
if (code == error_duplicate) {
viewModel.setVerifyStatus(bean, SystemMessageInfoStatus.Passed);
binding.contactListView.updateContactData(bean);
}
toastResult(false, bean.data.getInfoType(), code);
}
@Override
public void onException(@Nullable Throwable exception) {
toastResult(false, bean.data.getInfoType(), 0);
}
});
}
});
return viewHolder;
}
return null;
}
});
}
protected int getEmptyStateViewRes() {
return R.drawable.ic_contact_empty;
}
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.view;
import android.view.ViewGroup;
import com.netease.yunxin.kit.contactkit.ui.IContactFactory;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder.ContactViewHolder;
import com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder.EntranceViewHolder;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
/** viewHolder factory */
public abstract class ContactViewHolderFactory implements IContactFactory {
@Override
public int getItemViewType(BaseContactBean data) {
return data.viewType;
}
public BaseContactViewHolder createViewHolder(ViewGroup view, int viewType) {
if (viewType == IViewTypeConstant.CONTACT_FRIEND) {
return new ContactViewHolder(view);
} else if (viewType == IViewTypeConstant.CONTACT_ACTION_ENTER) {
return new EntranceViewHolder(view);
} else if (viewType >= IViewTypeConstant.CUSTOM_START) {
return getCustomViewHolder(view, viewType);
}
return null;
}
protected abstract BaseContactViewHolder getCustomViewHolder(ViewGroup view, int viewType);
}

View File

@@ -0,0 +1,76 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.contactkit.ui.databinding.BlackListViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactBlackListBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
public class BlackListViewHolder extends BaseContactViewHolder {
private RelieveListener relieveListener;
private BlackListViewHolderBinding binding;
public BlackListViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = BlackListViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
ContactBlackListBean blackListBean = ((ContactBlackListBean) bean);
UserInfo friendData = ((ContactBlackListBean) bean).data;
binding.tvName.setText(blackListBean.getName());
binding.avatarView.setData(
friendData.getAvatar(),
blackListBean.getAvatarName(),
ColorUtils.avatarColor(friendData.getAccount()));
binding.tvRelieve.setOnClickListener(
v -> {
if (relieveListener != null) {
relieveListener.onUserRelieve((ContactBlackListBean) bean);
}
});
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
public void setRelieveListener(RelieveListener relieveListener) {
this.relieveListener = relieveListener;
}
public interface RelieveListener {
void onUserRelieve(ContactBlackListBean data);
}
}

View File

@@ -0,0 +1,81 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.contactkit.ui.databinding.FriendContactViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
public class ContactViewHolder extends BaseContactViewHolder {
FriendContactViewHolderBinding binding;
public ContactViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = FriendContactViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
FriendInfo friendInfo = ((ContactFriendBean) bean).data;
String nickName = friendInfo.getName();
String avatarName = friendInfo.getAvatarName();
binding.tvName.setText(nickName);
binding.avatarView.setData(
friendInfo.getAvatar(), avatarName, ColorUtils.avatarColor(friendInfo.getAccount()));
if (attrs.getShowSelector()) {
binding.rbSelector.setChecked(((ContactFriendBean) bean).isSelected());
binding.rbSelector.setVisibility(View.VISIBLE);
binding.rootView.setOnClickListener(
v -> {
boolean newStatue = !binding.rbSelector.isChecked();
((ContactFriendBean) bean).setSelected(newStatue);
binding.rbSelector.setChecked(newStatue);
if (actions != null && actions.getSelectorListener(bean.viewType) != null) {
actions.getSelectorListener(bean.viewType).onSelector(newStatue, bean);
}
});
} else {
binding.rbSelector.setVisibility(View.GONE);
binding.rootView.setOnClickListener(
v -> {
if (actions != null && actions.getContactListener(bean.viewType) != null) {
actions.getContactListener(bean.viewType).onClick(position, bean);
}
});
}
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
}

View File

@@ -0,0 +1,82 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.EntranceContactViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactEntranceBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
public class EntranceViewHolder extends BaseContactViewHolder {
EntranceContactViewHolderBinding binding;
private static final int maxNumber = 99;
public EntranceViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = EntranceContactViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
ContactEntranceBean entranceBean = (ContactEntranceBean) bean;
if (entranceBean.showRightArrow) {
binding.ivArrow.setVisibility(View.VISIBLE);
} else {
binding.ivArrow.setVisibility(View.GONE);
}
if (entranceBean.number > 0) {
binding.tvNumber.setVisibility(View.VISIBLE);
String numberValue = String.valueOf(entranceBean.number);
if (entranceBean.number > maxNumber) {
numberValue = context.getResources().getString(R.string.verify_max_count_text);
}
binding.tvNumber.setText(numberValue);
} else {
binding.tvNumber.setVisibility(View.GONE);
}
Glide.with(context).load(entranceBean.icon).centerCrop().into(binding.ivIcon);
binding.tvTitle.setText(entranceBean.title);
binding.rootView.setOnClickListener(
v -> {
if (actions != null && actions.getContactListener(bean.viewType) != null) {
actions.getContactListener(bean.viewType).onClick(position, bean);
}
});
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvTitle.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvTitle.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.flIvIcon.setRadius(attrs.getAvatarCornerRadius());
}
}
}

View File

@@ -0,0 +1,74 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.nimlib.sdk.team.model.Team;
import com.netease.yunxin.kit.contactkit.ui.databinding.TeamListViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactTeamBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
public class TeamListViewHolder extends BaseContactViewHolder {
private itemClickListener itemClickListener;
private TeamListViewHolderBinding binding;
public TeamListViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = TeamListViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
Team teamInfo = ((ContactTeamBean) bean).data;
binding.tvName.setText(teamInfo.getName());
binding.avatarView.setData(
teamInfo.getIcon(), teamInfo.getName(), ColorUtils.avatarColor(teamInfo.getId()));
binding.rootView.setOnClickListener(
v -> {
if (itemClickListener != null) {
itemClickListener.onClick((ContactTeamBean) bean);
}
});
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
public void setItemClickListener(TeamListViewHolder.itemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
public interface itemClickListener {
void onClick(ContactTeamBean data);
}
}

View File

@@ -0,0 +1,180 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.normal.view.viewholder;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.netease.yunxin.kit.common.ui.utils.AvatarColor;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.VerifyListViewHolderBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactVerifyInfoBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import com.netease.yunxin.kit.corekit.im.model.FriendNotify;
import com.netease.yunxin.kit.corekit.im.model.FriendVerifyType;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfo;
public class VerifyInfoViewHolder extends BaseContactViewHolder {
private VerifyListener verifyListener;
private VerifyListViewHolderBinding binding;
public VerifyInfoViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
}
@Override
public void initViewBinding(LayoutInflater layoutInflater, ViewGroup container) {
binding = VerifyListViewHolderBinding.inflate(layoutInflater, container, true);
}
@Override
public void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs) {
ContactVerifyInfoBean infoBean = ((ContactVerifyInfoBean) bean);
SystemMessageInfo info = infoBean.data;
String name = info.getFromUserName();
String targetName = info.getTargetName();
String avatar =
info.getTargetTeam() != null
? info.getTargetTeam().getIcon()
: info.getFromUserInfo().getAvatar();
binding.tvName.setText(name);
binding.avatarView.setData(avatar, name, AvatarColor.avatarColor(info.getFromAccount()));
int unreadCount = infoBean.getUnreadCount();
if (unreadCount > 1) {
binding.unreadTv.setVisibility(View.VISIBLE);
if (unreadCount > 99) {
binding.unreadTv.setText(context.getString(R.string.verify_max_count_text));
} else {
binding.unreadTv.setText(String.valueOf(unreadCount));
}
} else {
binding.unreadTv.setVisibility(View.GONE);
}
if (infoBean.data.getUnread()) {
binding.rootView.setBackgroundColor(context.getResources().getColor(R.color.color_ededef));
} else {
binding.rootView.setBackgroundColor(context.getResources().getColor(R.color.color_white));
}
switch (info.getInfoStatus()) {
case Init:
binding.llyVerifyResult.setVisibility(View.GONE);
binding.llyVerify.setVisibility(View.VISIBLE);
binding.tvAccept.setOnClickListener(
v -> {
if (verifyListener != null) {
verifyListener.onAccept((ContactVerifyInfoBean) bean);
}
});
binding.tvReject.setOnClickListener(
v -> {
if (verifyListener != null) {
verifyListener.onReject((ContactVerifyInfoBean) bean);
}
});
break;
case Passed:
showResult(context.getString(R.string.contact_verify_agreed), true);
break;
case Declined:
showResult(context.getString(R.string.contact_verify_rejected), false);
break;
case Ignored:
case Expired:
showResult(context.getString(R.string.contact_verify_expired), false);
break;
}
switch (info.getInfoType()) {
case ApplyJoinTeam:
binding.tvAction.setText(
String.format(context.getString(R.string.apply_to_join_group), targetName));
break;
case RejectTeamApply:
binding.tvAction.setText(
String.format(context.getString(R.string.apply_to_join_group), targetName));
showResult(context.getString(R.string.contact_verify_agreed), false);
break;
case TeamInvite:
binding.tvAction.setText(
String.format(context.getString(R.string.invited_to_join_group), targetName));
break;
case DeclineTeamInvite:
binding.tvAction.setText(
String.format(context.getString(R.string.invited_to_join_group), targetName));
showResult(context.getString(R.string.contact_verify_agreed), false);
break;
case AddFriend:
if (info.getAttachObject() != null && info.getAttachObject() instanceof FriendNotify) {
FriendNotify notifyAttach = (FriendNotify) info.getAttachObject();
if (notifyAttach.getType() == FriendVerifyType.AgreeAdd) {
binding.tvAction.setText(R.string.accept_your_friend_apply);
showResult(null, true);
} else if (notifyAttach.getType() == FriendVerifyType.DirectAdd) {
binding.tvAction.setText(R.string.direct_add_your_friend_apply);
showResult(null, true);
} else if (notifyAttach.getType() == FriendVerifyType.RejectAdd) {
binding.tvAction.setText(R.string.reject_your_friend_apply);
showResult(null, true);
} else if (notifyAttach.getType() == FriendVerifyType.VerifyRequest) {
binding.tvAction.setText(R.string.friend_apply);
}
} else {
binding.tvAction.setText(R.string.friend_apply);
}
break;
case Undefined:
break;
}
loadConfig(attrs);
}
private void loadConfig(ContactListViewAttrs attrs) {
if (attrs == null) {
return;
}
if (attrs.getNameTextColor() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextColor(attrs.getNameTextColor());
}
if (attrs.getNameTextSize() != ContactListViewAttrs.INT_NULL) {
binding.tvName.setTextSize(attrs.getNameTextSize());
}
if (attrs.getAvatarCornerRadius() != ContactListViewAttrs.INT_NULL) {
binding.avatarView.setCornerRadius(attrs.getAvatarCornerRadius());
}
}
private void showResult(String content, boolean agreeIcon) {
if (!TextUtils.isEmpty(content)) {
binding.tvVerifyResult.setText(content);
binding.ivVerifyResult.setImageResource(
agreeIcon ? R.mipmap.ic_agree_status : R.mipmap.ic_reject_status);
binding.llyVerifyResult.setVisibility(View.VISIBLE);
} else {
binding.llyVerifyResult.setVisibility(View.GONE);
}
binding.llyVerify.setVisibility(View.GONE);
}
public void setVerifyListener(VerifyListener verifyListener) {
this.verifyListener = verifyListener;
}
public interface VerifyListener {
void onAccept(ContactVerifyInfoBean bean);
void onReject(ContactVerifyInfoBean bean);
}
}

View File

@@ -0,0 +1,107 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.search;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.common.ui.viewholder.BaseViewHolder;
import com.netease.yunxin.kit.common.ui.viewholder.IViewHolderFactory;
import com.netease.yunxin.kit.common.ui.viewholder.ViewHolderClickListener;
import java.util.ArrayList;
import java.util.List;
public class SearchAdapter extends RecyclerView.Adapter<BaseViewHolder> {
private final String TAG = "SearchAdapter";
private IViewHolderFactory viewHolderFactory;
private final List<BaseBean> dataList = new ArrayList<>();
private ViewHolderClickListener clickListener;
public void setData(List<BaseBean> data) {
dataList.clear();
if (data != null) {
dataList.addAll(data);
notifyDataSetChanged();
}
}
public void addForwardData(List<BaseBean> data) {
if (data != null) {
dataList.addAll(0, data);
}
}
public void appendData(List<BaseBean> data) {
if (data != null) {
dataList.addAll(data);
}
}
public void removeData(BaseBean data) {
if (data == null) {
return;
}
int index = -1;
for (int j = 0; j < dataList.size(); j++) {
if (data.equals(dataList.get(j))) {
index = j;
break;
}
}
if (index > -1) {
removeData(index);
}
}
public void removeData(int position) {
if (position >= 0 && position < dataList.size()) {
dataList.remove(position);
notifyItemRemoved(position);
}
}
public void setViewHolderFactory(IViewHolderFactory factory) {
this.viewHolderFactory = factory;
}
public void setViewHolderClickListener(ViewHolderClickListener listener) {
this.clickListener = listener;
}
@NonNull
@Override
public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
BaseViewHolder viewHolder = null;
if (viewHolderFactory != null) {
viewHolder = viewHolderFactory.createViewHolder(parent, viewType);
}
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
holder.onBindData(dataList.get(position), position);
holder.setItemOnClickListener(clickListener);
}
@Override
public int getItemViewType(int position) {
return dataList.get(position).viewType;
}
@Override
public int getItemCount() {
return dataList.size();
}
public BaseBean getData(int index) {
if (index >= 0 && index < dataList.size()) {
return dataList.get(index);
}
return null;
}
}

View File

@@ -0,0 +1,200 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.search;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.chatkit.model.FriendSearchInfo;
import com.netease.yunxin.kit.chatkit.model.TeamSearchInfo;
import com.netease.yunxin.kit.chatkit.repo.SearchRepo;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.common.ui.viewmodel.BaseViewModel;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.model.SearchFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.SearchTeamBean;
import com.netease.yunxin.kit.contactkit.ui.model.SearchTitleBean;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import java.util.ArrayList;
import java.util.List;
/**
* to provider search data and operation
*/
public class SearchViewModel extends BaseViewModel {
private static final String TAG = "SearchViewModel";
private static final String LIB_TAG = "SearchKit-UI";
private final MutableLiveData<FetchResult<List<BaseBean>>> queryLiveData =
new MutableLiveData<>();
private final List<BaseBean> resultList = new ArrayList<>();
public MutableLiveData<FetchResult<List<BaseBean>>> getQueryLiveData() {
return queryLiveData;
}
protected String routerFriend;
protected String routerTeam;
public void setRouter(String friend, String team) {
routerFriend = friend;
routerTeam = team;
}
public void query(String text) {
resultList.clear();
if (!TextUtils.isEmpty(text)) {
SearchRepo.searchFriend(
text,
new FetchCallback<List<FriendSearchInfo>>() {
@Override
public void onSuccess(@Nullable List<FriendSearchInfo> param) {
if (param != null && param.size() > 0) {
List<BaseBean> friendResult = new ArrayList<>();
friendResult.add(new SearchTitleBean(R.string.global_search_friend_title));
ALog.d(LIB_TAG, TAG, "searchFriend,onSuccess,friend");
for (int index = 0; index < param.size(); index++) {
friendResult.add(new SearchFriendBean(param.get(index), routerFriend));
ALog.d(
LIB_TAG,
TAG,
"searchFriend,onSuccess:" + param.get(index).getFriendInfo().getName());
}
resultList.addAll(0, friendResult);
}
FetchResult<List<BaseBean>> fetchResult = new FetchResult<>(LoadStatus.Success);
fetchResult.setData(resultList);
queryLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "searchFriend,onFailed:" + code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "searchFriend,onException");
}
});
SearchRepo.searchGroup(
text,
new FetchCallback<List<TeamSearchInfo>>() {
@Override
public void onSuccess(@Nullable List<TeamSearchInfo> param) {
if (param != null && param.size() > 0) {
List<BaseBean> groupResult = new ArrayList<>();
groupResult.add(new SearchTitleBean(R.string.global_search_group_title));
for (int index = 0; index < param.size(); index++) {
groupResult.add(new SearchTeamBean(param.get(index), routerTeam));
}
ALog.d(LIB_TAG, TAG, "searchTeamm,onSuccess,team:" + param.size());
resultList.addAll(groupResult);
}
FetchResult<List<BaseBean>> fetchResult = new FetchResult<>(LoadStatus.Success);
fetchResult.setData(resultList);
queryLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "searchFriend,onFailed:" + code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "searchFriend:onException");
}
});
SearchRepo.searchTeam(
text,
new FetchCallback<List<TeamSearchInfo>>() {
@Override
public void onSuccess(@Nullable List<TeamSearchInfo> param) {
if (param != null && param.size() > 0) {
List<BaseBean> friendResult = new ArrayList<>();
friendResult.add(new SearchTitleBean(R.string.global_search_team_title));
for (int index = 0; index < param.size(); index++) {
friendResult.add(new SearchTeamBean(param.get(index), routerTeam));
}
resultList.addAll(friendResult);
ALog.d(LIB_TAG, TAG, "searchTeam,onSuccess:" + friendResult.size());
}
FetchResult<List<BaseBean>> fetchResult = new FetchResult<>(LoadStatus.Success);
fetchResult.setData(resultList);
queryLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "searchFriend,onFailed:" + code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "searchFriend,onException");
}
});
} else {
FetchResult<List<BaseBean>> fetchResult = new FetchResult<>(LoadStatus.Success);
fetchResult.setData(resultList);
queryLiveData.postValue(fetchResult);
}
}
public void queryFriend(String text) {
resultList.clear();
if (!TextUtils.isEmpty(text)) {
SearchRepo.searchFriend(
text,
new FetchCallback<List<FriendSearchInfo>>() {
@Override
public void onSuccess(@Nullable List<FriendSearchInfo> param) {
if (param != null && param.size() > 0) {
List<BaseBean> friendResult = new ArrayList<>();
friendResult.add(new SearchTitleBean(R.string.global_search_friend_title));
ALog.d(LIB_TAG, TAG, "searchFriend,onSuccess,friend");
for (int index = 0; index < param.size(); index++) {
friendResult.add(new SearchFriendBean(param.get(index), routerFriend));
ALog.d(
LIB_TAG,
TAG,
"searchFriend,onSuccess:" + param.get(index).getFriendInfo().getName());
}
resultList.addAll(0, friendResult);
}
FetchResult<List<BaseBean>> fetchResult = new FetchResult<>(LoadStatus.Success);
fetchResult.setData(resultList);
queryLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "searchFriend,onFailed:" + code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "searchFriend,onException");
}
});
} else {
FetchResult<List<BaseBean>> fetchResult = new FetchResult<>(LoadStatus.Success);
fetchResult.setData(resultList);
queryLiveData.postValue(fetchResult);
}
}
}

View File

@@ -0,0 +1,178 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.search.page;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.common.ui.viewholder.ViewHolderClickListener;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.common.utils.KeyboardUtils;
import com.netease.yunxin.kit.contactkit.ui.search.SearchAdapter;
import com.netease.yunxin.kit.contactkit.ui.search.SearchViewModel;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.Objects;
/** search your friend or team */
public abstract class BaseSearchActivity extends BaseActivity {
protected RecyclerView searchRv;
private View rootView;
protected View clearView;
protected EditText searchEditText;
protected View emptyView;
protected View backView;
protected SearchViewModel viewModel;
protected SearchAdapter searchAdapter;
protected Handler searchHandler;
protected String routerFriend = RouterConstant.PATH_CHAT_P2P_PAGE;
protected String routerTeam = RouterConstant.PATH_CHAT_TEAM_PAGE;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootView = initViewAndGetRootView();
checkViews();
setContentView(rootView);
bindView();
initData();
showKeyBoard();
}
protected abstract View initViewAndGetRootView();
protected void checkViews() {
Objects.requireNonNull(rootView);
Objects.requireNonNull(clearView);
Objects.requireNonNull(searchEditText);
Objects.requireNonNull(emptyView);
Objects.requireNonNull(backView);
}
protected void bindView() {
if (searchRv != null) {
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
searchRv.setLayoutManager(layoutManager);
searchAdapter = new SearchAdapter();
searchAdapter.setViewHolderClickListener(
new ViewHolderClickListener() {
@Override
public boolean onClick(View v, BaseBean data, int position) {
if (!TextUtils.isEmpty(data.router)) {
XKitRouter.withKey(data.router)
.withParam(data.paramKey, data.param)
.withContext(BaseSearchActivity.this)
.navigate();
}
return true;
}
@Override
public boolean onLongClick(View v, BaseBean data, int position) {
return false;
}
});
searchRv.setAdapter(searchAdapter);
}
if (clearView != null && searchEditText != null) {
clearView.setOnClickListener(v -> searchEditText.setText(""));
}
if (searchEditText != null) {
searchEditText.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) {
searchHandler.removeCallbacksAndMessages(null);
searchHandler.postDelayed(() -> viewModel.query(String.valueOf(s)), 500);
if (clearView != null) {
if (TextUtils.isEmpty(String.valueOf(s))) {
clearView.setVisibility(View.GONE);
} else {
clearView.setVisibility(View.VISIBLE);
}
}
}
});
searchEditText.setOnEditorActionListener(
(v, actionId, event) -> event.getKeyCode() == KeyEvent.KEYCODE_ENTER);
}
if (backView != null) {
backView.setOnClickListener(v -> onBackPressed());
}
}
private void initData() {
searchHandler = new Handler();
viewModel = new ViewModelProvider(this).get(SearchViewModel.class);
viewModel.setRouter(routerFriend, routerTeam);
viewModel
.getQueryLiveData()
.observe(
this,
result -> {
if (result.getLoadStatus() == LoadStatus.Success) {
showEmpty(
(result.getData() == null || result.getData().size() < 1)
&& !TextUtils.isEmpty(String.valueOf(searchEditText.getEditableText())));
searchAdapter.setData(result.getData());
}
});
}
private void showKeyBoard() {
searchHandler.postDelayed(
() -> {
if (searchEditText != null) {
searchEditText.requestFocus();
KeyboardUtils.showKeyboard(searchEditText);
}
},
300);
}
private void showEmpty(boolean show) {
if (emptyView != null) {
emptyView.setVisibility(show ? View.VISIBLE : View.GONE);
if (searchRv != null) {
searchRv.setVisibility(show ? View.GONE : View.VISIBLE);
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (searchHandler != null) {
searchHandler.removeCallbacksAndMessages(null);
}
}
}

View File

@@ -0,0 +1,526 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.selector;
import static com.netease.yunxin.kit.corekit.im.utils.RouterConstant.KEY_REQUEST_SELECTOR_NAME;
import static com.netease.yunxin.kit.corekit.im.utils.RouterConstant.REQUEST_CONTACT_SELECTOR_KEY;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.msg.MsgService;
import com.netease.nimlib.sdk.msg.constant.SessionTypeEnum;
import com.netease.nimlib.sdk.msg.model.RecentContact;
import com.netease.yunxin.kit.chatkit.model.ConversationInfo;
import com.netease.yunxin.kit.chatkit.repo.ContactRepo;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.common.ui.utils.ToastUtils;
import com.netease.yunxin.kit.common.ui.viewholder.BaseBean;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.common.ui.widgets.BackTitleBar;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.contact.ContactViewModel;
import com.netease.yunxin.kit.contactkit.ui.interfaces.ContactActions;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.model.SearchFriendBean;
import com.netease.yunxin.kit.contactkit.ui.search.SearchViewModel;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListView;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
public abstract class BaseContactSelectorActivity extends BaseActivity {
public static final int DEFAULT_MAX_SELECT_COUNT = 500;
private View rootView;
protected BackTitleBar titleBar;
protected RecyclerView rvSelected;
protected EditText editSearch;
protected View ivClean;
protected ViewGroup emptyGroup;
protected ContactListView contactListView;
protected ContactViewModel viewModel;
protected SearchViewModel searchViewModel;
protected BaseSelectedListAdapter<?> selectedListAdapter;
protected ArrayList<String> filterUser;
protected int maxSelectCount = DEFAULT_MAX_SELECT_COUNT;
protected boolean selectFinalCheckCountEnable = false;
protected boolean checkNetworkEnable = true;
protected boolean enableReturnName;
protected ContactListView loveContactView;
protected ContactListView lastContactView;
protected ContactListView searchContactView;
protected LinearLayout layoutContactList;
protected LinearLayout layoutSearchList;
protected TextView tvMemberslist;
protected TextView tvMembersLast;
protected TextView tvMembersSpecialGz;
protected boolean isInvite;
protected int type;// 0 创建群 选好友, 1 转发消息 选好友
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// isInvite = getIntent().getBooleanExtra("isinvite", false);
rootView = initViewAndGetRootView(savedInstanceState);
checkViews();
setContentView(rootView);
viewModel = new ViewModelProvider(this).get(ContactViewModel.class);
searchViewModel = new ViewModelProvider(this).get(SearchViewModel.class);
initData();
initView();
updateView();
}
protected abstract View initViewAndGetRootView(Bundle savedInstanceState);
protected abstract List<ContactFriendBean> getFollowList(); //获取关注列表
public void updateView() {
}
protected void checkViews() {
Objects.requireNonNull(rootView);
Objects.requireNonNull(titleBar);
Objects.requireNonNull(rvSelected);
Objects.requireNonNull(emptyGroup);
Objects.requireNonNull(contactListView);
}
protected ArrayList<String> listAccount = new ArrayList<>();
protected ArrayList<String> listname = new ArrayList<>();
protected void initView() {
configTitle(titleBar);
ContactActions actions = new ContactActions();
if (type == 1) { //单选
actions.addContactListener(
IViewTypeConstant.CONTACT_FRIEND,
(position, data) -> {
FriendInfo friendInfo = ((ContactFriendBean) data).data;
//转发选取一个结束
Intent result = new Intent();
// if (!selectedListAdapter.getSelectedFriends().isEmpty()) {
// result.putExtra(REQUEST_CONTACT_SELECTOR_KEY, getSelectedAccount());
// result.putExtra(KEY_REQUEST_SELECTOR_NAME, getSelectedName());
//// if (enableReturnName) {
//// result.putExtra(KEY_REQUEST_SELECTOR_NAME, getSelectedName());
//// }
// }
listAccount.clear();
listname.clear();
listAccount.add(friendInfo.getAccount());
listname.add(friendInfo.getName());
result.putExtra(REQUEST_CONTACT_SELECTOR_KEY, listAccount);
result.putExtra(KEY_REQUEST_SELECTOR_NAME, listname);
setResult(RESULT_OK, result);
finish();
// XKitRouter.withKey(RouterConstant.PATH_FUN_CHAT_P2P_PAGE)
// .withParam(RouterConstant.CHAT_ID_KRY, friendInfo.getAccount())
// .withContext(requireActivity())
// .navigate();
// XKitRouter.withKey(RouterConstant.PATH_FUN_USER_INFO_PAGE)
// .withContext(requireContext())
// .withParam(RouterConstant.KEY_ACCOUNT_ID_KEY, friendInfo.getAccount())
// .navigate();
});
} else {
actions.addSelectorListener(
IViewTypeConstant.CONTACT_FRIEND,
(selector, data) -> {
if (selector) {
if (selectedListAdapter.getItemCount() >= maxSelectCount
&& !selectFinalCheckCountEnable) {
Toast.makeText(
this,
getString(
R.string.contact_selector_max_count, String.valueOf(maxSelectCount)),
Toast.LENGTH_LONG)
.show();
((ContactFriendBean) data).setSelected(false);
contactListView.updateContactData(data);
lastContactView.updateContactData(data);
searchContactView.updateContactData(data);
loveContactView.updateContactData(data);
} else {
((ContactFriendBean) data).setSelected(true);
contactListView.updateContactData(data);
lastContactView.updateContactData(data);
searchContactView.updateContactData(data);
loveContactView.updateContactData(data);
selectedListAdapter.addFriend((ContactFriendBean) data);
}
} else {
((ContactFriendBean) data).setSelected(false);
selectedListAdapter.removeFriend((ContactFriendBean) data);
contactListView.updateContactData(data);
lastContactView.updateContactData(data);
loveContactView.updateContactData(data);
searchContactView.updateContactData(data);
}
// titleBar.setActionText(getString(R.string.selector_sure_without_num));
// int count = selectedListAdapter.getItemCount();
// if (count <= 0) {
// titleBar.setActionText(getString(R.string.selector_sure_without_num));
// } else {
// titleBar.setActionText(
// String.format(
// getString(R.string.selector_sure), selectedListAdapter.getItemCount()));
// }
});
}
contactListView.setContactAction(actions);
lastContactView.setContactAction(actions);
searchContactView.setContactAction(actions);
loveContactView.setContactAction(actions);
//top selected list
LinearLayoutManager layoutManager =
new LinearLayoutManager(this, RecyclerView.HORIZONTAL, false);
rvSelected.setLayoutManager(layoutManager);
selectedListAdapter = getSelectedListAdapter();
selectedListAdapter.setItemClickListener(
item -> {
item.setSelected(false);
contactListView.updateContactData(item);
lastContactView.updateContactData(item);
loveContactView.updateContactData(item);
searchContactView.updateContactData(item);
//int count = selectedListAdapter.getItemCount();
// titleBar.setActionText(getString(R.string.selector_sure_without_num));
// if (count <= 0) {
// titleBar.setActionText(getString(R.string.selector_sure_without_num));
// } else {
// titleBar.setActionText(
// String.format(
// getString(R.string.selector_sure), selectedListAdapter.getItemCount()));
// }
});
rvSelected.setAdapter(selectedListAdapter);
editSearch.setOnEditorActionListener((textView, actionId, keyEvent) ->
{
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String search = textView.getEditableText().toString();
//搜索好友
searchViewModel.queryFriend(search);
}
return false;
});
editSearch.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (TextUtils.isEmpty(editSearch.getText().toString())) {
ivClean.setVisibility(View.GONE);
layoutContactList.setVisibility(View.VISIBLE);
layoutSearchList.setVisibility(View.GONE);
hideSoftInput(BaseContactSelectorActivity.this);
} else {
ivClean.setVisibility(View.VISIBLE);
}
}
});
ivClean.setOnClickListener(view -> editSearch.setText(""));
}
public void hideSoftInput(Activity activity) {
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
if (getCurrentFocus() != null && null != imm) {
imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
}
protected BaseSelectedListAdapter<? extends ViewBinding> getSelectedListAdapter() {
return null;
}
// protected void configTitle(BackTitleBar titleBar) {
// titleBar
// .setOnBackIconClickListener(v -> onBackPressed())
// .setTitle(R.string.select)
// .setActionText(getString(R.string.selector_sure_without_num))
// .setActionTextColor(getResources().getColor(R.color.color_6b3689))
// .setActionListener(
// v -> {
// if (checkNetworkEnable && !NetworkUtils.isConnected()) {
// Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
// return;
// }
// List<String> select = getSelectedAccount();
// if (select.size() < 1) {
// Toast.makeText(this, getString(R.string.select_empty_tips), Toast.LENGTH_LONG)
// .show();
// return;
// }
// if (selectedListAdapter.getItemCount() >= maxSelectCount
// && selectFinalCheckCountEnable) {
// Toast.makeText(this, R.string.contact_selector_over_count, Toast.LENGTH_LONG)
// .show();
// return;
// }
// Intent result = new Intent();
// if (!selectedListAdapter.getSelectedFriends().isEmpty()) {
// result.putExtra(REQUEST_CONTACT_SELECTOR_KEY, getSelectedAccount());
// if (enableReturnName) {
// result.putExtra(KEY_REQUEST_SELECTOR_NAME, getSelectedName());
// }
// }
// setResult(RESULT_OK, result);
// finish();
// });
// }
protected void configTitle(BackTitleBar titleBar) {
}
protected void initData() {
filterUser = getIntent().getStringArrayListExtra(RouterConstant.SELECTOR_CONTACT_FILTER_KEY);
selectFinalCheckCountEnable =
getIntent()
.getBooleanExtra(RouterConstant.KEY_CONTACT_SELECTOR_FINAL_CHECK_COUNT_ENABLE, false);
checkNetworkEnable =
getIntent().getBooleanExtra(RouterConstant.KEY_CONTACT_SELECTOR_CHECK_NETWORK_ENABLE, true);
maxSelectCount =
getIntent()
.getIntExtra(RouterConstant.KEY_CONTACT_SELECTOR_MAX_COUNT, DEFAULT_MAX_SELECT_COUNT);
type = getIntent().getIntExtra("type", 0);
enableReturnName =
getIntent().getBooleanExtra(RouterConstant.KEY_REQUEST_SELECTOR_NAME_ENABLE, false);
viewModel
.getContactLiveData()
.observe(
this,
contactBeansResult -> {
if (contactBeansResult.getLoadStatus() == LoadStatus.Success) {
List<ContactFriendBean> accountList = filterUser(contactBeansResult.getData());
tvMemberslist.setText(String.format(getString(R.string.fun_contactselector_members), accountList.size()));
contactListView.onFriendDataSourceChanged(accountList);
showEmptyView(accountList == null || accountList.size() < 1);
}
});
viewModel.fetchContactList();
List<RecentContact> recentContact = NIMClient.getService(MsgService.class).queryRecentContactsBlock();
if (recentContact != null && recentContact.size() > 0) {
List<ContactFriendBean> list = convertConvacationList(recentContact);
if (list.size() > 0) {
tvMembersLast.setVisibility(View.VISIBLE);
lastContactView.setVisibility(View.VISIBLE);
tvMembersLast.setText(String.format(getString(R.string.fun_contactselector_last), list.size()));
lastContactView.onFriendDataSourceChanged(list);
}
}
searchViewModel.getQueryLiveData().observe(this, result -> {
if (result.getLoadStatus() == LoadStatus.Success) {
if (result.getData() != null && result.getData().size() > 0) {
layoutSearchList.setVisibility(View.VISIBLE);
layoutContactList.setVisibility(View.GONE);
searchContactView.onFriendDataSourceChanged(convertSearchList(result.getData()));
}
}
});
List<ContactFriendBean> DATA = getFollowList();
Log.i("D", DATA + "");
if (lastContactView != null && DATA.size() > 0) {
tvMembersSpecialGz.setVisibility(View.VISIBLE);
loveContactView.setVisibility(View.VISIBLE);
tvMembersSpecialGz.setText(String.format(getString(R.string.fun_contactselector_mylove), DATA.size()));
loveContactView.onFriendDataSourceChanged(getFollowList());
}
}
// public List<ContactFriendBean> convertConvacationList(List<ConversationBean> list) {
// List<ContactFriendBean> contactFriendBeans = new ArrayList<>();
//
// for (ConversationBean bean : list) {
// ConversationInfo conversationInfo = bean.infoData;
// if (conversationInfo.getFriendInfo() != null) {
// FriendInfo friendInfo = conversationInfo.getFriendInfo();
// friendInfo.setUserInfo(conversationInfo.getUserInfo());
// ContactFriendBean item = new ContactFriendBean(friendInfo);
// contactFriendBeans.add(item);
//
// }
// }
// return contactFriendBeans;
// }
public List<ContactFriendBean> convertConvacationList(List<RecentContact> list) {
List<ContactFriendBean> contactFriendBeans = new ArrayList<>();
for (RecentContact recentContact : list) {
if (recentContact.getSessionType() == SessionTypeEnum.P2P) {
if (ContactRepo.isFriend(recentContact.getContactId())) {
FriendInfo friendInfo = ContactRepo.getFriend(recentContact.getContactId());
friendInfo.setUserInfo(ContactRepo.getUserInfo(recentContact.getContactId()));
ContactFriendBean item = new ContactFriendBean(friendInfo);
contactFriendBeans.add(item);
}
}
}
return contactFriendBeans;
}
public List<ContactFriendBean> convertSearchList(List<BaseBean> list) {
List<ContactFriendBean> contactFriendBeans = new ArrayList<>();
LinkedList<ContactFriendBean> selected = selectedListAdapter.getSelectedFriends();
for (BaseBean bean : list) {
if (bean instanceof SearchFriendBean) {
SearchFriendBean searchFriendBean = (SearchFriendBean) bean;
FriendInfo friendInfo = searchFriendBean.friendSearchInfo.getFriendInfo();
ContactFriendBean item = new ContactFriendBean(friendInfo);
if (selected.size() > 0 && selected.contains(item)) {
contactFriendBeans.add(selected.get(selected.indexOf(item)));
} else {
contactFriendBeans.add(item);
}
}
}
return contactFriendBeans;
}
protected void showEmptyView(boolean show) {
if (show) {
emptyGroup.setVisibility(View.VISIBLE);
contactListView.setVisibility(View.GONE);
} else {
contactListView.setVisibility(View.VISIBLE);
emptyGroup.setVisibility(View.GONE);
}
}
protected List<ContactFriendBean> filterUser(List<ContactFriendBean> source) {
if (filterUser == null || filterUser.isEmpty()) {
return source;
}
List<ContactFriendBean> result = new ArrayList<>(source);
for (ContactFriendBean friendBean : source) {
if (filterUser.contains(friendBean.data.getAccount())) {
result.remove(friendBean);
}
}
return result;
}
protected List<ContactFriendBean> getLastContactList() {
return lastContactView.getAdapter().getFriendList();
}
protected List<ContactFriendBean> getContactAllList() {
return contactListView.getAdapter().getFriendList();
}
protected ArrayList<String> getSelectedAccount() {
ArrayList<String> result = new ArrayList<>();
for (ContactFriendBean bean : selectedListAdapter.getSelectedFriends()) {
result.add(bean.data.getAccount());
}
return result;
}
protected ArrayList<String> getSelectedName() {
ArrayList<String> result = new ArrayList<>();
for (ContactFriendBean bean : selectedListAdapter.getSelectedFriends()) {
String name = bean.data.getName();
result.add(TextUtils.isEmpty(name) ? bean.data.getAccount() : name);
}
return result;
}
protected ArrayList<String> getSelectedAvatar() {
ArrayList<String> result = new ArrayList<>();
for (ContactFriendBean bean : selectedListAdapter.getSelectedFriends()) {
if (bean.data.getUserInfo() == null) {
continue;
}
String avatar = bean.data.getUserInfo().getAvatar();
result.add(TextUtils.isEmpty(avatar) ? "" : avatar);
}
return result;
}
public boolean onTouchEvent(MotionEvent event) {
if (null != this.getCurrentFocus()) {
/**
* 点击空白位置 隐藏软键盘
*/
InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
return super.onTouchEvent(event);
}
}

View File

@@ -0,0 +1,87 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.selector;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import java.util.LinkedList;
public abstract class BaseSelectedListAdapter<R extends ViewBinding>
extends RecyclerView.Adapter<BaseSelectedListAdapter.SelectedViewHolder<R>> {
protected final LinkedList<ContactFriendBean> selectedFriends;
protected ItemClickListener itemClickListener;
public BaseSelectedListAdapter() {
selectedFriends = new LinkedList<>();
}
protected abstract R provideViewBinding(@NonNull ViewGroup parent, int viewType);
@NonNull
@Override
public final SelectedViewHolder<R> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new SelectedViewHolder<>(provideViewBinding(parent, viewType));
}
@Override
public final void onBindViewHolder(@NonNull SelectedViewHolder<R> holder, int position) {
ContactFriendBean bean = selectedFriends.get(position);
if (bean == null) {
return;
}
FriendInfo friendData = bean.data;
handleBindViewHolder(holder, bean, friendData);
}
protected abstract void handleBindViewHolder(
SelectedViewHolder<R> holder, ContactFriendBean bean, FriendInfo friendData);
public void setItemClickListener(ItemClickListener itemClickListener) {
this.itemClickListener = itemClickListener;
}
@Override
public int getItemCount() {
return selectedFriends.size();
}
public LinkedList<ContactFriendBean> getSelectedFriends() {
return selectedFriends;
}
public void addFriend(ContactFriendBean friend) {
int index = selectedFriends.size();
selectedFriends.add(friend);
notifyItemInserted(index);
}
public void removeFriend(ContactFriendBean friend) {
int pos = selectedFriends.indexOf(friend);
if (pos >= 0) {
selectedFriends.remove(friend);
notifyItemRemoved(pos);
}
}
protected static class SelectedViewHolder<R extends ViewBinding> extends RecyclerView.ViewHolder {
public R binding;
public SelectedViewHolder(@NonNull R binding) {
super(binding.getRoot());
this.binding = binding;
}
}
public interface ItemClickListener {
void onItemClick(ContactFriendBean item);
}
}

View File

@@ -0,0 +1,93 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.team;
import androidx.lifecycle.ViewModelProvider;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.activity.BaseListActivity;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactTeamBean;
import com.netease.yunxin.kit.corekit.im.IMKitClient;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import java.util.Collections;
import java.util.Comparator;
public abstract class BaseTeamListActivity extends BaseListActivity {
protected TeamListViewModel viewModel;
protected boolean isSelector;
protected abstract void configViewHolderFactory();
@Override
protected void initView() {
configTitle(binding);
configViewHolderFactory();
}
protected void configTitle(BaseListActivityLayoutBinding binding) {
binding.title.setTitle(R.string.my_team);
}
@Override
protected void initData() {
isSelector = getIntent().getBooleanExtra(RouterConstant.KEY_TEAM_LIST_SELECT, false);
viewModel = new ViewModelProvider(this).get(TeamListViewModel.class);
configRoutePath(viewModel);
viewModel
.getFetchResult()
.observe(
this,
result -> {
if (result.getLoadStatus() == LoadStatus.Success) {
binding.contactListView.clearContactData();
binding.contactListView.addContactData(result.getData());
} else if (result.getLoadStatus() == LoadStatus.Finish) {
if (result.getType() == FetchResult.FetchType.Add) {
binding.contactListView.addForwardContactData(result.getData());
binding.contactListView.scrollToPosition(0);
} else if (result.getType() == FetchResult.FetchType.Remove) {
binding.contactListView.removeContactData(result.getData());
}
}
// Collections.sort(binding.contactListView.getAdapter().getDataList(), teambeanComparator);
});
}
@Override
protected void onResume() {
super.onResume();
viewModel.fetchTeamList();
}
protected void configRoutePath(TeamListViewModel viewModel) {
}
//按照群聊时间
// public Comparator<BaseContactBean> teambeanComparator =
// (bean1, bean2) -> {
// int result = 1;
// if (bean1 == null) {
// result = 1;
// } else if (bean2 == null) {
// result = -1;
// } else if (bean1 instanceof ContactTeamBean && bean2 instanceof ContactTeamBean) { //群聊
// ContactTeamBean teamBean1 = (ContactTeamBean) bean1;
// ContactTeamBean teamBean2 = (ContactTeamBean) bean2;
// long chattime = teamBean1.getConversationTime() - teamBean2.getConversationTime();
// return (chattime > 0 ? -1 : 1);
// } else {
// result = -1;
// }
// return result;
// };
}

View File

@@ -0,0 +1,150 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.team;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import com.netease.nimlib.sdk.Observer;
import com.netease.nimlib.sdk.team.model.Team;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.chatkit.repo.TeamObserverRepo;
import com.netease.yunxin.kit.chatkit.repo.TeamRepo;
import com.netease.yunxin.kit.common.ui.viewmodel.BaseViewModel;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.model.ContactTeamBean;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import java.util.ArrayList;
import java.util.List;
public class TeamListViewModel extends BaseViewModel {
private final String TAG = "TeamListViewModel";
private final MutableLiveData<FetchResult<List<ContactTeamBean>>> resultLiveData =
new MutableLiveData<>();
private final FetchResult<List<ContactTeamBean>> fetchResult =
new FetchResult<>(LoadStatus.Finish);
private final List<ContactTeamBean> teamBeanList = new ArrayList<>();
private final Observer<List<Team>> teamUpdateObserver;
private final Observer<Team> teamRemoveObserver;
private String defaultRoutePath = RouterConstant.PATH_CHAT_TEAM_PAGE;
public void configRoutePath(String path) {
this.defaultRoutePath = path;
}
public MutableLiveData<FetchResult<List<ContactTeamBean>>> getFetchResult() {
return resultLiveData;
}
public TeamListViewModel() {
teamUpdateObserver = this::updateTeamData;
teamRemoveObserver = this::removeTeamData;
TeamObserverRepo.registerTeamUpdateObserver(teamUpdateObserver);
TeamObserverRepo.registerTeamRemoveObserver(teamRemoveObserver);
}
public void fetchTeamList() {
ALog.d(LIB_TAG, TAG, "fetchTeamList");
fetchResult.setStatus(LoadStatus.Loading);
resultLiveData.postValue(fetchResult);
TeamRepo.getTeamList(
new FetchCallback<List<Team>>() {
@Override
public void onSuccess(List<Team> param) {
ALog.d(
LIB_TAG, TAG, "fetchTeamList,onSuccess:" + (param == null ? "null" : param.size()));
teamBeanList.clear();
if (param != null && param.size() > 0) {
fetchResult.setStatus(LoadStatus.Success);
for (Team teamInfo : param) {
ContactTeamBean teamBean = new ContactTeamBean(teamInfo);
teamBean.router = defaultRoutePath;
teamBeanList.add(0, teamBean);
}
fetchResult.setData(teamBeanList);
} else {
fetchResult.setData(null);
fetchResult.setStatus(LoadStatus.Success);
}
resultLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "fetchTeamList,onFailed:" + code);
fetchResult.setError(code, "");
resultLiveData.postValue(fetchResult);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "fetchTeamList,onException");
fetchResult.setError(-1, "");
resultLiveData.postValue(fetchResult);
}
});
}
private void removeTeamData(Team teamInfo) {
if (teamInfo != null) {
ALog.d(LIB_TAG, TAG, "removeTeamData:" + teamInfo.getId());
List<ContactTeamBean> remove = new ArrayList<>();
for (ContactTeamBean bean : teamBeanList) {
if (TextUtils.equals(teamInfo.getId(), bean.data.getId())) {
remove.add(bean);
break;
}
}
if (remove.size() > 0) {
fetchResult.setFetchType(FetchResult.FetchType.Remove);
fetchResult.setData(remove);
resultLiveData.postValue(fetchResult);
} else {
fetchTeamList();
}
}
}
private void updateTeamData(List<Team> teamInfoList) {
if (teamInfoList != null && !teamInfoList.isEmpty()) {
ALog.d(LIB_TAG, TAG, "updateTeamData:" + teamInfoList.size());
List<ContactTeamBean> add = new ArrayList<>();
for (Team teamInfo : teamInfoList) {
boolean has = false;
for (ContactTeamBean bean : teamBeanList) {
if (TextUtils.equals(teamInfo.getId(), bean.data.getId())) {
has = true;
break;
}
}
if (!has) {
ContactTeamBean teamBean = new ContactTeamBean(teamInfo);
teamBean.router = defaultRoutePath;
add.add(teamBean);
teamBeanList.add(0, teamBean);
}
}
if (add.size() > 0) {
fetchResult.setFetchType(FetchResult.FetchType.Add);
fetchResult.setData(add);
resultLiveData.postValue(fetchResult);
}
}
}
@Override
protected void onCleared() {
super.onCleared();
TeamObserverRepo.unregisterTeamUpdateObserver(teamUpdateObserver);
TeamObserverRepo.unregisterTeamRemoveObserver(teamRemoveObserver);
}
}

View File

@@ -0,0 +1,74 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.userinfo;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.common.ui.widgets.BackTitleBar;
import com.netease.yunxin.kit.common.ui.widgets.CleanableEditText;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import java.util.Objects;
public abstract class BaseCommentActivity extends BaseActivity {
public static final String REQUEST_COMMENT_NAME_KEY = "comment";
private View rootView;
protected CleanableEditText edtComment;
protected BackTitleBar titleBar;
protected abstract View initViewAndGetRootView(Bundle savedInstanceState);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootView = initViewAndGetRootView(savedInstanceState);
checkViews();
setContentView(rootView);
initView();
}
protected void checkViews() {
Objects.requireNonNull(rootView);
Objects.requireNonNull(edtComment);
Objects.requireNonNull(titleBar);
}
private void initView() {
String comment = getIntent().getStringExtra(REQUEST_COMMENT_NAME_KEY);
comment = comment == null ? "" : comment;
edtComment.setText(comment);
int textLength = edtComment.getBinding().editText.length();
edtComment.getBinding().editText.setSelection(textLength);
edtComment.getBinding().editText.requestFocus();
configTitle(titleBar);
}
protected void configTitle(BackTitleBar titleBar) {
titleBar
.setTitle(R.string.comment_name)
.setOnBackIconClickListener(v -> onBackPressed())
.setActionText(R.string.save)
.setActionListener(
v -> {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
return;
}
Intent intent = new Intent();
if (!TextUtils.isEmpty(edtComment.getText())) {
intent.putExtra(REQUEST_COMMENT_NAME_KEY, edtComment.getText());
}
setResult(RESULT_OK, intent);
finish();
});
}
}

View File

@@ -0,0 +1,291 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.userinfo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import com.netease.yunxin.kit.common.ui.activities.BaseActivity;
import com.netease.yunxin.kit.common.ui.dialog.BottomConfirmDialog;
import com.netease.yunxin.kit.common.ui.dialog.ConfirmListener;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.common.ui.widgets.BackTitleBar;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.model.ContactUserInfoBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactInfoView;
import com.netease.yunxin.kit.corekit.im.model.FriendVerifyType;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.utils.RouterConstant;
import com.netease.yunxin.kit.corekit.route.XKitRouter;
import java.util.Objects;
public abstract class BaseUserInfoActivity extends BaseActivity {
protected UserInfoViewModel viewModel;
protected ContactUserInfoBean userInfoData;
protected String accId;
private View rootView;
protected BackTitleBar titleBar;
protected ContactInfoView contactInfoView;
protected ActivityResultLauncher<Intent> commentLauncher;
protected abstract View initViewAndGetRootView(Bundle savedInstanceState);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootView = initViewAndGetRootView(savedInstanceState);
checkViews();
setContentView(rootView);
titleBar.setOnBackIconClickListener(v -> onBackPressed());
initView();
initData();
registerResult();
}
protected void checkViews() {
Objects.requireNonNull(rootView);
Objects.requireNonNull(titleBar);
Objects.requireNonNull(contactInfoView);
}
private void registerResult() {
commentLauncher =
registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == BaseCommentActivity.RESULT_OK
&& result.getData() != null) {
String comment =
result.getData().getStringExtra(BaseCommentActivity.REQUEST_COMMENT_NAME_KEY);
userInfoData.friendInfo.setAlias(comment);
contactInfoView.setData(userInfoData);
viewModel.updateAlias(userInfoData.data.getAccount(), comment);
}
});
}
private void initView() {
contactInfoView.setUserCallback(
new ContactInfoView.IUserCallback() {
@Override
public void goChat() {
if (userInfoData == null || userInfoData.data == null) {
if (!NetworkUtils.isConnected()) {
Toast.makeText(
BaseUserInfoActivity.this,
R.string.contact_network_error_tip,
Toast.LENGTH_SHORT)
.show();
return;
}
return;
}
BaseUserInfoActivity.this.goChat();
}
@Override
public void addFriend() {
if (userInfoData == null || userInfoData.data == null) {
return;
}
if (!NetworkUtils.isConnected()) {
Toast.makeText(
BaseUserInfoActivity.this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT)
.show();
return;
}
addNewFriend();
}
@Override
public void openMessageNotify(boolean open) {
//todo 打开或者关闭消息通知
}
@Override
public void addBlackList(boolean add) {
if (!NetworkUtils.isConnected()) {
Toast.makeText(
BaseUserInfoActivity.this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT)
.show();
return;
}
if (userInfoData == null || userInfoData.data == null) {
return;
}
if (add) {
viewModel.addBlack(userInfoData.data.getAccount());
} else {
viewModel.removeBlack(userInfoData.data.getAccount());
}
}
});
contactInfoView.setCommentClickListener(
v -> {
if (userInfoData == null || userInfoData.data == null) {
return;
}
Intent intent = new Intent();
intent.setClass(this, getCommentActivity());
intent.putExtra(
BaseCommentActivity.REQUEST_COMMENT_NAME_KEY, userInfoData.friendInfo.getAlias());
commentLauncher.launch(intent);
});
contactInfoView.setDeleteClickListener(
v -> {
if (userInfoData == null || userInfoData.data == null) {
return;
}
showDeleteConfirmDialog();
});
}
protected void goChat() {
XKitRouter.withKey(RouterConstant.PATH_CHAT_P2P_PAGE)
.withParam(RouterConstant.CHAT_ID_KRY, userInfoData.data.getAccount())
.withContext(BaseUserInfoActivity.this)
.navigate();
finish();
}
protected Class<? extends Activity> getCommentActivity() {
return null;
}
protected void showDeleteConfirmDialog() {
BottomConfirmDialog bottomConfirmDialog = new BottomConfirmDialog();
bottomConfirmDialog
.setTitleStr(
String.format(getString(R.string.delete_contact_account), userInfoData.getName()))
.setPositiveStr(getString(R.string.delete_friend))
.setNegativeStr(getString(R.string.cancel))
.setConfirmListener(
new ConfirmListener() {
@Override
public void onNegative() {
//do nothing
}
@Override
public void onPositive() {
if (!NetworkUtils.isConnected()) {
Toast.makeText(
BaseUserInfoActivity.this,
R.string.contact_network_error_tip,
Toast.LENGTH_SHORT)
.show();
return;
}
viewModel.deleteFriend(userInfoData.data.getAccount());
finish();
}
})
.show(getSupportFragmentManager());
}
private void initData() {
viewModel = new ViewModelProvider(this).get(UserInfoViewModel.class);
accId = getIntent().getStringExtra(RouterConstant.KEY_ACCOUNT_ID_KEY);
if (TextUtils.isEmpty(accId)) {
finish();
}
viewModel.init(accId);
viewModel
.getFriendFetchResult()
.observe(
this,
mapFetchResult -> {
if (mapFetchResult.getLoadStatus() == LoadStatus.Success) {
userInfoData = mapFetchResult.getData();
contactInfoView.setData(userInfoData);
} else {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
}
}
});
viewModel
.getUserInfoLiveData()
.observe(
this,
userInfoResult -> {
if (userInfoResult.getLoadStatus() == LoadStatus.Finish
&& userInfoResult.getData() != null) {
for (UserInfo userInfo : userInfoResult.getData()) {
if (TextUtils.equals(userInfo.getAccount(), accId)) {
if (userInfoData != null) {
userInfoData.data = userInfo;
contactInfoView.setData(userInfoData);
}
}
}
} else {
if (!NetworkUtils.isConnected()) {
Toast.makeText(this, R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
}
}
});
viewModel
.getFriendChangeLiveData()
.observe(
this,
result -> {
if (result.getLoadStatus() == LoadStatus.Finish) {
viewModel.fetchData(accId);
}
});
viewModel.fetchData(accId);
}
private void addNewFriend() {
viewModel.addFriend(
userInfoData.data.getAccount(),
FriendVerifyType.AgreeAdd,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
viewModel.fetchData(userInfoData.data.getAccount());
Toast.makeText(
BaseUserInfoActivity.this,
getResources().getString(R.string.add_friend_operate_success),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onFailed(int code) {
String tips = getResources().getString(R.string.add_friend_operate_fail);
Toast.makeText(
BaseUserInfoActivity.this,
String.format(tips, String.valueOf(code)),
Toast.LENGTH_SHORT)
.show();
}
@Override
public void onException(@Nullable Throwable exception) {
String tips = getResources().getString(R.string.add_friend_operate_fail);
Toast.makeText(
BaseUserInfoActivity.this,
String.format(tips, exception.getMessage()),
Toast.LENGTH_SHORT)
.show();
}
});
}
}

View File

@@ -0,0 +1,338 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.userinfo;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import com.netease.nimlib.sdk.NIMClient;
import com.netease.nimlib.sdk.RequestCallback;
import com.netease.nimlib.sdk.friend.FriendService;
import com.netease.nimlib.sdk.friend.constant.VerifyType;
import com.netease.nimlib.sdk.friend.model.AddFriendData;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.chatkit.repo.ContactObserverRepo;
import com.netease.yunxin.kit.chatkit.repo.ContactRepo;
import com.netease.yunxin.kit.common.ui.viewmodel.BaseViewModel;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.model.ContactUserInfoBean;
import com.netease.yunxin.kit.corekit.im.model.FriendInfo;
import com.netease.yunxin.kit.corekit.im.model.FriendVerifyType;
import com.netease.yunxin.kit.corekit.im.model.UserInfo;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.provider.FriendChangeType;
import com.netease.yunxin.kit.corekit.im.provider.FriendObserver;
import com.netease.yunxin.kit.corekit.im.provider.UserInfoObserver;
import java.util.ArrayList;
import java.util.List;
public class UserInfoViewModel extends BaseViewModel {
private final String TAG = "UserInfoViewModel";
private final MutableLiveData<FetchResult<ContactUserInfoBean>> friendLiveData =
new MutableLiveData<>();
private final FetchResult<ContactUserInfoBean> fetchResult = new FetchResult<>(LoadStatus.Finish);
private final MutableLiveData<FetchResult<List<UserInfo>>> userInfoLiveData =
new MutableLiveData<>();
private final FetchResult<List<UserInfo>> userInfoFetchResult =
new FetchResult<>(LoadStatus.Finish);
private final MutableLiveData<FetchResult<String>> friendChangeLiveData = new MutableLiveData<>();
private final FetchResult<String> friendChangeFetchResult = new FetchResult<>(LoadStatus.Finish);
private String accountId;
public UserInfoViewModel() {
registerObserver();
}
public void init(String account) {
accountId = account;
}
private final UserInfoObserver userInfoObserver =
userList -> {
userInfoFetchResult.setLoadStatus(LoadStatus.Finish);
userInfoFetchResult.setData(userList);
userInfoFetchResult.setType(FetchResult.FetchType.Update);
userInfoLiveData.setValue(userInfoFetchResult);
};
private final FriendObserver friendObserver =
(friendChangeType, accountList) -> {
if (friendChangeType == FriendChangeType.Add
|| friendChangeType == FriendChangeType.Delete) {
for (String account : accountList) {
if (TextUtils.equals(accountId, account)) {
friendChangeFetchResult.setData(account);
friendChangeFetchResult.setLoadStatus(LoadStatus.Finish);
if (friendChangeType == FriendChangeType.Add) {
friendChangeFetchResult.setFetchType(FetchResult.FetchType.Add);
} else {
friendChangeFetchResult.setFetchType(FetchResult.FetchType.Remove);
}
friendChangeLiveData.setValue(friendChangeFetchResult);
}
}
}
};
public void registerObserver() {
ContactObserverRepo.registerUserInfoObserver(userInfoObserver);
ContactObserverRepo.registerFriendObserver(friendObserver);
}
public MutableLiveData<FetchResult<ContactUserInfoBean>> getFriendFetchResult() {
return friendLiveData;
}
public MutableLiveData<FetchResult<List<UserInfo>>> getUserInfoLiveData() {
return userInfoLiveData;
}
public MutableLiveData<FetchResult<String>> getFriendChangeLiveData() {
return friendChangeLiveData;
}
public void fetchData(String account) {
ALog.d(LIB_TAG, TAG, "fetchData:" + account);
if (TextUtils.isEmpty(account)) {
return;
}
List<String> accountList = new ArrayList<>();
accountList.add(account);
ContactRepo.fetchFriend(
account,
new FetchCallback<FriendInfo>() {
@Override
public void onSuccess(@Nullable FriendInfo param) {
ALog.d(
LIB_TAG,
TAG,
"fetchData,onSuccess:" + (param == null ? "null" : param.getAccount()));
if (param != null) {
ContactUserInfoBean userInfo = new ContactUserInfoBean(param.getUserInfo());
userInfo.friendInfo = param;
userInfo.isBlack = isBlack(account);
userInfo.isFriend = isFriend(account);
fetchResult.setData(userInfo);
fetchResult.setStatus(LoadStatus.Success);
} else {
fetchResult.setError(-1, "");
}
friendLiveData.postValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "fetchData,onFailed:" + code);
fetchResult.setError(code, "");
friendLiveData.postValue(fetchResult);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "fetchData,onException");
fetchResult.setError(-1, "");
friendLiveData.postValue(fetchResult);
}
});
}
public boolean isBlack(String account) {
ALog.d(LIB_TAG, TAG, "isBlack:" + account);
return ContactRepo.isBlackList(account);
}
public boolean isFriend(String account) {
ALog.d(LIB_TAG, TAG, "isFriend:" + account);
return ContactRepo.isFriend(account);
}
public void addBlack(String account) {
ALog.d(LIB_TAG, TAG, "addBlack:" + account);
ContactRepo.addBlackList(
account,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
ALog.d(LIB_TAG, TAG, "addBlack,onSuccess");
fetchData(account);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "addBlack,onFailed:" + code);
fetchResult.setError(code, "");
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "addBlack,onException");
fetchResult.setError(-1, "");
}
});
}
public void removeBlack(String account) {
ALog.d(LIB_TAG, TAG, "removeBlack:" + account);
ContactRepo.removeBlackList(
account,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
ALog.d(LIB_TAG, TAG, "removeBlack,onSuccess");
fetchData(account);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "removeBlack,onFailed:" + code);
fetchResult.setError(code, "");
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "removeBlack,onException");
fetchResult.setError(-1, "");
}
});
}
public void deleteFriend(String account) {
ALog.d(LIB_TAG, TAG, "deleteFriend:" + account);
ContactRepo.deleteFriend(
account,
true,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
ALog.d(LIB_TAG, TAG, "deleteFriend,onSuccess");
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "deleteFriend,onFailed:" + code);
fetchResult.setError(code, "");
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "deleteFriend,onException");
fetchResult.setError(-1, "");
}
});
}
public void addFriend(String account, FriendVerifyType type, FetchCallback<Void> callback) {
ALog.d(LIB_TAG, TAG, "addFriend:" + account);
if (isBlackList(account)) {
ALog.d(LIB_TAG, TAG, "addFriendaccount in blacklist:" + account);
removeBlackList(
account,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
ALog.d(LIB_TAG, TAG, "addFriend,removeBlackList onSuccess:" + account);
ContactRepo.addFriend(account, type, callback);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "addFriend,removeBlackList onFailed:" + code);
callback.onFailed(code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(
LIB_TAG, TAG, "addFriend,removeBlackList onException:" + exception.getMessage());
callback.onException(exception);
}
});
} else {
ContactRepo.addFriend(account, type, callback);
}
}
//验证添加好友
public void addFriendVerifyMsg(String account, String verifymsg, boolean isVerifyMsg, FetchCallback<Void> callback) {
if (isBlackList(account)) {
ALog.d(LIB_TAG, TAG, "addFriendaccount in blacklist:" + account);
removeBlackList(
account,
new FetchCallback<Void>() {
@Override
public void onSuccess(@Nullable Void param) {
ALog.d(LIB_TAG, TAG, "addFriend,removeBlackList onSuccess:" + account);
setRequestAddFriends(accountId, verifymsg,isVerifyMsg, callback);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "addFriend,removeBlackList onFailed:" + code);
callback.onFailed(code);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(
LIB_TAG, TAG, "addFriend,removeBlackList onException:" + exception.getMessage());
callback.onException(exception);
}
});
} else {
setRequestAddFriends(account, verifymsg, isVerifyMsg, callback);
}
}
public void setRequestAddFriends(String accountId, String msg, boolean isVerify, FetchCallback callback) {
NIMClient.getService(FriendService.class).addFriend(new AddFriendData(accountId, isVerify ? VerifyType.VERIFY_REQUEST : VerifyType.DIRECT_ADD, msg))
.setCallback(new RequestCallback<Void>() {
@Override
public void onSuccess(Void result) {
callback.onSuccess(result);
}
@Override
public void onFailed(int code) {
callback.onFailed(code);
}
@Override
public void onException(Throwable exception) {
callback.onException(exception);
}
});
}
public boolean isBlackList(String account) {
return ContactRepo.isBlackList(account);
}
public void removeBlackList(String account, FetchCallback<Void> callback) {
ALog.d(LIB_TAG, TAG, "removeBlackList:" + account);
ContactRepo.removeBlackList(account, callback);
}
public void updateAlias(String account, String alias) {
ALog.d(LIB_TAG, TAG, "updateAlias:" + account);
ContactRepo.updateAlias(account, alias);
}
@Override
protected void onCleared() {
super.onCleared();
ContactObserverRepo.unregisterUserInfoObserver(userInfoObserver);
ContactObserverRepo.unregisterFriendObserver(friendObserver);
}
}

View File

@@ -0,0 +1,40 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.utils;
import android.text.TextUtils;
import com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView;
import com.netease.yunxin.kit.contactkit.ui.R;
public class ColorUtils {
public static int avatarColor(String content) {
if (!TextUtils.isEmpty(content)) {
return content.charAt(content.length() - 1);
}
return 0;
}
public static int avatarColor(long value) {
return (int) value % Integer.MAX_VALUE;
}
public static void loadAvator(String headUrl, ContactAvatarView contactAvatarView) {
if (TextUtils.isEmpty(headUrl)) {
contactAvatarView.setData(R.mipmap.default_head_img, "", 0);
} else {
contactAvatarView.setData(headUrl, "");
}
}
public static void loadGroupAvator(String headUrl, ContactAvatarView contactAvatarView) {
if (TextUtils.isEmpty(headUrl)) {
contactAvatarView.setData(R.mipmap.ic_group_defaulthead, "", 0);
} else {
contactAvatarView.setData(headUrl, "");
}
}
}

View File

@@ -0,0 +1,91 @@
package com.netease.yunxin.kit.contactkit.ui.utils;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* json解析工具类 其实对于数组解析有一些问题
* @author
*/
public class GsonUtils {
public static Gson gson = new Gson();
/**
* 返回List对象
* @param str
* @param type new TypeToken<ArrayList<T>>(){}.getType()
* @param <T>
* @return
*/
public static <T> T getListFromJSON(String str, Type type) {
if (!TextUtils.isEmpty(str)) {
return gson.fromJson(str, type);
}
return null;
}
/**
* 返回List对象
* @param str
* @param cls
* @param <T>
* @return
*/
public static <T> List<T> getListFromJSON(String str, Class<T> cls)
{
Type type = new TypeToken<ArrayList<JsonObject>>()
{}.getType();
ArrayList<JsonObject> jsonObjects = gson.fromJson(str, type);
ArrayList<T> arrayList = new ArrayList<>();
for (JsonObject jsonObject : jsonObjects)
{
arrayList.add(gson.fromJson(jsonObject, cls));
}
return arrayList;
}
/**
* 返回对象
* @param str
* @param cls
* @param <T>
* @return
*/
public static <T> T getObjFromJSON(String str, Class<T> cls) {
try {
if (!TextUtils.isEmpty(str)) {
// LogUtils.i("参数:"+str);
return gson.fromJson(str, cls);
}
return null;
}catch (Exception e) {
return null;
}
}
/**
* 返回JsonString
* @return
*/
public static String beanToJSONString(Object bean) {
return new Gson().toJson(bean);
}
public static String JSONTokener(String in) {
// consume an optional byte order mark (BOM) if it exists
if (in != null && in.startsWith("\ufeff")) {
in = in.substring(1);
}
return in;
}
}

View File

@@ -0,0 +1,168 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.verify;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import androidx.lifecycle.ViewModelProvider;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.ILoadListener;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.activity.BaseListActivity;
import com.netease.yunxin.kit.contactkit.ui.databinding.BaseListActivityLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactVerifyInfoBean;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoType;
import java.util.List;
public class BaseVerifyListActivity extends BaseListActivity implements ILoadListener {
protected VerifyViewModel viewModel;
protected boolean hasInit = false;
protected int seriesPageCount = 0;
protected final int seriesPageLimit = 20;
protected final int error_duplicate = 509;
protected final int error_teamdismiss = 803;
protected final int error_joinedteam = 809; //已经在群内
protected void configViewHolderFactory() {
}
@Override
protected void initView() {
configTitle(binding);
configViewHolderFactory();
binding.contactListView.setLoadMoreListener(this);
}
protected void configTitle(BaseListActivityLayoutBinding binding) {
binding
.title
.setTitle(R.string.verify_msg);
// .setActionText(R.string.clear_all)
// .setActionListener(v -> viewModel.clearNotify());
}
@Override
protected void onResume() {
super.onResume();
updateView();
}
@Override
protected void onStop() {
super.onStop();
viewModel.resetUnreadCount();
binding.contactListView.getAdapter().notifyDataSetChanged();
}
@Override
protected void initData() {
viewModel = new ViewModelProvider(this).get(VerifyViewModel.class);
viewModel
.getFetchResult()
.observe(
this,
result -> {
if (result.getLoadStatus() == LoadStatus.Success) {
binding.contactListView.addContactData(result.getData());
if (result.getData() == null
|| (result.getData() != null && result.getData().size() < 10)) {
if (viewModel.hasMore() && seriesPageCount < seriesPageLimit) {
seriesPageCount += result.getData() != null ? result.getData().size() : 0;
viewModel.fetchVerifyList(true);
}
seriesPageCount = 0;
}
seriesPageCount = 0;
} else if (result.getLoadStatus() == LoadStatus.Finish) {
if (result.getType() == FetchResult.FetchType.Remove) {
binding.contactListView.removeContactData(result.getData());
} else if (result.getType() == FetchResult.FetchType.Add) {
addNotifyData(result.getData());
} else if (result.getType() == FetchResult.FetchType.Update) {
for (ContactVerifyInfoBean bean : result.getData()) {
binding.contactListView.updateContactDataAndSort(bean);
}
}
}
hasInit = true;
updateView();
});
viewModel.fetchVerifyList(false);
}
protected void updateView() {
if (binding.contactListView.getItemCount() > 0 || !hasInit) {
binding.contactListView.configEmptyViewRes(getEmptyStateViewRes());
binding.contactListView.setEmptyViewVisible(View.GONE, null);
} else {
binding.contactListView.setEmptyViewVisible(
View.VISIBLE, getString(R.string.verify_empty_text));
}
}
protected int getEmptyStateViewRes() {
return 0;
}
protected void toastResult(boolean agree, SystemMessageInfoType type, int errorCode) {
String content = null;
if (errorCode == error_duplicate) {
content = getResources().getString(R.string.verify_duplicate_fail);
} else if (type == SystemMessageInfoType.AddFriend) {
content =
agree
? getResources().getString(R.string.agree_add_friend_fail)
: getResources().getString(R.string.disagree_add_friend_fail);
} else if ((type == SystemMessageInfoType.ApplyJoinTeam)) {
content =
agree
? getResources().getString(R.string.agree_apply_join_team_fail)
: getResources().getString(R.string.disagree_apply_join_team_fail);
} else if ((type == SystemMessageInfoType.TeamInvite)) {
content =
agree
? getResources().getString(R.string.agree_invite_team_fail)
: getResources().getString(R.string.disagree_invite_team_fail);
}
Toast.makeText(this, content, Toast.LENGTH_SHORT).show();
}
protected void addNotifyData(List<ContactVerifyInfoBean> addList) {
if (addList == null || addList.size() < 1) {
return;
}
binding.contactListView.addForwardContactData(addList);
}
@Override
public boolean hasMore() {
return viewModel.hasMore();
}
@Override
public void loadMore(Object last) {
viewModel.fetchVerifyList(true);
}
public boolean onTouchEvent(MotionEvent event) {
if(null != this.getCurrentFocus()){
/**
* 点击空白位置 隐藏软键盘
*/
InputMethodManager mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
return mInputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(), 0);
}
return super .onTouchEvent(event);
}
}

View File

@@ -0,0 +1,270 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.verify;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.lifecycle.MutableLiveData;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.chatkit.repo.ContactObserverRepo;
import com.netease.yunxin.kit.chatkit.repo.ContactRepo;
import com.netease.yunxin.kit.chatkit.repo.TeamRepo;
import com.netease.yunxin.kit.common.ui.viewmodel.BaseViewModel;
import com.netease.yunxin.kit.common.ui.viewmodel.FetchResult;
import com.netease.yunxin.kit.common.ui.viewmodel.LoadStatus;
import com.netease.yunxin.kit.contactkit.ui.model.ContactVerifyInfoBean;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfo;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoStatus;
import com.netease.yunxin.kit.corekit.im.model.SystemMessageInfoType;
import com.netease.yunxin.kit.corekit.im.provider.FetchCallback;
import com.netease.yunxin.kit.corekit.im.provider.SystemMessageInfoObserver;
import java.util.ArrayList;
import java.util.List;
public class VerifyViewModel extends BaseViewModel {
private final String TAG = "VerifyViewModel";
private final int PAGE_LIMIT = 100;
//7 day expire time
private final long expireLimit = 604800000;
private int index = 0;
private boolean hasMore = true;
private final MutableLiveData<FetchResult<List<ContactVerifyInfoBean>>> resultLiveData =
new MutableLiveData<>();
private final FetchResult<List<ContactVerifyInfoBean>> fetchResult =
new FetchResult<>(LoadStatus.Finish);
private final List<ContactVerifyInfoBean> verifyBeanList = new ArrayList<>();
private final List<ContactVerifyInfoBean> updateList = new ArrayList<>();
private final SystemMessageInfoObserver infoObserver;
public MutableLiveData<FetchResult<List<ContactVerifyInfoBean>>> getFetchResult() {
return resultLiveData;
}
public VerifyViewModel() {
infoObserver =
info -> {
if (info.getId() > 0) {
List<SystemMessageInfo> msgInfo = new ArrayList<>();
msgInfo.add(info);
ContactRepo.fillNotificationWithUserAndTeam(
msgInfo,
new FetchCallback<List<SystemMessageInfo>>() {
@Override
public void onSuccess(@Nullable List<SystemMessageInfo> param) {
ALog.d(
LIB_TAG,
TAG,
"infoObserver,onSuccess:" + (param == null ? "null" : param.size()));
List<ContactVerifyInfoBean> add = new ArrayList<>();
if (param != null && !param.isEmpty()) {
resetMessageStatus(param);
add = mergeSystemMessageList(param);
// 如果有新的消息合并,需要更新原有消息
if (updateList.size() > 0) {
List<ContactVerifyInfoBean> update = new ArrayList<>(updateList);
updateList.clear();
fetchResult.setData(update);
fetchResult.setFetchType(FetchResult.FetchType.Update);
resultLiveData.setValue(fetchResult);
}
}
//update
if (add.size() > 0) {
fetchResult.setData(add);
fetchResult.setFetchType(FetchResult.FetchType.Add);
resultLiveData.setValue(fetchResult);
}
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "infoObserver,onFailed:" + code);
fetchResult.setError(code, "");
resultLiveData.setValue(fetchResult);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "infoObserver,onException");
fetchResult.setError(-1, "");
resultLiveData.setValue(fetchResult);
}
});
}
};
ContactObserverRepo.registerNotificationObserver(infoObserver);
}
public void fetchVerifyList(boolean nextPage) {
ALog.d(LIB_TAG, TAG, "fetchVerifyList,nextPage:" + nextPage);
fetchResult.setStatus(LoadStatus.Loading);
resultLiveData.postValue(fetchResult);
if (nextPage) {
if (!hasMore) {
return;
}
index += PAGE_LIMIT;
} else {
index = 0;
}
ContactRepo.getNotificationList(
index,
PAGE_LIMIT,
new FetchCallback<List<SystemMessageInfo>>() {
@Override
public void onSuccess(@Nullable List<SystemMessageInfo> param) {
ALog.d(
LIB_TAG,
TAG,
"fetchVerifyList,onSuccess:" + (param == null ? "null" : param.size()));
fetchResult.setStatus(LoadStatus.Success);
hasMore = (param != null && param.size() == PAGE_LIMIT);
if (param != null && param.size() > 0) {
resetMessageStatus(param);
List<ContactVerifyInfoBean> add = mergeSystemMessageList(param);
fetchResult.setData(add);
} else {
fetchResult.setData(null);
}
resultLiveData.setValue(fetchResult);
}
@Override
public void onFailed(int code) {
ALog.d(LIB_TAG, TAG, "fetchVerifyList,onFailed:" + code);
fetchResult.setError(code, "");
resultLiveData.setValue(fetchResult);
}
@Override
public void onException(@Nullable Throwable exception) {
ALog.d(LIB_TAG, TAG, "fetchVerifyList,onException");
fetchResult.setError(-1, "");
resultLiveData.setValue(fetchResult);
}
});
}
public boolean hasMore() {
return hasMore;
}
public void clearNotify() {
ALog.d(LIB_TAG, TAG, "clearNotify");
ContactRepo.clearNotification();
fetchResult.setFetchType(FetchResult.FetchType.Remove);
fetchResult.setData(new ArrayList<>(verifyBeanList));
verifyBeanList.clear();
resultLiveData.setValue(fetchResult);
}
public void agree(ContactVerifyInfoBean bean, FetchCallback<Void> callback) {
ALog.d(LIB_TAG, TAG, "agree:" + (bean == null ? "null" : bean.data.getId()));
SystemMessageInfo info = bean.data;
SystemMessageInfoType type = info.getInfoType();
SystemMessageInfoStatus status = info.getInfoStatus();
String account = info.getFromAccount();
if (status == SystemMessageInfoStatus.Init && !TextUtils.isEmpty(account)) {
if (type == SystemMessageInfoType.AddFriend) {
ContactRepo.acceptAddFriend(account, true, callback);
} else if (type == SystemMessageInfoType.ApplyJoinTeam) {
TeamRepo.agreeTeamApply(info.getTargetId(), account, callback);
} else if (type == SystemMessageInfoType.TeamInvite) {
TeamRepo.acceptTeamInvite(info.getTargetId(), account, callback);
}
}
}
public void disagree(ContactVerifyInfoBean bean, FetchCallback<Void> callback) {
ALog.d(LIB_TAG, TAG, "disagree:" + (bean == null ? "null" : bean.data.getId()));
if (bean == null || bean.data == null) {
return;
}
SystemMessageInfo info = bean.data;
SystemMessageInfoType type = info.getInfoType();
SystemMessageInfoStatus status = info.getInfoStatus();
String account = info.getFromAccount();
if (status == SystemMessageInfoStatus.Init && !TextUtils.isEmpty(account)) {
if (type == SystemMessageInfoType.AddFriend) {
ContactRepo.acceptAddFriend(info.getFromAccount(), false, callback);
} else if (type == SystemMessageInfoType.ApplyJoinTeam
&& !TextUtils.isEmpty(info.getTargetId())) {
TeamRepo.rejectTeamApply(info.getTargetId(), account, "", callback);
} else if (type == SystemMessageInfoType.TeamInvite
&& !TextUtils.isEmpty(info.getTargetId())) {
TeamRepo.rejectTeamInvite(info.getTargetId(), account, "", callback);
}
}
}
public void setVerifyStatus(ContactVerifyInfoBean bean, SystemMessageInfoStatus status) {
if (bean == null || status == null) {
return;
}
ALog.d(LIB_TAG, TAG, "setVerifyStatus:" + (status == null ? "null" : status.name()));
for (SystemMessageInfo messageInfo : bean.messageList) {
ContactRepo.setNotificationStatus(messageInfo.getId(), status);
}
bean.updateStatus(status);
}
public void resetUnreadCount() {
ALog.d(LIB_TAG, TAG, "resetUnreadCount");
ContactRepo.clearNotificationUnreadCount();
for (ContactVerifyInfoBean verifyInfoBean : verifyBeanList) {
verifyInfoBean.clearUnreadCount();
}
}
public void getUnreadCount(FetchCallback<Integer> callback){
ContactRepo.getNotificationUnreadCount(callback);
}
private void resetMessageStatus(List<SystemMessageInfo> infoList) {
ALog.d(LIB_TAG, TAG, "resetMessageStatus:" + (infoList == null ? "null" : infoList.size()));
if (infoList != null && infoList.size() > 0) {
long lastTime = System.currentTimeMillis() - expireLimit;
for (SystemMessageInfo info : infoList) {
if (info.getInfoStatus() == SystemMessageInfoStatus.Init && info.getTime() < lastTime) {
info.setInfoStatus(SystemMessageInfoStatus.Expired);
}
}
}
}
private List<ContactVerifyInfoBean> mergeSystemMessageList(List<SystemMessageInfo> infoList) {
List<ContactVerifyInfoBean> add = new ArrayList<>();
updateList.clear();
if (infoList != null) {
for (int index = 0; index < infoList.size(); index++) {
boolean hasInsert = false;
for (ContactVerifyInfoBean bean : verifyBeanList) {
if (bean.pushMessageIfSame(infoList.get(index))) {
hasInsert = true;
updateList.add(bean);
break;
}
}
if (!hasInsert) {
ContactVerifyInfoBean infoBean = new ContactVerifyInfoBean(infoList.get(index));
verifyBeanList.add(infoBean);
add.add(infoBean);
}
}
}
return add;
}
@Override
protected void onCleared() {
super.onCleared();
ContactObserverRepo.unregisterNotificationObserver(infoObserver);
}
}

View File

@@ -0,0 +1,179 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.view;
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.common.utils.NetworkUtils;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.UserInfoLayoutBinding;
import com.netease.yunxin.kit.contactkit.ui.model.ContactUserInfoBean;
import com.netease.yunxin.kit.contactkit.ui.utils.ColorUtils;
public class ContactInfoView extends FrameLayout {
private UserInfoLayoutBinding binding;
private IUserCallback userCallback;
public ContactInfoView(@NonNull Context context) {
super(context);
init(null);
}
public ContactInfoView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ContactInfoView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
binding = UserInfoLayoutBinding.inflate(layoutInflater, this, true);
}
public void setUserCallback(IUserCallback userCallback) {
this.userCallback = userCallback;
}
public void setData(ContactUserInfoBean userInfo) {
String name = userInfo.data.getUserInfoName();
String nickName = null;
if (userInfo.friendInfo != null) {
nickName = userInfo.friendInfo.getAlias();
}
Log.i("LAILIAOLIAO","LAILIAOLIAO用户信息"+userInfo.data.getAvatar());
//avatar
binding.avatarView.setData(
userInfo.data.getAvatar(), name, ColorUtils.avatarColor(userInfo.data.getAccount()));
//name
if (TextUtils.isEmpty(nickName)) {
binding.tvName.setText(name);
binding.tvAccount.setText(
String.format(
getContext().getString(R.string.contact_user_info_account),
userInfo.data.getAccount()));
binding.tvCommentName.setVisibility(GONE);
} else {
binding.tvName.setText(nickName);
binding.tvAccount.setText(
String.format(getContext().getString(R.string.contact_user_info_nickname), name));
binding.tvCommentName.setText(
String.format(
getContext().getString(R.string.contact_user_info_account),
userInfo.data.getAccount()));
binding.tvCommentName.setVisibility(VISIBLE);
}
binding.tvBirthday.setText(userInfo.data.getBirthday());
binding.tvPhone.setText(userInfo.data.getMobile());
binding.tvEmail.setText(userInfo.data.getEmail());
binding.tvSignature.setText(userInfo.data.getSignature());
if (userInfo.isFriend) {
binding.tvDelete.setText(getResources().getText(R.string.delete_friend));
} else {
binding.tvDelete.setText(getResources().getText(R.string.add_friend));
}
binding.scBlackList.setOnCheckedChangeListener(null);
binding.scBlackList.setChecked(userInfo.isBlack);
binding.scBlackList.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
if (!NetworkUtils.isConnected()) {
Toast.makeText(getContext(), R.string.contact_network_error_tip, Toast.LENGTH_SHORT).show();
binding.scBlackList.toggle();
return;
}
if (userCallback != null) {
userCallback.addBlackList(isChecked);
}
});
setIsFriend(userInfo.isFriend);
}
public void setCommentClickListener(OnClickListener onClickListener) {
binding.rlyComment.setOnClickListener(onClickListener);
}
public void setDeleteClickListener(OnClickListener clickListener) {
binding.tvDelete.setOnClickListener(clickListener);
}
private void setIsFriend(boolean isFriend) {
if (isFriend) {
binding.llyFriend.setVisibility(VISIBLE);
binding.rlyComment.setVisibility(VISIBLE);
binding.tvChat.setOnClickListener(
v -> {
if (userCallback != null) {
userCallback.goChat();
}
});
binding.tvChat.setText(R.string.chat);
binding.tvDelete.setVisibility(VISIBLE);
} else {
binding.llyFriend.setVisibility(GONE);
binding.rlyComment.setVisibility(GONE);
binding.tvChat.setText(R.string.add_friend);
binding.tvChat.setOnClickListener(
v -> {
if (userCallback != null) {
userCallback.addFriend();
}
});
binding.tvDelete.setVisibility(GONE);
}
}
public void configSCBlackSwitch(int trackRes, int thumbRes) {
binding.scBlackList.setTrackResource(trackRes);
binding.scBlackList.setThumbResource(thumbRes);
}
public void configAvatarCorner(int cornerSize) {
binding.avatarView.setCornerRadius(cornerSize);
}
public void configChatBtnColor(int color) {
binding.tvChat.setTextColor(color);
}
public void configDivideLineColor(int color) {
binding.divideLine1.setBackgroundColor(color);
binding.divideLine2.setBackgroundColor(color);
binding.divideLine3.setBackgroundColor(color);
}
public void configRootBgColor(int color) {
binding.getRoot().setBackgroundColor(color);
}
public void configSignatureMaxLines(int lines) {
binding.tvSignature.setMaxLines(lines);
}
public interface IUserCallback {
void goChat();
void addFriend();
void openMessageNotify(boolean open);
void addBlackList(boolean add);
}
}

View File

@@ -0,0 +1,75 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.netease.yunxin.kit.common.ui.widgets.TitleBarView;
import com.netease.yunxin.kit.contactkit.ui.databinding.ContactViewLayoutBinding;
public class ContactLayout extends LinearLayout {
private ContactViewLayoutBinding viewBinding;
public ContactLayout(@NonNull Context context) {
super(context);
init(null);
}
public ContactLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ContactLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
public ContactLayout(
@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs);
}
private void init(AttributeSet attrs) {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
viewBinding = ContactViewLayoutBinding.inflate(layoutInflater, this);
}
public TitleBarView getTitleBar() {
return viewBinding.contactTitleLayout;
}
public LinearLayout getTopLayout() {
return viewBinding.contactHeaderLayout;
}
public FrameLayout getBodyLayout() {
return viewBinding.contactBodyLayout;
}
public ContactListView getContactListView() {
return viewBinding.contactListview;
}
public FrameLayout getBottomLayout() {
return viewBinding.contactBottomLayout;
}
public FrameLayout getBodyTopLayout() {
return viewBinding.contactBodyTopLayout;
}
public View getDivideLineForTitle() {
return viewBinding.divideLineTopForTitle;
}
}

View File

@@ -0,0 +1,502 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.view;
import static com.netease.yunxin.kit.contactkit.ui.ContactConstant.LIB_TAG;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.widget.FrameLayout;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.nimlib.sdk.friend.model.MuteListChangedNotify;
import com.netease.nimlib.sdk.team.model.Team;
import com.netease.yunxin.kit.alog.ALog;
import com.netease.yunxin.kit.contactkit.ui.IContactFactory;
import com.netease.yunxin.kit.contactkit.ui.ILoadListener;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.databinding.ContactListViewBinding;
import com.netease.yunxin.kit.contactkit.ui.indexbar.suspension.SuspensionDecoration;
import com.netease.yunxin.kit.contactkit.ui.interfaces.ContactActions;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactDataChanged;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactListView;
import com.netease.yunxin.kit.contactkit.ui.interfaces.IContactViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactTeamBean;
import com.netease.yunxin.kit.contactkit.ui.view.adapter.ContactAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* list view for show contacts info
*/
public class ContactListView extends FrameLayout
implements IContactDataChanged, IContactListView, IContactViewAttrs {
private final String TAG = "ContactListView";
public ContactListViewBinding binding;
private ContactAdapter contactAdapter;
private SuspensionDecoration decoration;
private ILoadListener loadMoreListener;
private ContactListViewAttrs contactListViewAttrs;
private final int LOAD_MORE_DIFF = 5;
public ContactListView(Context context) {
super(context);
init(null);
}
public ContactListView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ContactListView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs);
}
private void init(AttributeSet attrs) {
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
binding = ContactListViewBinding.inflate(layoutInflater, this, true);
if (contactListViewAttrs == null) {
contactListViewAttrs = new ContactListViewAttrs();
}
contactListViewAttrs.parseAttrs(getContext(), attrs);
initRecyclerView();
}
private void initRecyclerView() {
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
contactAdapter = new ContactAdapter();
binding.contactList.setLayoutManager(layoutManager);
decoration = new SuspensionDecoration(getContext(), new ArrayList<>());
if (contactListViewAttrs.getShowPinTop()) {
binding.contactList.addItemDecoration(
decoration.setPaddingLeft(getContext().getResources().getDimension(R.dimen.dimen_20_dp)));
}
binding.contactList.setAdapter(contactAdapter);
binding.indexBar.setLayoutManager(layoutManager);
binding.contactList.addOnScrollListener(
new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int position = layoutManager.findLastVisibleItemPosition();
if (loadMoreListener != null
&& loadMoreListener.hasMore()
&& contactAdapter.getItemCount() < position + LOAD_MORE_DIFF
&& contactAdapter.getItemCount() > 0) {
BaseContactBean last =
contactAdapter.getDataList().get(contactAdapter.getItemCount() - 1);
loadMoreListener.loadMore(last);
}
}
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
}
});
loadAttar();
}
@Override
public void setViewHolderFactory(IContactFactory viewHolderFactory) {
if (contactAdapter != null) {
contactAdapter.setViewHolderFactory(viewHolderFactory);
}
}
@Override
public void setViewConfig(ContactListViewAttrs attrs) {
contactListViewAttrs = attrs;
loadAttar();
}
private void loadAttar() {
if (contactListViewAttrs.getShowIndexBar() != null) {
binding.indexBar.setVisibility(contactListViewAttrs.getShowIndexBar() ? VISIBLE : GONE);
}
if (contactAdapter != null) {
contactAdapter.setContactListViewAttrs(contactListViewAttrs);
}
if (contactListViewAttrs.getIndexTextSize() != ContactListViewAttrs.INT_NULL) {
decoration.setTitleFontSize(contactListViewAttrs.getIndexTextSize());
}
if (contactListViewAttrs.getIndexTextColor() != ContactListViewAttrs.INT_NULL) {
decoration.setColorTitleFont(contactListViewAttrs.getIndexTextColor());
}
if (contactListViewAttrs.getDivideLineColor() != ContactListViewAttrs.INT_NULL) {
decoration.setColorTitleBottomLine(contactListViewAttrs.getDivideLineColor());
}
}
@Override
public void setTitleColor(@ColorInt int color) {
if (contactAdapter != null) {
contactAdapter.getContactListViewAttrs().setNameTextColor(color);
}
}
@Override
public void showIndexBar(boolean show) {
if (show) {
binding.indexBar.setVisibility(VISIBLE);
} else {
binding.indexBar.setVisibility(GONE);
}
}
//是否有可选框
@Override
public void showSelector(boolean show) {
if (contactAdapter != null) {
contactAdapter.getContactListViewAttrs().setShowSelector(show);
}
}
@Override
public void setContactAction(ContactActions contactActions) {
if (contactAdapter != null) {
contactAdapter.setDefaultActions(contactActions);
}
}
public void setLoadMoreListener(ILoadListener listener) {
this.loadMoreListener = listener;
}
@Override
public ContactAdapter getAdapter() {
return contactAdapter;
}
@Override
public void onFriendDataSourceChanged(List<ContactFriendBean> contactItemBeanList) {
if (contactAdapter != null) {
binding
.indexBar
.setSourceDataAlreadySorted(false)
.setSourceData(contactItemBeanList)
.invalidate();
contactAdapter.updateFriendData(contactItemBeanList);
decoration.setData(contactAdapter.getDataList());
}
}
public void scrollToPosition(int index) {
binding.contactList.scrollToPosition(index);
}
@Override
public void addFriendData(List<ContactFriendBean> friend) {
if (contactAdapter != null && friend != null) {
List<ContactFriendBean> friendList = contactAdapter.getFriendList();
Map<String, ContactFriendBean> friendMap = new HashMap<>();
if (friendList != null) {
for (ContactFriendBean friendBean : friendList) {
if (friendBean != null && friendBean.data != null) {
friendMap.put(friendBean.data.getAccount(), friendBean);
}
}
}
for (ContactFriendBean friendBean : friend) {
if (friendBean != null && friendBean.data != null) {
if (friendMap.containsKey(friendBean.data.getAccount())) {
contactAdapter.getFriendList().remove(friendMap.get(friendBean.data.getAccount()));
}
contactAdapter.getFriendList().add(friendBean);
}
}
binding
.indexBar
.setSourceDataAlreadySorted(false)
.setSourceData(contactAdapter.getFriendList())
.invalidate();
contactAdapter.updateFriendData();
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void insertFrindData(int index, ContactFriendBean friend) {
if (contactAdapter != null && friend != null) {
List<ContactFriendBean> friendList = contactAdapter.getFriendList();
Map<String, ContactFriendBean> friendMap = new HashMap<>();
if (friendList != null) {
for (ContactFriendBean friendBean : friendList) {
if (friendBean != null && friendBean.data != null) {
friendMap.put(friendBean.data.getAccount(), friendBean);
}
}
}
if (friend != null && friend.data != null) {
if (friendMap.containsKey(friend.data.getAccount())) {
contactAdapter.getFriendList().remove(friendMap.get(friend.data.getAccount()));
}
contactAdapter.getFriendList().add(index, friend);
}
// binding
// .indexBar
// .setSourceDataAlreadySorted(false)
// .setSourceData(contactAdapter.getFriendList())
// .invalidate();
contactAdapter.updateFriendData();
// decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void updateGroupData(ContactTeamBean teamBeans) {
if (contactAdapter != null) {
contactAdapter.updateGroupData(teamBeans);
// if (teamBeans.isNeedToPinyin()) {
// binding.indexBar.setSourceData(contactAdapter.getDataList()).invalidate();
// }
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void removeFriendData(List<ContactFriendBean> friend) {
if (contactAdapter != null) {
contactAdapter.getFriendList().removeAll(friend);
binding
.indexBar
.setSourceDataAlreadySorted(false)
.setSourceData(contactAdapter.getFriendList())
.invalidate();
contactAdapter.updateFriendData();
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void updateFriendData(List<ContactFriendBean> friends) {
if (contactAdapter != null) {
boolean result = contactAdapter.getFriendList().removeAll(friends);
ALog.d(LIB_TAG, TAG, "updateFriendData, contactAdapter:" + result);
contactAdapter.getFriendList().addAll(friends);
binding
.indexBar
.setSourceDataAlreadySorted(false)
.setSourceData(contactAdapter.getFriendList())
.invalidate();
contactAdapter.updateFriendData();
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void updateContactData(BaseContactBean data) {
if (contactAdapter != null) {
contactAdapter.updateData(data);
if (data.isNeedToPinyin()) {
binding.indexBar.setSourceData(contactAdapter.getDataList()).invalidate();
}
decoration.setData(contactAdapter.getDataList());
}
}
public void updateContactDataAndSort(BaseContactBean data) {
if (contactAdapter != null) {
contactAdapter.updateDataAndSort(data);
if (data.isNeedToPinyin()) {
binding.indexBar.setSourceData(contactAdapter.getDataList()).invalidate();
}
decoration.setData(contactAdapter.getDataList());
}
}
//conversation
// public void setConverSationData(List<ConversationBean> data) {
// if (contactAdapter != null) {
// adapter.setData(data);
// }
// }
//
// public void addConverSationData(List<ConversationBean> data) {
// if (adapter != null) {
// adapter.appendData(data);
// }
// }
//
// public void updateConverSation(List<ConversationBean> data) {
// if (adapter != null) {
// ALog.d(LIB_TAG, TAG, "update ConversationBean list, start");
// adapter.update(data);
// ALog.d(LIB_TAG, TAG, "update ConversationBean list, end");
// }
// }
//
// public void updateConverSation(ConversationBean data) {
// if (adapter != null) {
// ALog.d(LIB_TAG, TAG, "update ConversationBean, start");
// adapter.update(data);
// ALog.d(LIB_TAG, TAG, "update ConversationBean, end");
// }
// }
//
// public void updateUserInfo(List<UserInfo> data) {
// if (adapter != null) {
// adapter.updateUserInfo(data);
// }
// }
//
// public void updateFriendInfo(List<FriendInfo> data) {
// if (contactAdapter != null) {
// contactAdapter.updateFriendInfo(data);
// }
// }
//
// public void updateTeamInfo(List<Team> data) {
// if (contactAdapter != null) {
// adapter.updateTeamInfo(data);
// }
// }
//
// public void updateMuteInfo(MuteListChangedNotify changedNotify) {
// if (contactAdapter != null) {
// adapter.updateMuteInfo(changedNotify);
// }
// }
//
// public void remove(List<ConversationBean> data) {
// if (contactAdapter != null) {
// adapter.removeData(data);
// }
// }
//
// public void removeAll() {
// if (contactAdapter != null) {
// adapter.removeAll();
// }
// }
//
// public void removeConversation(String id) {
// if (contactAdapter != null) {
// adapter.removeData(id);
// }
// }
//
// public void updateAit(List<String> idList) {
// if (contactAdapter != null) {
// adapter.updateAit(idList);
// }
// }
//
// public void addStickTop(String id) {
// if (contactAdapter != null) {
// adapter.addStickTop(id);
// }
// }
//
// public void removeStickTop(String id) {
// if (contactAdapter != null) {
// adapter.removeStickTop(id);
// }
// }
public int getItemCount() {
if (contactAdapter != null) {
return contactAdapter.getItemCount();
}
return 0;
}
@Override
public void updateContactData(int viewType, List<? extends BaseContactBean> data) {
if (contactAdapter != null) {
contactAdapter.updateData(viewType, data);
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void addContactData(BaseContactBean contactData) {
if (contactAdapter != null) {
contactAdapter.addData(contactData);
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void addContactData(List<? extends BaseContactBean> contactData) {
if (contactAdapter != null) {
contactAdapter.addListData(contactData);
decoration.setData(contactAdapter.getDataList());
}
}
public void addForwardContactData(List<? extends BaseContactBean> contactData) {
if (contactAdapter != null) {
contactAdapter.addForwardListData(contactData);
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void removeContactData(BaseContactBean contactData) {
if (contactAdapter != null) {
contactAdapter.removeData(contactData);
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void removeContactData(List<? extends BaseContactBean> contactData) {
if (contactAdapter != null) {
contactAdapter.removeListData(contactData);
decoration.setData(contactAdapter.getDataList());
}
}
@Override
public void clearContactData() {
if (contactAdapter != null) {
contactAdapter.clearData();
}
}
public void setEmptyViewVisible(int visible, String text) {
binding.contactEmptyView.setVisibility(visible);
binding.contactEmptyTv.setText(text);
}
public void configEmptyViewRes(int res) {
binding.emptyStateIv.setImageResource(res);
}
public void configIndexTextBGColor(int color) {
binding.indexBar.configIndexTextBGColor(color);
}
public SuspensionDecoration getDecoration() {
return decoration;
}
}

View File

@@ -0,0 +1,176 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import androidx.annotation.ColorInt;
import com.netease.yunxin.kit.contactkit.ui.R;
/**
* attrs for {@link ContactListView} all attrs should be object
*/
public class ContactListViewAttrs {
public static final int INT_NULL = -1;
private int nameTextColor = INT_NULL;
private int nameTextSize = INT_NULL;
private int indexTextColor = INT_NULL;
private int indexTextSize = INT_NULL;
private Boolean showIndexBar;
private Boolean showSelector;
private Boolean showPinTop;
private float avatarCornerRadius = INT_NULL;
private int divideLineColor = INT_NULL;
public ContactListViewAttrs() {
}
public void parseAttrs(Context context, AttributeSet attrs) {
if (attrs == null) {
return;
}
TypedArray t;
t = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ContactListView, 0, 0);
nameTextColor = t.getColor(R.styleable.ContactListView_nameTextColor, INT_NULL);
nameTextSize = t.getDimensionPixelSize(R.styleable.ContactListView_nameTextSize, INT_NULL);
indexTextColor = t.getColor(R.styleable.ContactListView_indexTextColor, INT_NULL);
indexTextSize = t.getDimensionPixelSize(R.styleable.ContactListView_indexTextSize, INT_NULL);
showIndexBar = t.getBoolean(R.styleable.ContactListView_showIndexBar, true);
showPinTop = t.getBoolean(R.styleable.ContactListView_showTopPin, false);
showSelector = t.getBoolean(R.styleable.ContactListView_showSelector, false);
avatarCornerRadius =
t.getDimensionPixelSize(R.styleable.ContactListView_avatarCornerRadius, INT_NULL);
divideLineColor = t.getColor(R.styleable.ContactListView_divideLineColor, INT_NULL);
t.recycle();
}
public ContactListViewAttrs setAll(ContactListViewAttrs other) {
if (other.nameTextColor != INT_NULL) {
this.nameTextColor = other.nameTextColor;
}
if (other.nameTextSize != INT_NULL) {
this.nameTextSize = other.nameTextSize;
}
if (other.indexTextColor != INT_NULL) {
this.indexTextColor = other.indexTextColor;
}
if (other.indexTextSize != INT_NULL) {
this.indexTextSize = other.indexTextSize;
}
if (other.avatarCornerRadius != INT_NULL) {
this.avatarCornerRadius = other.avatarCornerRadius;
}
if (other.showIndexBar != null) {
this.showIndexBar = other.showIndexBar;
}
if (other.showSelector != null) {
this.showSelector = other.showSelector;
}
if (other.showPinTop != null) {
this.showPinTop = other.showPinTop;
}
if (other.divideLineColor != INT_NULL) {
this.divideLineColor = other.divideLineColor;
}
return this;
}
public void setNameTextColor(@ColorInt int nameTextColor) {
this.nameTextColor = nameTextColor;
}
public int getNameTextColor() {
return nameTextColor;
}
public void setNameTextSize(int textSize) {
this.nameTextSize = textSize;
}
public int getNameTextSize() {
return nameTextSize;
}
public void setIndexTextSize(int textSize) {
this.indexTextSize = textSize;
}
public int getIndexTextSize() {
return indexTextSize;
}
public void setIndexTextColor(@ColorInt int color) {
this.indexTextColor = color;
}
public int getIndexTextColor() {
return indexTextColor;
}
public Boolean getShowIndexBar() {
return showIndexBar;
}
public Boolean getShowPinTop() {
return showPinTop;
}
public void setShowPinTop(Boolean showPinTop) {
this.showPinTop = showPinTop;
}
public void setShowIndexBar(Boolean showIndexBar) {
this.showIndexBar = showIndexBar;
}
public Boolean getShowSelector() {
return showSelector;
}
public void setShowSelector(Boolean showSelector) {
this.showSelector = showSelector;
}
public void setAvatarCornerRadius(float radius) {
this.avatarCornerRadius = radius;
}
public float getAvatarCornerRadius() {
return this.avatarCornerRadius;
}
public void setDivideLineColor(@ColorInt int color) {
this.divideLineColor = color;
}
public int getDivideLineColor() {
return divideLineColor;
}
}

View File

@@ -0,0 +1,319 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.view.adapter;
import android.annotation.SuppressLint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.yunxin.kit.chatkit.model.ConversationInfo;
import com.netease.yunxin.kit.contactkit.ui.IContactFactory;
import com.netease.yunxin.kit.contactkit.ui.R;
import com.netease.yunxin.kit.contactkit.ui.interfaces.ContactActions;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactFriendBean;
import com.netease.yunxin.kit.contactkit.ui.model.ContactTeamBean;
import com.netease.yunxin.kit.contactkit.ui.model.IViewTypeConstant;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
import com.netease.yunxin.kit.contactkit.ui.view.viewholder.BaseContactViewHolder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class ContactAdapter extends RecyclerView.Adapter<BaseContactViewHolder> {
private final List<BaseContactBean> dataList;
private final List<ContactFriendBean> friendList;
private final List<ContactTeamBean> teamList;
private List<ConversationInfo> conversationBeanFriendsList;
private ContactActions defaultActions;
public ContactAdapter() {
dataList = new ArrayList<>();
friendList = new LinkedList<>();
teamList = new LinkedList<>();
conversationBeanFriendsList = new ArrayList<>();
contactListViewAttrs = new ContactListViewAttrs();
}
private IContactFactory viewHolderFactory;
private final ContactListViewAttrs contactListViewAttrs;
private boolean isAll = true; //默认全部展开
public void setAll(boolean all) {
isAll = all;
}
@NonNull
@Override
public BaseContactViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view =
LayoutInflater.from(parent.getContext())
.inflate(R.layout.contact_list_item_container_layout, parent, false);
BaseContactViewHolder viewHolder =
viewHolderFactory.createViewHolder((ViewGroup) view, viewType);
viewHolder.setActions(defaultActions);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull BaseContactViewHolder holder, int position) {
BaseContactBean bean = dataList.get(position);
holder.onBind(bean, position, contactListViewAttrs);
}
// 通讯录调用,添加,更新,删除 好友列表
@SuppressLint("NotifyDataSetChanged")
public void updateFriendData(List<ContactFriendBean> list) {
if (list == null || list.isEmpty()) {
return;
}
// 以前调用 removeAll 操作,数量级为 mn
// 由于此处目的为了移除当前所有的好友数据,直接过滤源单列表,数量级缩减为 m
Iterator<BaseContactBean> iterator = dataList.iterator();
while (iterator.hasNext()) {
BaseContactBean item = iterator.next();
if (item.viewType == IViewTypeConstant.CONTACT_FRIEND) {
iterator.remove();
}
}
friendList.clear();
friendList.addAll(list);
dataList.addAll(list);
notifyDataSetChanged();
}
// 通讯录调用,添加,删除,更新 好友
@SuppressLint("NotifyDataSetChanged")
public void updateFriendData() {
int indexStart = 0;
if (!dataList.isEmpty()) {
while (dataList.size() > indexStart
&& dataList.get(indexStart) != null
&& !(dataList.get(indexStart) instanceof ContactFriendBean)) {
indexStart++;
}
while (indexStart < dataList.size()) {
dataList.remove(indexStart);
}
}
dataList.addAll(friendList);
notifyDataSetChanged();
}
// 黑名单调用
public void removeData(BaseContactBean data) {
if (data == null) {
return;
}
int index = dataList.indexOf(data);
if (index >= 0) {
dataList.remove(data);
notifyItemRemoved(index);
}
}
// 黑名单和验证消息列表调用
@SuppressLint("NotifyDataSetChanged")
public void removeListData(List<? extends BaseContactBean> listData) {
if (listData == null || listData.isEmpty()) {
return;
}
if (dataList.removeAll(listData)) {
notifyDataSetChanged();
}
}
// 群列表调用
@SuppressLint("NotifyDataSetChanged")
public void clearData() {
if (!dataList.isEmpty()) {
dataList.clear();
notifyDataSetChanged();
}
}
// 暂未调用
public void clearFriendData() {
if (!friendList.isEmpty()) {
friendList.clear();
}
}
// 通讯录调用,用于添加头部
public void addData(BaseContactBean data) {
if (data == null) {
return;
}
if (!dataList.isEmpty() && data.weight > 0) {
int index = 0;
while (dataList.size() > index
&& dataList.get(index) != null
&& dataList.get(index).weight >= data.weight) {
index++;
}
dataList.add(index, data);
notifyItemInserted(index);
} else {
dataList.add(data);
notifyItemInserted(dataList.size() - 1);
}
}
// 验证,好友选择,通讯录调用
public void updateData(BaseContactBean data) {
int index = dataList.indexOf(data);
if (index >= 0) {
dataList.set(index, data);
notifyItemChanged(index);
}
}
// 更新群聊
@SuppressLint("NotifyDataSetChanged")
public void updateGroupData(ContactTeamBean itembean) {
if (itembean == null) {
return;
}
for (BaseContactBean baseContactBean : dataList) {
if (baseContactBean instanceof ContactTeamBean) {
if (((ContactTeamBean) baseContactBean).data.getId().equals(itembean.data.getId())) {
dataList.remove(baseContactBean);
break;
}
}
}
dataList.add(0, itembean);
notifyDataSetChanged();
}
// 验证,好友选择,通讯录调用
public void updateDataAndSort(BaseContactBean data) {
int index = dataList.indexOf(data);
if (index >= 0) {
dataList.remove(index);
notifyItemRemoved(index);
dataList.add(0, data);
notifyItemInserted(0);
}
}
// 暂未调用
public void updateData(int viewType, List<? extends BaseContactBean> list) {
if (list == null || list.isEmpty()) {
return;
}
int indexStart = 0;
int indexEnd;
if (!dataList.isEmpty()) {
while (dataList.size() > indexStart
&& dataList.get(indexStart) != null
&& dataList.get(indexStart).viewType != viewType) {
indexStart++;
}
indexEnd = indexStart;
while (dataList.size() > indexEnd
&& dataList.get(indexEnd) != null
&& dataList.get(indexEnd).viewType == viewType) {
indexEnd++;
}
if (indexEnd < dataList.size()) {
List<BaseContactBean> temp = dataList.subList(indexStart, indexEnd);
dataList.removeAll(temp);
}
}
dataList.addAll(list);
indexEnd = indexStart + list.size();
notifyItemRangeChanged(indexStart, indexEnd);
}
// 用户配置,通讯录添加列表头部信息
public void addListData(List<? extends BaseContactBean> listData) {
if (listData == null || listData.isEmpty()) {
return;
}
if (listData.get(0).weight > 0) {
int index = 0;
while (dataList.size() > index
&& dataList.get(index) != null
&& dataList.get(index).weight >= listData.get(0).weight) {
index++;
}
dataList.addAll(index, listData);
notifyItemRangeInserted(index, listData.size());
} else {
dataList.addAll(listData);
notifyItemRangeInserted(dataList.size() - 1, listData.size());
}
}
// 验证消息调用
public void addForwardListData(List<? extends BaseContactBean> listData) {
if (listData == null || listData.isEmpty()) {
return;
}
dataList.addAll(0, listData);
notifyItemRangeInserted(0, listData.size());
}
@Override
public int getItemCount() {
// if (isAll) {
return dataList.size();
// } else {
// return Math.min(dataList.size(), 3);
// }
// return dataList.size();
}
@Override
public int getItemViewType(int position) {
if (dataList.get(position) != null) {
return viewHolderFactory.getItemViewType(dataList.get(position));
}
return super.getItemViewType(position);
}
public ContactListViewAttrs getContactListViewAttrs() {
return contactListViewAttrs;
}
public void setViewHolderFactory(IContactFactory factory) {
viewHolderFactory = factory;
}
public void setDefaultActions(ContactActions actions) {
defaultActions = actions;
}
public void setContactListViewAttrs(ContactListViewAttrs attrs) {
contactListViewAttrs.setAll(attrs);
}
public List<BaseContactBean> getDataList() {
return dataList;
}
public List<ContactFriendBean> getFriendList() {
return friendList;
}
}

View File

@@ -0,0 +1,36 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui.view.viewholder;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.netease.yunxin.kit.contactkit.ui.interfaces.ContactActions;
import com.netease.yunxin.kit.contactkit.ui.model.BaseContactBean;
import com.netease.yunxin.kit.contactkit.ui.view.ContactListViewAttrs;
public abstract class BaseContactViewHolder extends RecyclerView.ViewHolder {
protected ContactActions actions;
protected Context context;
public BaseContactViewHolder(@NonNull ViewGroup itemView) {
super(itemView);
context = itemView.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(context);
initViewBinding(layoutInflater, itemView);
}
public abstract void initViewBinding(LayoutInflater layoutInflater, ViewGroup container);
public void setActions(ContactActions actions) {
this.actions = actions;
}
public abstract void onBind(BaseContactBean bean, int position, ContactListViewAttrs attrs);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="6dp" />
<solid android:color="@color/color_f4f4f4" />
</shape>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="@color/fun_contact_search_edit_bg_color"/>
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="@dimen/dimen_12_dp"
android:topRightRadius="@dimen/dimen_12_dp" />
<solid android:color="@color/color_white" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_6_dp" />
<solid android:color="@color/color_58be6b" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_4_dp" />
<solid android:color="@color/color_white" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_12_dp" />
<solid android:color="@color/color_fe6881" />
</shape>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_6_dp" />
<solid android:color="@color/color_cccccc" />
</shape>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/fun_bottom_dialog_bg"
android:insetTop="88dp">
</inset>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="@drawable/fun_bottom_dialog_bg"
android:insetTop="290dp">
</inset>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_20_dp" />
<solid
android:color="@color/color_6877fe" />
</shape>

View File

@@ -0,0 +1,32 @@
<!-- Copyright (c) 2022 NetEase, Inc. All rights reserved. -->
<!-- Use of this source code is governed by a MIT license that can be -->
<!-- found in the LICENSE file. -->
<!--<vector xmlns:android="http://schemas.android.com/apk/res/android"-->
<!-- android:width="@dimen/dimen_20_dp"-->
<!-- android:height="@dimen/dimen_20_dp"-->
<!-- android:viewportWidth="20"-->
<!-- android:viewportHeight="20">-->
<!-- <path-->
<!-- android:strokeWidth="1"-->
<!-- android:pathData="M9,9m-8.5,0a8.5,8.5 0,1 1,17 0a8.5,8.5 0,1 1,-17 0"-->
<!-- android:fillColor="@android:color/white"-->
<!-- android:strokeColor="#d8d8d8" />-->
<!--</vector>-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_3_dp" />
<size
android:width="@dimen/dimen_20_dp"
android:height="@dimen/dimen_20_dp" />
<solid android:color="@android:color/white" />
<stroke
android:width="@dimen/dimen_1_dp"
android:color="@color/color_d8d8d8"/>
</shape>

View File

@@ -0,0 +1,16 @@
<!-- Copyright (c) 2022 NetEase, Inc. All rights reserved. -->
<!-- Use of this source code is governed by a MIT license that can be -->
<!-- found in the LICENSE file. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M9,9m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0"
android:fillColor="#58BE6B" />
<path
android:pathData="M5.1508,8.7407C4.7315,8.3813 4.1002,8.4299 3.7407,8.8492C3.3813,9.2685 3.4299,9.8998 3.8492,10.2593L5.1508,8.7407ZM8,12.5L7.3492,13.2593C7.7587,13.6102 8.3727,13.5733 8.7372,13.1757L8,12.5ZM14.2372,7.1757C14.6103,6.7686 14.5828,6.136 14.1757,5.7628C13.7686,5.3896 13.136,5.4172 12.7628,5.8243L14.2372,7.1757ZM3.8492,10.2593L7.3492,13.2593L8.6508,11.7407L5.1508,8.7407L3.8492,10.2593ZM8.7372,13.1757L14.2372,7.1757L12.7628,5.8243L7.2628,11.8243L8.7372,13.1757Z"
android:fillColor="#ffffff" />
</vector>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (c) 2022 NetEase, Inc. All rights reserved. -->
<!-- Use of this source code is governed by a MIT license that can be -->
<!-- found in the LICENSE file. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/fun_ic_selected_conversation" android:state_checked="true" />
<item android:drawable="@drawable/fun_ic_unselector_conversation" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<corners android:radius="6dp"/>
<solid android:color="#f4f4f4"/>
</shape>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="@dimen/dimen_20_dp"
android:height="@dimen/dimen_20_dp" />
<solid android:color="@color/color_white" />
<stroke android:width="1dp" android:color="@color/color_dbdde4"/>
</shape>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="@dimen/dimen_20_dp"
android:height="@dimen/dimen_20_dp" />
<solid android:color="@color/color_white" />
<stroke android:width="1dp" android:color="@color/color_58be6b"/>
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/fun_switch_thumb_on" android:state_checked="true" />
<item android:drawable="@drawable/fun_switch_thumb_off" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_10_dp"/>
<size
android:width="@dimen/dimen_60_dp"
android:height="@dimen/dimen_20_dp" />
<solid android:color="@color/color_dbdde4" />
</shape>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_10_dp" />
<size
android:width="@dimen/dimen_60_dp"
android:height="@dimen/dimen_20_dp" />
<solid android:color="@color/color_58be6b" />
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/fun_switch_track_on" android:state_checked="true" />
<item android:drawable="@drawable/fun_switch_track_off" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_30_dp" />
<solid
android:color="@color/color_6d80ff_alpha70" />
</shape>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_30_dp" />
<solid
android:color="@color/color_6d80ff" />
</shape>

View File

@@ -0,0 +1,289 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<LinearLayout
android:id="@+id/layout_add_friend_title"
android:layout_width="match_parent"
android:layout_height="@dimen/page_title_bar_height"
android:orientation="horizontal"
tools:ignore="MissingConstraints">
<ImageView
android:id="@+id/iv_add_friend_back"
android:layout_width="@dimen/image_operate_width_height"
android:layout_height="@dimen/image_operate_width_height"
android:layout_gravity="center"
android:layout_marginLeft="@dimen/page_margin_left_right"
android:src="@mipmap/ic_back" />
<TextView
android:id="@+id/tv_add_friend_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/page_margin_left_right"
android:layout_marginRight="@dimen/page_margin_left_right"
android:layout_weight="1"
android:gravity="center_horizontal"
android:text="@string/search_friend_title"
android:textColor="@android:color/black"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_add_friend_more"
android:layout_width="@dimen/image_operate_width_height"
android:layout_height="@dimen/image_operate_width_height"
android:layout_gravity="center"
android:layout_marginRight="20dp" />
</LinearLayout>
<View
android:id="@+id/line_searchfriend_top"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_f7f7f7"
app:layout_constraintTop_toBottomOf="@+id/layout_add_friend_title" />
<TextView
android:id="@+id/tv_searchfriend_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:drawableStart="@drawable/ic_selected_search"
android:drawablePadding="@dimen/dimen_10_dp"
android:paddingTop="@dimen/dimen_30_dp"
android:text="@string/lailiaozhanghao_txt"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/line_searchfriend_top" />
<TextView
android:id="@+id/tv_searchfriend_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_60_dp"
android:drawableStart="@drawable/ic_unselected_search"
android:drawablePadding="@dimen/dimen_10_dp"
android:paddingTop="@dimen/dimen_30_dp"
android:text="@string/addfriends_phone"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
app:layout_constraintLeft_toRightOf="@+id/tv_searchfriend_id"
app:layout_constraintTop_toBottomOf="@+id/line_searchfriend_top" />
<!--Demo 布局-->
<LinearLayout
android:id="@+id/layout_add_friend_input"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_40_dp"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_30_dp"
android:layout_marginRight="@dimen/dimen_15_dp"
android:background="@drawable/fun_selected_user_bg"
android:gravity="center"
android:orientation="horizontal"
android:visibility="visible"
app:layout_constraintTop_toBottomOf="@id/tv_searchfriend_id"
tools:ignore="MissingConstraints">
<EditText
android:id="@+id/et_add_friend_account"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_weight="1"
android:background="@null"
android:hint="@string/add_friend_searchid_hint"
android:imeOptions="actionSearch"
android:singleLine="true"
android:textColorHint="@color/color_d4dbd6"
android:textCursorDrawable="@null"
android:textSize="@dimen/text_size_14" />
<ImageView
android:id="@+id/iv_friendaccount_clear"
android:layout_width="@dimen/dimen_12_dp"
android:layout_height="@dimen/dimen_12_dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/dimen_12_dp"
android:layout_marginRight="@dimen/dimen_12_dp"
android:src="@drawable/ic_clear"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_friendaccount_search"
android:layout_width="@dimen/dimen_12_dp"
android:layout_height="@dimen/dimen_12_dp"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/dimen_12_dp"
android:layout_marginRight="@dimen/dimen_15_dp"
android:src="@mipmap/ic_search" />
</LinearLayout>
<!--手机号输入布局-->
<RelativeLayout
android:id="@+id/ly_searchfriend_inputphone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_30_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@+id/tv_searchfriend_id">
<TextView
android:id="@+id/area_tv"
android:layout_width="wrap_content"
android:layout_height="@dimen/dimen_50_dp"
android:gravity="center_vertical"
android:paddingEnd="@dimen/dimen_10_dp"
android:text=" +886"
android:textColor="@color/color_333333"
android:textSize="18sp"
android:visibility="gone" />
<EditText
android:id="@+id/phone_et"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_50_dp"
android:layout_toRightOf="@+id/area_tv"
android:background="@null"
android:hint="@string/add_friend_searchphone_hint"
android:imeOptions="actionSearch"
android:inputType="phone"
android:paddingLeft="@dimen/dimen_20_dp"
android:singleLine="true"
android:textColor="@color/color_333333"
android:textColorHint="@color/color_bcbcbc"
android:textCursorDrawable="@null"
android:textSize="@dimen/text_size_18" />
<ImageView
android:id="@+id/iv_friend_clear"
android:layout_width="@dimen/dimen_12_dp"
android:layout_height="@dimen/dimen_12_dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/dimen_30_dp"
android:src="@drawable/ic_clear"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_friendphone_search"
android:layout_width="@dimen/dimen_16_dp"
android:layout_height="@dimen/dimen_16_dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
app:srcCompat="@mipmap/ic_search_edit" />
<View
android:id="@+id/line_searchfriend_phone"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_below="@+id/phone_et"
android:background="@color/color_d8d8d8" />
</RelativeLayout>
<LinearLayout
android:id="@+id/layout_userinfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_163_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_searchfriend_phone">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/av_indexfra_head"
android:layout_width="@dimen/dimen_64_dp"
android:layout_height="@dimen/dimen_64_dp"
app:avatarCorner="@dimen/dimen_64_dp" />
<TextView
android:id="@+id/tv_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_18"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/titleBarView" />
<TextView
android:id="@+id/tv_usertips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_5_dp"
android:textColor="@color/color_7e7b84"
android:textSize="@dimen/text_size_14"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/tv_userinfohint"
android:layout_width="@dimen/dimen_58_dp"
android:layout_height="@dimen/dimen_32_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:background="@drawable/cornor_6d94ff_20dp"
android:gravity="center"
android:text="@string/add_friend_search_result"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
<LinearLayout
android:id="@+id/add_friend_empty_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_weight="1"
tools:ignore="MissingConstraints">
<ImageView
android:id="@+id/empty_state_iv"
android:layout_width="@dimen/dimen_188_dp"
android:layout_height="@dimen/verify_name_max_len"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dimen_70_dp"
android:layout_marginBottom="@dimen/dimen_16_dp"
android:src="@drawable/fun_ic_contact_empty" />
<TextView
android:id="@+id/add_friend_empty_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/add_friend_search_empty_tips"
android:textColor="@color/color_b3b7bc" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_40_dp"
android:layout_height="@dimen/dimen_40_dp"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginBottom="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:textSize="@dimen/text_size_17"
android:textColor="@color/color_14131b"
android:maxLines="1"
android:ellipsize="end"
app:layout_constrainedWidth="true"
android:layout_marginEnd="@dimen/dimen_100_dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/avatar_view" />
<TextView
android:id="@+id/tv_relieve"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/title_color"
android:textSize="@dimen/text_size_14"
android:paddingHorizontal="@dimen/dimen_16_dp"
android:paddingVertical="@dimen/dimen_8_dp"
android:background="@drawable/negative_btn_bg"
android:layout_marginEnd="@dimen/dimen_20_dp"
android:text="@string/contact_black_list_relieve"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/rb_selector"
style="@style/FunSelectorRadioBtn"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:clickable="false"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:visibility="visible" />
<View
android:layout_width="0dp"
android:layout_height="@dimen/dimen_1_dp"
android:visibility="gone"
android:background="@color/color_e5e5e5"
app:layout_constraintStart_toStartOf="@+id/avatar_view"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#4D000000">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/fun_bottom_dialog_bg"
app:layout_constraintBottom_toBottomOf="parent">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/cavUserIcon"
android:layout_width="@dimen/dimen_45_dp"
android:layout_height="@dimen/dimen_45_dp"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
app:avatarCorner="@dimen/dimen_45_dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvUserName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/cavUserIcon"
app:layout_constraintLeft_toRightOf="@+id/cavUserIcon"
app:layout_constraintTop_toTopOf="@+id/cavUserIcon" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:background="@drawable/ic_dialog_close"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/cavUserIcon" />
<View
android:id="@+id/view_closeclick"
android:layout_width="@dimen/dimen_50_dp"
android:layout_height="@dimen/dimen_50_dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/cavUserIcon"
app:layout_constraintLeft_toRightOf="@+id/cavUserIcon"
app:layout_constraintTop_toTopOf="@+id/cavUserIcon" />
<TextView
android:id="@+id/tv_addfriends_verifymsg_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:text="@string/fun_contact_addfriends_verifymsg"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@+id/cavUserIcon"
app:layout_constraintTop_toBottomOf="@+id/cavUserIcon" />
<EditText
android:id="@+id/edit_addfriends_verifymsg"
android:layout_width="0dp"
android:layout_height="@dimen/dimen_92_dp"
android:layout_marginTop="@dimen/dimen_10_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:background="@drawable/fun_bg_addfriends_verifymsg"
android:gravity="top"
android:hint="@string/fun_contact_verifymsg_tips"
android:paddingStart="@dimen/dimen_10_dp"
android:paddingTop="@dimen/dimen_15_dp"
android:textColorHint="@color/color_bcbcbc"
android:textSize="@dimen/text_size_14"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/cavUserIcon"
app:layout_constraintTop_toBottomOf="@+id/tv_addfriends_verifymsg_tips" />
<TextView
android:id="@+id/tv_addfriends_remarks_tips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:text="@string/fun_contact_addfriends_remarks"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:textStyle="bold"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@+id/cavUserIcon"
app:layout_constraintTop_toBottomOf="@+id/edit_addfriends_verifymsg" />
<EditText
android:id="@+id/edit_addfriends_remarkmsg"
android:layout_width="0dp"
android:layout_height="@dimen/dimen_46_dp"
android:layout_marginTop="@dimen/dimen_10_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:background="@drawable/fun_bg_addfriends_verifymsg"
android:gravity="top"
android:hint="@string/fun_contact_remarks_tips"
android:paddingStart="@dimen/dimen_10_dp"
android:paddingTop="@dimen/dimen_15_dp"
android:visibility="gone"
android:textColorHint="@color/color_bcbcbc"
android:textSize="@dimen/text_size_14"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/cavUserIcon"
app:layout_constraintTop_toBottomOf="@+id/tv_addfriends_remarks_tips" />
<TextView
android:id="@+id/tv_addfriends_send"
android:layout_width="0dp"
android:layout_height="@dimen/dimen_40_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:layout_marginBottom="@dimen/dimen_60_dp"
android:background="@drawable/fun_cornor_12d_fe6881"
android:gravity="center"
android:text="@string/fun_contact_addfriends_send"
android:textColor="@android:color/white"
android:textSize="@dimen/text_size_16"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/cavUserIcon"
app:layout_constraintTop_toBottomOf="@+id/edit_addfriends_remarkmsg" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_ededed"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.netease.yunxin.kit.common.ui.widgets.CleanableEditText
android:id="@+id/edt_comment"
android:layout_width="match_parent"
android:maxLength="15"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="MissingDefaultResource">
<FrameLayout
android:id="@+id/contact_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/fun_bottom_dialog_bg"
android:paddingBottom="30dp">
<TextView
android:id="@+id/tvTip"
android:layout_width="0dp"
android:layout_height="51dp"
android:gravity="center"
android:textColor="@color/color_8f8f8f"
android:textSize="13dp"
app:layout_constraintBottom_toTopOf="@+id/tvDelete"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="SpUsage" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/color_ededed"
app:layout_constraintTop_toBottomOf="@id/tvTip" />
<TextView
android:id="@+id/tvDelete"
android:layout_width="0dp"
android:layout_height="51dp"
android:layout_marginBottom="12dp"
android:gravity="center"
android:text="@string/delete_friend"
android:textColor="@color/color_e6605c"
android:textSize="16dp"
app:layout_constraintBottom_toTopOf="@id/tvCancel"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="SpUsage" />
<View
android:layout_width="match_parent"
android:layout_height="6dp"
android:background="@color/color_f7f7f7"
app:layout_constraintTop_toBottomOf="@id/tvDelete" />
<TextView
android:id="@+id/tvCancel"
android:layout_width="0dp"
android:layout_height="42dp"
android:gravity="center"
android:text="@string/cancel"
android:textColor="@color/color_333333"
android:textSize="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="SpUsage" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.netease.yunxin.kit.contactkit.ui.view.ContactLayout
android:id="@+id/contact_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_15_dp"
android:orientation="vertical" />
<LinearLayout
android:id="@+id/empty_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_188_dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="@dimen/dimen_96_dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/fun_ic_contact_empty" />
<TextView
android:id="@+id/empty_tv"
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="@string/selector_friend_empty" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,191 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout_selectorroot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_f7f7f7" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_40_dp"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_10_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:background="@drawable/fun_bg_addfriends_verifymsg"
android:orientation="horizontal">
<EditText
android:id="@+id/edit_selector_friends"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0.9"
android:background="@null"
android:drawableStart="@drawable/fun_ic_conversation_search"
android:drawablePadding="@dimen/dimen_5_dp"
android:hint="@string/fun_contact_createteam_tips"
android:imeOptions="actionSearch"
android:paddingStart="@dimen/dimen_15_dp"
android:singleLine="true"
android:textColorHint="@color/color_cccccc"
android:textCursorDrawable="@null" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ic_clean_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.1"
android:visibility="gone"
app:srcCompat="@drawable/ic_selector_close" />
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_selected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:paddingHorizontal="@dimen/dimen_15_dp" />
<LinearLayout
android:id="@+id/layout_searchlist"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:text="@string/fun_searchresult_txt"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:textStyle="bold" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_searchlistView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
app:showIndexBar="false"
app:showSelector="true" />
</LinearLayout>
<!-- 最近联系 关注 全部-->
<LinearLayout
android:id="@+id/layout_contactlist"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_contactlast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:textColor="@color/color_333333"
android:visibility="gone"
android:textSize="@dimen/text_size_14" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_last"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
app:showIndexBar="false"
android:visibility="gone"
app:showSelector="true" />
<TextView
android:id="@+id/tv_contactspecialgz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:visibility="gone" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_listView_mylove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:visibility="gone"
app:showIndexBar="false"
app:showSelector="true" />
<TextView
android:id="@+id/tv_contactmember"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
app:showIndexBar="false"
app:showSelector="true" />
<LinearLayout
android:id="@+id/empty_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_188_dp"
android:layout_marginTop="@dimen/dimen_150_dp"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="@dimen/dimen_96_dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/fun_ic_contact_empty" />
<TextView
android:id="@+id/empty_tv"
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="@string/selector_friend_empty" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@@ -0,0 +1,232 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layout_selectorroot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:id="@+id/line_top"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_f7f7f7" />
<LinearLayout
android:id="@+id/lly_teamsetting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
android:orientation="horizontal"
android:visibility="gone">
<TextView
android:id="@+id/tv_teamqr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="@dimen/dimen_10_dp"
android:gravity="center"
android:text="@string/fun_contactselector_teamqr"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
app:drawableTopCompat="@drawable/ic_teamqrcode" />
<TextView
android:id="@+id/tv_teamurl"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="@dimen/dimen_10_dp"
android:gravity="center"
android:text="@string/fun_contactselector_teamurl"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
app:drawableTopCompat="@drawable/ic_teamurl" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="invisible" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_40_dp"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:background="@drawable/fun_bg_addfriends_verifymsg"
android:orientation="horizontal">
<EditText
android:id="@+id/edit_selector_friends"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0.9"
android:background="@null"
android:drawableStart="@drawable/fun_ic_conversation_search"
android:drawablePadding="@dimen/dimen_5_dp"
android:hint="@string/fun_contact_createteam_tips"
android:imeOptions="actionSearch"
android:paddingStart="@dimen/dimen_15_dp"
android:singleLine="true"
android:textColorHint="@color/color_cccccc"
android:textCursorDrawable="@null" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ic_clean_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="0.1"
android:visibility="gone"
app:srcCompat="@drawable/ic_selector_close" />
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_selected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:paddingHorizontal="@dimen/dimen_15_dp" />
<LinearLayout
android:id="@+id/layout_searchlist"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:text="查找结果"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:textStyle="bold" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_searchlistView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
app:showIndexBar="false"
app:showSelector="true" />
</LinearLayout>
<LinearLayout
android:id="@+id/layout_contactlist"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_contactlast"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:visibility="gone"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:textStyle="bold" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_last"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
app:showIndexBar="false"
android:visibility="gone"
app:showSelector="true" />
<TextView
android:id="@+id/tv_contactspecialgz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:visibility="gone"
android:textStyle="bold" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_listView_mylove"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
app:showIndexBar="false"
android:visibility="gone"
app:showSelector="true" />
<TextView
android:id="@+id/tv_contactmember"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:textStyle="bold" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
app:showIndexBar="false"
app:showSelector="true" />
<LinearLayout
android:id="@+id/empty_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_188_dp"
android:layout_marginTop="@dimen/dimen_150_dp"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="@dimen/dimen_96_dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/fun_ic_contact_empty" />
<TextView
android:id="@+id/empty_tv"
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="@string/selector_friend_empty" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:rightTitleText="@string/fun_creategroup_sure"
app:titleText="@string/fun_creategroup_title" />
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/ic_grouphead"
android:layout_width="@dimen/dimen_88_dp"
android:layout_height="@dimen/dimen_88_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
app:avatarCorner="@dimen/dimen_88_dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ic_groupheadtop"
android:layout_width="@dimen/dimen_88_dp"
android:layout_height="@dimen/dimen_88_dp"
app:srcCompat="@mipmap/ic_grouphead_setting"
app:layout_constraintBottom_toBottomOf="@+id/ic_grouphead"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dimen_8_dp"
android:background="#23000000"
android:text="@string/fun_creategroup_uploadhead"
android:textColor="@android:color/white"
android:textSize="@dimen/text_size_12"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/ic_grouphead"
app:layout_constraintEnd_toEndOf="@+id/ic_grouphead"
app:layout_constraintStart_toStartOf="@+id/ic_grouphead" />
<TextView
android:id="@+id/tvgroup_tipsname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:text="@string/fun_creategroup_name"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ic_grouphead" />
<LinearLayout
android:id="@+id/llgroupname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/tvgroup_tipsname"
app:layout_constraintTop_toBottomOf="@+id/tvgroup_tipsname">
<EditText
android:id="@+id/edit_groupname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:layout_weight="0.9"
android:background="@null"
android:ellipsize="end"
android:maxLength="20"
android:maxLines="1"
android:paddingLeft="@dimen/dimen_15_dp"
android:textColor="@color/color_333333"
android:textColorHint="@color/color_bcbcbc"
android:textSize="@dimen/text_size_16" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ic_clean_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:layout_marginEnd="@dimen/dimen_10_dp"
android:layout_weight="0.1"
android:paddingTop="@dimen/dimen_20_dp"
android:visibility="gone"
app:srcCompat="@drawable/ic_selector_close" />
</LinearLayout>
<View
android:id="@+id/line_top"
android:layout_width="0dp"
android:layout_height="@dimen/dimen_1_dp"
android:layout_marginTop="@dimen/dimen_15_dp"
android:background="@color/color_d8d8d8"
app:layout_constraintEnd_toEndOf="@+id/llgroupname"
app:layout_constraintStart_toStartOf="@+id/llgroupname"
app:layout_constraintTop_toBottomOf="@+id/llgroupname" />
<TextView
android:id="@+id/tvgroup_autojoin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_28_dp"
android:text="@string/fun_creategroup_autojoin"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/line_top" />
<androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/rb_selector"
style="@style/FunSelectorRadioBtn"
android:layout_width="@dimen/dimen_20_dp"
android:layout_height="@dimen/dimen_20_dp"
android:layout_marginTop="@dimen/dimen_28_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:checked="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/line_top" />
<View
android:id="@+id/rb_clickview"
android:layout_width="@dimen/dimen_60_dp"
android:layout_height="@dimen/dimen_60_dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/line_top" />
<TextView
android:id="@+id/tvgroup_autojoin_tips"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
android:layout_marginEnd="@dimen/dimen_50_dp"
android:text="@string/fun_creategroup_autojoin_tips"
android:textColor="@color/color_7f7b81"
android:textSize="@dimen/text_size_14"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/tvgroup_autojoin"
app:layout_constraintTop_toBottomOf="@+id/tvgroup_autojoin" />
<TextView
android:id="@+id/tvgroup_members"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_30_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvgroup_autojoin_tips" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_members"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:paddingHorizontal="@dimen/dimen_15_dp"
app:layout_constraintTop_toBottomOf="@+id/tvgroup_members" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="36dp"
android:layout_marginHorizontal="@dimen/dimen_8_dp"
android:layout_marginBottom="@dimen/dimen_8_dp"
android:background="@drawable/fun_contact_top_search_bg">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:id="@+id/ivTip"
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_search"
android:layout_gravity="center_vertical"
app:layout_constraintHorizontal_chainStyle="packed" />
<TextView
android:id="@+id/tvTip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_5_dp"
android:text="@string/fun_contact_search"
android:textColor="@color/color_4d000000"
android:textSize="@dimen/dimen_16_dp"
app:layout_constraintStart_toEndOf="@id/ivTip"
android:layout_gravity="center_vertical"
tools:ignore="SpUsage" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/view_normal_selector"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.common.ui.widgets.RoundFrameLayout
android:layout_width="@dimen/dimen_40_dp"
android:layout_height="@dimen/dimen_40_dp"
android:id="@+id/fl_iv_icon"
app:corner_radius="@dimen/dimen_4_dp"
android:background="@color/color_white"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginBottom="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="@dimen/dimen_40_dp"
android:layout_height="@dimen/dimen_40_dp" />
</com.netease.yunxin.kit.common.ui.widgets.RoundFrameLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:textSize="@dimen/text_size_17"
android:textColor="@color/color_333333"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/fl_iv_icon" />
<TextView android:id="@+id/tv_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@+id/iv_arrow"
android:background="@drawable/bg_shape_red_dot"
android:gravity="center"
android:paddingStart="@dimen/dimen_5_dp"
android:paddingEnd="@dimen/dimen_5_dp"
app:layout_goneMarginRight="@dimen/dimen_20_dp"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_12"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginRight="@dimen/dimen_20_dp"
android:src="@drawable/ic_right_arrow" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
android:background="@color/color_e5e5e5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/view_normal_selector">
<LinearLayout
android:id="@+id/layout_selector"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/rb_selector"
style="@style/FunSelectorRadioBtn"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:clickable="false"
android:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_selector"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:visibility="gone"
app:srcCompat="@drawable/fun_ic_notselector_conversation" />
</LinearLayout>
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_45_dp"
android:layout_height="@dimen/dimen_45_dp"
android:layout_marginLeft="@dimen/dimen_12_dp"
android:layout_marginTop="@dimen/dimen_10_dp"
android:layout_marginBottom="@dimen/dimen_10_dp"
app:avatarCorner="@dimen/dimen_45_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/layout_selector"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginLeft="0dp" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:layout_marginRight="@dimen/dimen_10_dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/messageLayout"
app:layout_constraintLeft_toRightOf="@+id/avatar_view"
app:layout_constraintRight_toLeftOf="@+id/tv_time"
app:layout_constraintTop_toTopOf="@+id/avatar_view" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:textColor="@color/color_7f7b81"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/tv_name"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_name" />
<LinearLayout
android:id="@+id/messageLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/avatar_view"
app:layout_constraintLeft_toLeftOf="@+id/tv_name"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/aitTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_4_dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/conversation_ait_tip"
android:textColor="@color/color_ff4e54"
android:textSize="@dimen/text_size_14"
android:visibility="gone" />
<TextView
android:id="@+id/tv_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/contact_hint"
android:textColor="@color/fun_conversation_item_sub_title_text_color"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
<TextView
android:id="@+id/unreadTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="15dp"
android:background="@drawable/bg_conversation_red_dot"
android:gravity="center"
android:visibility="gone"
android:paddingStart="@dimen/dimen_5_dp"
android:paddingEnd="@dimen/dimen_5_dp"
android:layout_marginBottom="@dimen/dimen_15_dp"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<View
android:layout_width="0dp"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_e5e5e5"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="@+id/avatar_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_15_dp">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_45_dp"
android:layout_height="@dimen/dimen_45_dp"
app:avatarCorner="@dimen/dimen_45_dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/ic_clean"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="@+id/avatar_view"
app:layout_constraintTop_toTopOf="@+id/avatar_view"
app:srcCompat="@drawable/ic_selector_close" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_5_dp"
android:ellipsize="end"
android:maxLines="1"
android:gravity="center"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_12"
app:layout_constraintEnd_toEndOf="@+id/avatar_view"
app:layout_constraintStart_toStartOf="@+id/avatar_view"
app:layout_constraintTop_toBottomOf="@+id/avatar_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_white">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/fun_title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_fcfcfc"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:titleText="@string/global_search_title"
tools:ignore="MissingConstraints" />
<EditText
android:id="@+id/etSearch"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginStart="20dp"
android:layout_marginTop="@dimen/dimen_16_dp"
android:layout_marginEnd="20dp"
android:autofillHints=""
android:background="@drawable/bg_search_et"
android:drawableStart="@drawable/ic_search"
android:drawablePadding="5dp"
android:hint="@string/global_search_hint"
android:maxLines="1"
android:paddingStart="15dp"
android:paddingEnd="15dp"
android:textColor="@color/color_333333"
android:textColorHint="@color/color_666666"
android:textSize="14dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/fun_title_bar"
tools:ignore="SpUsage" />
<ImageView
android:id="@+id/ivClear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_clear"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/etSearch"
app:layout_constraintEnd_toEndOf="@id/etSearch"
app:layout_constraintTop_toTopOf="@id/etSearch" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvSearch"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/etSearch" />
<LinearLayout
android:id="@+id/empty_ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_55_dp"
android:orientation="vertical"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etSearch"
tools:ignore="MissingConstraints">
<ImageView
android:id="@+id/ivEmpty"
android:layout_width="@dimen/dimen_188_dp"
android:layout_height="@dimen/dimen_120_dp"
android:src="@drawable/fun_ic_contact_empty" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_6_dp"
android:gravity="center"
android:text="@string/global_search_empty"
android:textColor="@color/color_666666"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="38dp"
android:background="@color/fun_contact_search_item_bg_color">
<View android:id="@+id/titleDivide"
android:layout_width="match_parent"
android:layout_height="8dp"
tools:ignore="MissingConstraints"
app:layout_constraintTop_toTopOf="parent"
android:background="@color/fun_contact_page_bg_color"/>
<TextView android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_16_dp"
android:layout_marginEnd="@dimen/dimen_20_dp"
android:textSize="@dimen/text_size_14"
android:textColor="@color/fun_contact_search_item_title_color"
app:layout_constraintTop_toBottomOf="@id/titleDivide"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:gravity="center_vertical"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="64dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:background="@color/fun_contact_search_item_bg_color">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/cavUserIcon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginStart="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:avatarCorner="@dimen/dimen_40_dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="68dp"
android:layout_marginEnd="15dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center_vertical"
tools:ignore="MissingConstraints">
<TextView
android:id="@+id/nickTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical|start"
android:singleLine="true"
android:textColor="@color/fun_contact_search_item_nick_color"
android:textSize="16dp"
tools:ignore="SpUsage"/>
<TextView
android:id="@+id/nameTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:paddingTop="4dp"
android:textColor="@color/fun_contact_search_item_name_color"
android:textSize="12dp"
tools:ignore="SpUsage"
android:maxLines="1"
android:ellipsize="end"
android:visibility="visible"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/view_normal_selector">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_45_dp"
android:layout_height="@dimen/dimen_45_dp"
android:layout_marginTop="@dimen/dimen_10_dp"
android:layout_marginBottom="@dimen/dimen_10_dp"
app:avatarCorner="@dimen/dimen_45_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/messageLayout"
app:layout_constraintLeft_toRightOf="@+id/avatar_view"
app:layout_constraintRight_toLeftOf="@+id/tv_time"
app:layout_constraintTop_toTopOf="@+id/avatar_view" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:textColor="@color/color_7f7b81"
android:textSize="12sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/tv_name"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/tv_name" />
<LinearLayout
android:id="@+id/messageLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="@+id/avatar_view"
app:layout_constraintLeft_toLeftOf="@+id/tv_name"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/aitTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_4_dp"
android:ellipsize="end"
android:maxLines="1"
android:text="@string/conversation_ait_tip"
android:textColor="@color/color_ff4e54"
android:textSize="@dimen/text_size_14"
android:visibility="gone" />
<TextView
android:id="@+id/tv_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/fun_conversation_item_sub_title_text_color"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
<TextView
android:id="@+id/unreadTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="15dp"
android:background="@drawable/bg_conversation_red_dot"
android:gravity="center"
android:visibility="gone"
android:paddingStart="@dimen/dimen_5_dp"
android:paddingEnd="@dimen/dimen_5_dp"
android:layout_marginBottom="@dimen/dimen_15_dp"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<View
android:layout_width="0dp"
android:layout_height="@dimen/dimen_1_dp"
android:layout_marginStart="@dimen/dimen_80_dp"
android:background="@color/color_e5e5e5"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactInfoView
android:id="@+id/contact_user"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/titleBarView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:titleText="@string/friend_apply" />
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_45_dp"
android:layout_height="@dimen/dimen_45_dp"
android:layout_marginStart="@dimen/dimen_30_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
app:avatarCorner="@dimen/dimen_45_dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/titleBarView" />
<TextView
android:id="@+id/unread_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_52_dp"
android:layout_marginTop="@dimen/dimen_6_dp"
android:background="@drawable/bg_shape_red_dot"
android:paddingStart="@dimen/dimen_4_dp"
android:paddingEnd="@dimen/dimen_4_dp"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_12"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginEnd="@dimen/dimen_30_dp"
android:ellipsize="end"
android:gravity="start"
android:maxWidth="@dimen/verify_name_max_len"
android:maxLines="1"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
android:textStyle="bold"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@+id/avatar_view"
app:layout_constraintTop_toTopOf="@+id/avatar_view" />
<TextView
android:id="@+id/tv_action"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_30_dp"
android:layout_marginBottom="@dimen/dimen_2_dp"
android:ellipsize="end"
android:gravity="start"
android:text="@string/friend_apply"
android:maxLines="2"
android:textSize="@dimen/text_size_14"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="@+id/tv_name"
app:layout_constraintTop_toBottomOf="@+id/tv_name" />
<TextView
android:id="@+id/tv_verifytitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_20_dp"
android:text="@string/fun_verifymsg"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="@+id/avatar_view"
app:layout_constraintTop_toBottomOf="@+id/avatar_view" />
<TextView
android:id="@+id/tv_verifymsg"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_10_dp"
android:background="@drawable/fun_bg_addfriends_verifymsg"
android:paddingLeft="@dimen/dimen_10_dp"
android:paddingTop="@dimen/dimen_15_dp"
android:paddingEnd="@dimen/dimen_10_dp"
android:paddingBottom="@dimen/dimen_15_dp"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_14"
app:layout_constraintEnd_toEndOf="@+id/tv_name"
app:layout_constraintStart_toStartOf="@+id/avatar_view"
app:layout_constraintTop_toBottomOf="@+id/tv_verifytitle" />
<TextView
android:id="@+id/tv_agree"
android:layout_width="0dp"
android:layout_height="@dimen/dimen_48_dp"
android:layout_marginTop="@dimen/dimen_60_dp"
android:background="@drawable/fun_positive_btn_bg"
android:gravity="center"
android:text="@string/contact_verify_agree"
android:textColor="@android:color/white"
android:textSize="@dimen/text_size_16"
app:layout_constraintEnd_toEndOf="@+id/tv_name"
app:layout_constraintStart_toStartOf="@+id/avatar_view"
app:layout_constraintTop_toBottomOf="@+id/tv_verifymsg" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:background="@android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_45_dp"
android:layout_height="@dimen/dimen_45_dp"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginBottom="@dimen/dimen_12_dp"
app:avatarCorner="@dimen/dimen_45_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/unread_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_52_dp"
android:layout_marginTop="@dimen/dimen_6_dp"
android:background="@drawable/bg_shape_red_dot"
android:paddingStart="@dimen/dimen_4_dp"
android:paddingEnd="@dimen/dimen_4_dp"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_12"
android:visibility="gone"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="MissingConstraints" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:ellipsize="end"
android:gravity="start"
android:maxWidth="@dimen/verify_name_max_len"
android:maxLines="1"
android:textColor="@color/color_333333"
android:textSize="@dimen/text_size_16"
android:textStyle="bold"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@id/lly_operate"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@+id/avatar_view"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_action"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:layout_marginBottom="@dimen/dimen_2_dp"
android:ellipsize="end"
android:gravity="start"
android:maxLines="1"
android:textSize="@dimen/text_size_14"
app:layout_constraintEnd_toStartOf="@id/lly_operate"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="@+id/tv_name"
app:layout_constraintTop_toBottomOf="@+id/tv_name" />
<LinearLayout
android:id="@+id/lly_operate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/lly_verify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_reject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_10_dp"
android:paddingStart="@dimen/dimen_15_dp"
android:paddingEnd="@dimen/dimen_15_dp"
android:paddingTop="@dimen/dimen_7_dp"
android:paddingBottom="@dimen/dimen_7_dp"
android:background="@drawable/negative_btn_bg"
android:gravity="center"
android:text="@string/contact_verify_reject"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_14" />
<TextView
android:id="@+id/tv_accept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dimen_15_dp"
android:paddingEnd="@dimen/dimen_15_dp"
android:paddingTop="@dimen/dimen_7_dp"
android:paddingBottom="@dimen/dimen_7_dp"
android:background="@drawable/fun_positive_btn_bg"
android:gravity="center"
android:text="@string/contact_verify_agree"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
<LinearLayout
android:id="@+id/lly_verify_result"
android:layout_width="wrap_content"
android:layout_height="@dimen/dimen_20_dp"
android:orientation="horizontal"
android:visibility="gone">
<ImageView
android:id="@+id/iv_verify_result"
android:layout_width="@dimen/dimen_16_dp"
android:layout_height="@dimen/dimen_16_dp"
android:layout_gravity="center_vertical"
android:src="@mipmap/ic_agree_status" />
<TextView
android:id="@+id/tv_verify_result"
android:layout_width="wrap_content"
android:layout_height="@dimen/dimen_20_dp"
android:layout_marginStart="@dimen/dimen_3_dp"
android:maxLines="1"
android:textColor="@color/color_b3b7bc"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_e5e5e5"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/avatar_view" />
</androidx.constraintlayout.widget.ConstraintLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="fun_contact_page_bg_color">#ededed</color>
<!-- search page-->
<color name="fun_contact_search_hit_text_color">#58BE6B</color>
<color name="fun_contact_search_edit_bg_color">#FFFFFF</color>
<color name="fun_contact_search_item_bg_color">#FFFFFF</color>
<color name="fun_contact_search_item_title_color">#737373</color>
<color name="fun_contact_search_item_nick_color">#333333</color>
<color name="fun_contact_search_item_name_color">#666666</color>
<color name="fun_contact_list_item_hint_color">#7F7B81</color>
</resources>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<style name="FunSelectorRadioBtn" parent="Widget.AppCompat.CompoundButton.RadioButton">
<item name="android:button">@null</item>
<item name="android:background">@drawable/fun_radio_button_selector</item>
</style>
<style name="SelectorFriendsBottomDialogTheme" parent="ThemeOverlay.AppCompat.Dialog">
<!-- 背景透明 -->
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<!-- Dialog以外的区域模糊效果 -->
<item name="android:backgroundDimEnabled">true</item>
<!-- 半透明 -->
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowAnimationStyle">@style/BottomDialogAnimation</item>
<!-- 浮于Activity之上 -->
<item name="android:windowIsFloating">false</item>
<item name="android:windowFullscreen">false</item>
<!-- 无标题 -->
<item name="android:windowNoTitle">true</item>
<!-- 边框 -->
<item name="android:windowFrame">@null</item>
<item name="android:windowCloseOnTouchOutside">false</item>
</style>
</resources>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout android:id="@+id/layout_add_friend_title"
android:layout_width="match_parent"
android:layout_height="@dimen/page_title_bar_height"
android:orientation="horizontal"
tools:ignore="MissingConstraints">
<ImageView
android:id="@+id/iv_add_friend_back"
android:layout_width="@dimen/image_operate_width_height"
android:layout_height="@dimen/image_operate_width_height"
android:layout_gravity="center"
android:layout_marginLeft="@dimen/page_margin_left_right"
android:src="@mipmap/ic_back" />
<TextView
android:id="@+id/tv_add_friend_title"
android:layout_width="0dp"
android:layout_weight="1"
android:text="@string/add_friend_title"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_14"
android:textColor="@color/title_color"
android:textStyle="bold"
android:layout_gravity="center_vertical"
android:gravity="center_horizontal"
android:layout_marginLeft="@dimen/page_margin_left_right"
android:layout_marginRight="@dimen/page_margin_left_right" />
<TextView android:id="@+id/tv_add_friend_more"
android:layout_gravity="center"
android:layout_width="@dimen/image_operate_width_height"
android:layout_height="@dimen/image_operate_width_height"/>
</LinearLayout>
<LinearLayout android:id="@+id/layout_add_friend_input"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_32_dp"
android:background="@color/color_f2f4f5"
android:orientation="horizontal"
android:layout_marginLeft="@dimen/page_margin_left_right"
android:layout_marginRight="@dimen/page_margin_left_right"
android:layout_marginTop="@dimen/dimen_12_dp"
app:layout_constraintTop_toBottomOf="@id/layout_add_friend_title"
tools:ignore="MissingConstraints" >
<ImageView
android:src="@mipmap/ic_search"
android:layout_marginLeft="@dimen/dimen_12_dp"
android:layout_marginRight="@dimen/dimen_12_dp"
android:layout_gravity="center_vertical"
android:layout_width="@dimen/dimen_12_dp"
android:layout_height="@dimen/dimen_12_dp"/>
<EditText android:id="@+id/et_add_friend_account"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@null"
android:textCursorDrawable="@null"
android:imeOptions="actionSearch"
android:singleLine="true"
android:layout_marginRight="@dimen/dimen_12_dp"
android:textSize="@dimen/text_size_14"
android:hint="@string/add_friend_search_hint"/>
<ImageView android:id="@+id/iv_friend_clear"
android:src="@drawable/ic_clear"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/dimen_12_dp"
android:layout_marginRight="@dimen/dimen_12_dp"
android:layout_width="@dimen/dimen_12_dp"
android:layout_height="@dimen/dimen_12_dp"
android:visibility="gone"/>
</LinearLayout>
<LinearLayout android:id="@+id/add_friend_empty_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@id/layout_add_friend_input"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_weight="1"
android:orientation="vertical"
android:visibility="gone"
tools:ignore="MissingConstraints" >
<ImageView
android:id="@+id/empty_state_iv"
android:layout_width="@dimen/dimen_188_dp"
android:layout_height="@dimen/verify_name_max_len"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dimen_70_dp"
android:layout_marginBottom="@dimen/dimen_16_dp"
android:src="@drawable/ic_contact_empty"/>
<TextView android:id="@+id/add_friend_empty_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/add_friend_search_empty_tips"
android:textColor="@color/color_b3b7bc"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/image_contact_width_height"
android:layout_height="@dimen/image_contact_width_height"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginBottom="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:textSize="@dimen/text_size_14"
android:textColor="@color/color_14131b"
android:maxLines="1"
android:ellipsize="end"
app:layout_constrainedWidth="true"
android:layout_marginEnd="@dimen/dimen_100_dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/avatar_view" />
<TextView
android:id="@+id/tv_relieve"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_337eff"
android:textSize="@dimen/text_size_14"
android:paddingHorizontal="@dimen/dimen_16_dp"
android:paddingVertical="@dimen/dimen_8_dp"
android:background="@drawable/positive_btn_bg"
android:layout_marginEnd="@dimen/dimen_20_dp"
android:text="@string/contact_black_list_relieve"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_eff1f4"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.netease.yunxin.kit.common.ui.widgets.CleanableEditText
android:id="@+id/edt_comment"
android:layout_width="match_parent"
android:maxLength="15"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_20_dp" />
</LinearLayout>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:ignore="MissingDefaultResource">
<FrameLayout
android:id="@+id/contact_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.contactkit.ui.view.ContactLayout
android:id="@+id/contact_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
<LinearLayout android:id="@+id/empty_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_188_dp"
android:layout_marginTop="400dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
android:orientation="vertical">
<ImageView
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="@dimen/dimen_96_dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_contact_empty"/>
<TextView android:id="@+id/empty_tv"
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="@string/selector_friend_empty"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_selected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_f2f4f5"
android:paddingHorizontal="@dimen/dimen_20_dp" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:showSelector="true" />
<LinearLayout
android:id="@+id/empty_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_188_dp"
android:layout_marginTop="@dimen/dimen_150_dp"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="@dimen/dimen_96_dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/ic_contact_empty" />
<TextView
android:id="@+id/empty_tv"
android:layout_width="@dimen/dimen_118_dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center"
android:text="@string/selector_friend_empty" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/view_normal_selector"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.common.ui.widgets.RoundFrameLayout
android:layout_width="@dimen/image_contact_width_height"
android:layout_height="@dimen/image_contact_width_height"
android:id="@+id/fl_iv_icon"
app:corner_radius="@dimen/dimen_30_dp"
android:background="@color/color_white"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginBottom="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="@dimen/image_contact_width_height"
android:layout_height="@dimen/image_contact_width_height" />
</com.netease.yunxin.kit.common.ui.widgets.RoundFrameLayout>
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:textSize="@dimen/text_size_14"
android:textColor="@color/color_14131b"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/fl_iv_icon" />
<TextView android:id="@+id/tv_number"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toLeftOf="@+id/iv_arrow"
android:background="@drawable/bg_shape_red_dot"
android:gravity="center"
android:paddingStart="@dimen/dimen_5_dp"
android:paddingEnd="@dimen/dimen_5_dp"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_12"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginRight="@dimen/dimen_20_dp"
android:src="@drawable/ic_right_arrow" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
android:background="@color/color_f5f8fc"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/dimen_8_dp"
android:paddingEnd="@dimen/dimen_20_dp"
android:background="@drawable/view_normal_selector"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.appcompat.widget.AppCompatRadioButton
android:id="@+id/rb_selector"
android:layout_width="@dimen/dimen_18_dp"
android:layout_height="@dimen/dimen_18_dp"
android:layout_marginStart="@dimen/dimen_12_dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
style="@style/SelectorRadioBtn"
android:clickable="false"
android:visibility="gone"
tools:visibility="visible" />
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/image_contact_width_height"
android:layout_height="@dimen/image_contact_width_height"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginBottom="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_12_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/rb_selector" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_12_dp"
android:textSize="@dimen/text_size_14"
android:textColor="@color/color_14131b"
android:maxLines="1"
app:layout_constrainedWidth="true"
android:ellipsize="end"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/avatar_view"
tools:text="@string/comment_name" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dimen_7_dp"
android:paddingBottom="@dimen/dimen_9_dp"
android:paddingEnd="@dimen/dimen_10_dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/image_contact_width_height"
android:layout_height="@dimen/image_contact_width_height"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/global_title_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_46_dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:titleText="@string/global_search_title"
tools:ignore="MissingConstraints" />
<EditText
android:id="@+id/etSearch"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginStart="20dp"
android:layout_marginTop="@dimen/dimen_16_dp"
android:layout_marginEnd="20dp"
android:autofillHints=""
android:background="@drawable/bg_search_et"
android:drawableStart="@drawable/ic_search"
android:drawablePadding="5dp"
android:hint="@string/global_search_hint"
android:maxLines="1"
android:paddingStart="15dp"
android:paddingEnd="15dp"
android:textColor="@color/color_333333"
android:textColorHint="@color/color_666666"
android:textSize="14dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/global_title_bar"
tools:ignore="SpUsage" />
<ImageView
android:id="@+id/ivClear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:src="@drawable/ic_clear"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/etSearch"
app:layout_constraintEnd_toEndOf="@id/etSearch"
app:layout_constraintTop_toTopOf="@id/etSearch" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvSearch"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/etSearch" />
<LinearLayout android:id="@+id/empty_ll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen_55_dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/etSearch"
android:orientation="vertical"
tools:ignore="MissingConstraints"
android:visibility="gone">
<ImageView
android:id="@+id/ivEmpty"
android:layout_width="@dimen/dimen_188_dp"
android:layout_height="@dimen/dimen_120_dp"
android:src="@drawable/ic_contact_empty"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/global_search_empty"
android:textColor="@color/color_666666"
android:layout_marginTop="@dimen/dimen_6_dp"
android:gravity="center"
android:textSize="@dimen/text_size_14"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_36_dp"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_20_dp"
android:layout_marginEnd="@dimen/dimen_20_dp"
android:textSize="@dimen/text_size_14"
android:textColor="@color/color_b3b7bc"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:gravity="center_vertical"
tools:ignore="MissingConstraints" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_d9d9d9"
android:layout_marginStart="@dimen/dimen_20_dp"
android:layout_marginEnd="@dimen/dimen_20_dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/cavUserIcon"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_marginStart="20dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginStart="77dp"
android:layout_marginEnd="15dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center_vertical"
tools:ignore="MissingConstraints">
<TextView
android:id="@+id/tvNickName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:gravity="center_vertical|start"
android:singleLine="true"
android:textColor="@color/color_333333"
android:textSize="16dp"
android:ellipsize="end"
tools:ignore="SpUsage" />
<TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:paddingTop="4dp"
android:textColor="@color/color_666666"
android:textSize="12dp"
tools:ignore="SpUsage"
android:maxLines="1"
android:ellipsize="end"
android:visibility="visible"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/view_normal_selector"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_42_dp"
android:layout_height="@dimen/dimen_42_dp"
android:layout_marginVertical="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_14_dp"
android:textColor="@color/color_14131b"
android:maxLines="1"
android:ellipsize="end"
android:textSize="@dimen/text_size_16"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/dimen_20_dp"/>
</LinearLayout>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactInfoView
android:id="@+id/contact_user"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

View File

@@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/image_contact_width_height"
android:layout_height="@dimen/image_contact_width_height"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginBottom="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView android:id="@+id/unread_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_shape_red_dot"
android:paddingStart="@dimen/dimen_4_dp"
android:paddingEnd="@dimen/dimen_4_dp"
android:textColor="@color/color_white"
android:textSize="@dimen/text_size_12"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="@dimen/dimen_12_dp"
android:layout_marginStart="@dimen/dimen_42_dp"
app:layout_constraintLeft_toLeftOf="parent"
tools:ignore="MissingConstraints" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:layout_marginStart="@dimen/dimen_16_dp"
android:textSize="@dimen/text_size_14"
android:textColor="@color/color_14131b"
android:maxLines="1"
android:layout_marginTop="@dimen/dimen_15_dp"
android:maxWidth="@dimen/verify_name_max_len"
app:layout_constrainedWidth="true"
android:ellipsize="end"
android:gravity="start"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="@id/lly_operate"
app:layout_constraintStart_toEndOf="@+id/avatar_view"/>
<TextView
android:id="@+id/tv_action"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintHorizontal_weight="1"
android:textSize="@dimen/text_size_14"
android:ellipsize="end"
android:maxLines="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/tv_name"
app:layout_constraintTop_toBottomOf="@+id/tv_name"
app:layout_constraintEnd_toStartOf="@id/lly_operate"
android:gravity="start"
android:layout_marginBottom="@dimen/dimen_3_dp"/>
<LinearLayout android:id="@+id/lly_operate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dimen_20_dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/lly_verify"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_reject"
android:layout_width="@dimen/dimen_60_dp"
android:layout_height="@dimen/dimen_32_dp"
android:layout_marginEnd="@dimen/dimen_8_dp"
android:background="@drawable/negative_btn_bg"
android:gravity="center"
android:text="@string/contact_verify_reject"
android:textColor="@color/title_color"
android:textSize="@dimen/text_size_14" />
<TextView
android:id="@+id/tv_accept"
android:layout_width="@dimen/dimen_60_dp"
android:layout_height="@dimen/dimen_32_dp"
android:background="@drawable/positive_btn_bg"
android:text="@string/contact_verify_agree"
android:textColor="@color/color_337eff"
android:gravity="center"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
<LinearLayout
android:id="@+id/lly_verify_result"
android:layout_width="wrap_content"
android:layout_height="@dimen/dimen_20_dp"
android:orientation="horizontal"
android:visibility="gone">
<ImageView
android:id="@+id/iv_verify_result"
android:layout_width="@dimen/dimen_16_dp"
android:layout_height="@dimen/dimen_16_dp"
android:layout_gravity="center_vertical"
android:src="@mipmap/ic_agree_status" />
<TextView
android:id="@+id/tv_verify_result"
android:layout_width="wrap_content"
android:layout_height="@dimen/dimen_20_dp"
android:layout_marginStart="@dimen/dimen_3_dp"
android:maxLines="1"
android:textColor="@color/color_b3b7bc"
android:textSize="@dimen/text_size_14" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<style name="SelectorRadioBtn" parent="Widget.AppCompat.CompoundButton.RadioButton">
<item name="android:button">@drawable/radio_button_selector</item>
</style>
</resources>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="@color/color_f2f4f5"/>
</shape>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (c) 2022 NetEase, Inc. All rights reserved. -->
<!-- Use of this source code is governed by a MIT license that can be -->
<!-- found in the LICENSE file. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="9dp" />
<solid android:color="@color/color_f24957" />
</shape>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="@dimen/dimen_20_dp" />
<solid android:color="@color/color_6d94ff"/>
</shape>

View File

@@ -0,0 +1,18 @@
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="26dp"
android:height="26dp"
android:viewportWidth="26"
android:viewportHeight="26">
<path
android:pathData="M0,0h26v26h-26z"
android:fillColor="#00000000" />
<path
android:pathData="M17.7071,5.7071C18.0976,5.3166 18.0976,4.6834 17.7071,4.2929C17.3166,3.9024 16.6834,3.9024 16.2929,4.2929L17.7071,5.7071ZM9,13L8.2929,12.2929C7.9024,12.6834 7.9024,13.3166 8.2929,13.7071L9,13ZM15.7929,21.2071C16.1834,21.5976 16.8166,21.5976 17.2071,21.2071C17.5976,20.8166 17.5976,20.1834 17.2071,19.7929L15.7929,21.2071ZM16.2929,4.2929L8.2929,12.2929L9.7071,13.7071L17.7071,5.7071L16.2929,4.2929ZM8.2929,13.7071L15.7929,21.2071L17.2071,19.7929L9.7071,12.2929L8.2929,13.7071Z"
android:fillColor="#333333" />
</vector>

View File

@@ -0,0 +1,15 @@
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M8,0C3.5891,0 0,3.5884 0,8C0,12.4116 3.5884,16 8,16C12.4109,16 16,12.4116 16,8C16,3.5884 12.4116,0 8,0ZM11.2633,10.464C11.3191,10.5165 11.3638,10.5796 11.3948,10.6497C11.4257,10.7198 11.4423,10.7954 11.4434,10.872C11.4446,10.9486 11.4303,11.0246 11.4015,11.0956C11.3727,11.1666 11.3299,11.2311 11.2757,11.2852C11.2215,11.3393 11.1569,11.382 11.0859,11.4107C11.0149,11.4393 10.9388,11.4535 10.8622,11.4522C10.7856,11.4509 10.71,11.4342 10.64,11.4031C10.57,11.372 10.5069,11.3272 10.4545,11.2713L7.9971,8.8058L5.5316,11.2451C5.4792,11.3017 5.4158,11.347 5.3453,11.3785C5.2748,11.4099 5.1987,11.4267 5.1216,11.428C5.0444,11.4292 4.9678,11.4148 4.8964,11.3856C4.825,11.3564 4.7602,11.3131 4.7059,11.2582C4.6517,11.2033 4.6091,11.1381 4.5808,11.0663C4.5525,10.9945 4.5389,10.9177 4.5411,10.8406C4.5432,10.7635 4.5609,10.6876 4.5932,10.6175C4.6254,10.5474 4.6715,10.4845 4.7287,10.4327L7.1905,7.9971L4.7375,5.536C4.6835,5.4831 4.6405,5.42 4.6111,5.3503C4.5816,5.2807 4.5663,5.2059 4.566,5.1302C4.5657,5.0546 4.5803,4.9797 4.6092,4.9098C4.638,4.8399 4.6804,4.7764 4.734,4.723C4.7875,4.6697 4.8511,4.6274 4.9211,4.5988C4.991,4.5701 5.066,4.5556 5.1416,4.5562C5.2172,4.5567 5.292,4.5722 5.3615,4.6018C5.4311,4.6315 5.4941,4.6746 5.5469,4.7287L8.0036,7.1934L10.4691,4.7542C10.5224,4.7015 10.5856,4.6598 10.6551,4.6315C10.7245,4.6032 10.7989,4.5888 10.8739,4.5893C10.9489,4.5897 11.0231,4.6049 11.0922,4.634C11.1613,4.6631 11.224,4.7056 11.2767,4.7589C11.3294,4.8123 11.3711,4.8755 11.3994,4.9449C11.4277,5.0144 11.4421,5.0887 11.4416,5.1637C11.4412,5.2387 11.426,5.3129 11.3969,5.382C11.3678,5.4511 11.3253,5.5138 11.272,5.5665L8.8095,8.0029L11.2633,10.464Z"
android:fillColor="#B3B7BC" />
</vector>

View File

@@ -0,0 +1,16 @@
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M5.1032,3.5123C4.8629,3.272 4.8629,2.8823 5.1032,2.642C5.3435,2.4017 5.7332,2.4017 5.9735,2.642L5.1032,3.5123ZM10.4614,8.0002L10.8966,7.5651C11.1369,7.8054 11.1369,8.1951 10.8966,8.4354L10.4614,8.0002ZM6.2812,13.0508C6.0409,13.2911 5.6512,13.2911 5.4109,13.0508C5.1706,12.8104 5.1706,12.4208 5.4109,12.1805L6.2812,13.0508ZM5.9735,2.642L10.8966,7.5651L10.0263,8.4354L5.1032,3.5123L5.9735,2.642ZM10.8966,8.4354L6.2812,13.0508L5.4109,12.1805L10.0263,7.5651L10.8966,8.4354Z"
android:fillColor="#999999" />
</vector>

View File

@@ -0,0 +1,15 @@
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M6.9706,2.7743C9.12,2.7777 10.9176,4.4086 11.1293,6.5476C11.3413,8.6866 9.8984,10.6385 7.7916,11.0634C5.6845,11.4884 3.598,10.2484 2.9643,8.1945C2.3305,6.1405 3.3552,3.9405 5.3352,3.1042C5.8526,2.8853 6.4088,2.7731 6.9706,2.7743ZM6.9706,1.8046C4.1144,1.8046 1.7988,4.1201 1.7988,6.9763C1.7988,9.8324 4.1144,12.148 6.9706,12.148C9.8267,12.148 12.1423,9.8324 12.1423,6.9763C12.1423,4.1201 9.8267,1.8046 6.9706,1.8046ZM13.7099,14.2005C13.6462,14.2007 13.5831,14.1881 13.5243,14.1637C13.4654,14.1393 13.412,14.1035 13.3671,14.0583L10.9105,11.6017C10.7269,11.4114 10.7295,11.109 10.9164,10.9219C11.1035,10.7349 11.4059,10.7322 11.596,10.9161L14.0526,13.3726C14.1203,13.4405 14.1665,13.5268 14.1852,13.6209C14.2039,13.7149 14.1943,13.8123 14.1576,13.9009C14.121,13.9896 14.059,14.0654 13.9793,14.1187C13.8996,14.172 13.8058,14.2005 13.7099,14.2005Z"
android:fillColor="#A6ADB6"/>
</vector>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_20_dp" />
<solid
android:width="@dimen/dimen_1_dp"
android:color="@color/color_fe6881" />
</shape>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="4dp" />
<stroke
android:width="@dimen/dimen_1_dp"
android:color="@color/color_337eff" />
</shape>

View File

@@ -0,0 +1,15 @@
<!-- Copyright (c) 2022 NetEase, Inc. All rights reserved. -->
<!-- Use of this source code is governed by a MIT license that can be -->
<!-- found in the LICENSE file. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:strokeWidth="1"
android:pathData="M9,9m-8.5,0a8.5,8.5 0,1 1,17 0a8.5,8.5 0,1 1,-17 0"
android:fillColor="#00000000"
android:strokeColor="#969AA0" />
</vector>

View File

@@ -0,0 +1,16 @@
<!-- Copyright (c) 2022 NetEase, Inc. All rights reserved. -->
<!-- Use of this source code is governed by a MIT license that can be -->
<!-- found in the LICENSE file. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M9,9m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0"
android:fillColor="#337EFF" />
<path
android:pathData="M5.1508,8.7407C4.7315,8.3813 4.1002,8.4299 3.7407,8.8492C3.3813,9.2685 3.4299,9.8998 3.8492,10.2593L5.1508,8.7407ZM8,12.5L7.3492,13.2593C7.7587,13.6102 8.3727,13.5733 8.7372,13.1757L8,12.5ZM14.2372,7.1757C14.6103,6.7686 14.5828,6.136 14.1757,5.7628C13.7686,5.3896 13.136,5.4172 12.7628,5.8243L14.2372,7.1757ZM3.8492,10.2593L7.3492,13.2593L8.6508,11.7407L5.1508,8.7407L3.8492,10.2593ZM8.7372,13.1757L14.2372,7.1757L12.7628,5.8243L7.2628,11.8243L8.7372,13.1757Z"
android:fillColor="#ffffff" />
</vector>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (c) 2022 NetEase, Inc. All rights reserved. -->
<!-- Use of this source code is governed by a MIT license that can be -->
<!-- found in the LICENSE file. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/radio_button_selected" android:state_checked="true" />
<item android:drawable="@drawable/radio_button_not_selected" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="@dimen/dimen_20_dp"
android:height="@dimen/dimen_20_dp" />
<solid android:color="@color/color_white" />
<stroke android:width="1dp" android:color="@color/color_dbdde4"/>
</shape>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<size
android:width="@dimen/dimen_20_dp"
android:height="@dimen/dimen_20_dp" />
<solid android:color="@color/color_white" />
<stroke android:width="1dp" android:color="@color/color_337eff"/>
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/switch_thumb_on" android:state_checked="true" />
<item android:drawable="@drawable/switch_thumb_off" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_12_dp"/>
<size
android:width="@dimen/dimen_44_dp"
android:height="@dimen/dimen_24_dp" />
<solid android:color="@color/color_eeeeee" />
</shape>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen_12_dp" />
<size
android:width="@dimen/dimen_44_dp"
android:height="@dimen/dimen_24_dp" />
<solid android:color="@color/color_09bb07" />
</shape>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/switch_track_on" android:state_checked="true" />
<item android:drawable="@drawable/switch_track_off" android:state_checked="false" />
</selector>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<selector xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" tools:ignore="MissingDefaultResource">
<item android:drawable="@color/color_white" android:state_pressed="false" />
<item android:drawable="@color/color_ededef" android:state_pressed="true" />
</selector>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_white"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.BackTitleBar
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tv_Tips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_15_dp"
android:layout_marginVertical="@dimen/dimen_4_dp"
android:lineSpacingExtra="@dimen/dimen_5_dp"
android:textColor="@color/color_999999"
android:textSize="@dimen/text_size_14"
android:visibility="gone" />
<TextView
android:id="@+id/heimindan_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_40_dp"
android:text="@string/black_list"
android:textColor="#ff333333"
android:textSize="14sp"
android:visibility="gone" />
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_listView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:layout_weight="0.9"
android:background="@android:color/white"
app:showIndexBar="false" />
<LinearLayout
android:id="@+id/layoutEmpty"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.9"
android:background="@android:color/white"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:layout_width="@dimen/dimen_100_dp"
android:layout_height="@dimen/dimen_100_dp"
android:layout_gravity="center_horizontal"
android:src="@mipmap/ic_nodate" />
<TextView
android:id="@+id/empty_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/nodate_txt" />
</LinearLayout>
<TextView
android:id="@+id/tv_blackremove"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_40_dp"
android:layout_marginStart="@dimen/dimen_15_dp"
android:layout_marginTop="@dimen/dimen_20_dp"
android:layout_marginEnd="@dimen/dimen_15_dp"
android:layout_marginBottom="@dimen/dimen_20_dp"
android:background="@drawable/fun_themeblue_btn_bg"
android:gravity="center"
android:text="@string/removeblack_txt"
android:textColor="@android:color/white"
android:visibility="gone" />
</LinearLayout>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/contactList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<com.netease.yunxin.kit.contactkit.ui.indexbar.IndexBar
android:id="@+id/index_bar"
android:layout_width="@dimen/dimen_13_dp"
android:visibility="gone"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/dimen_115_dp"
android:layout_marginBottom="@dimen/dimen_118_dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:indexBarTextSize="@dimen/text_size_8" />
<FrameLayout
android:id="@+id/contact_empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_white"
android:visibility="gone" >
<ImageView
android:id="@+id/emptyStateIv"
android:layout_width="@dimen/dimen_120_dp"
android:layout_height="@dimen/dimen_92_dp"
android:src="@drawable/ic_contact_empty"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dimen_150_dp"/>
<TextView android:id="@+id/contact_empty_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="250dp"
android:layout_gravity="center_horizontal"/>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout android:id="@+id/contact_header_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.netease.yunxin.kit.common.ui.widgets.TitleBarView
android:id="@+id/contact_title_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_52_dp"
app:head_title="@string/contact_title"
app:head_title_color="@color/title_color"/>
<View
android:id="@+id/divide_line_top_for_title"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_e9eff5"
android:alpha="0.6"
tools:ignore="MissingConstraints" />
</LinearLayout>
<FrameLayout android:id="@+id/contact_body_top_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
<FrameLayout android:id="@+id/contact_body_layout"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.netease.yunxin.kit.contactkit.ui.view.ContactListView
android:id="@+id/contact_listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:head_img_visible="gone"
app:head_title="@string/contact_title"
app:head_title_color="@color/title_color"/>
</FrameLayout>
<FrameLayout android:id="@+id/contact_bottom_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</FrameLayout>
</merge>

View File

@@ -0,0 +1,312 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_white"
android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dimen_20_dp"
android:layout_marginVertical="@dimen/dimen_15_dp">
<com.netease.yunxin.kit.common.ui.widgets.ContactAvatarView
android:id="@+id/avatar_view"
android:layout_width="@dimen/dimen_60_dp"
android:layout_height="@dimen/dimen_60_dp"
app:avatarTextSize="@dimen/text_size_22"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dimen_20_dp"
android:textColor="@color/title_color"
android:textSize="@dimen/text_size_22"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedWidth="true"
app:layout_constraintLeft_toRightOf="@+id/avatar_view" />
<TextView
android:id="@+id/tv_account"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="@dimen/dimen_12_dp"
android:textColor="@color/color_666666"
android:maxLines="1"
android:ellipsize="end"
android:layout_marginStart="@dimen/dimen_20_dp"
android:layout_marginTop="@dimen/dimen_4_dp"
app:layout_constraintTop_toBottomOf="@+id/tv_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/avatar_view"
tools:ignore="SpUsage" />
<TextView
android:id="@+id/tv_comment_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textSize="@dimen/dimen_12_dp"
android:textColor="@color/color_666666"
android:layout_marginStart="@dimen/dimen_20_dp"
android:visibility="gone"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="@+id/tv_account"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/avatar_view"
tools:ignore="SpUsage" />
</androidx.constraintlayout.widget.ConstraintLayout>
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_marginStart="@dimen/dimen_20_dp"
android:background="@color/color_f5f8fc" />
<RelativeLayout
android:id="@+id/rly_comment"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_46_dp"
android:paddingHorizontal="@dimen/dimen_20_dp">
<TextView
android:id="@+id/tv_comment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/comment_name"
android:textSize="@dimen/text_size_16"
android:textColor="@color/title_color" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_right_arrow" />
</RelativeLayout>
<View
android:id="@+id/divide_line1"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_6_dp"
android:background="@color/color_eff1f4" />
<LinearLayout
android:id="@+id/lly_friend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_46_dp"
android:paddingHorizontal="@dimen/dimen_20_dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/birthday"
android:textSize="@dimen/text_size_16"
android:textColor="@color/title_color" />
<TextView
android:id="@+id/tv_birthday"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:textSize="@dimen/text_size_12"
android:textColor="@color/color_a6adb6" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_alignParentBottom="true"
android:background="@color/color_f5f8fc" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_46_dp"
android:paddingHorizontal="@dimen/dimen_20_dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/phone"
android:textSize="@dimen/text_size_16"
android:textColor="@color/title_color" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:textSize="@dimen/text_size_12"
android:textColor="@color/color_a6adb6" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_alignParentBottom="true"
android:background="@color/color_f5f8fc" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_46_dp"
android:paddingHorizontal="@dimen/dimen_20_dp">
<TextView android:id="@+id/tv_email_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/email"
android:layout_centerVertical="true"
android:textSize="@dimen/text_size_16"
android:textColor="@color/title_color" />
<TextView
android:id="@+id/tv_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:textSize="@dimen/text_size_12"
android:layout_marginStart="@dimen/dimen_45_dp"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/color_a6adb6" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_alignParentBottom="true"
android:background="@color/color_f5f8fc" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_46_dp"
android:paddingHorizontal="@dimen/dimen_20_dp">
<TextView android:id="@+id/tv_signature_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/personal_signature"
android:layout_centerVertical="true"
android:textSize="@dimen/text_size_16"
android:textColor="@color/title_color" />
<TextView
android:id="@+id/tv_signature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:textSize="@dimen/text_size_12"
android:gravity="end"
android:layout_marginStart="@dimen/dimen_75_dp"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/color_a6adb6" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_alignParentBottom="true"
android:background="@color/color_f5f8fc" />
</RelativeLayout>
<View
android:id="@+id/divide_line2"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_6_dp"
android:background="@color/color_eff1f4" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_46_dp"
android:paddingHorizontal="@dimen/dimen_20_dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/add_black_list"
android:textColor="@color/title_color"
android:textSize="@dimen/text_size_16" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/sc_black_list"
android:layout_width="@dimen/dimen_60_dp"
android:layout_height="@dimen/dimen_28_dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:thumb="@drawable/switch_thumb_selector"
app:track="@drawable/switch_track_selector" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:layout_alignParentBottom="true"
android:background="@color/color_f5f8fc" />
</RelativeLayout>
<View
android:id="@+id/divide_line3"
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_6_dp"
android:background="@color/color_eff1f4" />
</LinearLayout>
<TextView
android:id="@+id/tv_chat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/chat"
android:textSize="@dimen/text_size_16"
android:textColor="@color/color_337eff"
android:paddingVertical="@dimen/dimen_14_dp" />
<View
android:layout_width="match_parent"
android:layout_height="@dimen/dimen_1_dp"
android:background="@color/color_f5f8fc" />
<TextView
android:id="@+id/tv_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/delete_friend"
android:textSize="@dimen/text_size_16"
android:textColor="@color/color_e6605c"
android:paddingVertical="@dimen/dimen_14_dp" />
</LinearLayout>
</FrameLayout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

View File

@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be found in the LICENSE file.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- contact list -->
<string name="contact_title">Contacts</string>
<string name="contact_list_verify_msg">Verifications</string>
<string name="contact_list_black_list">Blacklist</string>
<string name="contact_list_my_group">My Groups</string>
<string name="contact_list_my_computer">My Computer</string>
<!-- add friend page-->
<string name="add_friend_title">Add Contact</string>
<string name="search_friend_title">Search Contact</string>
<string name="add_friend_search_hint">Enter Account</string>
<string name="add_friend_searchphone_hint">Please enter phone number</string>
<string name="add_friend_searchid_hint">Enter your friends account</string>
<string name="add_friend_search_empty_tips">No exist</string>
<!-- verify page -->
<string name="contact_verify_agree">Approve</string>
<string name="contact_verify_reject">Reject</string>
<string name="contact_verify_agreed">Approved</string>
<string name="contact_verify_rejected"> Rejected</string>
<string name="contact_verify_expired"> Expired</string>
<string name="friend_apply">Friend Request</string>
<string name="invited_to_join_group">invite you to groups&#160;%s</string>
<string name="apply_to_join_group">Apply to join group%s</string>
<string name="accept_your_friend_apply">Approved your apply</string>
<string name="direct_add_your_friend_apply">add your contact</string>
<string name="reject_your_friend_apply">reject your apply</string>
<string name="verify_msg">Verification Message</string>
<string name="activity_msg">Push Notification</string>
<string name="verify_max_count_text">99+</string>
<string name="verify_agree_message_text">Nice to meet youlet\'s chat</string>
<string name="verify_empty_text">Verifications Empty</string>
<!-- my team page -->
<string name="my_team">My Groups</string>
<!-- black page -->
<string name="black_list">Blacklist</string>
<string name="black_list_tips">After blocking a friend, you will no longer receive messages from the other party. To contact a blocked account, please unblock it first. </string>
<string name="contact_black_list_relieve">Release</string>
<string name="add_black_error">%s Failed to block</string>
<!-- userinfo page -->
<string name="clear_all">Clear</string>
<string name="birthday">Birthday</string>
<string name="phone">Mobile</string>
<string name="email">Mail</string>
<string name="personal_signature">Whats up</string>
<string name="comment_name">Nick Name</string>
<string name="notify_message">Message Notification</string>
<string name="add_black_list">Block</string>
<string name="chat">Chat</string>
<string name="delete_friend">Delete Contact</string>
<string name="add_friend">添加好友</string>
<string name="contact_user_info_account">Account:%s</string>
<string name="contact_user_info_nickname">Nick Name:%s</string>
<string name="delete_contact_account">Dlete “%s”</string>
<string name="save">Save</string>
<string name="add_friend_operate_success">Sent Contact apply</string>
<string name="add_friend_operate_fail">Failed to apply contact %s</string>
<!--添加好友页面 -->
<string name="add_friend_search_error">Failed to search</string>
<string name="add_friend_search_result">Add</string>
<!-- error toast -->
<string name="agree_add_friend_fail">Failed</string>
<string name="disagree_add_friend_fail">Failed</string>
<string name="agree_apply_join_team_fail">Failed</string>
<string name="disagree_apply_join_team_fail">Failed</string>
<string name="agree_invite_team_fail">Failed</string>
<string name="disagree_invite_team_fail">Failed</string>
<string name="remove_black_fail">Failed to remove blacklist</string>
<string name="verify_duplicate_fail">Already done on other devices</string>
<!-- selector-->
<string name="select">Select Friends</string>
<!-- <string name="selector_sure">Yes(%d)</string>-->
<string name="selector_sure">Next</string>
<string name="invite_sure">invite</string>
<!-- <string name="selector_sure_without_num">Yes</string>-->
<string name="selector_sure_without_num">Next</string>
<string name="contact_selector_max_count">select up to %s friends</string>
<string name="contact_selector_over_count">exceeding the maximum capacity</string>
<string name="selector_friend_empty">No Member</string>
<string name="selector_group_empty">No Group</string>
<string name="select_empty_tips">No Member</string>
<string name="fun_selector_close">Close</string>
<string name="fun_contact_search">Search</string>
<!-- search -->
<string name="global_search_hint"> Please enter username/group chat name keyword</string>
<string name="global_search_title">Search</string>
<string name="global_searchfriend_title">Search Friends</string>
<string name="global_search_friend_title">Contacts</string>
<string name="global_search_group_title">Temperature Group</string>
<string name="global_search_team_title">Group</string>
<string name="global_search_empty">No exist</string>
<!-- common-->
<string name="cancel"> Cancel </string>
<string name="contact_network_error_tip">No Internet</string>
<string name="contact_hint">You haven\'t talked about it yet~</string>
<string name="fun_contact_search_hint_text">Search</string>
<string name="fun_contact_searchresult_text">Home Page</string>
<string name="fun_contact_searchresult_addtext">Add To</string>
<string name="fun_contact_searchself_txt">Cannot add your own account as a friend! </string>
<string name="fun_contact_searchfriends_txt">You are already friends! </string>
<string name="fun_contact_addfriends_verifymsg">Fill in the verification information</string>
<string name="fun_contact_verifymsg_tips">Please enter verification information</string>
<string name="fun_contact_addfriends_remarks">Remarks</string>
<string name="fun_contact_remarks_tips">Please enter remarks</string>
<string name="fun_contact_addfriends_send">Send</string>
<string name="fun_contact_createteam_tips">Search using name</string>
<string name="fun_creategroup_members">Member %d</string>
<string name="fun_creategroup_uploadhead">Upload avatar</string>
<string name="fun_creategroup_autojoin_tips">Users will join the group after receiving the invitation. After turning off this setting, requiring members to join the group requires manual acceptance of the invitation. </string>
<string name="fun_creategroup_autojoin">User automatically joins</string>
<string name="fun_creategroup_name">Group name</string>
<string name="fun_creategroup_title">Set group file</string>
<string name="fun_creategroup_sure">Create</string>
<string name="fun_contactselector_last">Latest Contact %d</string>
<string name="fun_contactselector_mylove">Special attention %d</string>
<string name="fun_contactselector_members">All friends %d</string>
<string name="fun_contactselector_result">Find results</string>
<string name="fun_verifymsg">Verification information</string>
<string name="fun_specialgz_txt">Special attention</string>
<string name="fun_searchresult_txt">Search results</string>
<string name="fun_addmembers_txt">New</string>
<string name="fun_addmemberstoast_txt">Keep at least one friend</string>
<string name="fun_contactselector_teamqr">QR code</string>
<string name="fun_contactselector_teamurl">Invitation URL</string>
<string name="nmhmylg_txt">You haven\'t talked about it yet~</string>
<string name="voom_message_txt">[Short video message]</string>
<string name="gerenminp_txt">[Business Card]</string>
<string name="zhuanzhang_chat_txt">[Transfer]</string>
<string name="hongbao_chat_txt">[Red envelope]</string>
<string name="albums_chat_txt">[Albums]</string>
<string name="helppay_chat_txt">[Help with Payment]</string>
<string name="inviteGroup_chat_txt">[Invite to Group]</string>
<string name="custom_chat_txt">[Custom Message]</string>
<string name="removeblack_txt">Unblock</string>
<string name="teamdissmiss_txt">This group has been disbanded</string>
<string name="teamjoined_txt">You are already in this group</string>
<string name="know_txt">I see</string>
<string name="nodate_txt">No data</string>
</resources>

View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<!-- 連絡先リスト -->
<string name="contact_title">連絡帳</string>
<string name="contact_list_verify_msg">確認メッセージ</string>
<string name="contact_list_black_list">ブラックリスト</string>
<string name="contact_list_my_group">グループ チャット</string>
<string name="contact_list_my_computer">私のコンピュータ</string>
<!-- 友達ページを追加 -->
<string name="add_friend_title">友達を追加</string>
<string name="search_friend_title">友達を検索</string>
<string name="add_friend_search_hint">アカウント番号を入力してください</string>
<string name="add_friend_searchphone_hint">携帯電話番号を入力してください</string>
<string name="add_friend_searchid_hint">友達のアカウントを入力してください</string>
<string name="add_friend_search_empty_tips">このユーザーは存在しません</string>
<!-- ページを確認 -->
<string name="contact_verify_agree">同意する</string>
<string name="contact_verify_reject">拒否</string>
<string name="contact_verify_agreed">同意しました</string>
<string name="contact_verify_rejected">拒否されました</string>
<string name="contact_verify_expired">期限切れ</string>
<string name="friend_apply">友達申請</string>
<string name="invited_to_join_group">グループに招待します&#160;%s</string>
<string name="apply_to_join_group">%s グループへの参加を申請</string>
<string name="accept_your_friend_apply">友達リクエストを承認しました</string>
<string name="direct_add_your_friend_apply">あなたを友達として直接追加します</string>
<string name="reject_your_friend_apply">友達リクエストを拒否しました</string>
<string name="verify_msg">確認メッセージ</string>
<string name="activity_msg">プッシュ通知</string>
<string name="verify_max_count_text">99+</string>
<string name="verify_agree_message_text">あなたの申請に同意しました。今すぐチャットを始めましょう~</string>
<string name="verify_empty_text">友達申請情報がありません</string>
<!-- 私のチーム ページ -->
<string name="my_team">私のグループ チャット</string>
<!-- 黒いページ -->
<string name="black_list">ブラックリスト</string>
<string name="black_list_tips">友達をブロックすると、相手からメッセージを受信できなくなります。 ブロックされたアカウントに連絡するには、まずブロックを解除してください。 </string>
<string name="contact_black_list_relieve">リリース</string>
<string name="add_black_error">%s をブラックリストに追加できませんでした</string>
<!-- ユーザー情報ページ -->
<string name="clear_all">クリア</string>
<string name="birthday">誕生日</string>
<string name="phone">携帯電話</string>
<string name="email">メール</string>
<string name="personal_signature">個人の署名</string>
<string name="comment_name">コメント名</string>
<string name="notify_message">メッセージ通知</string>
<string name="add_black_list">ブラックリストに追加</string>
<string name="chat">チャット</string>
<string name="delete_friend">友達を削除</string>
<string name="add_friend">友達を追加</string>
<string name="contact_user_info_account">アカウント:%s</string>
<string name="contact_user_info_nickname">ニックネーム:%s</string>
<string name="delete_contact_account">連絡先「%s」を削除</string>
<string name="save">保存</string>
<string name="add_friend_operate_success">正常に送信されました。友達の確認を待っています</string>
<string name="add_friend_operate_fail">送信に失敗しました %s</string>
<!-- 友達ページを追加 -->
<string name="add_friend_search_error">検索に失敗しました</string>
<string name="add_friend_search_result">新規</string>
<!-- トーストエラー -->
<string name="agree_add_friend_fail">友達申請に同意できませんでした</string>
<string name="disagree_add_friend_fail">友達申請を拒否できませんでした</string>
<string name="agree_apply_join_team_fail">チームへの参加に同意できませんでした</string>
<string name="disagree_apply_join_team_fail">グループ申請を拒否できませんでした</string>
<string name="agree_invite_team_fail">グループへの招待に同意できませんでした</string>
<string name="disagree_invite_team_fail">グループへの招待を拒否できませんでした</string>
<string name="remove_black_fail">ブラックリストの削除に失敗しました</string>
<string name="verify_duplicate_fail">確認メッセージは相手側で処理されました</string>
<!-- セレクター-->
<string name="select">友達を選択</string>
<!-- <string name="selector_sure">確認(%d)</string>-->
<string name="selector_sure">次のステップ</string>
<string name="invite_sure">招待</string>
<!-- <string name="selector_sure_without_num">OK</string>-->
<string name="selector_sure_without_num">次のステップ</string>
<string name="contact_selector_max_count">最大 %s 人の友達を選択します</string>
<string name="contact_selector_over_count">人数制限を超えています</string>
<string name="selector_friend_empty">まだ友達がいません</string>
<string name="selector_group_empty">グループ チャットなし</string>
<string name="select_empty_tips">連絡先を選択してください</string>
<string name="fun_selector_close">閉じる</string>
<string name="fun_contact_search">検索</string>
<!-- 検索 -->
<string name="global_search_hint">ユーザー名/グループチャット名のキーワードを入力してください</string>
<string name="global_search_title">検索</string>
<string name="global_searchfriend_title">友達を検索</string>
<string name="global_search_friend_title">友達</string>
<string name="global_search_group_title">ディスカッション グループ</string>
<string name="global_search_team_title">グループ</string>
<string name="global_search_empty">このユーザーは存在しません</string>
<!-- 共通-->
<string name="cancel">キャンセル</string>
<string name="contact_network_error_tip">ネットワークは現在利用できません。ネットワーク設定を確認してください。 </string>
<string name="contact_hint">まだ話していませんね~</string>
<string name="fun_contact_search_hint_text">検索</string>
<string name="fun_contact_searchresult_text">ホームページ</string>
<string name="fun_contact_searchresult_addtext">に追加</string>
<string name="fun_contact_searchself_txt">自分のアカウントを友達として追加できません。 </string>
<string name="fun_contact_searchfriends_txt">あなたはすでに友達です! </string>
<string name="fun_contact_addfriends_verifymsg">確認情報を入力してください</string>
<string name="fun_contact_verifymsg_tips">確認情報を入力してください</string>
<string name="fun_contact_addfriends_remarks">備考</string>
<string name="fun_contact_remarks_tips">備考を入力してください</string>
<string name="fun_contact_addfriends_send">送信</string>
<string name="fun_contact_createteam_tips">名前で検索</string>
<string name="fun_creategroup_members">メンバー %d</string>
<string name="fun_creategroup_uploadhead">アバターをアップロード</string>
<string name="fun_creategroup_autojoin_tips">ユーザーは、招待を受信した後にグループに参加します。この設定をオフにした後、メンバーにグループへの参加を要求するには、招待を手動で承諾する必要があります。 </string>
<string name="fun_creategroup_autojoin">ユーザーは自動的に参加します</string>
<string name="fun_creategroup_name">グループ名</string>
<string name="fun_creategroup_title">グループ ファイルを設定する</string>
<string name="fun_creategroup_sure">作成</string>
<string name="fun_contactselector_last">最新の連絡先 %d</string>
<string name="fun_contactselector_mylove">特別な注意 %d</string>
<string name="fun_contactselector_members">友達全員 %d</string>
<string name="fun_contactselector_result">検索結果</string>
<string name="fun_contactselector_teamqr">QR コード</string>
<string name="fun_contactselector_teamurl">招待 URL</string>
<string name="fun_verifymsg">検証情報</string>
<string name="fun_specialgz_txt">特別な注意</string>
<string name="fun_searchresult_txt">検索結果</string>
<string name="fun_addmembers_txt">新規</string>
<string name="fun_addmemberstoast_txt">少なくとも 1 人の友達を維持してください</string>
<string name="nmhmylg_txt">まだ話していませんね~</string>
<string name="voom_message_txt">[短いビデオと音声メッセージ]</string>
<string name="gerenminp_txt">[名刺]</string>
<string name="zhuanzhang_chat_txt">[転送]</string>
<string name="hongbao_chat_txt">[红包]</string>
<string name="albums_chat_txt">[アルバム]</string>
<string name="helppay_chat_txt">[支払いを手伝う]</string>
<string name="inviteGroup_chat_txt">[グループへの招待]</string>
<string name="custom_chat_txt">[カスタム メッセージ]</string>
<string name="removeblack_txt">ブロックを解除する</string>
<string name="teamdissmiss_txt">このグループは解散しました</string>
<string name="teamjoined_txt">あなたはすでにこのグループに属しています</string>
<string name="know_txt">なるほど</string>
<string name="nodate_txt">まだ情報がありません</string>
</resources>

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<!-- contact list -->
<string name="contact_title">通讯录</string>
<string name="contact_list_verify_msg">验证消息</string>
<string name="contact_list_black_list">黑名单</string>
<string name="contact_list_my_group">我的群聊</string>
<string name="contact_list_my_computer">我的电脑</string>
<!-- add friend page-->
<string name="add_friend_title">添加好友</string>
<string name="search_friend_title">搜索好友</string>
<string name="add_friend_search_hint">请输入账号</string>
<string name="add_friend_searchphone_hint">请输入手机号</string>
<string name="add_friend_searchid_hint">输入好友的账号</string>
<string name="add_friend_search_empty_tips">该用户不存在</string>
<!-- verify page -->
<string name="contact_verify_agree">同意</string>
<string name="contact_verify_reject">拒绝</string>
<string name="contact_verify_agreed">已同意</string>
<string name="contact_verify_rejected">已拒绝</string>
<string name="contact_verify_expired">已过期</string>
<string name="friend_apply">好友申请</string>
<string name="invited_to_join_group">邀请你加入群&#160;%s</string>
<string name="apply_to_join_group">申请加入%s群</string>
<string name="accept_your_friend_apply">同意了你的好友请求</string>
<string name="direct_add_your_friend_apply">直接添加你为好友</string>
<string name="reject_your_friend_apply">拒绝了你的好友请求</string>
<string name="verify_msg">验证消息</string>
<string name="activity_msg">推送通知</string>
<string name="verify_max_count_text">99+</string>
<string name="verify_agree_message_text">我已经同意了你的申请,现在开始聊天吧~</string>
<string name="verify_empty_text">暂无好友申请信息</string>
<!-- my team page -->
<string name="my_team">我的群聊</string>
<!-- black page -->
<string name="black_list">黑名单</string>
<string name="black_list_tips">屏蔽好友后,将不会再收到对方消息。要联系已屏蔽的账号,请先解除屏蔽。</string>
<string name="contact_black_list_relieve">解除</string>
<string name="add_black_error">%s加入黑名单失败</string>
<!-- userinfo page -->
<string name="clear_all">清空</string>
<string name="birthday">生日</string>
<string name="phone">手机</string>
<string name="email">邮箱</string>
<string name="personal_signature">个性签名</string>
<string name="comment_name">备注名</string>
<string name="notify_message">消息通知</string>
<string name="add_black_list">加入黑名单</string>
<string name="chat">聊天</string>
<string name="delete_friend">删除好友</string>
<string name="add_friend">添加好友</string>
<string name="contact_user_info_account">账号:%s</string>
<string name="contact_user_info_nickname">昵称:%s</string>
<string name="delete_contact_account">将联系人“%s”删除</string>
<string name="save">保存</string>
<string name="add_friend_operate_success">发送成功,等待好友验证</string>
<string name="add_friend_operate_fail">发送失败 %s</string>
<!--添加好友页面 -->
<string name="add_friend_search_error">搜索失败</string>
<string name="add_friend_search_result">添加</string>
<!-- error toast -->
<string name="agree_add_friend_fail">同意好友申请失败</string>
<string name="disagree_add_friend_fail">拒绝好友申请失败</string>
<string name="agree_apply_join_team_fail">同意入群申请失败</string>
<string name="disagree_apply_join_team_fail">拒绝入群申请失败</string>
<string name="agree_invite_team_fail">同意入群邀请失败</string>
<string name="disagree_invite_team_fail">拒绝入群邀请失败</string>
<string name="remove_black_fail">黑名单移除失败</string>
<string name="verify_duplicate_fail">该验证消息已在其他端处理</string>
<!-- selector-->
<string name="select">选择好友</string>
<!-- <string name="selector_sure">确定(%d)</string>-->
<string name="selector_sure">下一步</string>
<string name="invite_sure">邀请</string>
<!-- <string name="selector_sure_without_num">确定</string>-->
<string name="selector_sure_without_num">下一步</string>
<string name="contact_selector_max_count">最多选择%s个好友</string>
<string name="contact_selector_over_count">超出人数限制</string>
<string name="selector_friend_empty">暂无好友</string>
<string name="selector_group_empty">暂无群聊</string>
<string name="select_empty_tips">请选择联系人</string>
<string name="fun_selector_close">关闭</string>
<string name="fun_contact_search">搜索</string>
<!-- search -->
<string name="global_search_hint">请输入用户名/群聊名关键字</string>
<string name="global_search_title">搜索</string>
<string name="global_searchfriend_title">搜索好友</string>
<string name="global_search_friend_title">好友</string>
<string name="global_search_group_title">讨论组</string>
<string name="global_search_team_title">群組</string>
<string name="global_search_empty">该用户不存在</string>
<!-- common-->
<string name="cancel">取消</string>
<string name="contact_network_error_tip">当前网络不可用,请检查你当网络设置。</string>
<string name="contact_hint">你们还没有聊过哦~</string>
<string name="fun_contact_search_hint_text">搜索</string>
<string name="fun_contact_searchresult_text">主页</string>
<string name="fun_contact_searchresult_addtext">添加</string>
<string name="fun_contact_searchself_txt">无法添加自己的账号为好友哦!</string>
<string name="fun_contact_searchfriends_txt">你们已经是好友了哦!</string>
<string name="fun_contact_addfriends_verifymsg">填写验证信息</string>
<string name="fun_contact_verifymsg_tips">请输入验证信息</string>
<string name="fun_contact_addfriends_remarks">备注</string>
<string name="fun_contact_remarks_tips">请输入备注</string>
<string name="fun_contact_addfriends_send">发送</string>
<string name="fun_contact_createteam_tips">使用名称进行搜索</string>
<string name="fun_creategroup_members">成员 %d</string>
<string name="fun_creategroup_uploadhead">上传头像</string>
<string name="fun_creategroup_autojoin_tips">用户在收到邀请后就会加入群组,关闭此设定后要求成员加入群组需手动接受邀请。</string>
<string name="fun_creategroup_autojoin">用户自动加入</string>
<string name="fun_creategroup_name">群组名称</string>
<string name="fun_creategroup_title">设定群组档案</string>
<string name="fun_creategroup_sure">建立</string>
<string name="fun_contactselector_last">最近联系 %d</string>
<string name="fun_contactselector_mylove">特别关注 %d</string>
<string name="fun_contactselector_members">全部好友 %d</string>
<string name="fun_contactselector_result">查找结果</string>
<string name="fun_verifymsg">验证信息</string>
<string name="fun_specialgz_txt">特别关注</string>
<string name="fun_searchresult_txt">查找结果</string>
<string name="fun_addmembers_txt">新增</string>
<string name="fun_addmemberstoast_txt">最少保留一位好友</string>
<string name="fun_contactselector_teamqr">行动码</string>
<string name="fun_contactselector_teamurl">邀请网址</string>
<string name="nmhmylg_txt">你们还没有聊过哦~</string>
<string name="voom_message_txt">[短影音消息]</string>
<string name="gerenminp_txt">[名片]</string>
<string name="zhuanzhang_chat_txt">[转账]</string>
<string name="hongbao_chat_txt">[红包]</string>
<string name="albums_chat_txt">[相册]</string>
<string name="helppay_chat_txt">[帮忙代付]</string>
<string name="inviteGroup_chat_txt">[邀请进群]</string>
<string name="custom_chat_txt">[自定义消息]</string>
<string name="removeblack_txt">解除屏蔽</string>
<string name="teamdissmiss_txt">该群已被解散</string>
<string name="teamjoined_txt">您已经在该群组中</string>
<string name="know_txt">我知道了</string>
<string name="nodate_txt">暂无数据</string>
</resources>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<declare-styleable name="ContactListView">
<attr name="nameTextColor" format="color" />
<attr name="nameTextSize" format="dimension" />
<attr name="indexTextColor" format="color" />
<attr name="indexTextSize" format="dimension" />
<attr name="divideLineColor" format="color" />
<attr name="showIndexBar" format="boolean" />
<attr name="showTopPin" format="boolean" />
<attr name="showSelector" format="boolean" />
<attr name="avatarCornerRadius" format="dimension" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<declare-styleable name="IndexBar">
<attr name="indexBarTextSize" format="dimension" />
<attr name="indexBarTextColor" format="color" />
<attr name="indexBarTextColorPress" format="color" />
<attr name="indexBarPressBackground" format="color" />
</declare-styleable>
</resources>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<declare-styleable name="TitleBarView">
<attr name="head_img_src" format="integer" />
<attr name="head_img_visible" format="enum">
<enum name="visible" value="0"/>
<enum name="invisible" value="4"/>
<enum name="gone" value="8"/>
</attr>
<attr name="right_img_src" format="integer"/>
<attr name="right_img_second_src" format="integer"/>
<attr name="head_title" format="string"/>
<attr name="head_title_color" format="color"/>
</declare-styleable>
</resources>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<color name="title_color">#333333</color>
<color name="color_14131b">#ff14131b</color>
<color name="color_ffffff">#ffffffff</color>
<color name="color_f5f8fc">#F5F8FC</color>
<color name="color_e5e5e5">#E5E5E5</color>
<color name="color_58be6b">#58BE6B</color>
<color name="color_ededed">#ededed</color>
<color name="color_f7f7f7">#F7F7F7</color>
<color name="color_525c8c">#525C8C</color>
<color name="color_537ff4">#537FF4</color>
<color name="color_f2f4f5">#F2F4F5</color>
<color name="color_337eff">#337EFF</color>
<color name="color_b3b7bc">#B3B7BC</color>
<color name="color_d9d9d9">#D9D9D9</color>
<color name="color_666666">#666666</color>
<color name="color_a6adb6">#A6ADB6</color>
<color name="color_e6605c">#E6605C</color>
<color name="color_eff1f4">#EFF1F4</color>
<color name="color_8f8f8f">#8F8F8F</color>
<color name="color_929299">#929299</color>
<color name="color_3eaf96">#3eaf96</color>
<color name="color_4d000000">#4d000000</color>
<color name="color_f24957">#f24957</color>
<color name="color_ededef">#ededef</color>
<color name="color_e9eff5">#e9eff5</color>
<color name="color_dbdde4">#dbdde4</color>
<color name="color_dbe0e8">#DBE0E8</color>
<color name="title_transfer">#00000000</color>
<color name="color_white">#FFFFFF</color>
<color name="color_fe6881">#FE6881</color>
<color name="color_eeeeee">#eeeeee</color>
<color name="color_09bb07">#09BB07</color>
<color name="color_fcfcfc">#FCFCFC</color>
<color name="color_bcbcbc">#BCBCBC</color>
<color name="color_d8d8d8">#D8D8D8</color>
<color name="color_d4dbd6">#D4DBD6</color>
<color name="color_7f7b81">#7F7B81</color>
<color name="color_6877fe">#6877FE</color>
<color name="color_6d80ff">#6d80ff</color>
<color name="color_6d80ff_alpha70">#4d6D80FF</color> <!--透明度70-->
<color name="color_999999">#999999</color> <!--透明度70-->
<color name="color_6d94ff">#6d94ff</color>
</resources>

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<dimen name="page_margin_left_right">20dp</dimen>
<dimen name="page_title_size">20dp</dimen>
<dimen name="page_title_bar_height">60dp</dimen>
<dimen name="image_operate_width_height">24dp</dimen>
<dimen name="image_operate_margin">12dp</dimen>
<dimen name="image_head_width_height">32dp</dimen>
<dimen name="image_contact_width_height">36dp</dimen>
<dimen name="dimen_1_dp">1dp</dimen>
<dimen name="dimen_3_dp">3dp</dimen>
<dimen name="dimen_4_dp">4dp</dimen>
<dimen name="dimen_5_dp">5dp</dimen>
<dimen name="dimen_6_dp">6dp</dimen>
<dimen name="dimen_7_dp">7dp</dimen>
<dimen name="dimen_8_dp">8dp</dimen>
<dimen name="dimen_9_dp">9dp</dimen>
<dimen name="dimen_10_dp">10dp</dimen>
<dimen name="dimen_12_dp">12dp</dimen>
<dimen name="dimen_13_dp">13dp</dimen>
<dimen name="dimen_14_dp">14dp</dimen>
<dimen name="dimen_15_dp">15dp</dimen>
<dimen name="dimen_16_dp">16dp</dimen>
<dimen name="dimen_18_dp">18dp</dimen>
<dimen name="dimen_20_dp">20dp</dimen>
<dimen name="dimen_23_dp">23dp</dimen>
<dimen name="dimen_24_dp">24dp</dimen>
<dimen name="dimen_25_dp">25dp</dimen>
<dimen name="dimen_26_dp">26dp</dimen>
<dimen name="dimen_28_dp">28dp</dimen>
<dimen name="dimen_32_dp">32dp</dimen>
<dimen name="dimen_42_dp">42dp</dimen>
<dimen name="dimen_46_dp">46dp</dimen>
<dimen name="dimen_52_dp">52dp</dimen>
<dimen name="dimen_55_dp">55dp</dimen>
<dimen name="dimen_56_dp">56dp</dimen>
<dimen name="dimen_60_dp">60dp</dimen>
<dimen name="dimen_64_dp">64dp</dimen>
<dimen name="dimen_88_dp">64dp</dimen>
<dimen name="dimen_100_dp">100dp</dimen>
<dimen name="dimen_115_dp">115dp</dimen>
<dimen name="dimen_118_dp">118dp</dimen>
<dimen name="verify_name_max_len">120dp</dimen>
<dimen name="dimen_163_dp">163dp</dimen>
<dimen name="dimen_188_dp">188dp</dimen>
<dimen name="verify_action_max_len">200dp</dimen>
<dimen name="text_size_8">8sp</dimen>
<dimen name="text_size_11">11sp</dimen>
<dimen name="text_size_12">12sp</dimen>
<dimen name="text_size_13">13sp</dimen>
<dimen name="text_size_14">14sp</dimen>
<dimen name="text_size_16">16sp</dimen>
<dimen name="text_size_18">18sp</dimen>
<dimen name="text_size_22">22sp</dimen>
</resources>

View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (c) 2022 NetEase, Inc. All rights reserved.
~ Use of this source code is governed by a MIT license that can be
~ found in the LICENSE file.
-->
<resources>
<!-- contact list -->
<string name="contact_title">通訊錄</string>
<string name="contact_list_verify_msg">驗證訊息</string>
<string name="contact_list_black_list">黑名單</string>
<string name="contact_list_my_group">我的群組聊天</string>
<string name="contact_list_my_computer">我的電腦</string>
<!-- add friend page-->
<string name="add_friend_title">新增好友</string>
<string name="search_friend_title">搜尋好友</string>
<string name="add_friend_search_hint">請輸入帳號</string>
<string name="add_friend_searchphone_hint">請輸入手機號碼</string>
<string name="add_friend_searchid_hint">輸入好友的帳號</string>
<string name="add_friend_search_empty_tips">該使用者不存在</string>
<!-- verify page -->
<string name="contact_verify_agree">同意</string>
<string name="contact_verify_reject">拒絕</string>
<string name="contact_verify_agreed">已同意</string>
<string name="contact_verify_rejected">已拒絕</string>
<string name="contact_verify_expired">已過期</string>
<string name="friend_apply">好友申請</string>
<string name="invited_to_join_group">邀請你加入群組&#160;%s</string>
<string name="apply_to_join_group">申請加入%s群</string>
<string name="accept_your_friend_apply">同意了你的好友請求</string>
<string name="direct_add_your_friend_apply">直接加入你為好友</string>
<string name="reject_your_friend_apply">拒絕了你的好友請求</string>
<string name="verify_msg">驗證訊息</string>
<string name="activity_msg">推送通知</string>
<string name="verify_max_count_text">99+</string>
<string name="verify_agree_message_text">我已經同意了你的申請,現在開始聊天吧~</string>
<string name="verify_empty_text">暫無好友申請資料</string>
<!-- my team page -->
<string name="my_team">我的群組聊天</string>
<!-- black page -->
<string name="black_list">黑名單</string>
<string name="black_list_tips">封鎖好友後,將不會再收到對方訊息。若要聯絡已封鎖的帳號,請先解除封鎖。</string>
<string name="contact_black_list_relieve">解除</string>
<string name="add_black_error">%s加入黑名單失敗</string>
<!-- userinfo page -->
<string name="clear_all">清空</string>
<string name="birthday">生日</string>
<string name="phone">手機</string>
<string name="email">郵箱</string>
<string name="personal_signature">個性簽名</string>
<string name="comment_name">備註名稱</string>
<string name="notify_message">訊息通知</string>
<string name="add_black_list">加入黑名單</string>
<string name="chat">聊天</string>
<string name="delete_friend">刪除好友</string>
<string name="add_friend">新增好友</string>
<string name="contact_user_info_account">帳號:%s</string>
<string name="contact_user_info_nickname">暱稱:%s</string>
<string name="delete_contact_account">將聯絡人「%s」刪除</string>
<string name="save">儲存</string>
<string name="add_friend_operate_success">發送成功,等待好友驗證</string>
<string name="add_friend_operate_fail">傳送失敗 %s</string>
<!--新增好友頁面 -->
<string name="add_friend_search_error">搜尋失敗</string>
<string name="add_friend_search_result">新增</string>
<!-- error toast -->
<string name="agree_add_friend_fail">同意好友申請失敗</string>
<string name="disagree_add_friend_fail">拒絕好友申請失敗</string>
<string name="agree_apply_join_team_fail">同意入群申請失敗</string>
<string name="disagree_apply_join_team_fail">拒絕入群申請失敗</string>
<string name="agree_invite_team_fail">同意入群邀請失敗</string>
<string name="disagree_invite_team_fail">拒絕入群邀請失敗</string>
<string name="remove_black_fail">黑名單移除失敗</string>
<string name="verify_duplicate_fail">該驗證訊息已在其他端處理</string>
<!-- selector-->
<string name="select">選擇好友</string>
<!-- <string name="selector_sure">確定(%d)</string>-->
<string name="selector_sure">下一步</string>
<string name="invite_sure">邀請</string>
<!-- <string name="selector_sure_without_num">確定</string>-->
<string name="selector_sure_without_num">下一步</string>
<string name="contact_selector_max_count">最多選擇%s個好友</string>
<string name="contact_selector_over_count">超出人數限制</string>
<string name="selector_friend_empty">暫無好友</string>
<string name="selector_group_empty">暫無群組聊天</string>
<string name="select_empty_tips">請選擇聯絡人</string>
<string name="fun_selector_close">關閉</string>
<string name="fun_contact_search">搜尋</string>
<!-- search -->
<string name="global_search_hint">請輸入使用者名稱/群組聊天名稱關鍵字</string>
<string name="global_search_title">搜尋</string>
<string name="global_searchfriend_title">搜尋好友</string>
<string name="global_search_friend_title">好友</string>
<string name="global_search_group_title">討論群組</string>
<string name="global_search_team_title">群組</string>
<string name="global_search_empty">該使用者不存在</string>
<!-- common-->
<string name="cancel">取消</string>
<string name="contact_network_error_tip">目前網路不可用,請檢查你當網路設定。 </string>
<string name="contact_hint">你們還沒聊喔~</string>
<string name="fun_contact_search_hint_text">搜索</string>
<string name="fun_contact_searchresult_text">首頁</string>
<string name="fun_contact_searchresult_addtext">添加</string>
<string name="fun_contact_searchself_txt">無法新增自己的帳號為好友喔! </string>
<string name="fun_contact_searchfriends_txt">你們已經是好友了喔! </string>
<string name="fun_contact_addfriends_verifymsg">填入驗證資訊</string>
<string name="fun_contact_verifymsg_tips">請輸入驗證資訊</string>
<string name="fun_contact_addfriends_remarks">備註</string>
<string name="fun_contact_remarks_tips">請輸入備註</string>
<string name="fun_contact_addfriends_send">發送</string>
<string name="fun_contact_createteam_tips">使用名稱進行搜尋</string>
<string name="fun_creategroup_members">成員 %d</string>
<string name="fun_creategroup_uploadhead">上傳頭像</string>
<string name="fun_creategroup_autojoin_tips">使用者收到邀請後就會加入群組,關閉此設定後要求成員加入群組需手動接受邀請。 </string>
<string name="fun_creategroup_autojoin">使用者自動加入</string>
<string name="fun_creategroup_name">群組名稱</string>
<string name="fun_creategroup_title">設定群組檔案</string>
<string name="fun_creategroup_sure">建立</string>
<string name="fun_contactselector_last">最近聯絡 %d</string>
<string name="fun_contactselector_mylove">特別關注 %d</string>
<string name="fun_contactselector_members">全部好友 %d</string>
<string name="fun_contactselector_result">找結果</string>
<string name="fun_contactselector_teamqr">行動碼</string>
<string name="fun_contactselector_teamurl">邀請網址</string>
<string name="fun_verifymsg">驗證資訊</string>
<string name="fun_specialgz_txt">特別關注</string>
<string name="fun_searchresult_txt">找結果</string>
<string name="fun_addmembers_txt">新增</string>
<string name="fun_addmemberstoast_txt">最少保留一位好友</string>
<string name="nmhmylg_txt">你們還沒聊過喔~</string>
<string name="voom_message_txt">[短影音訊息]</string>
<string name="gerenminp_txt">[名片]</string>
<string name="zhuanzhang_chat_txt">[轉帳]</string>
<string name="hongbao_chat_txt">[紅包]</string>
<string name="albums_chat_txt">[相簿]</string>
<string name="helppay_chat_txt">[幫忙代付]</string>
<string name="inviteGroup_chat_txt">[邀請進群]</string>
<string name="custom_chat_txt">[自訂訊息]</string>
<string name="removeblack_txt">解除屏蔽</string>
<string name="teamdissmiss_txt">該群已被解散</string>
<string name="teamjoined_txt">您已經在該群組中</string>
<string name="know_txt">我知道了</string>
<string name="nodate_txt">暫無數據</string>
</resources>

View File

@@ -0,0 +1,21 @@
// Copyright (c) 2022 NetEase, Inc. All rights reserved.
// Use of this source code is governed by a MIT license that can be
// found in the LICENSE file.
package com.netease.yunxin.kit.contactkit.ui;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}