org.jorge.lolin1.func.chat.ChatIntentService.java Source code

Java tutorial

Introduction

Here is the source code for org.jorge.lolin1.func.chat.ChatIntentService.java

Source

package org.jorge.lolin1.func.chat;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.IntentService;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;

import com.crashlytics.android.Crashlytics;
import com.github.theholywaffle.lolchatapi.ChatServer;
import com.github.theholywaffle.lolchatapi.LoLChat;
import com.github.theholywaffle.lolchatapi.listeners.ChatListener;
import com.github.theholywaffle.lolchatapi.listeners.FriendListener;
import com.github.theholywaffle.lolchatapi.wrapper.Friend;

import org.jivesoftware.smack.SmackAndroid;
import org.jivesoftware.smack.SmackException;
import org.jorge.lolin1.func.auth.AccountAuthenticator;
import org.jorge.lolin1.ui.activities.ChatRoomActivity;
import org.jorge.lolin1.utils.LoLin1Utils;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.SSLException;

import static org.jorge.lolin1.utils.LoLin1DebugUtils.logString;

/**
 * This file is part of LoLin1.
 * <p/>
 * LoLin1 is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <p/>
 * LoLin1 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * <p/>
 * You should have received a copy of the GNU General Public License
 * along with LoLin1. If not, see <http://www.gnu.org/licenses/>.
 * <p/>
 * Created by Jorge Antonio Diaz-Benito Soriano on 05/05/2014.
 */
public class ChatIntentService extends IntentService {

    public static final String ACTION_CONNECT = "CONNECT", ACTION_DISCONNECT = "DISCONNECT";
    public static final String KEY_MESSAGE_CONTENTS = "MESSAGE_CONTENTS";
    public static final String KEY_MESSAGE_SOURCE = "SOURCE_FRIEND";
    private final IBinder mBinder = new ChatBinder();
    private static LoLChat api;
    private SmackAndroid mSmackAndroid;
    private static Boolean isConnected = Boolean.FALSE;
    private AsyncTask<Void, Void, Void> loginTask;

    public ChatIntentService() {
        super(ChatIntentService.class.getName());
    }

    static List<Friend> getOnlineFriends() {
        return api.getOnlineFriends();
    }

