<commit_before>/*
 * Copyright (c) 2016 Qiscus.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.qiscus.sdk;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;

import com.google.firebase.iid.FirebaseInstanceId;
import com.google.gson.Gson;
import com.qiscus.sdk.data.local.QiscusCacheManager;
import com.qiscus.sdk.data.local.QiscusDataBaseHelper;
import com.qiscus.sdk.data.local.QiscusDataStore;
import com.qiscus.sdk.data.model.QiscusAccount;
import com.qiscus.sdk.data.model.QiscusChatConfig;
import com.qiscus.sdk.data.model.QiscusChatRoom;
import com.qiscus.sdk.data.remote.QiscusApi;
import com.qiscus.sdk.event.QiscusUserEvent;
import com.qiscus.sdk.service.QiscusPusherService;
import com.qiscus.sdk.ui.QiscusChatActivity;
import com.qiscus.sdk.ui.fragment.QiscusChatFragment;
import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider;

import org.greenrobot.eventbus.EventBus;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * The main class of Qiscus SDK. Init qiscus engine sdk, set qiscus user, start the chatting and all
 * about qiscus configuration can be accessed from here.
 */
public class Qiscus {

    private static Application appInstance;
    private static volatile Context applicationContext;
    private static volatile Handler appHandler;
    private static LocalDataManager localDataManager;
    private static QiscusDataStore dataStore;
    private static QiscusChatConfig chatConfig;

    private static String appServer;
    private static long heartBeat;

    private Qiscus() {
    }

    /**
     * The first method you need to be invoke to using qiscus sdk. Call this method from your Application
     * class. You can not using another qiscus feature if you not invoke this method first. Here sample
     * to call this method:
     * <pre>
     * {@code
     * public class SampleApps extends Application {
     *  public void onCreate() {
     *      super.onCreate();
     *      Qiscus.init(this, "yourQiscusAppId");
     *  }
     * }
     * }
     * </pre>
     *
     * @param application Application instance
     * @param qiscusAppId Your qiscus application Id
     */
    public static void init(Application application, String qiscusAppId) {
        initWithCustomServer(application, "https://" + qiscusAppId + ".qiscus.com");
    }

    /**
     * The first method you need to be invoke to using qiscus sdk. Call this method from your Application
     * class. You can not using another qiscus feature if you not invoke this method first. Here sample
     * to call this method:
     * <pre>
     * {@code
     * public class SampleApps extends Application {
     *  public void onCreate() {
     *      super.onCreate();
     *      Qiscus.initWithCustomServer(this, "http://myserver.com");
     *  }
     * }
     * }
     * </pre>
     *
     * @param application   Application instance
     * @param serverBaseUrl Your qiscus chat engine base url
     */
    public static void initWithCustomServer(Application application, String serverBaseUrl) {
        appInstance = application;
        appServer = serverBaseUrl;
        applicationContext = appInstance.getApplicationContext();
        appHandler = new Handler(applicationContext.getMainLooper());
        localDataManager = new LocalDataManager();
        dataStore = new QiscusDataBaseHelper();
        chatConfig = new QiscusChatConfig();
        heartBeat = 60000;
        appInstance.registerActivityLifecycleCallbacks(QiscusActivityCallback.INSTANCE);

        startPusherService();
        QiscusCacheManager.getInstance().setLastChatActivity(false, 0);

        configureFcmToken();

        EmojiManager.install(new EmojiOneProvider());
    }

    public static void startPusherService() {
        checkAppIdSetup();
        applicationContext.startService(new Intent(applicationContext, QiscusPusherService.class));
    }

    /**
     * Use this method set qiscus user. If user doesn't exist at your qiscus engine, He/She will be
     * registered automatically.
     *
     * @param userEmail The email or username of qiscus user
     * @param userKey   Qiscus user key
     * @return User builder
     */
    public static SetUserBuilder setUser(String userEmail, String userKey) {
        return new SetUserBuilder(userEmail, userKey);
    }

