/** * 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.samples.rps; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.*; import com.facebook.*; import com.facebook.model.GraphObject; import static com.facebook.samples.rps.OpenGraphUtils.*; import static com.facebook.samples.rps.RpsGameUtils.*; import com.facebook.model.OpenGraphAction; import com.facebook.model.OpenGraphObject; import com.facebook.widget.FacebookDialog; import java.util.Arrays; import java.util.Random; public class RpsFragment extends Fragment { private static final String SHARE_GAME_LINK = "https://developers.facebook.com/android"; private static final String SHARE_GAME_NAME = "Rock, Papers, Scissors Sample Application"; private static final String DEFAULT_GAME_OBJECT_TITLE = "an awesome game of Rock, Paper, Scissors"; private static final String WIN_KEY = "wins"; private static final String LOSS_KEY = "losses"; private static final String TIE_KEY = "ties"; private static final String PLAYER_CHOICE_KEY = "player_choice"; private static final String COMPUTER_CHOICE_KEY = "computer_choice"; private static final String STATE_KEY = "state"; private static final String RESULT_KEY = "result"; private static final String PENDING_PUBLISH_KEY = "pending_publish"; private static final String IMPLICIT_PUBLISH_KEY = "implicitly_publish"; private static final String ADDITIONAL_PERMISSIONS = "publish_actions"; private static final String PHOTO_REQUEST_NAME = "photorequest"; private static final String PHOTO_REQUEST_RESULT = "{result=photorequest:$.uri}"; private static final String GAME_REQUEST_NAME = "gamerequest"; private static final String GAME_REQUEST_RESULT = "{result=gamerequest:$.id}"; private static final int INITIAL_DELAY_MILLIS = 500; private static final int DEFAULT_DELAY_MILLIS = 1000; private static final String TAG = RpsFragment.class.getName(); private static String[] PHOTO_URIS = { null, null, null }; private TextView [] gestureTextViews = new TextView[3]; private TextView shootTextView; private ImageView playerChoiceView; private ImageView computerChoiceView; private TextView resultTextView; private ViewGroup shootGroup; private ViewGroup resultGroup; private ViewGroup playerChoiceGroup; private Button againButton; private ImageButton [] gestureImages = new ImageButton[3]; private ImageButton fbButton; private TextView statsTextView; private ViewFlipper rpsFlipper; private int wins = 0; private int losses = 0; private int ties = 0; private int playerChoice = INVALID_CHOICE; private int computerChoice = INVALID_CHOICE; private RpsState currentState = RpsState.INIT; private RpsResult result = RpsResult.INVALID; private InitHandler handler = new InitHandler(); private Random random = new Random(System.currentTimeMillis()); private boolean pendingPublish; private boolean shouldImplicitlyPublish = true; private Session.StatusCallback newPermissionsCallback = new Session.StatusCallback() { @Override public void call(Session session, SessionState state, Exception exception) { if (exception != null || !session.isOpened() || !session.getPermissions().contains(ADDITIONAL_PERMISSIONS)) { // this means the user did not grant us write permissions, so // we don't do implicit publishes shouldImplicitlyPublish = false; pendingPublish = false; } else { publishResult(); } } }; private DialogInterface.OnClickListener canPublishClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { final Session session = Session.getActiveSession(); if (session != null && session.isOpened()) { // if they choose to publish, then we request for publish permissions shouldImplicitlyPublish = true; pendingPublish = true; Session.NewPermissionsRequest newPermissionsRequest = new Session.NewPermissionsRequest(RpsFragment.this, ADDITIONAL_PERMISSIONS) .setDefaultAudience(SessionDefaultAudience.FRIENDS) .setCallback(newPermissionsCallback); session.requestNewPublishPermissions(newPermissionsRequest); } } }; private DialogInterface.OnClickListener dontPublishClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { // if they choose not to publish, then we save that choice, and don't prompt them // until they restart the app pendingPublish = false; shouldImplicitlyPublish = false; } }; private class InitHandler extends Handler { @Override public void handleMessage(Message msg) { if (!isResumed()) { // if we're not in between onResume and onPause, don't do animation transitions return; } switch (msg.what) { case ROCK: showViews(gestureTextViews[ROCK], gestureImages[ROCK]); sendNextMessage(PAPER); break; case PAPER: showViews(gestureTextViews[PAPER], gestureImages[PAPER]); sendNextMessage(SCISSORS); break; case SCISSORS: showViews(gestureTextViews[SCISSORS], gestureImages[SCISSORS]); sendNextMessage(SHOOT); break; case SHOOT: showViews(shootTextView); switchState(RpsState.PLAYING, false); break; default: Log.e(TAG, "Unexpected message received: " + msg.toString()); break; } } private void sendNextMessage(int what) { Message newMsg = new Message(); newMsg.what = what; sendMessageDelayed(newMsg, DEFAULT_DELAY_MILLIS); } } private void switchState(RpsState newState, boolean isOnResume) { if (!isResumed()) { // if we're not in between onResume and onPause, don't transition states return; } switch (newState) { case INIT: playerChoice = INVALID_CHOICE; computerChoice = INVALID_CHOICE; result = RpsResult.INVALID; showViews(shootGroup, playerChoiceGroup, rpsFlipper); rpsFlipper.startFlipping(); hideViews(gestureImages); hideViews(gestureTextViews); hideViews(resultGroup, shootTextView, againButton); enableViews(false, gestureImages); enableViews(false, againButton); Message initMessage = new Message(); initMessage.what = ROCK; handler.sendMessageDelayed(initMessage, INITIAL_DELAY_MILLIS); break; case PLAYING: enableViews(true, gestureImages); showViews(rpsFlipper); rpsFlipper.startFlipping(); break; case RESULT: hideViews(shootGroup, playerChoiceGroup); playerChoiceView.setImageResource(DRAWABLES_HUMAN[playerChoice]); computerChoiceView.setImageResource(DRAWABLES_COMPUTER[computerChoice]); resultTextView.setText(result.getStringId()); showViews(resultGroup, againButton); enableViews(true, againButton); if (!isOnResume) { // don't publish if we're switching states because onResumed is being called publishResult(); } break; default: Log.e(TAG, "Unexpected state reached: " + newState.toString()); break; } String statsFormat = getResources().getString(R.string.stats_format); statsTextView.setText(String.format(statsFormat, wins, losses, ties)); currentState = newState; } private void hideViews(View ... views) { for (View view : views) { view.setVisibility(View.INVISIBLE); } } private void showViews(View ... views) { for (View view : views) { view.setVisibility(View.VISIBLE); } } private void enableViews(boolean enabled, View ... views) { for (View view : views) { view.setEnabled(enabled); } } private void playerPlayed(int choice) { playerChoice = choice; computerChoice = getComputerChoice(); result = RESULTS[playerChoice][computerChoice]; switch (result) { case WIN: wins++; break; case LOSS: losses++; break; case TIE: ties++; break; default: Log.e(TAG, "Unexpected result: " + result.toString()); break; } switchState(RpsState.RESULT, false); } private int getComputerChoice() { return random.nextInt(3); } private boolean canPublish() { final Session session = Session.getActiveSession(); if (session != null && session.isOpened()) { if (session.getPermissions().contains(ADDITIONAL_PERMISSIONS)) { // if we already have publish permissions, then go ahead and publish return true; } else { // otherwise we ask the user if they'd like to publish to facebook new AlertDialog.Builder(getActivity()) .setTitle(R.string.share_with_friends_title) .setMessage(R.string.share_with_friends_message) .setPositiveButton(R.string.share_with_friends_yes, canPublishClickListener) .setNegativeButton(R.string.share_with_friends_no, dontPublishClickListener) .show(); return false; } } return false; } private Request publishPlayerPhoto(final int choice) { Bitmap bitmap = BitmapFactory.decodeResource(getResources(), DRAWABLES_HUMAN[choice]); Request request = Request.newUploadStagingResourceWithImageRequest( Session.getActiveSession(), bitmap, new Request.Callback() { @Override public void onCompleted(Response response) { if (response.getError() != null) { Log.e(TAG, "photo staging upload failed: " + response.getError()); } else { PHOTO_URIS[choice] = response.getGraphObject().getProperty("uri").toString(); } } }); request.setBatchEntryName(PHOTO_REQUEST_NAME); request.setBatchEntryOmitResultOnSuccess(false); return request; } private void publishResult() { if (shouldImplicitlyPublish && canPublish()) { RequestBatch batch = new RequestBatch(); String photoUri = PHOTO_URIS[playerChoice]; if (photoUri == null) { batch.add(publishPlayerPhoto(playerChoice)); photoUri = PHOTO_REQUEST_RESULT; } GameGraphObject gameObject = createGameObject(); gameObject.setImageUrls(Arrays.asList(photoUri)); Request gameRequest = Request.newPostOpenGraphObjectRequest(Session.getActiveSession(), gameObject, new Request.Callback() { @Override public void onCompleted(Response response) { if (response.getError() != null) { Log.e(TAG, "game object creation failed: " + response.getError()); } } }); gameRequest.setBatchEntryName(GAME_REQUEST_NAME); batch.add(gameRequest); PlayAction playAction = createPlayActionWithGame(GAME_REQUEST_RESULT); Request playRequest = Request.newPostOpenGraphActionRequest(Session.getActiveSession(), playAction, new Request.Callback() { @Override public void onCompleted(Response response) { if (response.getError() != null) { Log.e(TAG, "Play action creation failed: " + response.getError()); } else { PostResponse postResponse = response.getGraphObjectAs(PostResponse.class); Log.i(TAG, "Posted OG Action with id: " + postResponse.getId()); } } }); batch.add(playRequest); batch.executeAsync(); } } private GameGraphObject createGameObject() { GameGraphObject gameGraphObject = OpenGraphObject.Factory.createForPost(GameGraphObject.class, GameGraphObject.TYPE); gameGraphObject.setTitle(DEFAULT_GAME_OBJECT_TITLE); GraphObject dataObject = GraphObject.Factory.create(); dataObject.setProperty("player_gesture", CommonObjects.BUILT_IN_OPEN_GRAPH_OBJECTS[playerChoice]); dataObject.setProperty("opponent_gesture", CommonObjects.BUILT_IN_OPEN_GRAPH_OBJECTS[computerChoice]); dataObject.setProperty("result", getString(result.getResultStringId())); gameGraphObject.setData(dataObject); return gameGraphObject; } private PlayAction createPlayActionWithGame(String game) { PlayAction playAction = OpenGraphAction.Factory.createForPost(PlayAction.class, PlayAction.TYPE); playAction.setProperty("game", game); return playAction; } private GestureGraphObject getBuiltInGesture(int choice) { if (choice < 0 || choice >= CommonObjects.BUILT_IN_OPEN_GRAPH_OBJECTS.length) { throw new IllegalArgumentException("Invalid choice"); } GestureGraphObject gesture = GraphObject.Factory.create(GestureGraphObject.class); gesture.setId(CommonObjects.BUILT_IN_OPEN_GRAPH_OBJECTS[choice]); return gesture; } public void shareUsingNativeDialog() { if (playerChoice == INVALID_CHOICE || computerChoice == INVALID_CHOICE) { FacebookDialog.ShareDialogBuilder builder = new FacebookDialog.ShareDialogBuilder(getActivity()) .setLink(SHARE_GAME_LINK) .setName(SHARE_GAME_NAME) .setFragment(this); // share the app if (builder.canPresent()) { builder.build().present(); } } else { ThrowAction throwAction = OpenGraphAction.Factory.createForPost(ThrowAction.class, ThrowAction.TYPE); throwAction.setGesture(getBuiltInGesture(playerChoice)); throwAction.setOpposingGesture(getBuiltInGesture(computerChoice)); // The OG objects have their own bitmaps we could rely on, but in order to demonstrate attaching // an in-memory bitmap (e.g., a game screencap) we'll send the bitmap explicitly ourselves. ImageButton view = gestureImages[playerChoice]; BitmapDrawable drawable = (BitmapDrawable) view.getBackground(); Bitmap bitmap = drawable.getBitmap(); FacebookDialog.OpenGraphActionDialogBuilder builder = new FacebookDialog.OpenGraphActionDialogBuilder( getActivity(), throwAction, ThrowAction.PREVIEW_PROPERTY_NAME) .setFragment(this) .setImageAttachmentsForAction(Arrays.asList(bitmap)); // share the game play if (builder.canPresent()) { builder.build().present(); } } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View view = inflater.inflate(R.layout.rps_fragment, container, false); gestureTextViews[ROCK] = (TextView) view.findViewById(R.id.text_rock); gestureTextViews[PAPER] = (TextView) view.findViewById(R.id.text_paper); gestureTextViews[SCISSORS] = (TextView) view.findViewById(R.id.text_scissors); shootTextView = (TextView) view.findViewById(R.id.shoot); playerChoiceView = (ImageView) view.findViewById(R.id.player_choice); computerChoiceView = (ImageView) view.findViewById(R.id.computer_choice); resultTextView = (TextView) view.findViewById(R.id.who_won); shootGroup = (ViewGroup) view.findViewById(R.id.shoot_display_group); resultGroup = (ViewGroup) view.findViewById(R.id.result_display_group); playerChoiceGroup = (ViewGroup) view.findViewById(R.id.player_choice_display_group); againButton = (Button) view.findViewById(R.id.again_button); gestureImages[ROCK] = (ImageButton) view.findViewById(R.id.player_rock); gestureImages[PAPER] = (ImageButton) view.findViewById(R.id.player_paper); gestureImages[SCISSORS] = (ImageButton) view.findViewById(R.id.player_scissors); fbButton = (ImageButton) view.findViewById(R.id.facebook_button); statsTextView = (TextView) view.findViewById(R.id.stats); rpsFlipper = (ViewFlipper) view.findViewById(R.id.rps_flipper); gestureImages[ROCK].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { playerPlayed(ROCK); } }); gestureImages[PAPER].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { playerPlayed(PAPER); } }); gestureImages[SCISSORS].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { playerPlayed(SCISSORS); } }); againButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { switchState(RpsState.INIT, false); } }); fbButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { getActivity().openOptionsMenu(); } }); return view; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { wins = savedInstanceState.getInt(WIN_KEY); losses = savedInstanceState.getInt(LOSS_KEY); ties = savedInstanceState.getInt(TIE_KEY); computerChoice = savedInstanceState.getInt(COMPUTER_CHOICE_KEY); playerChoice = savedInstanceState.getInt(PLAYER_CHOICE_KEY); currentState = (RpsState) savedInstanceState.getSerializable(STATE_KEY); result = (RpsResult) savedInstanceState.getSerializable(RESULT_KEY); pendingPublish = savedInstanceState.getBoolean(PENDING_PUBLISH_KEY); shouldImplicitlyPublish = savedInstanceState.getBoolean(IMPLICIT_PUBLISH_KEY); } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (Session.getActiveSession() != null) { Session.getActiveSession().onActivityResult(getActivity(), requestCode, resultCode, data); } } @Override public void onResume() { super.onResume(); switchState(currentState, true); } @Override public void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); bundle.putInt(WIN_KEY, wins); bundle.putInt(LOSS_KEY, losses); bundle.putInt(TIE_KEY, ties); bundle.putInt(COMPUTER_CHOICE_KEY, computerChoice); bundle.putInt(PLAYER_CHOICE_KEY, playerChoice); bundle.putSerializable(STATE_KEY, currentState); bundle.putSerializable(RESULT_KEY, result); bundle.putBoolean(PENDING_PUBLISH_KEY, pendingPublish); bundle.putBoolean(IMPLICIT_PUBLISH_KEY, shouldImplicitlyPublish); } }