001package com.gigya.android.sdk.providers.provider;
002
003import android.content.Context;
004import android.content.Intent;
005import android.os.Bundle;
006import android.support.annotation.Nullable;
007import android.support.v7.app.AppCompatActivity;
008
009import com.facebook.AccessToken;
010import com.facebook.AccessTokenTracker;
011import com.facebook.CallbackManager;
012import com.facebook.FacebookCallback;
013import com.facebook.FacebookException;
014import com.facebook.login.LoginBehavior;
015import com.facebook.login.LoginManager;
016import com.facebook.login.LoginResult;
017import com.gigya.android.sdk.api.IBusinessApiService;
018import com.gigya.android.sdk.persistence.IPersistenceService;
019import com.gigya.android.sdk.ui.HostActivity;
020import com.gigya.android.sdk.utils.FileUtils;
021import com.gigya.android.sdk.utils.ObjectUtils;
022
023import org.json.JSONObject;
024
025import java.util.Arrays;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029
030import static com.gigya.android.sdk.GigyaDefinitions.Providers.FACEBOOK;
031
032public class FacebookProvider extends Provider {
033
034    public FacebookProvider(Context context,
035                            IPersistenceService persistenceService,
036                            IBusinessApiService businessApiService,
037                            ProviderCallback providerCallback) {
038        super(context, persistenceService, businessApiService, providerCallback);
039    }
040
041    private static final String[] DEFAULT_READ_PERMISSIONS = {"email"};
042
043    private final CallbackManager _callbackManager = CallbackManager.Factory.create();
044    private AccessTokenTracker _tokenTracker;
045
046    @Override
047    public String getName() {
048        return FACEBOOK;
049    }
050
051    public static boolean isAvailable(FileUtils fileUtils) {
052        try {
053            String fbAppId = fileUtils.stringFromMetaData("com.facebook.sdk.ApplicationId");
054            Class.forName("com.facebook.login.LoginManager");
055            return fbAppId != null;
056        } catch (Throwable t) {
057            return false;
058        }
059    }
060
061    @Override
062    public void login(final Map<String, Object> loginParams, final String loginMode) {
063        if (_connecting) {
064            return;
065        }
066        _connecting = true;
067        _loginMode = loginMode;
068        // Get login permissions.
069        final List<String> readPermissions = getReadPermissions(loginParams);
070
071        AccessToken accessToken = AccessToken.getCurrentAccessToken();
072        // Check login state.
073        boolean isLoggedIn = accessToken != null && !accessToken.isExpired() && permissionsGranted(readPermissions);
074        if (isLoggedIn) {
075            onLoginSuccess(loginParams, getProviderSessions(accessToken.getToken(), accessToken.getExpires().getTime() / 1000, null), loginMode);
076            return;
077        }
078        // Start new login flow.
079        HostActivity.present(_context, new HostActivity.HostActivityLifecycleCallbacks() {
080            @Override
081            public void onCreate(final AppCompatActivity activity, @Nullable Bundle savedInstanceState) {
082                final LoginManager loginManager = LoginManager.getInstance();
083                // Set login behaviour.
084                LoginBehavior loginBehaviour = LoginBehavior.NATIVE_WITH_FALLBACK;
085                if (loginParams != null && loginParams.containsKey(LOGIN_BEHAVIOUR)) {
086                    Object behaviour = loginParams.get(LOGIN_BEHAVIOUR);
087                    if (behaviour instanceof LoginBehavior) {
088                        loginBehaviour = (LoginBehavior) behaviour;
089                    }
090                }
091                loginManager.setLoginBehavior(loginBehaviour);
092                loginManager.registerCallback(_callbackManager, new FacebookCallback<LoginResult>() {
093                    @Override
094                    public void onSuccess(LoginResult loginResult) {
095                        loginManager.unregisterCallback(_callbackManager);
096                        AccessToken accessToken = AccessToken.getCurrentAccessToken();
097                        onLoginSuccess(loginParams, getProviderSessions(accessToken.getToken(), accessToken.getExpires().getTime() / 1000, null), loginMode);
098                        activity.finish();
099                    }
100
101                    @Override
102                    public void onCancel() {
103                        loginManager.unregisterCallback(_callbackManager);
104                        onCanceled();
105                        activity.finish();
106                    }
107
108                    @Override
109                    public void onError(FacebookException error) {
110                        loginManager.unregisterCallback(_callbackManager);
111                        onLoginFailed(error.getLocalizedMessage());
112                        activity.finish();
113                    }
114                });
115                // Request login.
116                loginManager.logInWithReadPermissions(activity, readPermissions);
117            }
118
119            @Override
120            public void onActivityResult(AppCompatActivity activity, int requestCode, int resultCode, @Nullable Intent data) {
121                _callbackManager.onActivityResult(requestCode, resultCode, data);
122            }
123
124        });
125    }
126
127    @Override
128    public void logout() {
129        super.logout();
130        if (_tokenTracker != null) {
131            _tokenTracker.stopTracking();
132        }
133        if (AccessToken.getCurrentAccessToken() != null) {
134            LoginManager.getInstance().logOut();
135        }
136    }
137
138    @Override
139    public String getProviderSessions(String tokenOrCode, long expiration, String uid) {
140        // token & expiration is relevant
141        try {
142            return new JSONObject()
143                    .put("facebook", new JSONObject()
144                            .put("authToken", tokenOrCode).put("tokenExpiration", expiration)).toString();
145        } catch (Exception ex) {
146            _connecting = false;
147            ex.printStackTrace();
148        }
149        return null;
150    }
151
152    @Override
153    public boolean supportsTokenTracking() {
154        return true;
155    }
156
157    @Override
158    public void trackTokenChange() {
159        _tokenTracker = new AccessTokenTracker() {
160
161            @Override
162            protected void onCurrentAccessTokenChanged(AccessToken oldAccessToken, AccessToken currentAccessToken) {
163                // Send api request.
164                final String newAuthToken = currentAccessToken.getToken();
165                final long expiresInSeconds = currentAccessToken.getExpires().getTime() / 1000;
166                _tokenTrackingListener.onTokenChange(getName(), getProviderSessions(newAuthToken, expiresInSeconds, null), null);
167            }
168        };
169    }
170
171    //region SPECIFIC PROVIDER LOGIC
172
173    public static final String LOGIN_BEHAVIOUR = "facebookLoginBehavior";
174    public static final String READ_PERMISSIONS = "facebookReadPermissions";
175    public static final String PUBLISH_PERMISSIONS = "facebookPublishPermissions";
176
177    private List<String> getReadPermissions(Map<String, Object> loginParams) {
178        List<String> readPermissions = Arrays.asList(DEFAULT_READ_PERMISSIONS);
179        if (loginParams != null && loginParams.containsKey(READ_PERMISSIONS)) {
180            String userDefinedReadPermissions = (String) loginParams.get(READ_PERMISSIONS);
181            if (userDefinedReadPermissions != null) {
182                final String[] split = userDefinedReadPermissions.split(",");
183                readPermissions = ObjectUtils.mergeRemovingDuplicates(readPermissions, Arrays.asList(split));
184            }
185        }
186        return readPermissions;
187    }
188
189    private boolean permissionsGranted(List<String> permissions) {
190        AccessToken fbAccessToken = AccessToken.getCurrentAccessToken();
191        Set<String> grantedPermissions = fbAccessToken.getPermissions();
192        for (String permission : permissions) {
193            if (!grantedPermissions.contains(permission))
194                return false;
195        }
196        return true;
197    }
198
199    //endregion
200}