    /**
     * Use this method if we need application context instance
     *
     * @return Your application instance
     */
    public static Application getApps() {
        checkAppIdSetup();
        return appInstance;
    }

    /**
     * Will return the exact apps name
     *
     * @return The apps name.
     */
    public static String getAppsName() {
        checkAppIdSetup();
        return appInstance.getApplicationInfo().loadLabel(appInstance.getPackageManager()).toString();
    }

    /**
     * Needed to run something at main thread handler
     *
     * @return Main thread handler
     */
    public static Handler getAppsHandler() {
        checkAppIdSetup();
        return appHandler;
    }

    /**
     * Accessor to get current qiscus app id
     *
     * @return Current qiscus app id
     */
    public static String getAppServer() {
        checkAppIdSetup();
        return appServer;
    }

    /**
     * For checking is qiscus user has been setup
     *
     * @return true if already setup, false if not yet
     */
    public static boolean hasSetupUser() {
        return localDataManager.isLogged();
    }

    /**
     * Accessor to get current qiscus user account
     *
     * @return Current qiscus user account
     */
    public static QiscusAccount getQiscusAccount() {
        checkUserSetup();
        return localDataManager.getAccountInfo();
    }

    /**
     * Accessor to get current qiscus user token
     *
     * @return Current qiscus user token
     */
    public static String getToken() {
        checkUserSetup();
        return localDataManager.getToken();
    }

    /**
     * Get current qiscus chat data store
     *
     * @return Singleton of qiscus data store
     */
    public static QiscusDataStore getDataStore() {
        return dataStore;
    }

    /**
     * Use this method if you want to use your own data store implementation, e.g using Realm, your own table,
     * your own orm, etc
     *
     * @param dataStore Your own chat datastore
     */
    public static void setDataStore(QiscusDataStore dataStore) {
        Qiscus.dataStore = dataStore;
    }

    /**
     * Use this method to get current qiscus chatting configuration, you can also modify it.
     *
     * @return Current qiscus chatting configuration
     */
    public static QiscusChatConfig getChatConfig() {
        checkAppIdSetup();
        return chatConfig;
    }

    /**
     * Use this method to open a chatting room with other user.
     *
     * @param email Email or username of other user to chat.
     * @return Chat room builder
     */
    public static ChatBuilder buildChatRoomWith(String email) {
        checkUserSetup();
        return new ChatBuilder(email);
    }

    /**
     * Use this method to start an Activity for chatting with other user.
     *
     * @param email Email or username of other user to chat.
     * @return Chatting Activity builder
     */
    public static ChatActivityBuilder buildChatWith(String email) {
        checkUserSetup();
        return new ChatActivityBuilder(email);
    }

    /**
     * Use this method if you want to generate a Fragment for chatting with other user.
     *
     * @param email Email or username of other user to chat.
     * @return Chatting Fragment builder
     */
    public static ChatFragmentBuilder buildChatFragmentWith(String email) {
        checkUserSetup();
        return new ChatFragmentBuilder(email);
    }

    /**
     * Use this method to create new group chat.
     *
     * @param email Email or username of group chat member.
     * @return Group Chat room builder
     */
    public static GroupChatBuilder buildGroupChatRoom(String name, String email) {
        checkUserSetup();
        return new GroupChatBuilder(name, email);
    }

    /**
     * Use this method to create new group chat.
     *
     * @param emails Emails or username of group chat member.
     * @return Group Chat room builder
     */
    public static GroupChatBuilder buildGroupChatRoom(String name, List<String> emails) {
        checkUserSetup();
        return new GroupChatBuilder(name, emails);
    }

    /**
     * Set the heartbeat of qiscus synchronization chat data. Default value is 500ms
     *
     * @param heartBeat Heartbeat duration in milliseconds
     */
    public static void setHeartBeat(long heartBeat) {
        checkAppIdSetup();
        Qiscus.heartBeat = heartBeat;
    }