    public static Boolean isLoggedIn() {
        return isConnected;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        isConnected = Boolean.FALSE;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent == null) {
            throw new IllegalArgumentException("No intent found");
        }
        if (TextUtils.isEmpty(intent.getAction())) {
            throw new IllegalArgumentException("Empty action is not supported");
        }
        logString("debug", "ChatIntentService receiving intent " + intent.getAction());
        switch (intent.getAction()) {
        case ACTION_CONNECT:
            logString("debug", "Action requested: " + ACTION_CONNECT);
            connect();
            break;
        case ACTION_DISCONNECT:
            logString("debug", "Action requested: " + ACTION_DISCONNECT);
            disconnect();
            break;
        default:
            throw new IllegalArgumentException("Action " + intent.getAction() + " not yet supported");
        }
    }

    public class ChatBinder extends Binder {
        @SuppressWarnings("unused")
        public ChatIntentService getService() {
            return ChatIntentService.this;
        }
    }

    private void connect() {
        mSmackAndroid = LoLChat.init(getApplicationContext());
        loginTask = new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                logString("debug", "Commencing login");
                Boolean loginSuccess = login(
                        LoLin1Utils.getRealm(getApplicationContext()).toUpperCase(Locale.ENGLISH));
                if (loginSuccess) {
                    logString("debug", "Login successful");
                    launchBroadcastLoginSuccessful();
                    setUpChatOverviewListener();
                } else {
                    logString("debug", "Login unsuccessful");
                    launchBroadcastLoginUnsuccessful();
                }
                return null;
            }
        };
        loginTask.execute();
    }

    private void setUpChatOverviewListener() {
        api.addFriendListener(new FriendListener() {

            @Override
            public void onFriendLeave(Friend friend) {
                logString("debug", "onFriendLeave " + friend.getName());
                ChatIntentService.this.launchBroadcastFriendEvent();
            }

            @Override
            public void onFriendJoin(Friend friend) {
                logString("debug", "onFriendJoin " + friend.getName());
                ChatIntentService.this.launchBroadcastFriendEvent();
            }

            @Override
            public void onFriendAvailable(Friend friend) {
                logString("debug", "onFriendAvailable " + friend.getName());
                ChatIntentService.this.launchBroadcastFriendEvent();
            }

            @Override
            public void onFriendAway(Friend friend) {
                logString("debug", "onFriendAway " + friend.getName());
                ChatIntentService.this.launchBroadcastFriendEvent();
            }

            @Override
            public void onFriendBusy(Friend friend) {
                logString("debug", "onFriendBusy " + friend.getName());
                ChatIntentService.this.launchBroadcastFriendEvent();
            }

            @Override
            public void onFriendStatusChange(Friend friend) {
                logString("debug", "onFriendStatusChange " + friend.getName());
                ChatIntentService.this.launchBroadcastFriendEvent();
            }
        });

        api.addChatListener(new ChatListener() {
            @Override
            public void onMessage(Friend friend, String message) {
                ChatMessageWrapper messageWrapper = new ChatMessageWrapper(message, System.currentTimeMillis(),
                        friend);
                logString("debug", "Adding external message received");
                ChatBundleManager.addMessageToFriendChat(messageWrapper, friend);
                logString("debug", "Added external message received");
                launchBroadcastMessageReceived(message, friend.getName());
                logString("debug", "Class evaluating if it's on top: " + ChatRoomActivity.class.getName());
                if (!LoLin1Utils.getCurrentForegroundActivityClass(getApplicationContext())
                        .contentEquals((ChatRoomActivity.class.getName()))) {
                    ChatNotificationManager.createOrUpdateMessageReceivedNotification(getApplicationContext(),
                            message, friend);
                }
            }
        });
    }

    private void launchBroadcastMessageReceived(String message, String sourceFriendName) {
        Intent intent = new Intent();
        intent.setAction(LoLin1Utils.getString(getApplicationContext(), "event_message_received", null));
        intent.putExtra(KEY_MESSAGE_CONTENTS, message);
        intent.putExtra(KEY_MESSAGE_SOURCE, sourceFriendName);
        sendLocalBroadcast(intent);
    }

    private void sendLocalBroadcast(Intent intent) {
        logString("debug", "Sending local broadcast - " + intent.getAction());
        LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
    }

    private void launchBroadcastLoginSuccessful() {
        Intent intent = new Intent();
        intent.setAction(LoLin1Utils.getString(getApplicationContext(), "event_login_successful", null));
        sendLocalBroadcast(intent);
    }

    private void launchBroadcastLoginUnsuccessful() {
        Intent intent = new Intent();
        intent.setAction(LoLin1Utils.getString(getApplicationContext(), "event_login_failed", null));
        sendLocalBroadcast(intent);
    }

    private void launchBroadcastFriendEvent() {
        Intent intent = new Intent();
        intent.setAction(LoLin1Utils.getString(getApplicationContext(), "event_chat_overview", null));
        sendLocalBroadcast(intent);
    }

    private Boolean login(String upperCaseRealm) {
        ChatServer chatServer;
        switch (upperCaseRealm) {
        case "NA":
            chatServer = ChatServer.NA;
            break;
        case "EUW":
            chatServer = ChatServer.EUW;
            break;
        case "EUNE":
            chatServer = ChatServer.EUNE;
            break;
        case "TR":
            chatServer = ChatServer.TR;
            break;
        case "BR":
            chatServer = ChatServer.BR;
            break;
        case "OCE":
            chatServer = ChatServer.OCE;
            break;
        case "LAN":
            chatServer = ChatServer.LAN;
            break;
        case "LAS":
            chatServer = ChatServer.LAS;
            break;
        case "KR":
            chatServer = ChatServer.KR;
            break;
        case "RU":
            chatServer = ChatServer.RU;
            break;
        default:
            throw new IllegalArgumentException("Region " + upperCaseRealm + " not yet implemented");
        }
        try {
            logString("debug", "Assigning api...");
            api = new LoLChat(chatServer, Boolean.FALSE);
        } catch (IOException e) {
            logString("debug", "Exception when constructing the object!");
            e.printStackTrace(System.err);
            if (!(e instanceof SSLException)) {
                launchBroadcastLoginUnsuccessful();
            }
            return Boolean.FALSE;
        }
        final AccountManager accountManager = AccountManager.get(getApplicationContext());
        Account[] accounts = accountManager
                .getAccountsByType(LoLin1Utils.getString(getApplicationContext(), "account_type", null));
        Account thisRealmAccount = null;
        for (Account acc : accounts) {
            if (acc.name.contentEquals(upperCaseRealm)) {
                thisRealmAccount = acc;
                break;
            }
        }
        if (thisRealmAccount == null) {
            return Boolean.FALSE;//There's no account associated to this realm
        }
        AsyncTask<Account, Void, String[]> credentialsTask = new AsyncTask<Account, Void, String[]>() {
            @Override
            protected String[] doInBackground(Account... params) {
                String[] processedAuthToken = null;
                try {
                    processedAuthToken = accountManager.blockingGetAuthToken(params[0], "none", Boolean.TRUE)
                            .split(AccountAuthenticator.TOKEN_GENERATION_JOINT);
                } catch (OperationCanceledException | IOException | AuthenticatorException e) {
                    Crashlytics.logException(e);
                }
                return processedAuthToken;
            }
        };
        //It's necessary to run the task in an executor because the main one is already full and if we add this one a livelock will occur
        ExecutorService loginExecutor = Executors.newFixedThreadPool(1);
        credentialsTask.executeOnExecutor(loginExecutor, thisRealmAccount);
        String[] processedAuthToken = new String[0];
        try {
            processedAuthToken = credentialsTask.get();
        } catch (InterruptedException | ExecutionException e) {
            Crashlytics.logException(e);
        }
        Boolean loginSuccess = Boolean.FALSE;
        try {
            loginSuccess = api.login(processedAuthToken[0], processedAuthToken[1]);
        } catch (IOException e) {
            Crashlytics.logException(e);
        }
        if (loginSuccess) {
            api.reloadRoster();
            isConnected = Boolean.TRUE;
            return Boolean.TRUE;
        } else {
            isConnected = Boolean.FALSE;
            return Boolean.FALSE;
        }
    }

    private void disconnect() {
        //All the null checks are necessary because this method is run when an account is added from out of the app as well
        try {
            if (loginTask != null)
                loginTask.get(); // Disconnecting in the middle of a login may be troublesome
        } catch (InterruptedException | ExecutionException e) {
            Crashlytics.logException(e);
        }
        try {
            if (api != null) {
                api.disconnect();
                api = null;
            }
        } catch (SmackException.NotConnectedException e) {
            Crashlytics.logException(e);
        }
        if (mSmackAndroid != null)
            mSmackAndroid.onDestroy();
        isConnected = Boolean.FALSE;
    }
}