/*
* Created by Itzik Braun on 12/3/2015.
* Copyright (c) 2015 deluge. All rights reserved.
*
* Last Modification at: 3/12/15 4:32 PM
*/
package com.braunster.chatsdk.activities;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import com.braunster.chatsdk.R;
import com.braunster.chatsdk.Utils.Debug;
import com.braunster.chatsdk.Utils.DialogUtils;
import com.braunster.chatsdk.Utils.helper.ChatSDKUiHelper;
import com.braunster.chatsdk.dao.BThread;
import com.braunster.chatsdk.dao.BUser;
import com.braunster.chatsdk.network.AbstractNetworkAdapter;
import com.braunster.chatsdk.network.BFacebookManager;
import com.braunster.chatsdk.network.BNetworkManager;
import com.braunster.chatsdk.object.BError;
import com.facebook.Session;
import com.facebook.SessionState;
import com.facebook.UiLifecycleHelper;
import com.github.johnpersano.supertoasts.SuperCardToast;
import com.github.johnpersano.supertoasts.SuperToast;
import org.apache.commons.lang3.StringUtils;
import org.jdeferred.DoneCallback;
import org.jdeferred.FailCallback;
import org.jdeferred.Promise;
import java.util.concurrent.Callable;
import timber.log.Timber;
/**
* Created by braunster on 18/06/14.
*/
public class ChatSDKBaseActivity extends Activity implements ChatSDKBaseActivityInterface {
private static final String TAG = ChatSDKBaseActivity.class.getSimpleName();
private static final boolean DEBUG = Debug.BaseActivity;
public static final String FROM_LOGIN = "From_Login";
private UiLifecycleHelper uiHelper;
private ProgressDialog progressDialog;
/** If true the app will check if the user is online each time the activity is resumed.
* When the app is on the background the Android system can kill the app.
* When the app is killed the firebase api will set the user online status to false.
* Also all the listeners attached to the user, threads, friends etc.. are gone so each time the activity is resumed we will
* check to see if the user is online and if not start the auth process for registering to all events.*/
private boolean checkOnlineOnResumed = false;
/** If true the activity will implement facebook SDK components like sessionChangeState and the facebook UI helper.
* This is good for caching a press on the logout button in the main activity or in any activity that will implement the facebook login button.*/
protected boolean integratedWithFacebook = false;
/** A flag indicates that the activity in opened from the login activity so we wont do auth check when the activity will get to the onResume state.*/
boolean fromLoginActivity = false;
/** Need to be set before on create, If true card toast will be available while activity run, You need to add a layout with a special id for this to work.
* Example can be seen in the chat activity.
* You cant use the card toast until onResume is called due to the config of the card toast. If you need it you can call initCardToast.*/
private boolean enableCardToast = false;
protected ChatSDKUiHelper chatSDKUiHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
chatSDKUiHelper = ChatSDKUiHelper.getInstance().get(this);
if (integratedWithFacebook && getNetworkAdapter().facebookEnabled())
{
uiHelper = new UiLifecycleHelper(this, callback);
uiHelper.onCreate(savedInstanceState);
}
if (getIntent() != null && getIntent().getExtras() != null)
{
if (DEBUG) Timber.d("From login");
fromLoginActivity = getIntent().getExtras().getBoolean(FROM_LOGIN, false);
// So we wont encounter this flag again.
getIntent().removeExtra(FROM_LOGIN);
} else fromLoginActivity = false;
if (savedInstanceState != null)
fromLoginActivity = savedInstanceState.getBoolean(FROM_LOGIN, false);
if (enableCardToast)
{
SuperCardToast.onRestoreState(savedInstanceState, ChatSDKBaseActivity.this);
}
// Setting the default task description.
setTaskDescription(getTaskDescriptionBitmap(), getTaskDescriptionLabel(), getTaskDescriptionColor());
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
}
/**
* @return the bitmap that will be used for the screen overview also called the recents apps.
**/
protected Bitmap getTaskDescriptionBitmap(){
return BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
protected int getTaskDescriptionColor(){
TypedValue typedValue = new TypedValue();
Resources.Theme theme = getTheme();
theme.resolveAttribute(R.attr.colorPrimary, typedValue, true);
return typedValue.data;
}
protected String getTaskDescriptionLabel(){
return (String) getTitle();
}
protected void setTaskDescription(Bitmap bm, String label, int color){
// Color the app topbar label and icon in the overview screen
//http://www.bignerdranch.com/blog/polishing-your-Android-overview-screen-entry/
// Placed in the post create so it would be called after the action bar is initialized and we have a title.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ActivityManager.TaskDescription td = new ActivityManager.TaskDescription(label, bm, color);
setTaskDescription(td);
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null && intent.getExtras() != null)
{
if (DEBUG) Timber.d("From login");
fromLoginActivity = intent.getExtras().getBoolean(FROM_LOGIN, false);
// So we wont encounter this flag again.
intent.removeExtra(FROM_LOGIN);
}
}
@Override
protected void onPause() {
super.onPause();
if (integratedWithFacebook && getNetworkAdapter().facebookEnabled())
uiHelper.onPause();
}
@Override
protected void onResume() {
super.onResume();
if (enableCardToast)
chatSDKUiHelper.initCardToast();
if (DEBUG) Timber.v("onResumed, From login: %s, Check online: %s", fromLoginActivity, checkOnlineOnResumed);
if (integratedWithFacebook && getNetworkAdapter().facebookEnabled())
uiHelper.onResume();
if (checkOnlineOnResumed && !fromLoginActivity)
{
if(DEBUG) Timber.d("Check online on resumed");
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
getNetworkAdapter().isOnline()
.done(new DoneCallback<Boolean>() {
@Override
public void onDone(Boolean online) {
if (online == null) return;
if(DEBUG) Timber.d("Check done, ", online);
if (!online)
{
authenticate().done(new DoneCallback<BUser>() {
@Override
public void onDone(BUser bUser) {
if (DEBUG) Timber.d("Authenticated!");
onAuthenticated();
}
}).fail(new FailCallback<BError>() {
@Override
public void onFail(BError bError) {
if (DEBUG) Timber.d("Authenticated Failed!");
onAuthenticationFailed();
}
});
}
}
})
.fail(new FailCallback<BError>() {
@Override
public void onFail(BError error) {
if (DEBUG) Timber.e("Check online failed!, Error message: %s", error.message);
onAuthenticationFailed();
}
});
}
});
}
fromLoginActivity = false;
}
@Override
protected void onStart() {
super.onStart();
getNetworkAdapter().setUserOnline();
}
@Override
protected void onStop() {
super.onStop();
getNetworkAdapter().setUserOffline();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (integratedWithFacebook && getNetworkAdapter().facebookEnabled())
uiHelper.onDestroy();
dismissProgDialog();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (integratedWithFacebook && getNetworkAdapter().facebookEnabled()) uiHelper.onSaveInstanceState(outState);
outState.putBoolean(FROM_LOGIN, fromLoginActivity);
if (enableCardToast)
SuperCardToast.onSaveState(outState);
}
/** Set up the ui so every view and nested view that is not EditText will listen to touch event and dismiss the keyboard if touched.
* http://stackoverflow.com/questions/4165414/how-to-hide-soft-keyboard-on-android-after-clicking-outside-edittext*/
public void setupTouchUIToDismissKeyboard(View view) {
ChatSDKUiHelper.setupTouchUIToDismissKeyboard(view, new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
hideSoftKeyboard(ChatSDKBaseActivity.this);
return false;
}
});
}
public void setupTouchUIToDismissKeyboard(View view, final Integer... exceptIDs) {
ChatSDKUiHelper.setupTouchUIToDismissKeyboard(view, new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
hideSoftKeyboard(ChatSDKBaseActivity.this);
return false;
}
}, exceptIDs);
}
public void setupTouchUIToDismissKeyboard(View view, View.OnTouchListener onTouchListener, final Integer... exceptIDs) {
ChatSDKUiHelper.setupTouchUIToDismissKeyboard(view, onTouchListener, exceptIDs);
}
/** Hide the Soft Keyboard.*/
public static void hideSoftKeyboard(Activity activity) {
ChatSDKUiHelper.hideSoftKeyboard(activity);
}
/** Show a SuperToast with the given text. */
protected void showToast(String text){
if (chatSDKUiHelper==null || StringUtils.isEmpty(text))
return;
chatSDKUiHelper.getToast().setText(text);
chatSDKUiHelper.getToast().show();
}
protected void showAlertToast(String text){
if (chatSDKUiHelper==null || StringUtils.isEmpty(text))
return;
chatSDKUiHelper.getAlertToast().setText(text);
chatSDKUiHelper.getAlertToast().show();
}
protected void showProgressCard(String text){
chatSDKUiHelper.showProgressCard(text);
}
protected void dismissProgressCard(){
dismissProgressCard(0);
}
protected void dismissProgressCardWithSmallDelay(){
dismissProgressCard(1500);
}
protected void dismissProgressCard(long delay){
chatSDKUiHelper.dismissProgressCard(delay);
}
/** Authenticates the current user.*/
public Promise<BUser, BError, Void> authenticate(){
return getNetworkAdapter().checkUserAuthenticated();
}
/** Create a thread for given users and name, When thread and all users are all pushed to the server the chat activity for this thread will be open.*/
protected void createAndOpenThreadWithUsers(String name, BUser...users){
getNetworkAdapter().createThreadWithUsers(name, users)
.done(new DoneCallback<BThread>() {
@Override
public void onDone(final BThread thread) {
dismissProgDialog();
if (thread == null) {
if (DEBUG) Timber.e("thread added is null");
return;
}
if (isOnMainThread()) {
startChatActivityForID(thread.getId());
} else ChatSDKBaseActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
startChatActivityForID(thread.getId());
}
});
}
})
.fail(new FailCallback<BError>() {
@Override
public void onFail(BError error) {
if (isOnMainThread())
showAlertToast(getString(R.string.create_thread_with_users_fail_toast));
else ChatSDKBaseActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
showAlertToast(getString(R.string.create_thread_with_users_fail_toast));
}
});
}
});
}
/** Start the chat activity for the given thread id.
* @param id is the long value of local db id.*/
public void startChatActivityForID(long id){
if (chatSDKUiHelper != null)
chatSDKUiHelper.startChatActivityForID(id);
}
public void startLoginActivity(boolean loggedOut){
if (chatSDKUiHelper != null)
chatSDKUiHelper.startLoginActivity(loggedOut);
}
public void startMainActivity(){
if (chatSDKUiHelper != null)
chatSDKUiHelper.startMainActivity();
}
public void startSearchActivity() {
if (chatSDKUiHelper != null)
chatSDKUiHelper.startSearchActivity();
}
public void startPickFriendsActivity() {
if (chatSDKUiHelper != null)
chatSDKUiHelper.startPickFriendsActivity();
}
public void startShareWithFriendsActivity() {
if (chatSDKUiHelper != null)
chatSDKUiHelper.startShareWithFriendsActivity();
}
public void startShareLocationActivityActivity() {
if (chatSDKUiHelper != null)
chatSDKUiHelper.startShareLocationActivityActivity();
}
protected void showProgDialog(String message){
if (progressDialog == null)
progressDialog = new ProgressDialog(this);
if (!progressDialog.isShowing())
{
progressDialog = new ProgressDialog(this);
progressDialog.setMessage(message);
progressDialog.show();
}
}
protected void showOrUpdateProgDialog(String message){
if (progressDialog == null)
progressDialog = new ProgressDialog(this);
if (!progressDialog.isShowing())
{
progressDialog = new ProgressDialog(this);
progressDialog.setMessage(message);
progressDialog.show();
} else progressDialog.setMessage(message);
}
protected void dismissProgDialog(){
// For handling orientation changed.
try {
if (progressDialog != null && progressDialog.isShowing())
progressDialog.dismiss();
} catch (Exception e) {
}
}
protected void showAlertDialog(String title, String alert, String p, String n, final Callable neg, final Callable pos){
DialogUtils.showAlertDialog(this, title, alert, p, n, neg, pos);
}
protected void onSessionStateChange(Session session, final SessionState state, Exception exception){
BFacebookManager.onSessionStateChange(session, state, exception)
.fail(new FailCallback<BError>() {
@Override
public void onFail(BError bError) {
if (DEBUG) Timber.e("onDoneWithError. Error: %s", bError.message);
// Facebook session is closed so we need to disconnect from firebase.
getNetworkAdapter().logout();
startLoginActivity(true);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(DEBUG) Timber.v("onActivityResult");
if (integratedWithFacebook && getNetworkAdapter().facebookEnabled())
uiHelper.onActivityResult(requestCode, resultCode, data);
}
// Facebook Login stuff.
private Session.StatusCallback callback = new Session.StatusCallback() {
@Override
public void call(Session session, SessionState state, Exception exception) {
if (integratedWithFacebook && getNetworkAdapter().facebookEnabled())
onSessionStateChange(session, state, exception);
}
};
/** When enabled the app will check the user online ref to see if he is not offline each time that the activity is resumed.
* This method is good for times that the app is in the background and killed by the android system and we need to listen to all the user details again.
* i.e Authenticate again.*/
public void enableCheckOnlineOnResumed(boolean checkOnlineOnResumed) {
this.checkOnlineOnResumed = checkOnlineOnResumed;
}
public void enableFacebookIntegration(boolean integratedWithFacebook) {
this.integratedWithFacebook = integratedWithFacebook;
}
public void setEnableCardToast(boolean enableCardToast) {
this.enableCardToast = enableCardToast;
}
@Override
public void onAuthenticated() {
}
@Override
public void onAuthenticationFailed() {
startLoginActivity(true);
}
protected boolean isOnMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
return false;
}
return true;
}
/*Getters and Setters*/
public void setAlertToast(SuperToast alertToast) {
chatSDKUiHelper.setAlertToast(alertToast);
}
@Override
public AbstractNetworkAdapter getNetworkAdapter() {
return BNetworkManager.sharedManager().getNetworkAdapter();
}
public void setChatSDKUiHelper(ChatSDKUiHelper chatSDKUiHelper) {
this.chatSDKUiHelper = chatSDKUiHelper;
}
public void setToast(SuperToast toast) {
chatSDKUiHelper.setToast(toast);
}
public SuperToast getToast() {
return chatSDKUiHelper.getToast();
}
public SuperToast getAlertToast() {
return chatSDKUiHelper.getAlertToast();
}
}
interface ChatSDKBaseActivityInterface extends ChatSDKUiHelper.ChatSDKUiHelperInterface {
/** This method is called after the activity authenticated the user. When the activity is resumed the activity
* checks if the user is authenticated(optional {@link ChatSDKBaseActivity#checkOnlineOnResumed}),
* If the user was re - authenticated when it was resumed this method will be called. */
public void onAuthenticated();
public void onAuthenticationFailed();
public AbstractNetworkAdapter getNetworkAdapter();
}