Preparations
Let's look at the required libraries and project structure to run the app.
Project folder structure
Project (excluding files other than the app folder)
├─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
Required changes
If you download the sample code, be sure to change the 'channelKey' value.
You can check the part to be changed through the explanation below and change it.
For folder structure, refer to [project folder structure
] (#Project-Folder-Structure).
android source code 💾
- Copy the 'Channel Key' of the chat room opened on the CMS screen.
- Download the sample app for Android.
- Unzip the file and open the
Constant.java
file in the app\src\main\java\com\vchatcloud\androidsample\resourse
folder with an editor. - Paste the key value copied in step 1 to the value of the variable named
ROOM_ID
.
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 object
vchatcloud class
A VChatCloud instance can be called as a singleton.
VChatCloud.getInstance()
1
channel method declaration
channel is a method implemented on the vchatCloud instance.
You can use the channel method to enter a chat room and control message events.
The code below deals only with entering the chat room, and for detailed event descriptions such as sending messages and notifications, please refer to 'Message' in the left menu list.
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", "" + history.get(historySize) );
}
Log.d("History", "Room start");
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
write code
Actual app as code for operation
It consists of entry to the chat room / chat / whisper / public notice / expulsion / restriction on writing.
Please note that you will be directed to a login pop-up where you enter the chat room number and alias upon entering.
Below are the java code parts that control each event.
This is the explanation code of MainActivity for logging in.
It is supposed to receive nickname and profile information and pass the value to ChatActivity,
There are some parts that receive notification rights and process them.
view login window java code
@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("Please enter at least one character for your user name.");
}
}
}
@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, "Please enter a user ID.", 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, "Please select a character.", 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
This method draws the chat text on the screen.
If this app is inactive, it is designed to send a notification message.
Messages entered on the actual screen are exposed through the adapter.
View java code for writing in chat area
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
It is intended to be exposed by activating the emoticon fragment.
When activated, change the color of the icon,
The emoji screen is exposed at the bottom.
view emoticon window exposure java code
@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
Whisper is implemented to pop up an AlertDialog using a long click event on an already written chat post.
Whisper pop-up view java code
$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() + "Send whisper to Nick");
ad.setMessage("");
ad.setView(et);
ad.setPositiveButton("Send", 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,"message transmission error");
}
$RecyclerView.smoothScrollToPosition($Adapter.getItemCount());
}
});
dialog. dismiss();
}
});
ad.setNegativeButton("Cancel", 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