Java tutorial
/** * Copyright 2012 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.android.friendsmash; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONObject; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import com.facebook.FacebookException; import com.facebook.FacebookOperationCanceledException; import com.facebook.FacebookRequestError; import com.facebook.HttpMethod; import com.facebook.Request; import com.facebook.Response; import com.facebook.Session; import com.facebook.SessionDefaultAudience; import com.facebook.model.GraphObject; import com.facebook.model.GraphUser; import com.facebook.widget.ProfilePictureView; import com.facebook.widget.WebDialog; /** * Fragment to be shown once the user is logged in on the social version of the game or * the start screen for the non-social version of the game */ public class HomeFragment extends Fragment { // Tag used when logging errors private static final String TAG = HomeFragment.class.getSimpleName(); // Store the Application (as you can't always get to it when you can't access the Activity - e.g. during rotations) private FriendSmashApplication application; // FrameLayout of the gameOverContainer private FrameLayout gameOverContainer; // FrameLayout of the progressContainer private FrameLayout progressContainer; // TextView for the You Scored message private TextView youScoredTextView; // Buttons ... private ImageView playButton; private ImageView scoresButton; private ImageView challengeButton; private ImageView bragButton; // Parameters of a WebDialog that should be displayed private WebDialog dialog = null; private String dialogAction = null; private Bundle dialogParams = null; // Runnable task used to show the Game Over message briefly at the end of a game // so that the user doesn't accidentally press any buttons once the game // is over private Runnable gameOverTask = null; // Boolean indicating whether or not the game over message is displaying private boolean gameOverMessageDisplaying = false; // Handler for putting messages on Main UI thread from background threads periodically private Handler timerHandler; // Boolean indicating if the game has been launched directly from deep linking already // so that it isn't launched again when the view is created (e.g. on rotation) private boolean gameLaunchedFromDeepLinking = false; // Attributes for posting back to Facebook private static final List<String> PERMISSIONS = Arrays.asList("publish_actions"); private static final int REAUTH_ACTIVITY_CODE = 100; private static final String PENDING_POST_KEY = "pendingPost"; private boolean pendingPost = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); // Instantiate the timerHandler timerHandler = new Handler(); application = (FriendSmashApplication) getActivity().getApplication(); } @SuppressWarnings("unused") @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { View v; if (!FriendSmashApplication.IS_SOCIAL) { v = inflater.inflate(R.layout.fragment_home, parent, false); } else { v = inflater.inflate(R.layout.fragment_home_fb_logged_in, parent, false); scoresButton = (ImageView) v.findViewById(R.id.scoresButton); scoresButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { onScoresButtonTouched(); return false; } }); challengeButton = (ImageView) v.findViewById(R.id.challengeButton); challengeButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { onChallengeButtonTouched(); return false; } }); bragButton = (ImageView) v.findViewById(R.id.bragButton); bragButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { onBragButtonTouched(); return false; } }); updateButtonVisibility(); } gameOverContainer = (FrameLayout) v.findViewById(R.id.gameOverContainer); progressContainer = (FrameLayout) v.findViewById(R.id.progressContainer); youScoredTextView = (TextView) v.findViewById(R.id.youScoredTextView); updateYouScoredTextView(); playButton = (ImageView) v.findViewById(R.id.playButton); playButton.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { onPlayButtonTouched(); return false; } }); // Instantiate the gameOverTask gameOverTask = new Runnable() { public void run() { // Hide the gameOverContainer gameOverContainer.setVisibility(View.INVISIBLE); // Set the gameOverMessageDisplaying boolean to false gameOverMessageDisplaying = false; if (FriendSmashApplication.IS_SOCIAL) { // Set the scoreboardEntriesList to null so that the scoreboard is refreshed // now that the player has played another game in case they have a higher score or // any of their friends have a higher score application.setScoreboardEntriesList(null); } } }; // Hide the gameOverContainer gameOverContainer.setVisibility(View.INVISIBLE); // Hide the progressContainer progressContainer.setVisibility(View.INVISIBLE); // Restore the state restoreState(savedInstanceState); return v; } // Personalize this HomeFragment (social-version only) void personalizeHomeFragment() { } // Restores the state during onCreateView private void restoreState(Bundle savedInstanceState) { if (savedInstanceState != null) { pendingPost = savedInstanceState.getBoolean(PENDING_POST_KEY, false); } } @Override public void onPause() { super.onPause(); // Cancel any running gameOverTasks timerHandler.removeCallbacks(gameOverTask); // Hide the gameOverContainer gameOverContainer.setVisibility(View.INVISIBLE); } @Override public void onResume() { super.onResume(); if (!gameLaunchedFromDeepLinking && gameOverMessageDisplaying) { // The game hasn't just been launched from deep linking and the game over message should still be displaying, so ... // Complete the game over logic completeGameOver(750); } } @Override public void onStart() { super.onStart(); // If a dialog exists, create a new dialog (as the screen may have rotated so needs // new dimensions) and show it if (dialog != null) { showDialogWithoutNotificationBar(dialogAction, dialogParams); } } @Override public void onStop() { super.onStop(); // If a dialog exists and is showing, dismiss it if (dialog != null) { dialog.dismiss(); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(PENDING_POST_KEY, pendingPost); } // Called when the Play button is touched private void onPlayButtonTouched() { if (application.IS_SOCIAL == true) { if (application.getFriends() == null) { ((HomeActivity) getActivity()) .showError("Complete the Personalize Chapter of the Friend Smash Tutorial first!", false); } else if (application.getFriends().size() <= 0) { ((HomeActivity) getActivity()).showError("You don't have any friends to smash!", false); } else { startGame(); } } else { startGame(); } } private void startGame() { Intent i = new Intent(getActivity(), GameActivity.class); startActivityForResult(i, 0); } // Called when the Challenge button is touched private void onChallengeButtonTouched() { sendChallenge(); } // Called when the Brag button is touched private void onBragButtonTouched() { sendBrag(); } // Called when the Scores button is touched private void onScoresButtonTouched() { } // Called when the Activity is returned to - needs to be caught for the following two scenarios: // 1. Returns from an authentication dialog requesting write permissions - tested with // requestCode == REAUTH_ACTIVITY_CODE - if successfully got permissions, execute a session // state change callback to then attempt to post their information to Facebook (again) // 2. Returns from a finished game - test status with resultCode and if successfully ended, update // their score and complete the game over process, otherwise show an error if there is one public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REAUTH_ACTIVITY_CODE) { // This ensures a session state change is recorded so that the tokenUpdated() callback is triggered // to attempt a post if the write permissions have been granted Log.i(FriendSmashApplication.TAG, "Reauthorized with publish permissions."); Session.getActiveSession().onActivityResult(getActivity(), requestCode, resultCode, data); } else if (resultCode == Activity.RESULT_OK && data != null) { // Finished a game Bundle bundle = data.getExtras(); application.setScore(bundle.getInt("score")); updateYouScoredTextView(); updateButtonVisibility(); completeGameOver(1500); } else if (resultCode == Activity.RESULT_FIRST_USER && data != null) { // Came from the ScoreboardFragment, so start a game with the specific user who has been clicked Intent i = new Intent(getActivity(), GameActivity.class); Bundle bundle = new Bundle(); bundle.putString("user_id", data.getStringExtra("user_id")); i.putExtras(bundle); startActivityForResult(i, 0); } else if (resultCode == Activity.RESULT_CANCELED && data != null) { Bundle bundle = data.getExtras(); ((HomeActivity) getActivity()).showError(bundle.getString("error"), false); } else if (resultCode == Activity.RESULT_CANCELED && ((FriendSmashApplication) getActivity().getApplication()) .getGameFragmentFBRequestError() != null) { ((HomeActivity) getActivity()).handleError( ((FriendSmashApplication) getActivity().getApplication()).getGameFragmentFBRequestError(), false); ((FriendSmashApplication) getActivity().getApplication()).setGameFragmentFBRequestError(null); } } // Start a Game with a specified user id (called from the ScoreboardFragment) void startGame(String userId) { Intent i = new Intent(getActivity(), GameActivity.class); Bundle bundle = new Bundle(); bundle.putString("user_id", userId); i.putExtras(bundle); startActivityForResult(i, 0); } // Update with the user's score void updateYouScoredTextView() { if (youScoredTextView != null) { if (application.getScore() >= 0) { youScoredTextView.setText("You Scored " + application.getScore() + "!"); } else { youScoredTextView.setText(getResources().getString(R.string.no_score)); } } } // Hide/show buttons based on whether the user has played a game yet or not void updateButtonVisibility() { if (scoresButton != null && challengeButton != null && bragButton != null) { if (application.getScore() >= 0) { // The player has played at least one game, so show the buttons scoresButton.setVisibility(View.VISIBLE); challengeButton.setVisibility(View.VISIBLE); bragButton.setVisibility(View.VISIBLE); } else { // The player hasn't played a game yet, so hide the buttons (except scoresButton // that should always be shown) scoresButton.setVisibility(View.VISIBLE); challengeButton.setVisibility(View.INVISIBLE); bragButton.setVisibility(View.INVISIBLE); } } } // Complete the game over process private void completeGameOver(int millisecondsToShow) { // Show the gameOverContainer gameOverContainer.setVisibility(View.VISIBLE); // Set the gameOverMessageDisplaying boolean to true gameOverMessageDisplaying = true; // Cancel any running gameOverTasks timerHandler.removeCallbacks(gameOverTask); // Hide the gameOverContainer after a short period of time if (gameOverTask != null) { timerHandler.postDelayed(gameOverTask, millisecondsToShow); } } /* Facebook Integration */ // Pop up a request dialog for the user to invite their friends to smash them back in Friend Smash private void sendChallenge() { } // Pop up a filtered request dialog for the user to invite their friends that have Android devices // to smash them back in Friend Smash private void sendFilteredChallenge() { // Okay, we're going to filter our friends by their device, we're looking for friends with an Android device // Show the progressContainer during the network call progressContainer.setVisibility(View.VISIBLE); // Get a list of the user's friends' names and devices final Session session = Session.getActiveSession(); Request friendDevicesGraphPathRequest = Request.newGraphPathRequest(session, "me/friends", new Request.Callback() { @Override public void onCompleted(Response response) { // Hide the progressContainer now that the network call has completed progressContainer.setVisibility(View.INVISIBLE); FacebookRequestError error = response.getError(); if (error != null) { Log.e(FriendSmashApplication.TAG, error.toString()); ((HomeActivity) getActivity()).handleError(error, false); } else if (session == Session.getActiveSession()) { if (response != null) { // Get the result GraphObject graphObject = response.getGraphObject(); JSONArray dataArray = (JSONArray) graphObject.getProperty("data"); if (dataArray.length() > 0) { // Ensure the user has at least one friend ... // Store the filtered friend ids in the following List ArrayList<String> filteredFriendIDs = new ArrayList<String>(); for (int i = 0; i < dataArray.length(); i++) { JSONObject currentUser = dataArray.optJSONObject(i); if (currentUser != null) { JSONArray currentUserDevices = currentUser.optJSONArray("devices"); if (currentUserDevices != null) { // The user has at least one (mobile) device logged into Facebook for (int j = 0; j < currentUserDevices.length(); j++) { JSONObject currentUserDevice = currentUserDevices .optJSONObject(j); if (currentUserDevice != null) { String currentUserDeviceOS = currentUserDevice .optString("os"); if (currentUserDeviceOS != null) { if (currentUserDeviceOS.equals("Android")) { filteredFriendIDs.add(currentUser.optString("id")); } } } } } } } // Now we have a list of friends with an Android device, we can send requests to them Bundle params = new Bundle(); // Uncomment following link once uploaded on Google Play for deep linking // params.putString("link", "https://play.google.com/store/apps/details?id=com.facebook.android.friendsmash"); // We create our parameter dictionary as we did before params.putString("message", "I just smashed " + application.getScore() + " friends! Can you beat it?"); // We have the same list of suggested friends String[] suggestedFriends = { "695755709", "685145706", "569496010", "286400088", "627802916", }; // Of course, not all of our suggested friends will have Android devices - we need to filter them down ArrayList<String> validSuggestedFriends = new ArrayList<String>(); // So, we loop through each suggested friend for (String suggestedFriend : suggestedFriends) { // If they are on our device filtered list, we know they have an Android device if (filteredFriendIDs.contains(suggestedFriend)) { // So we can call them valid validSuggestedFriends.add(suggestedFriend); } } params.putString("suggestions", TextUtils.join(",", validSuggestedFriends .toArray(new String[validSuggestedFriends.size()]))); // Show FBDialog without a notification bar showDialogWithoutNotificationBar("apprequests", params); } } } } }); // Pass in the fields as extra parameters, then execute the Request Bundle extraParamsBundle = new Bundle(); extraParamsBundle.putString("fields", "name,devices"); friendDevicesGraphPathRequest.setParameters(extraParamsBundle); Request.executeBatchAsync(friendDevicesGraphPathRequest); } // Pop up a feed dialog for the user to brag to their friends about their score and to offer // them the opportunity to smash them back in Friend Smash private void sendBrag() { } // Show a dialog (feed or request) without a notification bar (i.e. full screen) private void showDialogWithoutNotificationBar(String action, Bundle params) { } // Called when the session state has changed void tokenUpdated() { if (pendingPost) { facebookPostAll(); } } // Post all information to Facebook for the user (score, achievement and custom OG action) private void facebookPostAll() { pendingPost = false; Session session = Session.getActiveSession(); if (session == null || !session.isOpened()) { return; } List<String> permissions = session.getPermissions(); if (!permissions.containsAll(PERMISSIONS)) { pendingPost = true; requestPublishPermissions(session); return; } // If you get this far, then you'll have write permissions to post // Post the score to Facebook postScore(); // Post Achievemnt to Facebook postAchievement(); // Post Custom OG action to Facebook postOG(); } void requestPublishPermissions(Session session) { if (session != null) { Session.NewPermissionsRequest newPermissionsRequest = new Session.NewPermissionsRequest(this, PERMISSIONS) // demonstrate how to set an audience for the publish permissions, // if none are set, this defaults to FRIENDS .setDefaultAudience(SessionDefaultAudience.FRIENDS) .setRequestCode(REAUTH_ACTIVITY_CODE); session.requestNewPublishPermissions(newPermissionsRequest); } } // Post score to Facebook private void postScore() { final int score = application.getScore(); if (score > 0) { // Only post the score if they smashed at least one friend! // Post the score to our servers for the high score table AsyncTask.execute(new Runnable() { public void run() { HttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost("http://www.friendsmash.com/scores"); try { // Add data List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2); nameValuePairs.add(new BasicNameValuePair("fbid", application.getCurrentFBUser().getId())); nameValuePairs.add(new BasicNameValuePair("score", "" + score)); httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs)); // Execute the HTTP Post request and log the result HttpResponse responsePost = httpClient.execute(httpPost); HttpEntity responseEntity = responsePost.getEntity(); String response = EntityUtils.toString(responseEntity); Log.i(FriendSmashApplication.TAG, "Score post to server: " + response); } catch (Exception e) { Log.e(FriendSmashApplication.TAG, e.toString()); Log.e(FriendSmashApplication.TAG, "Posting Score to Server failed: " + e.getMessage()); } } }); } } // Post achievement to Facebook private void postAchievement() { } // Post custom OG action to Facebook private void postOG() { } // Getters & Setters public boolean isPendingPost() { return pendingPost; } public void setPendingPost(boolean pendingPost) { this.pendingPost = pendingPost; } }