    /**
     * Get the current qiscus heartbeat duration
     *
     * @return Heartbeat duration in milliseconds
     */
    public static long getHeartBeat() {
        return heartBeat;
    }

    /**
     * Set the FCM token to configure push notification with firebase cloud messaging
     *
     * @param fcmToken the token
     */
    public static void setFcmToken(String fcmToken) {
        if (hasSetupUser()) {
            QiscusApi.getInstance().registerFcmToken(fcmToken)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(aVoid -> {
                    }, Throwable::printStackTrace);
        }

        localDataManager.setFcmToken(fcmToken);
    }

    /**
     * @return current fcm token, null if not set
     */
    public static String getFcmToken() {
        return localDataManager.getFcmToken();
    }

    private static void configureFcmToken() {
        if (hasSetupUser()) {
            String fcmToken = getFcmToken();
            if (fcmToken != null) {
                setFcmToken(fcmToken);
            } else {
                Observable.just(null)
                        .doOnNext(o -> {
                            try {
                                FirebaseInstanceId.getInstance().deleteInstanceId();
                            } catch (IOException ignored) {
                                //Do nothing
                            }
                        })
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(aVoid -> {
                        }, throwable -> {
                        });
            }
        }
    }

    public static String getProviderAuthorities() {
        return appInstance.getPackageName() + ".qiscus.sdk.provider";
    }

    /**
     * Check is apps opened on foreground
     *
     * @return true if apps on foreground, and false if on background
     */
    public static boolean isOnForeground() {
        return QiscusActivityCallback.INSTANCE.isForeground();
    }

