/** * 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.widget; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.facebook.*; import com.facebook.android.R; import com.facebook.internal.AnalyticsEvents; import com.facebook.internal.ImageDownloader; import com.facebook.internal.ImageRequest; import com.facebook.internal.ImageResponse; import com.facebook.model.GraphUser; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.List; /** * A Fragment that displays a Login/Logout button as well as the user's * profile picture and name when logged in. * <p/> * This Fragment will create and use the active session upon construction * if it has the available data (if the app ID is specified in the manifest). * It will also open the active session if it does not require user interaction * (i.e. if the session is in the {@link com.facebook.SessionState#CREATED_TOKEN_LOADED} state. * Developers can override the use of the active session by calling * the {@link #setSession(com.facebook.Session)} method. */ public class UserSettingsFragment extends FacebookFragment { private static final String NAME = "name"; private static final String ID = "id"; private static final String PICTURE = "picture"; private static final String FIELDS = "fields"; private static final String REQUEST_FIELDS = TextUtils.join(",", new String[] {ID, NAME, PICTURE}); private LoginButton loginButton; private LoginButton.LoginButtonProperties loginButtonProperties = new LoginButton.LoginButtonProperties(); private TextView connectedStateLabel; private GraphUser user; private Session userInfoSession; // the Session used to fetch the current user info private Drawable userProfilePic; private String userProfilePicID; private Session.StatusCallback sessionStatusCallback; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.com_facebook_usersettingsfragment, container, false); loginButton = (LoginButton) view.findViewById(R.id.com_facebook_usersettingsfragment_login_button); loginButton.setProperties(loginButtonProperties); loginButton.setFragment(this); loginButton.setLoginLogoutEventName(AnalyticsEvents.EVENT_USER_SETTINGS_USAGE); Session session = getSession(); if (session != null && !session.equals(Session.getActiveSession())) { loginButton.setSession(session); } connectedStateLabel = (TextView) view.findViewById(R.id.com_facebook_usersettingsfragment_profile_name); // if no background is set for some reason, then default to Facebook blue if (view.getBackground() == null) { view.setBackgroundColor(getResources().getColor(R.color.com_facebook_blue)); } else { view.getBackground().setDither(true); } return view; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } /** * @throws com.facebook.FacebookException if errors occur during the loading of user information */ @Override public void onResume() { super.onResume(); fetchUserInfo(); updateUI(); } /** * Set the Session object to use instead of the active Session. Since a Session * cannot be reused, if the user logs out from this Session, and tries to * log in again, a new Active Session will be used instead. * <p/> * If the passed in session is currently opened, this method will also attempt to * load some user information for display (if needed). * * @param newSession the Session object to use * @throws com.facebook.FacebookException if errors occur during the loading of user information */ @Override public void setSession(Session newSession) { super.setSession(newSession); if (loginButton != null) { loginButton.setSession(newSession); } fetchUserInfo(); updateUI(); } /** * Sets the default audience to use when the session is opened. * This value is only useful when specifying write permissions for the native * login dialog. * * @param defaultAudience the default audience value to use */ public void setDefaultAudience(SessionDefaultAudience defaultAudience) { loginButtonProperties.setDefaultAudience(defaultAudience); } /** * Gets the default audience to use when the session is opened. * This value is only useful when specifying write permissions for the native * login dialog. * * @return the default audience value to use */ public SessionDefaultAudience getDefaultAudience() { return loginButtonProperties.getDefaultAudience(); } /** * Set the permissions to use when the session is opened. The permissions here * can only be read permissions. If any publish permissions are included, the login * attempt by the user will fail. The LoginButton can only be associated with either * read permissions or publish permissions, but not both. Calling both * setReadPermissions and setPublishPermissions on the same instance of LoginButton * will result in an exception being thrown unless clearPermissions is called in between. * <p/> * This method is only meaningful if called before the session is open. If this is called * after the session is opened, and the list of permissions passed in is not a subset * of the permissions granted during the authorization, it will log an error. * <p/> * Since the session can be automatically opened when the UserSettingsFragment is constructed, * it's important to always pass in a consistent set of permissions to this method, or * manage the setting of permissions outside of the LoginButton class altogether * (by managing the session explicitly). * * @param permissions the read permissions to use * * @throws UnsupportedOperationException if setPublishPermissions has been called */ public void setReadPermissions(List<String> permissions) { loginButtonProperties.setReadPermissions(permissions, getSession()); } /** * Set the permissions to use when the session is opened. The permissions here * can only be read permissions. If any publish permissions are included, the login * attempt by the user will fail. The LoginButton can only be associated with either * read permissions or publish permissions, but not both. Calling both * setReadPermissions and setPublishPermissions on the same instance of LoginButton * will result in an exception being thrown unless clearPermissions is called in between. * <p/> * This method is only meaningful if called before the session is open. If this is called * after the session is opened, and the list of permissions passed in is not a subset * of the permissions granted during the authorization, it will log an error. * <p/> * Since the session can be automatically opened when the UserSettingsFragment is constructed, * it's important to always pass in a consistent set of permissions to this method, or * manage the setting of permissions outside of the LoginButton class altogether * (by managing the session explicitly). * * @param permissions the read permissions to use * * @throws UnsupportedOperationException if setPublishPermissions has been called */ public void setReadPermissions(String... permissions) { loginButtonProperties.setReadPermissions(Arrays.asList(permissions), getSession()); } /** * Set the permissions to use when the session is opened. The permissions here * should only be publish permissions. If any read permissions are included, the login * attempt by the user may fail. The LoginButton can only be associated with either * read permissions or publish permissions, but not both. Calling both * setReadPermissions and setPublishPermissions on the same instance of LoginButton * will result in an exception being thrown unless clearPermissions is called in between. * <p/> * This method is only meaningful if called before the session is open. If this is called * after the session is opened, and the list of permissions passed in is not a subset * of the permissions granted during the authorization, it will log an error. * <p/> * Since the session can be automatically opened when the LoginButton is constructed, * it's important to always pass in a consistent set of permissions to this method, or * manage the setting of permissions outside of the LoginButton class altogether * (by managing the session explicitly). * * @param permissions the read permissions to use * * @throws UnsupportedOperationException if setReadPermissions has been called * @throws IllegalArgumentException if permissions is null or empty */ public void setPublishPermissions(List<String> permissions) { loginButtonProperties.setPublishPermissions(permissions, getSession()); } /** * Set the permissions to use when the session is opened. The permissions here * should only be publish permissions. If any read permissions are included, the login * attempt by the user may fail. The LoginButton can only be associated with either * read permissions or publish permissions, but not both. Calling both * setReadPermissions and setPublishPermissions on the same instance of LoginButton * will result in an exception being thrown unless clearPermissions is called in between. * <p/> * This method is only meaningful if called before the session is open. If this is called * after the session is opened, and the list of permissions passed in is not a subset * of the permissions granted during the authorization, it will log an error. * <p/> * Since the session can be automatically opened when the LoginButton is constructed, * it's important to always pass in a consistent set of permissions to this method, or * manage the setting of permissions outside of the LoginButton class altogether * (by managing the session explicitly). * * @param permissions the read permissions to use * * @throws UnsupportedOperationException if setReadPermissions has been called * @throws IllegalArgumentException if permissions is null or empty */ public void setPublishPermissions(String... permissions) { loginButtonProperties.setPublishPermissions(Arrays.asList(permissions), getSession()); } /** * Clears the permissions currently associated with this LoginButton. */ public void clearPermissions() { loginButtonProperties.clearPermissions(); } /** * Sets the login behavior for the session that will be opened. If null is specified, * the default ({@link SessionLoginBehavior SessionLoginBehavior.SSO_WITH_FALLBACK} * will be used. * * @param loginBehavior The {@link SessionLoginBehavior SessionLoginBehavior} that * specifies what behaviors should be attempted during * authorization. */ public void setLoginBehavior(SessionLoginBehavior loginBehavior) { loginButtonProperties.setLoginBehavior(loginBehavior); } /** * Gets the login behavior for the session that will be opened. If null is returned, * the default ({@link SessionLoginBehavior SessionLoginBehavior.SSO_WITH_FALLBACK} * will be used. * * @return loginBehavior The {@link SessionLoginBehavior SessionLoginBehavior} that * specifies what behaviors should be attempted during * authorization. */ public SessionLoginBehavior getLoginBehavior() { return loginButtonProperties.getLoginBehavior(); } /** * Sets an OnErrorListener for this instance of UserSettingsFragment to call into when * certain exceptions occur. * * @param onErrorListener The listener object to set */ public void setOnErrorListener(LoginButton.OnErrorListener onErrorListener) { loginButtonProperties.setOnErrorListener(onErrorListener); } /** * Returns the current OnErrorListener for this instance of UserSettingsFragment. * * @return The OnErrorListener */ public LoginButton.OnErrorListener getOnErrorListener() { return loginButtonProperties.getOnErrorListener(); } /** * Sets the callback interface that will be called whenever the status of the Session * associated with this LoginButton changes. * * @param callback the callback interface */ public void setSessionStatusCallback(Session.StatusCallback callback) { this.sessionStatusCallback = callback; } /** * Sets the callback interface that will be called whenever the status of the Session * associated with this LoginButton changes. * @return the callback interface */ public Session.StatusCallback getSessionStatusCallback() { return sessionStatusCallback; } @Override protected void onSessionStateChange(SessionState state, Exception exception) { fetchUserInfo(); updateUI(); if (sessionStatusCallback != null) { sessionStatusCallback.call(getSession(), state, exception); } } // For Testing Only List<String> getPermissions() { return loginButtonProperties.getPermissions(); } private void fetchUserInfo() { final Session currentSession = getSession(); if (currentSession != null && currentSession.isOpened()) { if (currentSession != userInfoSession) { Request request = Request.newMeRequest(currentSession, new Request.GraphUserCallback() { @Override public void onCompleted(GraphUser me, Response response) { if (currentSession == getSession()) { user = me; updateUI(); } if (response.getError() != null) { loginButton.handleError(response.getError().getException()); } } }); Bundle parameters = new Bundle(); parameters.putString(FIELDS, REQUEST_FIELDS); request.setParameters(parameters); Request.executeBatchAsync(request); userInfoSession = currentSession; } } else { user = null; } } private void updateUI() { if (!isAdded()) { return; } if (isSessionOpen()) { connectedStateLabel.setTextColor(getResources().getColor(R.color.com_facebook_usersettingsfragment_connected_text_color)); connectedStateLabel.setShadowLayer(1f, 0f, -1f, getResources().getColor(R.color.com_facebook_usersettingsfragment_connected_shadow_color)); if (user != null) { ImageRequest request = getImageRequest(); if (request != null) { URI requestUrl = request.getImageUri(); // Do we already have the right picture? If so, leave it alone. if (!requestUrl.equals(connectedStateLabel.getTag())) { if (user.getId().equals(userProfilePicID)) { connectedStateLabel.setCompoundDrawables(null, userProfilePic, null, null); connectedStateLabel.setTag(requestUrl); } else { ImageDownloader.downloadAsync(request); } } } connectedStateLabel.setText(user.getName()); } else { connectedStateLabel.setText(getResources().getString( R.string.com_facebook_usersettingsfragment_logged_in)); Drawable noProfilePic = getResources().getDrawable(R.drawable.com_facebook_profile_default_icon); noProfilePic.setBounds(0, 0, getResources().getDimensionPixelSize(R.dimen.com_facebook_usersettingsfragment_profile_picture_width), getResources().getDimensionPixelSize(R.dimen.com_facebook_usersettingsfragment_profile_picture_height)); connectedStateLabel.setCompoundDrawables(null, noProfilePic, null, null); } } else { int textColor = getResources().getColor(R.color.com_facebook_usersettingsfragment_not_connected_text_color); connectedStateLabel.setTextColor(textColor); connectedStateLabel.setShadowLayer(0f, 0f, 0f, textColor); connectedStateLabel.setText(getResources().getString( R.string.com_facebook_usersettingsfragment_not_logged_in)); connectedStateLabel.setCompoundDrawables(null, null, null, null); connectedStateLabel.setTag(null); } } private ImageRequest getImageRequest() { ImageRequest request = null; try { ImageRequest.Builder requestBuilder = new ImageRequest.Builder( getActivity(), ImageRequest.getProfilePictureUrl( user.getId(), getResources().getDimensionPixelSize(R.dimen.com_facebook_usersettingsfragment_profile_picture_width), getResources().getDimensionPixelSize(R.dimen.com_facebook_usersettingsfragment_profile_picture_height))); request = requestBuilder.setCallerTag(this) .setCallback( new ImageRequest.Callback() { @Override public void onCompleted(ImageResponse response) { processImageResponse(user.getId(), response); } }) .build(); } catch (URISyntaxException e) { } return request; } private void processImageResponse(String id, ImageResponse response) { if (response != null) { Bitmap bitmap = response.getBitmap(); if (bitmap != null) { BitmapDrawable drawable = new BitmapDrawable(UserSettingsFragment.this.getResources(), bitmap); drawable.setBounds(0, 0, getResources().getDimensionPixelSize(R.dimen.com_facebook_usersettingsfragment_profile_picture_width), getResources().getDimensionPixelSize(R.dimen.com_facebook_usersettingsfragment_profile_picture_height)); userProfilePic = drawable; userProfilePicID = id; connectedStateLabel.setCompoundDrawables(null, drawable, null, null); connectedStateLabel.setTag(response.getRequest().getImageUri()); } } } }