Qiscus SDK Android
Quick Start
Create a new app
Register on https://www.qiscus.com/dashboard using your email and password and then create new application
You should create one application per service, regardless of the platform. For example, an app released in both Android and iOS would require only one application to be created in the Dashboard.
All users within the same Qiscus application are able to communicate with each other, across all platforms. This means users using iOS, Android, web clients, etc. can all chat with one another. However, users in different Qiscus applications cannot talk to each other.
Done! Now you can use the APP_ID into your apps and get chat functionality by implementing Qiscus into your app.
integrating SDK with an existing app
Add to your project build.gradle
allprojects {
repositories {
.....
maven {
url "https://dl.bintray.com/qiscustech/maven"
}
}
}
Then add to your app module build.gradle
dependencies {
compile 'com.qiscus.sdk:chat:2.14.0'
}
Authentication
Init with APP ID
Init Qiscus at your application class with your application ID
public class SampleApps extends Application {
@Override
public void onCreate() {
super.onCreate();
Qiscus.init(this, "APP_ID");
}
}
Setup user using userId and userKey
Before user can start chatting each other, they must login to qiscus engine.
Qiscus.setUser("[email protected]", "userKey")
.withUsername("Tony Stark")
.withAvatarUrl("http://avatar.url.com/handsome.jpg")
.save(new Qiscus.SetUserListener() {
@Override
public void onSuccess(QiscusAccount qiscusAccount) {
startActivity(new Intent(this, ConsultationListActivity.class));
}
@Override
public void onError(Throwable throwable) {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
}
);
Setup user using JWT Token
Another alternative is using jwt token. Using this authorization schema, you can only save your user unique identity such as email in your back-end server. You will no need to save two password, one for Qiscus SDK and one for your authorization logic. All you need is generating identity token using JWT in order to login or register an user.
First, you need to get nonce using QiscusApi.requestNonce() method. You do not need to send any parameter. Nonce will be expired 10 minutes after request. Afterwards, in your application back-end server you must generate the JWT token using this:
JOSE header
{
"alg": "HS256", // must be HMAC algorithm
"typ": "JWT", // must be JWT
"ver": "v2" // must be v2
}
JWT claim set
{
"iss": "QISCUS SDK APP ID", // your qiscus app id, can obtained from dashboard
"iat": 1502985644, // current timestamp in unix
"exp": 1502985704, // An arbitrary time in the future when this token should expire. In epoch/unix time. We encourage you to limit 2 minutes
"nbf": 1502985644, // current timestamp in unix
"nce": "nonce", // nonce string from nonce API
"prn": "[email protected]", // your user identity such as email
"name": "shark laser", // optional, string for user name
"avatar_url": "" // optional, string url of user avatar
}
Above JOSE header and claim set must be signed using QISCUS SDK SECRET key that you can get from dashboard with algorithm HMAC (HS256, HS384 or HS512). Then you can verify your identity token using Qiscus.setUser(String token, Qiscus.SetUserListener listener) method or Qiscus.setUserAsObservable(String token) method if you want to using RxJava. Here sample code how to set user using jwt token.
QiscusApi.getInstance().requestNonce() //Request nonce from qiscus api
.flatMap(nonce -> YourAppApi.getInstance().getJwtToken(nonce)) //Get jwt token from your backend api
.flatMap(Qiscus::setUserAsObservable) //Set qiscus user with the jwt token
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(qiscusAccount -> {
startActivity(new Intent(this, ConsultationListActivity.class));
}
, throwable -> {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
);
Updating a User Profile and Avatar
Updating user profile calls Qiscus.updateUser(name, avatar, listener) :
Qiscus.updateUser("Tony Stark", "http://avatar.url.com/handsome.jpg", new Qiscus.SetUserListener() {
@Override
public void onSuccess(QiscusAccount qiscusAccount) {
startActivity(new Intent(this, ConsultationListActivity.class));
}
@Override
public void onError(Throwable throwable) {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
}
);
Disconnect or Logout
Whenever you no longer want the user to receive update
Qiscus.clearUser();
Chat Rooms
Creating 1-to-1 chat
Start chat with target is very easy, all you need is just call
Qiscus.buildChatWith("[email protected]")
.build(this, new Qiscus.ChatActivityBuilderListener() {
@Override
public void onSuccess(Intent intent) {
startActivity(intent);
}
@Override
public void onError(Throwable throwable) {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
}
);
Creating a Group Room
Qiscus also support group chat. To create new group chat, all you need is just call
Qiscus.buildGroupChatRoom("GroupName", Arrays.asList("[email protected]", "[email protected]", "[email protected]"))
.withAvatar("http://avatar.url.com/group.jpg")
.build(new Qiscus.ChatBuilderListener() {
@Override
public void onSuccess(QiscusChatRoom qiscusChatRoom) {
startActivity(QiscusGroupChatActivity.generateIntent(MainActivity.this, qiscusChatRoom));
}
@Override
public void onError(Throwable throwable) {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
}
);
for accessing room that created by this call, you need to call it with its roomId. This methode is always creating new chat room.
Get a room by room id
When you already know your chat room id, you can easily go to that room. Just call
QiscusApi.getChatRoom(int roomId);
For example :
QiscusApi.getInstance()
.getChatRoom(123)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(qiscusChatRoom -> QiscusGroupChatActivity.generateIntent(this, qiscusChatRoom))
.subscribe(this::startActivity, throwable -> {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
);
Create or join room by defined id
You probably want to set defined id for the room you are creating so that the id can be reference for users to get into.
Usual usage for this is when user create common room or channel which expecting other users can join to the same channel by knowing the channel name or id, you can use the channel name or id as qiscus room defined id.
Additional note: If room with predefined unique id is not exist then it will create a new one with requester as the only one participant. Otherwise, if room with predefined unique id is already exist, it will return that room and add requester as a participant.
When first call (room is not exist), if requester did not send avatar_url and/or room name it will use default value. But, after the second call (room is exist) and user (requester) send avatar_url and/or room name, it will be updated to that value. Object changed will be true in first call and when avatar_url or room name is updated.
Qiscus.buildGroupChatRoomWith("UniqueId")
.withName("RoomName")
.withAvatar("http://avatar.url.com/group.jpg")
.build(new Qiscus.ChatBuilderListener() {
@Override
public void onSuccess(QiscusChatRoom qiscusChatRoom) {
startActivity(QiscusGroupChatActivity.generateIntent(MainActivity.this, qiscusChatRoom));
}
@Override
public void onError(Throwable throwable) {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
}
);
Inviting users to an existing Room
Currently we recommend to invite user into existing room through our REST API for simplicity and security reason
Leaving a Group Room
Currently we recommend to kick user out of specific room through our REST API for simplicity and security reason
Get room list
To get all room list you can call QiscusApi.getInstance().getChatRooms(int page, int limit, boolean showMembers), page start from 1, limit indicate the max rooms per page, showMembers is flag for load room members also or not. Here sample code:
QiscusApi.getInstance().getChatRooms(1, 20, true)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(qiscusChatRooms -> {
//qiscusChatRooms is list of rooms result.
}
, throwable -> {
//Something went wrong
}
);
Event Handler
Implement QiscusChatPresenter.View to your Activity Or Fragment
public class MainActivity extends AppCompatActivity implements QiscusChatPresenter.View {
private QiscusChatPresenter qiscusChatPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
qiscusChatPresenter = new QiscusChatPresenter(this, qiscusChatRoom);
}
@Override
public void initRoomData(QiscusChatRoom qiscusChatRoom, List<QiscusComment> comments) {
// Do your implementation
}
@Override
public void showComments(List<QiscusComment> qiscusComments) {
// Do your implementation
}
@Override
public void onLoadMore(List<QiscusComment> qiscusComments) {
// Do your implementation
}
@Override
public void onSendingComment(QiscusComment qiscusComment) {
// Do your implementation
}
@Override
public void onSuccessSendComment(QiscusComment qiscusComment) {
// Do your implementation
}
@Override
public void onFailedSendComment(QiscusComment qiscusComment) {
// Do your implementation
}
@Override
public void onNewComment(QiscusComment qiscusComment) {
// Do your implementation
}
@Override
public void onCommentDeleted(QiscusComment qiscusComment) {
// Do your implementation
}
@Override
public void refreshComment(QiscusComment qiscusComment) {
// Do your implementation
}
@Override
public void updateLastDeliveredComment(int lastDeliveredCommentId) {
// Do your implementation
}
@Override
public void updateLastReadComment(int lastReadCommentId) {
// Do your implementation
}
@Override
public void onFileDownloaded(File file, String mimeType) {
// Do your implementation
}
@Override
public void onUserTyping(String user, boolean typing) {
// Do your implementation
}
}
EventBus, so you can listen event from anywhere, It does not matter whether it's an activity or not. For example from your application class
public class SampleApps extends Application {
@Override
public void onCreate() {
super.onCreate();
Qiscus.init(this, "APP_ID");
EventBus.getDefault().register(this);
}
/**
* Subscribe anywhere to listen new message if you just got new message from someone
*/
@Subscribe
public void onGetNewQiscusComment(QiscusCommentReceivedEvent event) {
QiscusComment qiscusComment = event.getQiscusComment();
// Do your implementation
}
/**
* Call QiscusPusherApi.getInstance().listenRoom(qiscusChatRoom);
to get room event from anywhere at your application
*/
@Subscribe
public void onGetNewQiscusRoomEvent(QiscusChatRoomEvent event) {
switch (event.getEvent()) {
case TYPING:
// Someone is typing on this room event.getRoomId();
break;
case DELIVERED:
// Someone just received your message event.getCommentId()
break;
case READ:
// Someone just read your message event.getCommentId()
break;
}
}
/**
* Call QiscusPusherApi.getInstance().listenUserStatus("[email protected]");
to listen status of [email protected]
*/
@Subscribe
public void onUserStatusUpdated(QiscusUserStatusEvent event) {
// A user just changed his/her status from (online or offline)
// event.getUser() changed to event.isOnline() at event.getLastActive()
}
}
UI Customization
Theme Customization
Boring with default template? You can customized it, try it!, we have more items than those below code, its just example.
Qiscus.getChatConfig()
.setStatusBarColor(R.color.blue)
.setAppBarColor(R.color.red)
.setTitleColor(R.color.white)
.setLeftBubbleColor(R.color.green)
.setRightBubbleColor(R.color.yellow)
.setRightBubbleTextColor(R.color.white)
.setRightBubbleTimeColor(R.color.grey)
.setTimeFormat(date -> new SimpleDateFormat("HH:mm").format(date));
UI Source code
If you want full customisations, you can modify everything on the view by forking our repository or just right away modifying our ** CustomChatActivity.java **based on your needs.
Push Notifications
First install FCM to your apps, you can follow this steps. You can skip this step, if your apps already use FCM. Then put your api key to qiscus dashboard.
Now lets integrate with Qiscus client sdk, first enable FCM at Qiscus chat config.
Qiscus.getChatConfig().setEnableFcmPushNotification(true);
After that, lets change your firebase service to extend Qiscus firebase service instead of firebase service class.
public class MyFirebaseIdService extends QiscusFirebaseIdService {
@Override
public void onTokenRefresh() {
super.onTokenRefresh();
// Must call super
//Below is your own apps specific code
// e.g register the token to your backend
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
sendTokenToMyBackend(refreshedToken);
}
}
public class MyFirebaseMessagingService extends QiscusFirebaseService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (handleMessageReceived(remoteMessage)) {
// For qiscus
return;
}
//Your FCM PN here
}
}
If extension is not possible or desirable, use the following code the ensure Qiscus handle the FCM.
public class MyFirebaseIdService extends FirebaseInstanceIdService {
@Override
public void onTokenRefresh() {
super.onTokenRefresh();
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
//Register token to qiscus
Qiscus.setFcmToken(refreshedToken);
//Below is your own apps specific code
// e.g register the token to your backend
sendTokenToMyBackend(refreshedToken);
}
}
public class MyFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (QiscusFirebaseService.handleMessageReceived(remoteMessage)) {
// For qiscus
return;
}
//Your FCM PN here
}
}
Offline Messages
Post Messages
During post message, if you don't have any internet connection, message will be store locally and will be automatically being send once your internet connection is back. For you want to enqueue a message manually you can call this api:
QiscusApi.getInstance().postComment(qiscusComment)
.doOnSubscribe(() -> Qiscus.getDataStore().addOrUpdate(qiscusComment))
.doOnError(throwable -> {
qiscusComment.setState(QiscusComment.STATE_PENDING);
Qiscus.getDataStore().addOrUpdate(qiscusComment);
}
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(commentSend -> {
//Success
}
, throwable -> {
//we will automatically retry again later
}
);
Get Messages
Messages are stored locally so you can still access the messages when you don't have internet connection. However any new messages will not being received after you have your internet connection back. To access data locally, you can use QiscusDataStore, here sample code to get local room and message
QiscusChatRoom room = Qiscus.getDataStore().getChatRoom(roomId);
List<QiscusComment> comments = Qiscus.getDataStore().getComments(room.getLastTopicId(), count);
Miscellaneous
Android Support Libraries
Qiscus SDK is using appcompat libraries to support some features. If your apps using appcompat too, we highly recommended to using the latest stable appcompat version, or using the same version with Qiscus SDK. You can check the appcompat version of Qiscus SDK here. You can also force Qiscus SDK to use your apps appcompat version. Use "exclude group" at your build.gradle, for example:
//Qiscus sdk without android support libraries compile('com.qiscus.sdk:chat:2.14.0') {
transitive = true
exclude group: 'com.android.support'
}
//Qiscus sdk needs all of this android support libraries //Just add the same version with your apps dependencies compile 'com.android.support:support-v4:yourVersion' compile 'com.android.support:appcompat-v7:yourVersion' compile 'com.android.support:recyclerview-v7:yourVersion' compile 'com.android.support:cardview-v7:yourVersion' compile 'com.android.support:design:yourVersion' compile 'com.android.support:customtabs:yourVersion' compile 'com.android.support:support-v13:yourVersion'
If you have problem can not download android support libraries, please add Google's Maven repository to your project build.gradle
allprojects {
repositories {
.....
maven {
url "https://maven.google.com"
}
}
}
RxJava support
// Setup qiscus account with rxjava example Qiscus.setUser("[email protected]", "password")
.withUsername("Tony Stark")
.withAvatarUrl("http://avatar.url.com/handsome.jpg")
.save()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(qiscusAccount -> {
startActivity(new Intent(this, ConsultationListActivity.class));
}
, throwable -> {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
);
// Start a chat activity with rxjava example
Qiscus.buildChatWith("[email protected]")
.build(this)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(intent -> {
startActivity(intent);
}
, throwable -> {
if (throwable instanceof HttpException) {
//Error response from server
HttpException e = (HttpException) throwable;
try {
String errorMessage = e.response().errorBody().string();
Log.e(TAG, errorMessage);
showError(errorMessage);
}
catch (IOException e1) {
e1.printStackTrace();
}
}
else if (throwable instanceof IOException) {
//Error from network
showError("Can not connect to qiscus server!");
}
else {
//Unknown error
showError("Unexpected error!");
}
}
);
Doesn't like RxJava
For you who doesn't comport with RxJava method, we provide utility class to execute RxJava method. Here sample code how to get specific qiscus chat room by id.
QiscusRxExecutor.execute(QiscusApi.getInstance().getChatRoom(123), new QiscusRxExecutor.Listener<QiscusChatRoom>() {
@Override
public void onSuccess(QiscusChatRoom result) {
//Success getting the room
}
@Override
public void onError(Throwable throwable) {
//Something went wrong
}
}
);
Proguard
If you are using Proguard in your application, make sure you add Proguard rules of Qiscus from Qiscus Proguard Rules to your Proguard rules.
Sample Application
You can get the sample apps here