/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
* copy, modify, and distribute this software in source code or binary form for use
* in connection with the web services and APIs provided by Facebook.
*
* As with any software that integrates with the Facebook platform, your use of
* this software is subject to the Facebook Developer Principles and Policies
* [http://developers.facebook.com/policy/]. This copyright notice shall be
* included in all copies or substantial portions of the software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.example.switchuser;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.*;
import android.widget.*;
import com.facebook.AccessToken;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.FacebookSdk;
import com.facebook.Profile;
import com.facebook.ProfileTracker;
import com.facebook.login.LoginBehavior;
import com.facebook.login.LoginManager;
import com.facebook.login.LoginResult;
import com.facebook.login.widget.ProfilePictureView;
import java.util.ArrayList;
import java.util.Arrays;
public class SettingsFragment extends ListFragment {
public static final String TAG = "SettingsFragment";
private static final String CURRENT_SLOT_KEY = "CurrentSlot";
private SlotManager slotManager;
private Menu optionsMenu;
private CallbackManager callbackManager;
private ProfileTracker profileTracker;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
slotManager = new SlotManager();
slotManager.restore(
getActivity(),
savedInstanceState != null ?
savedInstanceState.getInt(CURRENT_SLOT_KEY, SlotManager.NO_SLOT) :
SlotManager.NO_SLOT);
ArrayList<Slot> slotList = new ArrayList<Slot>(
Arrays.asList(slotManager.getAllSlots()));
Slot currentSlot = slotManager.getSelectedSlot();
if (currentSlot != null && currentSlot.getAccessToken() != null) {
AccessToken.setCurrentAccessToken(currentSlot.getAccessToken());
}
setListAdapter(new SlotAdapter(slotList));
setHasOptionsMenu(true);
setUpCallbacks();
currentUserChanged();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.context_settings, menu);
optionsMenu = menu;
updateMenuVisibility();
}
private void setUpCallbacks() {
callbackManager = CallbackManager.Factory.create();
LoginManager manager = LoginManager.getInstance();
manager.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
Profile.fetchProfileForCurrentAccessToken();
}
@Override
public void onError(FacebookException exception) {
AccessToken.setCurrentAccessToken(null);
currentUserChanged();
}
@Override
public void onCancel() {
AccessToken.setCurrentAccessToken(null);
currentUserChanged();
}
});
profileTracker = new ProfileTracker() {
@Override
protected void onCurrentProfileChanged(Profile oldProfile, Profile currentProfile) {
Slot currentSlot = slotManager.getSelectedSlot();
AccessToken currentAccessToken = AccessToken.getCurrentAccessToken();
if(currentSlot != null && currentAccessToken != null && currentProfile != null) {
currentSlot.setUserInfo(
new UserInfo(currentProfile.getName(), currentAccessToken));
currentUserChanged();
}
}
};
}
@Override
public void onListItemClick(ListView l, View view, int position, long id) {
slotManager.setCurrentUserSlot(position);
Slot newSlot = slotManager.getSelectedSlot();
if (newSlot.getAccessToken() == null) {
final LoginManager manager = LoginManager.getInstance();
manager.setLoginBehavior(newSlot.getLoginBehavior());
manager.logInWithReadPermissions(this, null);
} else {
AccessToken.setCurrentAccessToken(newSlot.getAccessToken());
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Slot slot = slotManager.getSelectedSlot();
if (item.getItemId() == R.id.menu_item_clear_slot) {
if (slot.getUserId() != null) {
// Clear out data that this app stored in the cache
// Not calling Session.closeAndClearTokenInformation() because we have
// additional data stored in the cache.
slot.clear();
if (slot == slotManager.getSelectedSlot()) {
slotManager.setCurrentUserSlot(SlotManager.NO_SLOT);
}
currentUserChanged();
}
return true;
}
return super.onContextItemSelected(item);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(CURRENT_SLOT_KEY, slotManager.getSelectedSlotNumber());
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
callbackManager.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onDestroy() {
super.onDestroy();
profileTracker.stopTracking();
}
private void updateMenuVisibility() {
if (optionsMenu != null) {
if (slotManager.getSelectedSlot() == null) {
optionsMenu.setGroupVisible(0, false);
} else if (optionsMenu != null) {
optionsMenu.setGroupVisible(0, true);
}
}
}
private void currentUserChanged() {
if (slotManager == null) {
// Fragment has not had onCreate called yet.
return;
}
updateMenuVisibility();
updateListView();
Slot currentSlot = slotManager.getSelectedSlot();
AccessToken currentToken = (currentSlot != null) ? currentSlot.getAccessToken() : null;
AccessToken.setCurrentAccessToken(currentToken);
}
private void updateListView() {
SlotAdapter adapter = (SlotAdapter) getListAdapter();
// Adapter will be null if the list is not shown
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}
private class SlotManager {
static final int NO_SLOT = -1;
private final static int MAX_SLOTS = 4;
private static final String SETTINGS_CURRENT_SLOT_KEY = "CurrentSlot";
private static final String SETTINGS_NAME = "UserManagerSettings";
private SharedPreferences settings;
private int selectedSlotNumber = NO_SLOT;
private Slot[] slots;
public void restore(Context context, int oldSelectedSlot) {
if (context == null) {
throw new IllegalArgumentException("context cannot be null");
}
slots = new Slot[MAX_SLOTS];
for (int i = 0; i < MAX_SLOTS; i++) {
LoginBehavior loginBehavior = (i == 0) ?
LoginBehavior.NATIVE_WITH_FALLBACK :
LoginBehavior.WEB_ONLY;
slots[i] = new Slot(i, loginBehavior);
}
// Restore the last known state from when the app ran last.
settings = FacebookSdk.getApplicationContext().getSharedPreferences(
SETTINGS_NAME, Context.MODE_PRIVATE);
int savedSlotNumber = settings.getInt(SETTINGS_CURRENT_SLOT_KEY, NO_SLOT);
if (savedSlotNumber != NO_SLOT && savedSlotNumber != oldSelectedSlot) {
// This will trigger the full flow of login
setCurrentUserSlot(savedSlotNumber);
} else {
// We already knew which slot was selected. So don't notify that a new slot was
// selected since that will log out and start login process. And
// doing so will have the effect of clearing out state like the profile pic.
setCurrentUserSlot(savedSlotNumber);
}
}
public Slot getSelectedSlot() {
if (selectedSlotNumber == NO_SLOT) {
return null;
} else {
return getSlot(selectedSlotNumber);
}
}
public int getSelectedSlotNumber() {
return selectedSlotNumber;
}
public void setCurrentUserSlot(int slot) {
if (slot != selectedSlotNumber) {
// Store the selected slot number for when the app is closed and restarted
settings.edit().putInt(SETTINGS_CURRENT_SLOT_KEY, slot).apply();
selectedSlotNumber = slot;
currentUserChanged();
}
}
private Slot[] getAllSlots() {
return slots;
}
private Slot getSlot(int slot) {
validateSlot(slot);
return slots[slot];
}
private void validateSlot(int slot) {
if (slot <= NO_SLOT || slot >= MAX_SLOTS) {
throw new IllegalArgumentException(
String.format("Choose a slot between 0 and %d inclusively", MAX_SLOTS - 1));
}
}
}
private class SlotAdapter extends ArrayAdapter<Slot> {
public SlotAdapter(ArrayList<Slot> slots) {
super(getActivity(), android.R.layout.simple_list_item_1, slots);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (null == convertView) {
convertView = getActivity().getLayoutInflater()
.inflate(R.layout.list_item_user, parent, false);
}
Slot slot = getItem(position);
if (slot.getLoginBehavior() != LoginBehavior.WEB_ONLY) {
convertView.setBackgroundColor(Color.argb(50, 255, 255, 255));
}
String userName = slot.getUserName();
if (userName == null) {
userName = getString(R.string.empty_slot);
}
String userId = slot.getUserId();
ProfilePictureView profilePic = (ProfilePictureView) convertView.findViewById(
R.id.slotPic);
if (userId != null) {
profilePic.setProfileId(userId);
} else {
profilePic.setProfileId(null);
}
TextView userNameTextView = (TextView) convertView.findViewById(
R.id.slotUserName);
userNameTextView.setText(userName);
final CheckBox currentUserCheckBox = (CheckBox) convertView.findViewById(
R.id.currentUserIndicator);
currentUserCheckBox.setChecked(
slotManager.getSelectedSlot() == slot
&& slot.getUserInfo() != null);
currentUserCheckBox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (currentUserCheckBox.isChecked()) {
slotManager.setCurrentUserSlot(position);
} else {
slotManager.setCurrentUserSlot(SlotManager.NO_SLOT);
}
SlotAdapter adapter = (SlotAdapter) getListAdapter();
adapter.notifyDataSetChanged();
}
});
currentUserCheckBox.setEnabled(slot.getAccessToken() != null);
return convertView;
}
}
}