    private static void checkAppIdSetup() throws RuntimeException {
        if (appServer == null) {<commit_msg>Add ability to custom file provider authorities<commit_after>/*
 * Copyright (c) 2016 Qiscus.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.qiscus.sdk;

import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;

import com.google.firebase.iid.FirebaseInstanceId;
import com.google.gson.Gson;
import com.qiscus.sdk.data.local.QiscusCacheManager;
import com.qiscus.sdk.data.local.QiscusDataBaseHelper;
import com.qiscus.sdk.data.local.QiscusDataStore;
import com.qiscus.sdk.data.model.QiscusAccount;
import com.qiscus.sdk.data.model.QiscusChatConfig;
import com.qiscus.sdk.data.model.QiscusChatRoom;
import com.qiscus.sdk.data.remote.QiscusApi;
import com.qiscus.sdk.event.QiscusUserEvent;
import com.qiscus.sdk.service.QiscusPusherService;
import com.qiscus.sdk.ui.QiscusChatActivity;
import com.qiscus.sdk.ui.fragment.QiscusChatFragment;
import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider;

import org.greenrobot.eventbus.EventBus;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

/**
 * The main class of Qiscus SDK. Init qiscus engine sdk, set qiscus user, start the chatting and all
 * about qiscus configuration can be accessed from here.
 */
public class Qiscus {

    private static Application appInstance;
    private static volatile Context applicationContext;
    private static volatile Handler appHandler;
    private static LocalDataManager localDataManager;
    private static QiscusDataStore dataStore;
    private static QiscusChatConfig chatConfig;

    private static String appServer;
    private static long heartBeat;
    private static String authorities;

    private Qiscus() {
    }

    /**
     * The first method you need to be invoke to using qiscus sdk. Call this method from your Application
     * class. You can not using another qiscus feature if you not invoke this method first. Here sample
     * to call this method:
     * <pre>
     * {@code
     * public class SampleApps extends Application {
     *  public void onCreate() {
     *      super.onCreate();
     *      Qiscus.init(this, "yourQiscusAppId");
     *  }
     * }
     * }
     * </pre>
     *
     * @param application Application instance
     * @param qiscusAppId Your qiscus application Id
     */
    public static void init(Application application, String qiscusAppId) {
        initWithCustomServer(application, "https://" + qiscusAppId + ".qiscus.com");
    }

    /**
     * The first method you need to be invoke to using qiscus sdk. Call this method from your Application
     * class. You can not using another qiscus feature if you not invoke this method first. Here sample
     * to call this method:
     * <pre>
     * {@code
     * public class SampleApps extends Application {
     *  public void onCreate() {
     *      super.onCreate();
     *      Qiscus.initWithCustomServer(this, "http://myserver.com");
     *  }
     * }
     * }
     * </pre>
     *
     * @param application   Application instance
     * @param serverBaseUrl Your qiscus chat engine base url
     */
    public static void initWithCustomServer(Application application, String serverBaseUrl) {
        appInstance = application;
        appServer = serverBaseUrl;
        applicationContext = appInstance.getApplicationContext();
        appHandler = new Handler(applicationContext.getMainLooper());
        localDataManager = new LocalDataManager();
        dataStore = new QiscusDataBaseHelper();
        chatConfig = new QiscusChatConfig();
        heartBeat = 60000;
        appInstance.registerActivityLifecycleCallbacks(QiscusActivityCallback.INSTANCE);
        authorities = appInstance.getPackageName() + ".qiscus.sdk.provider";

        startPusherService();
        QiscusCacheManager.getInstance().setLastChatActivity(false, 0);

        configureFcmToken();

        EmojiManager.install(new EmojiOneProvider());
    }

    public static void startPusherService() {
        checkAppIdSetup();
        applicationContext.startService(new Intent(applicationContext, QiscusPusherService.class));
    }

    /**
     * Use this method set qiscus user. If user doesn't exist at your qiscus engine, He/She will be
     * registered automatically.
     *
     * @param userEmail The email or username of qiscus user
     * @param userKey   Qiscus user key
     * @return User builder
     */
    public static SetUserBuilder setUser(String userEmail, String userKey) {
        return new SetUserBuilder(userEmail, userKey);
    }

    /**
     * Use this method if we need application context instance
     *
     * @return Your application instance
     */
    public static Application getApps() {
        checkAppIdSetup();
        return appInstance;
    }

    /**
     * Will return the exact apps name
     *
     * @return The apps name.
     */
    public static String getAppsName() {
        checkAppIdSetup();
        return appInstance.getApplicationInfo().loadLabel(appInstance.getPackageManager()).toString();
    }

    /**
     * Needed to run something at main thread handler
     *
     * @return Main thread handler
     */
    public static Handler getAppsHandler() {
        checkAppIdSetup();
        return appHandler;
    }

    /**
     * Accessor to get current qiscus app id
     *
     * @return Current qiscus app id
     */
    public static String getAppServer() {
        checkAppIdSetup();
        return appServer;
    }

    /**
     * For checking is qiscus user has been setup
     *
     * @return true if already setup, false if not yet
     */
    public static boolean hasSetupUser() {
        return localDataManager.isLogged();
    }

    /**
     * Accessor to get current qiscus user account
     *
     * @return Current qiscus user account
     */
    public static QiscusAccount getQiscusAccount() {
        checkUserSetup();
        return localDataManager.getAccountInfo();
    }

    /**
     * Accessor to get current qiscus user token
     *
     * @return Current qiscus user token
     */
    public static String getToken() {
        checkUserSetup();
        return localDataManager.getToken();
    }

    /**
     * Get current qiscus chat data store
     *
     * @return Singleton of qiscus data store
     */
    public static QiscusDataStore getDataStore() {
        return dataStore;
    }

    /**
     * Use this method if you want to use your own data store implementation, e.g using Realm, your own table,
     * your own orm, etc
     *
     * @param dataStore Your own chat datastore
     */
    public static void setDataStore(QiscusDataStore dataStore) {
        Qiscus.dataStore = dataStore;
    }

    /**
     * Use this method to get current qiscus chatting configuration, you can also modify it.
     *
     * @return Current qiscus chatting configuration
     */
    public static QiscusChatConfig getChatConfig() {
        checkAppIdSetup();
        return chatConfig;
    }

    /**
     * Use this method to open a chatting room with other user.
     *
     * @param email Email or username of other user to chat.
     * @return Chat room builder
     */
    public static ChatBuilder buildChatRoomWith(String email) {
        checkUserSetup();
        return new ChatBuilder(email);
    }

    /**
     * Use this method to start an Activity for chatting with other user.
     *
     * @param email Email or username of other user to chat.
     * @return Chatting Activity builder
     */
    public static ChatActivityBuilder buildChatWith(String email) {
        checkUserSetup();
        return new ChatActivityBuilder(email);
    }

    /**
     * Use this method if you want to generate a Fragment for chatting with other user.
     *
     * @param email Email or username of other user to chat.
     * @return Chatting Fragment builder
     */
    public static ChatFragmentBuilder buildChatFragmentWith(String email) {
        checkUserSetup();
        return new ChatFragmentBuilder(email);
    }

    /**
     * Use this method to create new group chat.
     *
     * @param email Email or username of group chat member.
     * @return Group Chat room builder
     */
    public static GroupChatBuilder buildGroupChatRoom(String name, String email) {
        checkUserSetup();
        return new GroupChatBuilder(name, email);
    }

    /**
     * Use this method to create new group chat.
     *
     * @param emails Emails or username of group chat member.
     * @return Group Chat room builder
     */
    public static GroupChatBuilder buildGroupChatRoom(String name, List<String> emails) {
        checkUserSetup();
        return new GroupChatBuilder(name, emails);
    }

    /**
     * Set the heartbeat of qiscus synchronization chat data. Default value is 500ms
     *
     * @param heartBeat Heartbeat duration in milliseconds
     */
    public static void setHeartBeat(long heartBeat) {
        checkAppIdSetup();
        Qiscus.heartBeat = heartBeat;
    }

    /**
     * Get the current qiscus heartbeat duration
     *
     * @return Heartbeat duration in milliseconds
     */
    public static long getHeartBeat() {
        return heartBeat;
    }

    /**
     * Set the FCM token to configure push notification with firebase cloud messaging
     *
     * @param fcmToken the token
     */
    public static void setFcmToken(String fcmToken) {
        if (hasSetupUser()) {
            QiscusApi.getInstance().registerFcmToken(fcmToken)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(aVoid -> {
                    }, Throwable::printStackTrace);
        }

        localDataManager.setFcmToken(fcmToken);
    }

    /**
     * @return current fcm token, null if not set
     */
    public static String getFcmToken() {
        return localDataManager.getFcmToken();
    }

    private static void configureFcmToken() {
        if (hasSetupUser()) {
            String fcmToken = getFcmToken();
            if (fcmToken != null) {
                setFcmToken(fcmToken);
            } else {
                Observable.just(null)
                        .doOnNext(o -> {
                            try {
                                FirebaseInstanceId.getInstance().deleteInstanceId();
                            } catch (IOException ignored) {
                                //Do nothing
                            }
                        })
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(aVoid -> {
                        }, throwable -> {
                        });
            }
        }
    }

    public static String getProviderAuthorities() {
        return authorities;
    }

    public static void setProviderAuthorities(String providerAuthorities) {
        authorities = providerAuthorities;
    }

    /**
     * Check is apps opened on foreground
     *
     * @return true if apps on foreground, and false if on background
     */
    public static boolean isOnForeground() {
        return QiscusActivityCallback.INSTANCE.isForeground();
    }

    private static void checkAppIdSetup() throws RuntimeException {
        if (appServer == null) {
{'fraction_non_alphanumeric': 0.08098709569297805, 'fraction_numerical': 0.0014245014245014246, 'mean_word_length': 2.3928926794598437, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 21, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4254722', 'n_tokens_mistral': 6494, 'n_tokens_neox': 6172, 'n_words': 1977}