준비사항
앱을 실행하기 위한 필수 라이브러리와 프로젝트 구조를 알아보자
프로젝트의 폴더 구조
프로젝트 ( app 폴더 외 파일은 제외 )
├─build.gradle
├─libs
│ ├─android-support-v4.jar
│ └─BGAPhotoPicker.aar
├─src.test.java.com.vchatcloud.androidsample.ExampleUnitTest.java
├─src.androidTest.java.com.vchatcloud.androidsample.ExampleInstrumentedTest.java
└─src.main
├─AndroidManifest.xml
├─ic_launcher-playstore.png
├─res
│ ├─color
│ ├─drawable
│ ├─drawable-v24
│ ├─layout
│ ├─mipmap-anydpi-v26
│ ├─mipmap-hdpi
│ ├─mipmap-mdpi
│ ├─mipmap-xhdpi
│ ├─mipmap-xxhdpi
│ ├─mipmap-xxxhdpi
│ ├─values
│ └─xml
└─java.com.vchatcloud.androidsample
├─adapter
│ ├─ChatListAdapter.java
│ ├─EmojiAdapter.java
│ └─HorizontalListView.java
├─fragment
│ └─EmojiFragment.java
├─photoView
│ ├─HackyViewPager.java
│ └─ViewPagerActivity.java
├─resourse
│ ├─Constant.java
│ ├─EmojiRescourse.java
│ └─ProfileRescource.java
├─viewpagerindicator
│ ├─CirclePageIndicator.java
│ └─PageIndicator.java
├─BackKeyHandler.java
├─ChatActivity.java
├─ImageEdit.java
├─LoadingActivity.java
├─MainActivity.java
├─Message.java
├─PreferenceManager.java
└─ProgressRequestBody.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
필수 변경사항
샘플 코드를 다운로드 받으면 반드시 channelKey
값을 변경해야 합니다.
하단의 설명을 통해서 변경할 부분을 확인하여서 변경해주시면 됩니다.
폴더의 구성은 프로젝트의 폴더 구조
를 참고해 주세요.
안드로이드 소스코드 💾
- CMS 화면에서 개설한 채팅방의
Channel Key
를 복사한다. - 안드로이드용 샘플 앱을 다운로드 한다.
- 파일을 압축을 해제하여
app\src\main\java\com\vchatcloud\androidsample\resourse
폴더 안에 Constant.java
파일을 편집기로 열어준다. ROOM_ID
라는 변수명 값에 1번에서 복사한 key 값을 붙여 넣어준다.
public class Constant {
public static final String ROOM_ID = "VBrhzLZgGG-nAFo5CS7jp-20210802120142";
public static final String BASE = "https://vchatcloud.com";
public static final String SAVEFILE = "/api/openapi/saveFile";
public static final String LOADFILE = "/api/openapi/loadFile";
}
1
2
3
4
5
6
vchatcloud 객체
vchatcloud 클래스
VChatCloud 인스턴스는 싱글톤으로 호출이 가능하다.
VChatCloud.getInstance()
1
channel 메소드 선언
channel는 vchatCloud 인스턴스에 구현된 메소드입니다.
channel 메소드를 이용하여 채팅방에 진입하고, 메시지 이벤트들을 컨트롤할 수 있습니다.
아래 코드는 채팅방 진입만 다루고 있고, 메시지 전송, 공지등은 자세한 이벤트 설명은 좌측 메뉴리스트의 메시지
를 참고해주세요.
ChannelOptions options = new ChannelOptions();
options.setChannelKey(room_id);
options.setClientKey(deviceUuid);
options.setNickName(nick_name);
options.setUserInfo(userInfo);
Channel channel = VChatCloud.getInstance().joinChannel(options, new JoinChannelCallback() {
public void callback(JSONArray history, VChatCloudException e) {
super.callback(history, e);
int historySize = history.size() -1;
for (; historySize >= 0; historySize--) {
Log.d("히스토리", "" + history.get(historySize) );
}
Log.d("히스토리", "룸 시작");
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
파라미터 값(options)
값 | 식별자 | 설명 |
ChannelKey | String | 채팅방 생성 후 발급받은 Channel Key |
ClientKey | String | 접속 단말 설정 고유키 |
NickName | String | 채팅방 입장 유저의 별명 |
UserInfo | String | 유저 프로필정보 |
결과 값
값 | 식별자 | 설명 |
code | String | 에러 코드 |
type | String | 에러 타입 |
message | String | 에러 메시지 |
값 | 식별자 | 설명 |
logType | String | 로그타입 |
roomId | String | 채팅방 생성 후 발급받은 Channel Key |
clientKey | String | 접속 단말 설정 고유키 |
message | String | 메시지내용 |
mimeType | String | 메시지형태 (text: 일반텍스트, emoji: 이모지) |
messageType | String | 빈값이면 일반 메시지, 공지일경우 : "notice" |
nickName | String | 채팅방 입장 유저의 별명 |
date | String | 전송시간 |
grade | String | 유저등급 |
코드 작성
실제 앱을 동작을 위한 코드로
채팅방으로 입장 / 채팅 / 귓속말 / 전체공지 / 추방 / 글쓰기 제한 으로 구성되어 있습니다.
입장 시에 채팅방 번호 및 별칭을 입력하는 로그인 팝업으로 진행하오니 이점 참고해 주세요.
아래에는 각각 이벤트를 컨트롤하는 java 코드 부분입니다.
로그인을 하기 위한 MainActivity의 설명 코드 입니다.
닉네임과 프로필정보를 받아서 ChatActivity 로 값을 넘기도록 되어있고,
알림 권한을 받아서 처리하는 부분이 일부 들어가 있습니다.
로그인 창 java 코드보기
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
roomId = Constant.ROOM_ID;
setContentView(R.layout.main);
setUpHorizontalListView();
GradientDrawable myGrad = (GradientDrawable) getApplicationContext().getDrawable(R.drawable.profile_img_back_ground);
DisplayMetrics outMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
appMetrics = outMetrics;
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager.deleteNotificationChannel(String.valueOf(R.string.channel_id));
notificationChannel = new NotificationChannel(String.valueOf(R.string.channel_id), String.valueOf(R.string.channel_name), NotificationManager.IMPORTANCE_HIGH);
notificationChannel.setDescription(String.valueOf(R.string.channel_description));
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.BLUE);
notificationChannel.enableVibration(true);
notificationChannel.setShowBadge(true);
notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(notificationChannel);
}
et_nick = findViewById(R.id.et_nick);
btn_send = findViewById(R.id.btn_send);
et_nick.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
buttonFlag = true;
if (start < 1 && count < 1) {
if (charSequence.length() < 1) {
buttonFlag = false;
et_nick.setError("사용자 이름을 한글자 이상 입력해 주세요.");
}
}
}
@Override
public void afterTextChanged(Editable editable) {
}
});
et_nick.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
btn_send.callOnClick();
}
return true;
}
});
btn_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (buttonFlag && profile_select != 0) {
Intent intent = new Intent(MainActivity.this, ChatActivity.class);
intent.putExtra("room", roomId);
intent.putExtra("started", started);
intent.putExtra("nick", et_nick.getText().toString());
intent.putExtra("nickIcon", profile_select);
startActivity(intent);
} else {
if (!buttonFlag) {
Toast toast = Toast.makeText(MainActivity.this, "사용자 아이디를 입력해 주세요.", Toast.LENGTH_SHORT);
toast.show();
if (et_nick.requestFocus()) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);
}
} else if (profile_select == 0) {
Toast toast = Toast.makeText(MainActivity.this, "캐릭터를 선택해 주세요.", Toast.LENGTH_SHORT);
toast.show();
}
}
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
채팅글을 화면에 그리는 메소드 입니다.
본 앱이 비활성화일 경우 알림 메시지를 전송하도록 되어있습니다.
Adapter 를 통해 실제 화면에 입력된 메시지가 노출되어지고 있습니다.
채팅영역에 글쓰기 java 코드보기
public void messageExposure(Message message, final boolean sendScroll) {
int profile_index = 0;
if (ChatActivity.notificationFlag && (message.getMimeType().equalsIgnoreCase("text"))) {
++msgId;
NotificationCompat.Builder builder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new NotificationCompat.Builder(this, String.valueOf(R.string.channel_id));
}else {
builder = new NotificationCompat.Builder(this);
}
Intent resultIntent = new Intent(this, ChatActivity.class);
resultIntent.putExtra("scroll","true");
PendingIntent pendingIntent = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_CANCEL_CURRENT);
}
} catch (Exception e) {
e.printStackTrace();
}
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Ringtone ringtone = RingtoneManager.getRingtone(getApplicationContext(),defaultSoundUri);
ringtone.play();
if (message.getMessageType() != null) {
profile_index = Integer.parseInt((String) message.getMessageType().get("profile")) - 1;
}
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), MainActivity.ProfileInfo(profile_index));
Log.d(TAG, message.getClientKey());
builder
.setContentTitle(message.getNickName())
.setContentText(message.getMessage())
.setSmallIcon(R.drawable.vchat_logo)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setColor(ContextCompat.getColor(this, R.color.notificationColor))
.setLargeIcon(bitmap)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_SOUND)
.setContentIntent(pendingIntent);
Notification notification = builder.build();
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
String cId = message.getClientKey();
msgId = 0;
for (int i = 0; i < cId.length(); i++) {
msgId = (cId.charAt(i) - 64) + msgId;
}
notificationManager.notify(msgId, notification);
}
if ("emoji_img".equalsIgnoreCase(message.getMimeType())) {
message.setType("emoji_img");
} else if ("file".equalsIgnoreCase(message.getMimeType())) {
message.setType("file");
}
Log.d(TAG, message.toString());
$Adapter.addMsg(message);
runOnUiThread(new Runnable() {
@Override
public void run() {
$Adapter.notifyDataSetChanged();
if (scrollstate) {
$RecyclerView.smoothScrollToPosition($Adapter.getItemCount());
}
if (sendScroll) {
$RecyclerView.smoothScrollToPosition($Adapter.getItemCount());
}
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
이모티콘 프래그먼트를 활성화하여 노출되도록 되어있습니다.
활성화가 되어지면, 아이콘의 색상을 변경하고,
하단에 이모지 화면이 노출 됩니다.
이모티콘 창 노출 java 코드보기
@Override
protected void onCreate(Bundle savedInstanceState) {
$EmojiButton = (ImageButton) findViewById(R.id.button_open_channel_chat_emoji);
$EmojiButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (emojiFlag) {
closeEmoji();
} else {
openEmoji();
}
}
});
}
private void openEmoji() {
emojiFlag = true;
$EmojiButton.setImageResource(R.drawable.ic_round_mood_24_active);
transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.frameLayout, emojiFragment).commitAllowingStateLoss();
}
private void closeEmoji() {
emojiFlag = false;
$EmojiButton.setImageResource(R.drawable.ic_round_mood_24);
transaction = fragmentManager.beginTransaction();
transaction.remove(emojiFragment).commitAllowingStateLoss();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
귓속말은 이미 작성되어진 채팅 글에 롱 클릭 이벤트를 이용하여 AlertDialog 를 팝업하도록 구현되어있습니다.
귓속말 팝업창 java 코드보기
$Adapter.setOnItemLongClickListener(new ChatListAdapter.OnItemLongClickListener() {
@Override
public void onUserMessageItemLongClick(Message message, int position) {
Log.d("message >> ", message.toString());
AlertDialog.Builder ad = new AlertDialog.Builder(ChatActivity.this);
final EditText et = new EditText(ChatActivity.this);
ad.setTitle(message.getNickName() + " 님에게 귓속말 보내기");
ad.setMessage("");
ad.setView(et);
ad.setPositiveButton("전송", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG, "Yes Btn Click");
String value = et.getText().toString();
Log.d(TAG, value);
JSONObject json = new JSONObject();
json.put("receivedClientKey", message.getClientKey());
json.put("message", value);
json.put("mimeType", "text");
channel.sendWhisper(json, new ChannelCallback() {
@Override
public void callback(Object o, VChatCloudException e) {
if (e != null) {
Log.d(TAG,"메시지 전송오류");
}
$RecyclerView.smoothScrollToPosition($Adapter.getItemCount());
}
});
dialog.dismiss();
}
});
ad.setNegativeButton("취소", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Log.d(TAG,"No Btn Click");
dialog.dismiss();
}
});
ad.show();
}
});
if (longClickListener != null) {
itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
longClickListener.onUserMessageItemLongClick(message, position);
return true;
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59