All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.facebook.AuthorizationClient Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2010-present Facebook.
 *
 * 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.facebook;

import android.Manifest;
import org.holoeverywhere.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import org.holoeverywhere.preference.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.webkit.CookieSyncManager;
import org.holoeverywhere.addon.facebook.R;
import com.facebook.internal.ServerProtocol;
import com.facebook.internal.Utility;
import com.facebook.model.GraphMultiResult;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphObjectList;
import com.facebook.model.GraphUser;
import com.facebook.widget.WebDialog;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

class AuthorizationClient implements Serializable {
    private static final long serialVersionUID = 1L;
    private static final String TAG = "Facebook-AuthorizationClient";
    private static final String WEB_VIEW_AUTH_HANDLER_STORE =
            "com.facebook.AuthorizationClient.WebViewAuthHandler.TOKEN_STORE_KEY";
    private static final String WEB_VIEW_AUTH_HANDLER_TOKEN_KEY = "TOKEN";

    List handlersToTry;
    AuthHandler currentHandler;
    transient Context context;
    transient StartActivityDelegate startActivityDelegate;
    transient OnCompletedListener onCompletedListener;
    transient BackgroundProcessingListener backgroundProcessingListener;
    transient boolean checkedInternetPermission;
    AuthorizationRequest pendingRequest;

    interface OnCompletedListener {
        void onCompleted(Result result);
    }

    interface BackgroundProcessingListener {
        void onBackgroundProcessingStarted();

        void onBackgroundProcessingStopped();
    }

    interface StartActivityDelegate {
        public void startActivityForResult(Intent intent, int requestCode);

        public Activity getActivityContext();
    }

    void setContext(final Context context) {
        this.context = context;
        // We rely on individual requests to tell us how to start an activity.
        startActivityDelegate = null;
    }

    void setContext(final Activity activity) {
        this.context = activity;

        // If we are used in the context of an activity, we will always use that activity to
        // call startActivityForResult.
        startActivityDelegate = new StartActivityDelegate() {
            @Override
            public void startActivityForResult(Intent intent, int requestCode) {
                activity.startActivityForResult(intent, requestCode);
            }

            @Override
            public Activity getActivityContext() {
                return activity;
            }
        };
    }

    void startOrContinueAuth(AuthorizationRequest request) {
        if (getInProgress()) {
            continueAuth();
        } else {
            authorize(request);
        }
    }

    void authorize(AuthorizationRequest request) {
        if (request == null) {
            return;
        }

        if (pendingRequest != null) {
            throw new FacebookException("Attempted to authorize while a request is pending.");
        }

        if (request.needsNewTokenValidation() && !checkInternetPermission()) {
            // We're going to need INTERNET permission later and don't have it, so fail early.
            return;
        }
        pendingRequest = request;
        handlersToTry = getHandlerTypes(request);
        tryNextHandler();
    }

    void continueAuth() {
        if (pendingRequest == null || currentHandler == null) {
            throw new FacebookException("Attempted to continue authorization without a pending request.");
        }

        if (currentHandler.needsRestart()) {
            currentHandler.cancel();
            tryCurrentHandler();
        }
    }

    boolean getInProgress() {
        return pendingRequest != null && currentHandler != null;
    }

    void cancelCurrentHandler() {
        if (currentHandler != null) {
            currentHandler.cancel();
        }
    }

    boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == pendingRequest.getRequestCode()) {
            return currentHandler.onActivityResult(requestCode, resultCode, data);
        }
        return false;
    }

    private List getHandlerTypes(AuthorizationRequest request) {
        ArrayList handlers = new ArrayList();

        final SessionLoginBehavior behavior = request.getLoginBehavior();
        if (behavior.allowsKatanaAuth()) {
            if (!request.isLegacy()) {
                handlers.add(new GetTokenAuthHandler());
                handlers.add(new KatanaLoginDialogAuthHandler());
            }
            handlers.add(new KatanaProxyAuthHandler());
        }

        if (behavior.allowsWebViewAuth()) {
            handlers.add(new WebViewAuthHandler());
        }

        return handlers;
    }

    boolean checkInternetPermission() {
        if (checkedInternetPermission) {
            return true;
        }

        int permissionCheck = checkPermission(Manifest.permission.INTERNET);
        if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
            String errorType = context.getString(R.string.com_facebook_internet_permission_error_title);
            String errorDescription = context.getString(R.string.com_facebook_internet_permission_error_message);
            complete(Result.createErrorResult(errorType, errorDescription));

            return false;
        }

        checkedInternetPermission = true;
        return true;
    }

    void tryNextHandler() {
        while (handlersToTry != null && !handlersToTry.isEmpty()) {
            currentHandler = handlersToTry.remove(0);

            boolean started = tryCurrentHandler();

            if (started) {
                return;
            }
        }

        if (pendingRequest != null) {
            // We went through all handlers without successfully attempting an auth.
            completeWithFailure();
        }
    }

    private void completeWithFailure() {
        complete(Result.createErrorResult("Login attempt failed.", null));
    }

    boolean tryCurrentHandler() {
        if (currentHandler.needsInternetPermission() && !checkInternetPermission()) {
            return false;
        }
        return currentHandler.tryAuthorize(pendingRequest);
    }

    void completeAndValidate(Result outcome) {
        // Do we need to validate a successful result (as in the case of a reauth)?
        if (outcome.token != null && pendingRequest.needsNewTokenValidation()) {
            validateSameFbidAndFinish(outcome);
        } else {
            // We're done, just notify the listener.
            complete(outcome);
        }
    }

    void complete(Result outcome) {
        handlersToTry = null;
        currentHandler = null;
        pendingRequest = null;

        notifyOnCompleteListener(outcome);
    }

    OnCompletedListener getOnCompletedListener() {
        return onCompletedListener;
    }

    void setOnCompletedListener(OnCompletedListener onCompletedListener) {
        this.onCompletedListener = onCompletedListener;
    }

    BackgroundProcessingListener getBackgroundProcessingListener() {
        return backgroundProcessingListener;
    }

    void setBackgroundProcessingListener(BackgroundProcessingListener backgroundProcessingListener) {
        this.backgroundProcessingListener = backgroundProcessingListener;
    }

    StartActivityDelegate getStartActivityDelegate() {
        if (startActivityDelegate != null) {
            return startActivityDelegate;
        } else if (pendingRequest != null) {
            // Wrap the request's delegate in our own.
            return new StartActivityDelegate() {
                @Override
                public void startActivityForResult(Intent intent, int requestCode) {
                    pendingRequest.getStartActivityDelegate().startActivityForResult(intent, requestCode);
                }

                @Override
                public Activity getActivityContext() {
                    return pendingRequest.getStartActivityDelegate().getActivityContext();
                }
            };
        }
        return null;
    }

    int checkPermission(String permission) {
        return context.checkCallingOrSelfPermission(permission);
    }

    void validateSameFbidAndFinish(Result pendingResult) {
        if (pendingResult.token == null) {
            throw new FacebookException("Can't validate without a token");
        }

        RequestBatch batch = createReauthValidationBatch(pendingResult);

        notifyBackgroundProcessingStart();

        batch.executeAsync();
    }

    RequestBatch createReauthValidationBatch(final Result pendingResult) {
        // We need to ensure that the token we got represents the same fbid as the old one. We issue
        // a "me" request using the current token, a "me" request using the new token, and a "me/permissions"
        // request using the current token to get the permissions of the user.

        final ArrayList fbids = new ArrayList();
        final ArrayList tokenPermissions = new ArrayList();
        final String newToken = pendingResult.token.getToken();

        Request.Callback meCallback = new Request.Callback() {
            @Override
            public void onCompleted(Response response) {
                try {
                    GraphUser user = response.getGraphObjectAs(GraphUser.class);
                    if (user != null) {
                        fbids.add(user.getId());
                    }
                } catch (Exception ex) {
                }
            }
        };

        String validateSameFbidAsToken = pendingRequest.getPreviousAccessToken();
        Request requestCurrentTokenMe = createGetProfileIdRequest(validateSameFbidAsToken);
        requestCurrentTokenMe.setCallback(meCallback);

        Request requestNewTokenMe = createGetProfileIdRequest(newToken);
        requestNewTokenMe.setCallback(meCallback);

        Request requestCurrentTokenPermissions = createGetPermissionsRequest(validateSameFbidAsToken);
        requestCurrentTokenPermissions.setCallback(new Request.Callback() {
            @Override
            public void onCompleted(Response response) {
                try {
                    GraphMultiResult result = response.getGraphObjectAs(GraphMultiResult.class);
                    if (result != null) {
                        GraphObjectList data = result.getData();
                        if (data != null && data.size() == 1) {
                            GraphObject permissions = data.get(0);

                            // The keys are the permission names.
                            tokenPermissions.addAll(permissions.asMap().keySet());
                        }
                    }
                } catch (Exception ex) {
                }
            }
        });

        RequestBatch batch = new RequestBatch(requestCurrentTokenMe, requestNewTokenMe,
                requestCurrentTokenPermissions);
        batch.setBatchApplicationId(pendingRequest.getApplicationId());
        batch.addCallback(new RequestBatch.Callback() {
            @Override
            public void onBatchCompleted(RequestBatch batch) {
                try {
                    Result result = null;
                    if (fbids.size() == 2 && fbids.get(0) != null && fbids.get(1) != null &&
                            fbids.get(0).equals(fbids.get(1))) {
                        // Modify the token to have the right permission set.
                        AccessToken tokenWithPermissions = AccessToken
                                .createFromTokenWithRefreshedPermissions(pendingResult.token,
                                        tokenPermissions);
                        result = Result.createTokenResult(tokenWithPermissions);
                    } else {
                        result = Result
                                .createErrorResult("User logged in as different Facebook user.", null);
                    }
                    complete(result);
                } catch (Exception ex) {
                    complete(Result.createErrorResult("Caught exception", ex.getMessage()));
                } finally {
                    notifyBackgroundProcessingStop();
                }
            }
        });

        return batch;
    }

    Request createGetPermissionsRequest(String accessToken) {
        Bundle parameters = new Bundle();
        parameters.putString("fields", "id");
        parameters.putString("access_token", accessToken);
        return new Request(null, "me/permissions", parameters, HttpMethod.GET, null);
    }

    Request createGetProfileIdRequest(String accessToken) {
        Bundle parameters = new Bundle();
        parameters.putString("fields", "id");
        parameters.putString("access_token", accessToken);
        return new Request(null, "me", parameters, HttpMethod.GET, null);
    }

    private void notifyOnCompleteListener(Result outcome) {
        if (onCompletedListener != null) {
            onCompletedListener.onCompleted(outcome);
        }
    }

    private void notifyBackgroundProcessingStart() {
        if (backgroundProcessingListener != null) {
            backgroundProcessingListener.onBackgroundProcessingStarted();
        }
    }

    private void notifyBackgroundProcessingStop() {
        if (backgroundProcessingListener != null) {
            backgroundProcessingListener.onBackgroundProcessingStopped();
        }
    }

    abstract class AuthHandler implements Serializable {
        private static final long serialVersionUID = 1L;

        abstract boolean tryAuthorize(AuthorizationRequest request);

        boolean onActivityResult(int requestCode, int resultCode, Intent data) {
            return false;
        }

        boolean needsRestart() {
            return false;
        }

        boolean needsInternetPermission() {
            return false;
        }

        void cancel() {
        }
    }

    class WebViewAuthHandler extends AuthHandler {
        private static final long serialVersionUID = 1L;
        private transient WebDialog loginDialog;

        @Override
        boolean needsRestart() {
            // Because we are presenting WebView UI within the current context, we need to explicitly
            // restart the process if the context goes away and is recreated.
            return true;
        }

        @Override
        boolean needsInternetPermission() {
            return true;
        }

        @Override
        void cancel() {
            if (loginDialog != null) {
                loginDialog.dismiss();
                loginDialog = null;
            }
        }

        @Override
        boolean tryAuthorize(final AuthorizationRequest request) {
            String applicationId = request.getApplicationId();
            Bundle parameters = new Bundle();
            if (!Utility.isNullOrEmpty(request.getPermissions())) {
                parameters.putString(ServerProtocol.DIALOG_PARAM_SCOPE, TextUtils.join(",", request.getPermissions()));
            }

            String previousToken = request.getPreviousAccessToken();
            if (!Utility.isNullOrEmpty(previousToken) && (previousToken.equals(loadCookieToken()))) {
                parameters.putString(ServerProtocol.DIALOG_PARAM_ACCESS_TOKEN, previousToken);
            } else {
                // The call to clear cookies will create the first instance of CookieSyncManager if necessary
                Utility.clearFacebookCookies(context);
            }

            WebDialog.OnCompleteListener listener = new WebDialog.OnCompleteListener() {
                @Override
                public void onComplete(Bundle values, FacebookException error) {
                    onWebDialogComplete(request, values, error);
                }
            };

            WebDialog.Builder builder =
                    new AuthDialogBuilder(getStartActivityDelegate().getActivityContext(), applicationId, parameters)
                            .setOnCompleteListener(listener);
            loginDialog = builder.build();
            loginDialog.show();

            return true;
        }

        void onWebDialogComplete(AuthorizationRequest request, Bundle values,
                FacebookException error) {
            Result outcome;
            if (values != null) {
                AccessToken token = AccessToken
                        .createFromWebBundle(request.getPermissions(), values, AccessTokenSource.WEB_VIEW);
                outcome = Result.createTokenResult(token);

                // Ensure any cookies set by the dialog are saved
                // This is to work around a bug where CookieManager may fail to instantiate if CookieSyncManager
                // has never been created.
                CookieSyncManager syncManager = CookieSyncManager.createInstance(context);
                syncManager.sync();
                saveCookieToken(token.getToken());
            } else {
                if (error instanceof FacebookOperationCanceledException) {
                    outcome = Result.createCancelResult("User canceled log in.");
                } else {
                    outcome = Result.createErrorResult(error.getMessage(), null);
                }
            }
            completeAndValidate(outcome);
        }

        private void saveCookieToken(String token) {
            Context context = getStartActivityDelegate().getActivityContext();
            SharedPreferences sharedPreferences = org.holoeverywhere.preference.PreferenceManager.wrap(context, 
                    WEB_VIEW_AUTH_HANDLER_STORE,
                    Context.MODE_PRIVATE);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            editor.putString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, token);
            if (!editor.commit()) {
                Utility.logd(TAG, "Could not update saved web view auth handler token.");
            }
        }

        private String loadCookieToken() {
            Context context = getStartActivityDelegate().getActivityContext();
            SharedPreferences sharedPreferences = org.holoeverywhere.preference.PreferenceManager.wrap(context, 
                    WEB_VIEW_AUTH_HANDLER_STORE,
                    Context.MODE_PRIVATE);
            return sharedPreferences.getString(WEB_VIEW_AUTH_HANDLER_TOKEN_KEY, "");
        }
    }

    class GetTokenAuthHandler extends AuthHandler {
        private static final long serialVersionUID = 1L;
        private transient GetTokenClient getTokenClient;

        @Override
        void cancel() {
            if (getTokenClient != null) {
                getTokenClient.cancel();
                getTokenClient = null;
            }
        }

        boolean tryAuthorize(final AuthorizationRequest request) {
            getTokenClient = new GetTokenClient(context, request.getApplicationId());
            if (!getTokenClient.start()) {
                return false;
            }

            notifyBackgroundProcessingStart();

            GetTokenClient.CompletedListener callback = new GetTokenClient.CompletedListener() {
                @Override
                public void completed(Bundle result) {
                    getTokenCompleted(request, result);
                }
            };

            getTokenClient.setCompletedListener(callback);
            return true;
        }

        void getTokenCompleted(AuthorizationRequest request, Bundle result) {
            getTokenClient = null;

            notifyBackgroundProcessingStop();

            if (result != null) {
                ArrayList currentPermissions = result.getStringArrayList(NativeProtocol.EXTRA_PERMISSIONS);
                List permissions = request.getPermissions();
                if ((currentPermissions != null) &&
                        ((permissions == null) || currentPermissions.containsAll(permissions))) {
                    // We got all the permissions we needed, so we can complete the auth now.
                    AccessToken token = AccessToken
                            .createFromNativeLogin(result, AccessTokenSource.FACEBOOK_APPLICATION_SERVICE);
                    Result outcome = Result.createTokenResult(token);
                    completeAndValidate(outcome);
                    return;
                }

                // We didn't get all the permissions we wanted, so update the request with just the permissions
                // we still need.
                ArrayList newPermissions = new ArrayList();
                for (String permission : permissions) {
                    if (!currentPermissions.contains(permission)) {
                        newPermissions.add(permission);
                    }
                }
                request.setPermissions(newPermissions);
            }

            tryNextHandler();
        }
    }

    abstract class KatanaAuthHandler extends AuthHandler {
        private static final long serialVersionUID = 1L;

        protected boolean tryIntent(Intent intent, int requestCode) {
            if (intent == null) {
                return false;
            }

            try {
                getStartActivityDelegate().startActivityForResult(intent, requestCode);
            } catch (ActivityNotFoundException e) {
                return false;
            }

            return true;
        }
    }

    class KatanaLoginDialogAuthHandler extends KatanaAuthHandler {
        private static final long serialVersionUID = 1L;

        @Override
        boolean tryAuthorize(AuthorizationRequest request) {
            Intent intent = NativeProtocol.createLoginDialog20121101Intent(context, request.getApplicationId(),
                    new ArrayList(request.getPermissions()),
                    request.getDefaultAudience().getNativeProtocolAudience());

            return tryIntent(intent, request.getRequestCode());
        }

        @Override
        boolean onActivityResult(int requestCode, int resultCode, Intent data) {
            Result outcome;

            if (data == null) {
                // This happens if the user presses 'Back'.
                outcome = Result.createCancelResult("Operation canceled");
            } else if (NativeProtocol.isServiceDisabledResult20121101(data)) {
                outcome = null;
            } else if (resultCode == Activity.RESULT_CANCELED) {
                outcome = Result.createCancelResult(
                        data.getStringExtra(NativeProtocol.STATUS_ERROR_DESCRIPTION));
            } else if (resultCode != Activity.RESULT_OK) {
                outcome = Result.createErrorResult("Unexpected resultCode from authorization.", null);
            } else {
                outcome = handleResultOk(data);
            }

            if (outcome != null) {
                completeAndValidate(outcome);
            } else {
                tryNextHandler();
            }

            return true;
        }

        private Result handleResultOk(Intent data) {
            Bundle extras = data.getExtras();
            String errorType = extras.getString(NativeProtocol.STATUS_ERROR_TYPE);
            if (errorType == null) {
                return Result.createTokenResult(
                        AccessToken.createFromNativeLogin(extras, AccessTokenSource.FACEBOOK_APPLICATION_NATIVE));
            } else if (NativeProtocol.ERROR_SERVICE_DISABLED.equals(errorType)) {
                return null;
            } else if (NativeProtocol.ERROR_USER_CANCELED.equals(errorType)) {
                return Result.createCancelResult(null);
            } else {
                return Result.createErrorResult(errorType, extras.getString("error_description"));
            }
        }
    }

    class KatanaProxyAuthHandler extends KatanaAuthHandler {
        private static final long serialVersionUID = 1L;

        @Override
        boolean tryAuthorize(AuthorizationRequest request) {
            Intent intent = NativeProtocol.createProxyAuthIntent(context,
                    request.getApplicationId(), request.getPermissions());

            return tryIntent(intent, request.getRequestCode());
        }

        @Override
        boolean onActivityResult(int requestCode, int resultCode, Intent data) {
            // Handle stuff
            Result outcome;

            if (data == null) {
                // This happens if the user presses 'Back'.
                outcome = Result.createCancelResult("Operation canceled");
            } else if (resultCode == Activity.RESULT_CANCELED) {
                outcome = Result.createCancelResult(data.getStringExtra("error"));
            } else if (resultCode != Activity.RESULT_OK) {
                outcome = Result.createErrorResult("Unexpected resultCode from authorization.", null);
            } else {
                outcome = handleResultOk(data);
            }

            if (outcome != null) {
                completeAndValidate(outcome);
            } else {
                tryNextHandler();
            }
            return true;
        }

        private Result handleResultOk(Intent data) {
            Bundle extras = data.getExtras();
            String error = extras.getString("error");
            if (error == null) {
                error = extras.getString("error_type");
            }

            if (error == null) {
                AccessToken token = AccessToken.createFromWebBundle(pendingRequest.getPermissions(), extras,
                        AccessTokenSource.FACEBOOK_APPLICATION_WEB);
                return Result.createTokenResult(token);
            } else if (ServerProtocol.errorsProxyAuthDisabled.contains(error)) {
                return null;
            } else if (ServerProtocol.errorsUserCanceled.contains(error)) {
                return Result.createCancelResult(null);
            } else {
                return Result.createErrorResult(error, extras.getString("error_description"));
            }
        }
    }

    static class AuthDialogBuilder extends WebDialog.Builder {
        private static final String OAUTH_DIALOG = "oauth";
        static final String REDIRECT_URI = "fbconnect://success";

        public AuthDialogBuilder(Context context, String applicationId, Bundle parameters) {
            super(context, applicationId, OAUTH_DIALOG, parameters);
        }

        @Override
        public WebDialog build() {
            Bundle parameters = getParameters();
            parameters.putString(ServerProtocol.DIALOG_PARAM_REDIRECT_URI, REDIRECT_URI);
            parameters.putString(ServerProtocol.DIALOG_PARAM_CLIENT_ID, getApplicationId());

            return new WebDialog(getContext(), OAUTH_DIALOG, parameters, getTheme(), getListener());
        }
    }

    static class AuthorizationRequest implements Serializable {
        private static final long serialVersionUID = 1L;

        private transient final StartActivityDelegate startActivityDelegate;
        private SessionLoginBehavior loginBehavior;
        private int requestCode;
        private boolean isLegacy = false;
        private List permissions;
        private SessionDefaultAudience defaultAudience;
        private String applicationId;
        private String previousAccessToken;

        AuthorizationRequest(SessionLoginBehavior loginBehavior, int requestCode, boolean isLegacy,
                List permissions, SessionDefaultAudience defaultAudience, String applicationId,
                String validateSameFbidAsToken, StartActivityDelegate startActivityDelegate) {
            this.loginBehavior = loginBehavior;
            this.requestCode = requestCode;
            this.isLegacy = isLegacy;
            this.permissions = permissions;
            this.defaultAudience = defaultAudience;
            this.applicationId = applicationId;
            this.previousAccessToken = validateSameFbidAsToken;
            this.startActivityDelegate = startActivityDelegate;

        }

        StartActivityDelegate getStartActivityDelegate() {
            return startActivityDelegate;
        }

        List getPermissions() {
            return permissions;
        }

        void setPermissions(List permissions) {
            this.permissions = permissions;
        }

        SessionLoginBehavior getLoginBehavior() {
            return loginBehavior;
        }

        int getRequestCode() {
            return requestCode;
        }

        SessionDefaultAudience getDefaultAudience() {
            return defaultAudience;
        }

        String getApplicationId() {
            return applicationId;
        }

        boolean isLegacy() {
            return isLegacy;
        }

        void setIsLegacy(boolean isLegacy) {
            this.isLegacy = isLegacy;
        }

        String getPreviousAccessToken() {
            return previousAccessToken;
        }

        boolean needsNewTokenValidation() {
            return previousAccessToken != null && !isLegacy;
        }
    }


    static class Result implements Serializable {
        private static final long serialVersionUID = 1L;

        enum Code {
            SUCCESS,
            CANCEL,
            ERROR
        }

        final Code code;
        final AccessToken token;
        final String errorMessage;

        private Result(Code code, AccessToken token, String errorMessage) {
            this.token = token;
            this.errorMessage = errorMessage;
            this.code = code;
        }

        static Result createTokenResult(AccessToken token) {
            return new Result(Code.SUCCESS, token, null);
        }

        static Result createCancelResult(String message) {
            return new Result(Code.CANCEL, null, message);
        }

        static Result createErrorResult(String errorType, String errorDescription) {
            String message = errorType;
            if (errorDescription != null) {
                message += ": " + errorDescription;
            }
            return new Result(Code.ERROR, null, message);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy