From 8900df5f51f48c869d4286e85ccac794aed3c591 Mon Sep 17 00:00:00 2001 From: Don Turner Date: Wed, 25 Sep 2019 19:08:50 +0100 Subject: [PATCH] Add deprecation notice Add Friends Functionality Add Friends Functionality Add Friends Functionality Add Friends Functionality Add Friends Functionality Add Friends Functionality Add Friends Functionality --- ButtonClicker/build.gradle | 12 +- .../google/example/games/bc/MainActivity.java | 2 +- .../src/main/res/values-v13/styles.xml | 2 +- CollectAllTheStars2/build.gradle | 4 +- .../example/games/catt2/MainActivity.java | 11 +- .../games/catt2/SelectSnapshotActivity.java | 7 +- .../games/catt2/SnapshotCoordinator.java | 2 +- .../src/main/res/values-v11/styles.xml | 2 +- .../src/main/res/values-v14/styles.xml | 2 +- README.md | 4 +- SkeletonTbmp/build.gradle | 4 +- .../games/tbmpskeleton/SkeletonActivity.java | 8 +- .../src/main/res/values-v11/styles.xml | 2 +- .../src/main/res/values-v14/styles.xml | 2 +- TypeANumber/build.gradle | 9 +- .../example/games/tanc/FriendsFragment.java | 188 ++++++++++++++++++ .../example/games/tanc/GameplayFragment.java | 3 +- .../example/games/tanc/MainActivity.java | 79 +++++--- .../example/games/tanc/MainMenuFragment.java | 14 +- .../example/games/tanc/WinFragment.java | 4 +- .../drawable-hdpi/pgs_signed_in_friend.png | Bin 0 -> 4528 bytes .../drawable-ldpi/pgs_signed_in_friend.png | Bin 0 -> 2020 bytes .../drawable-mdpi/pgs_signed_in_friend.png | Bin 0 -> 2792 bytes .../drawable-xhdpi/pgs_signed_in_friend.png | Bin 0 -> 6016 bytes .../src/main/res/layout/fragment_mainmenu.xml | 6 + .../src/main/res/layout/friends_row.xml | 37 ++++ .../src/main/res/layout/friends_screen.xml | 67 +++++++ .../src/main/res/values-v13/styles.xml | 2 +- TypeANumber/src/main/res/values/strings.xml | 3 + build.gradle | 6 +- gradle.properties | 2 + gradle/wrapper/gradle-wrapper.properties | 4 +- 32 files changed, 420 insertions(+), 68 deletions(-) create mode 100644 TypeANumber/src/main/java/com/google/example/games/tanc/FriendsFragment.java create mode 100644 TypeANumber/src/main/res/drawable-hdpi/pgs_signed_in_friend.png create mode 100644 TypeANumber/src/main/res/drawable-ldpi/pgs_signed_in_friend.png create mode 100644 TypeANumber/src/main/res/drawable-mdpi/pgs_signed_in_friend.png create mode 100644 TypeANumber/src/main/res/drawable-xhdpi/pgs_signed_in_friend.png create mode 100644 TypeANumber/src/main/res/layout/friends_row.xml create mode 100644 TypeANumber/src/main/res/layout/friends_screen.xml create mode 100644 gradle.properties diff --git a/ButtonClicker/build.gradle b/ButtonClicker/build.gradle index cfbc3a59..849ba361 100644 --- a/ButtonClicker/build.gradle +++ b/ButtonClicker/build.gradle @@ -22,13 +22,17 @@ android { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt') } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } } dependencies { - implementation "com.android.support:appcompat-v7:${appcompat_library_version}" - implementation "com.android.support:support-v4:${support_library_version}" - implementation "com.google.android.gms:play-services-games:${gms_library_version}" + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation "com.google.android.gms:play-services-games:20.0.1" implementation "com.google.android.gms:play-services-auth:${gms_library_version}" } @@ -36,4 +40,4 @@ buildscript { repositories { jcenter() } -} +} \ No newline at end of file diff --git a/ButtonClicker/src/main/java/com/google/example/games/bc/MainActivity.java b/ButtonClicker/src/main/java/com/google/example/games/bc/MainActivity.java index 2c872be9..593b3ff3 100644 --- a/ButtonClicker/src/main/java/com/google/example/games/bc/MainActivity.java +++ b/ButtonClicker/src/main/java/com/google/example/games/bc/MainActivity.java @@ -20,7 +20,7 @@ import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import android.view.KeyEvent; import android.view.View; diff --git a/ButtonClicker/src/main/res/values-v13/styles.xml b/ButtonClicker/src/main/res/values-v13/styles.xml index 997965b5..03b93037 100644 --- a/ButtonClicker/src/main/res/values-v13/styles.xml +++ b/ButtonClicker/src/main/res/values-v13/styles.xml @@ -16,5 +16,5 @@ --> - diff --git a/CollectAllTheStars2/src/main/res/values-v14/styles.xml b/CollectAllTheStars2/src/main/res/values-v14/styles.xml index 9e1013d7..54a8dc22 100644 --- a/CollectAllTheStars2/src/main/res/values-v14/styles.xml +++ b/CollectAllTheStars2/src/main/res/values-v14/styles.xml @@ -22,7 +22,7 @@ AppBaseTheme from BOTH res/values/styles.xml and res/values-v11/styles.xml on API 14+ devices. --> - diff --git a/README.md b/README.md index d35a561a..1ccfc9c1 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,12 @@ These are the Android samples for Google Play game services. * **CollectAllTheStars2**. Demonstrates how to use the Snapshots feature to save game data. The sample signs the user in, synchronizes their data from a named Snapshot, then updates the UI to reflect the game state saved in the Snapshot. -* **TypeANumber**. Demonstrates how to use leaderboards, achievements and events. In this exciting game, you type the score you think you deserve. But wait! There is a twist. If you are playing in easy mode, you get the score you requested. However, if you are playing in hard mode, you only get half! (tough game, we know). +* **TypeANumber**. Demonstrates how to use leaderboards, achievements, events, and friends. In this exciting game, you type the score you think you deserve. But wait! There is a twist. If you are playing in easy mode, you get the score you requested. However, if you are playing in hard mode, you only get half! (tough game, we know). You can also check how your friends perform in this game by checking out social leaderboards. * **SkeletonTbmp** A trivial turn-based-multiplayer game. In this thrilling game, you can invite many friends, then send a shared gamestate string back and forth until someone finishes, cancels, or the second-to-last player leaves. +> **Warning:** Real-time and turn-based multiplayer services are deprecated as of September 16th, 2019. These services are unavailable for new games. For more information, see [Ending support for multiplayer APIs in Play Games Services](https://support.google.com/googleplay/android-developer/answer/9469745). +

How to run a sample

1. Set up the project in the Developer Console by following [these instructions](https://developers.google.com/games/services/console/enabling). diff --git a/SkeletonTbmp/build.gradle b/SkeletonTbmp/build.gradle index 82ad2eba..261419c4 100644 --- a/SkeletonTbmp/build.gradle +++ b/SkeletonTbmp/build.gradle @@ -25,8 +25,8 @@ android { } dependencies { - implementation "com.android.support:appcompat-v7:${appcompat_library_version}" - implementation "com.android.support:support-v4:${support_library_version}" + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation "com.google.android.gms:play-services-games:${gms_library_version}" implementation "com.google.android.gms:play-services-auth:${gms_library_version}" } diff --git a/SkeletonTbmp/src/main/java/com/google/example/games/tbmpskeleton/SkeletonActivity.java b/SkeletonTbmp/src/main/java/com/google/example/games/tbmpskeleton/SkeletonActivity.java index 307af0af..40edb29e 100644 --- a/SkeletonTbmp/src/main/java/com/google/example/games/tbmpskeleton/SkeletonActivity.java +++ b/SkeletonTbmp/src/main/java/com/google/example/games/tbmpskeleton/SkeletonActivity.java @@ -17,16 +17,18 @@ package com.google.example.games.tbmpskeleton; import android.app.Activity; -import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.NonNull; import android.util.Log; import android.view.View; import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; + import com.google.android.gms.auth.api.signin.GoogleSignIn; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; import com.google.android.gms.auth.api.signin.GoogleSignInClient; @@ -71,7 +73,7 @@ * * @author Wolff (wolff@google.com), 2013 */ -public class SkeletonActivity extends Activity implements +public class SkeletonActivity extends AppCompatActivity implements View.OnClickListener { public static final String TAG = "SkeletonActivity"; diff --git a/SkeletonTbmp/src/main/res/values-v11/styles.xml b/SkeletonTbmp/src/main/res/values-v11/styles.xml index 3c02242a..a4a95bc4 100644 --- a/SkeletonTbmp/src/main/res/values-v11/styles.xml +++ b/SkeletonTbmp/src/main/res/values-v11/styles.xml @@ -4,7 +4,7 @@ Base application theme for API 11+. This theme completely replaces AppBaseTheme from res/values/styles.xml on API 11+ devices. --> - diff --git a/SkeletonTbmp/src/main/res/values-v14/styles.xml b/SkeletonTbmp/src/main/res/values-v14/styles.xml index a91fd037..664f4f16 100644 --- a/SkeletonTbmp/src/main/res/values-v14/styles.xml +++ b/SkeletonTbmp/src/main/res/values-v14/styles.xml @@ -5,7 +5,7 @@ AppBaseTheme from BOTH res/values/styles.xml and res/values-v11/styles.xml on API 14+ devices. --> - diff --git a/TypeANumber/build.gradle b/TypeANumber/build.gradle index cfbc3a59..0f516a0b 100644 --- a/TypeANumber/build.gradle +++ b/TypeANumber/build.gradle @@ -26,9 +26,12 @@ android { } dependencies { - implementation "com.android.support:appcompat-v7:${appcompat_library_version}" - implementation "com.android.support:support-v4:${support_library_version}" - implementation "com.google.android.gms:play-services-games:${gms_library_version}" + implementation 'androidx.appcompat:appcompat:1.3.0-alpha01' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation "androidx.activity:activity:1.2.0-alpha08" + implementation "androidx.activity:activity-ktx:1.2.0-alpha03" + implementation "androidx.fragment:fragment:1.3.0-alpha08" + implementation "com.google.android.gms:play-services-games:20.0.1" implementation "com.google.android.gms:play-services-auth:${gms_library_version}" } diff --git a/TypeANumber/src/main/java/com/google/example/games/tanc/FriendsFragment.java b/TypeANumber/src/main/java/com/google/example/games/tanc/FriendsFragment.java new file mode 100644 index 00000000..5cd5b601 --- /dev/null +++ b/TypeANumber/src/main/java/com/google/example/games/tanc/FriendsFragment.java @@ -0,0 +1,188 @@ +package com.google.example.games.tanc; + +import static com.google.example.games.tanc.MainActivity.RC_FRIEND_PROFILE; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.ImageButton; +import android.widget.ListView; +import android.widget.TextView; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultCallback; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.IntentSenderRequest; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import com.google.android.gms.common.api.ResolvableApiException; +import com.google.android.gms.common.data.DataBufferUtils; +import com.google.android.gms.games.AnnotatedData; +import com.google.android.gms.games.Player; +import com.google.android.gms.games.PlayerBuffer; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; +import java.util.ArrayList; + +/** Fragment that shows the list of friends. */ +public class FriendsFragment extends Fragment { + private static final String TAG = "FriendsFragment"; + private static final int PAGE_SIZE = 200; + + interface Listener { + // called when the user presses the `Back` button + void onBackButtonClicked(); + } + + private View mView; + private ListView mListView; + private View mSpinner; + private ArrayAdapter mAdapter; + private MainActivity mActivity; + private ActivityResultLauncher resolveLauncherFriendsConsent; + private ActivityResultLauncher resolveLauncherCompareProfile; + + private Listener mListener = null; + private OnCompleteListener> onCompleteListener = + new OnCompleteListener>() { + @Override + public void onComplete(@NonNull Task> task) { + if (task.isSuccessful()) { + if (task.getResult() == null) { + mListener.onBackButtonClicked(); + } + PlayerBuffer playerBuffer = task.getResult().get(); + try { + if (DataBufferUtils.hasNextPage(playerBuffer)) { + mActivity + .getPlayersClient() + .loadMoreFriends(PAGE_SIZE) + .addOnCompleteListener(mActivity, onCompleteListener); + } else { + LayoutInflater inflater = LayoutInflater.from(getContext()); + mAdapter = getAdapter(playerBuffer, inflater); + mListView.setAdapter(mAdapter); + mSpinner.setVisibility(View.GONE); + mListView.setVisibility(View.VISIBLE); + } + } finally { + playerBuffer.release(); + } + } else { + Log.e(TAG, "Getting friends failed with exception: " + task.getException()); + try { + if (task.getException() instanceof ResolvableApiException) { + PendingIntent pendingIntent = + ((ResolvableApiException) task.getException()).getResolution(); + resolveLauncherFriendsConsent.launch( + new IntentSenderRequest.Builder(pendingIntent).build()); + } + } catch (Exception e) { + Log.e(TAG, "Getting consent failed with exception: " + e); + mListener.onBackButtonClicked(); + } + } + } + }; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { + mView = inflater.inflate(R.layout.friends_screen, container, /* attachToRoot= */ false); + mListView = mView.findViewById(R.id.load_friends_game_list); + mSpinner = mView.findViewById(R.id.progress_bar); + mActivity = (MainActivity) getActivity(); + mView + .findViewById(R.id.back_button) + .setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View view) { + mListener.onBackButtonClicked(); + } + }); + resolveLauncherFriendsConsent = + registerForActivityResult( + new ActivityResultContracts.StartIntentSenderForResult(), + new ActivityResultCallback() { + @Override + public void onActivityResult(ActivityResult result) { + if (result.getResultCode() == Activity.RESULT_OK) { + refreshFriends(); + } else { + mListener.onBackButtonClicked(); + } + } + }); + resolveLauncherCompareProfile = + registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), + new ActivityResultCallback() { + @Override + public void onActivityResult(ActivityResult result) { + if (result.getResultCode() == Activity.RESULT_OK) { + refreshFriends(); + } + } + }); + refreshFriends(); + return mView; + } + + void setListener(Listener listener) { + mListener = listener; + } + + @NonNull + private ArrayAdapter getAdapter( + PlayerBuffer playerBuffer, final LayoutInflater inflater) { + ArrayList players = new ArrayList<>(); + for (int i = 0; i < playerBuffer.getCount(); i++) { + players.add(playerBuffer.get(i).freeze()); + } + return new ArrayAdapter(mActivity, R.layout.friends_row, players) { + @Override + public View getView(int position, View convertView, ViewGroup viewGroup) { + View rowView = inflater.inflate(R.layout.friends_row, viewGroup, /* attachToRoot= */ false); + final Player player = getItem(position); + TextView textView = rowView.findViewById(R.id.friend_name); + textView.setText(player.getDisplayName()); + ImageButton showProfileButton = rowView.findViewById(R.id.show_profile); + showProfileButton.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + mActivity + .getPlayersClient() + .getCompareProfileIntentWithAlternativeNameHints( + player.getPlayerId(), player.getDisplayName(), mActivity.getDisplayName()) + .addOnSuccessListener( + mActivity, + new OnSuccessListener() { + @Override + public void onSuccess(Intent intent) { + resolveLauncherCompareProfile.launch(intent); + } + }); + } + }); + return rowView; + } + }; + } + + void refreshFriends() { + mListView.setAdapter(null); + mSpinner.setVisibility(View.VISIBLE); + mActivity + .getPlayersClient() + .loadFriends(PAGE_SIZE, /* forceReload= */ false) + .addOnCompleteListener(mActivity, onCompleteListener); + } +} \ No newline at end of file diff --git a/TypeANumber/src/main/java/com/google/example/games/tanc/GameplayFragment.java b/TypeANumber/src/main/java/com/google/example/games/tanc/GameplayFragment.java index 2c58b734..6102f582 100644 --- a/TypeANumber/src/main/java/com/google/example/games/tanc/GameplayFragment.java +++ b/TypeANumber/src/main/java/com/google/example/games/tanc/GameplayFragment.java @@ -17,14 +17,13 @@ package com.google.example.games.tanc; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; - +import androidx.fragment.app.Fragment; import java.util.Locale; /** diff --git a/TypeANumber/src/main/java/com/google/example/games/tanc/MainActivity.java b/TypeANumber/src/main/java/com/google/example/games/tanc/MainActivity.java index 75b94023..bd96611b 100644 --- a/TypeANumber/src/main/java/com/google/example/games/tanc/MainActivity.java +++ b/TypeANumber/src/main/java/com/google/example/games/tanc/MainActivity.java @@ -16,15 +16,15 @@ package com.google.example.games.tanc; -import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; import android.util.Log; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import com.google.android.gms.auth.api.signin.GoogleSignIn; import com.google.android.gms.auth.api.signin.GoogleSignInAccount; import com.google.android.gms.auth.api.signin.GoogleSignInClient; @@ -46,28 +46,30 @@ /** * Our main activity for the game. - *

- * IMPORTANT: Before attempting to run this sample, please change + * + *

IMPORTANT: Before attempting to run this sample, please change * the package name to your own package name (not com.android.*) and * replace the IDs on res/values/ids.xml by your own IDs (you must * create a game in the developer console to get those IDs). - *

- * This is a very simple game where the user selects "easy mode" or + * + *

This is a very simple game where the user selects "easy mode" or * "hard mode" and then the "gameplay" consists of inputting the - * desired score (0 to 9999). In easy mode, you get the score you - * request; in hard mode, you get half. + * desired score (0 to 9999). In easy mode, you get the score + * you request; in hard mode, you get half. * * @author Bruno Oliveira */ public class MainActivity extends FragmentActivity implements MainMenuFragment.Listener, GameplayFragment.Callback, - WinFragment.Listener { + WinFragment.Listener, + FriendsFragment.Listener { // Fragments private MainMenuFragment mMainMenuFragment; private GameplayFragment mGameplayFragment; private WinFragment mWinFragment; + public FriendsFragment mFriendsFragment; // Client used to sign in with Google APIs private GoogleSignInClient mGoogleSignInClient; @@ -82,12 +84,18 @@ public class MainActivity extends FragmentActivity implements private static final int RC_UNUSED = 5001; private static final int RC_SIGN_IN = 9001; + static final int RC_FRIEND_PROFILE = 9002; + static final int RC_RESOLUTION = 9003; + // tag for debug logging private static final String TAG = "TanC"; // playing on hard mode? private boolean mHardMode = false; + // The diplay name of the signed in user. + private String mDisplayName = ""; + // achievements and scores we're pending to push to the cloud // (waiting for the user to sign in, for instance) private final AccomplishmentsOutbox mOutbox = new AccomplishmentsOutbox(); @@ -106,11 +114,13 @@ public void onCreate(Bundle savedInstanceState) { mMainMenuFragment = new MainMenuFragment(); mGameplayFragment = new GameplayFragment(); mWinFragment = new WinFragment(); + mFriendsFragment = new FriendsFragment(); // Set the listeners and callbacks of fragment events. mMainMenuFragment.setListener(this); mGameplayFragment.setCallback(this); mWinFragment.setListener(this); + mFriendsFragment.setListener(this); // Add initial Main Menu fragment. // IMPORTANT: if this Activity supported rotation, we'd have to be @@ -120,7 +130,6 @@ public void onCreate(Bundle savedInstanceState) { // we don't deal with that for code simplicity. getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, mMainMenuFragment).commit(); - checkPlaceholderIds(); } @@ -225,7 +234,7 @@ public void onComplete(@NonNull Task task) { onDisconnected(); } } - }); + }); } private void startSignInIntent() { @@ -259,7 +268,7 @@ public void onComplete(@NonNull Task task) { onDisconnected(); } - }); + }); } @Override @@ -277,10 +286,10 @@ public void onSuccess(Intent intent) { } }) .addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - handleException(e, getString(R.string.achievements_exception)); - } + @Override + public void onFailure(@NonNull Exception e) { + handleException(e, getString(R.string.achievements_exception)); + } }); } @@ -429,6 +438,7 @@ private void pushAccomplishments() { if (mOutbox.mBoredSteps > 0) { mAchievementsClient.increment(getString(R.string.achievement_really_bored), mOutbox.mBoredSteps); + mAchievementsClient.increment(getString(R.string.achievement_bored), mOutbox.mBoredSteps); mOutbox.mBoredSteps = 0; @@ -445,6 +455,14 @@ private void pushAccomplishments() { } } + public PlayersClient getPlayersClient() { + return mPlayersClient; + } + + public String getDisplayName() { + return mDisplayName; + } + /** * Update leaderboards with the user's score. * @@ -467,8 +485,7 @@ public void onWinScreenDismissed() { protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (requestCode == RC_SIGN_IN) { - Task task = - GoogleSignIn.getSignedInAccountFromIntent(intent); + Task task = GoogleSignIn.getSignedInAccountFromIntent(intent); try { GoogleSignInAccount account = task.getResult(ApiException.class); @@ -516,16 +533,16 @@ public void onComplete(@NonNull Task task) { handleException(e, getString(R.string.players_exception)); displayName = "???"; } + mDisplayName = displayName; mMainMenuFragment.setGreeting("Hello, " + displayName); } }); - // if we have accomplishments to push, push them if (!mOutbox.isEmpty()) { pushAccomplishments(); - Toast.makeText(this, getString(R.string.your_progress_will_be_uploaded), - Toast.LENGTH_LONG).show(); + Toast.makeText(this, + getString(R.string.your_progress_will_be_uploaded), Toast.LENGTH_LONG).show(); } loadAndPrintEvents(); @@ -557,6 +574,16 @@ public void onSignOutButtonClicked() { signOut(); } + @Override + public void onShowFriendsButtonClicked() { + switchToFragment(mFriendsFragment); + } + + @Override + public void onBackButtonClicked() { + switchToFragment(mMainMenuFragment); + } + private class AccomplishmentsOutbox { boolean mPrimeAchievement = false; boolean mHumbleAchievement = false; @@ -567,9 +594,9 @@ private class AccomplishmentsOutbox { int mHardModeScore = -1; boolean isEmpty() { - return !mPrimeAchievement && !mHumbleAchievement && !mLeetAchievement && - !mArrogantAchievement && mBoredSteps == 0 && mEasyModeScore < 0 && - mHardModeScore < 0; + return !mPrimeAchievement && !mHumbleAchievement && !mLeetAchievement + && !mArrogantAchievement && mBoredSteps == 0 && mEasyModeScore < 0 + && mHardModeScore < 0; } } diff --git a/TypeANumber/src/main/java/com/google/example/games/tanc/MainMenuFragment.java b/TypeANumber/src/main/java/com/google/example/games/tanc/MainMenuFragment.java index 9cd7b524..ac7b2ba8 100644 --- a/TypeANumber/src/main/java/com/google/example/games/tanc/MainMenuFragment.java +++ b/TypeANumber/src/main/java/com/google/example/games/tanc/MainMenuFragment.java @@ -16,12 +16,12 @@ package com.google.example.games.tanc; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; /** * Fragment with the main menu for the game. The main menu allows the player @@ -37,6 +37,7 @@ public class MainMenuFragment extends Fragment implements OnClickListener { private View mSignOutBarView; private View mShowAchievementsButton; private View mShowLeaderboardsButton; + private View mShowFriendsButton; interface Listener { // called when the user presses the `Easy` or `Okay` button; will pass in which via `hardMode` @@ -53,6 +54,9 @@ interface Listener { // called when the user presses the `Sign Out` button void onSignOutButtonClicked(); + + // called when the user presses the `Friends` button + void onShowFriendsButtonClicked(); } private Listener mListener = null; @@ -71,7 +75,8 @@ public View onCreateView(LayoutInflater inflater, R.id.show_achievements_button, R.id.show_leaderboards_button, R.id.sign_in_button, - R.id.sign_out_button + R.id.sign_out_button, + R.id.show_friends_button }; for (int clickableId : clickableIds) { @@ -81,6 +86,7 @@ public View onCreateView(LayoutInflater inflater, // cache views mShowAchievementsButton = view.findViewById(R.id.show_achievements_button); mShowLeaderboardsButton = view.findViewById(R.id.show_leaderboards_button); + mShowFriendsButton = view.findViewById(R.id.show_friends_button); mGreetingTextView = view.findViewById(R.id.text_greeting); mSignInBarView = view.findViewById(R.id.sign_in_bar); @@ -104,6 +110,7 @@ private void updateUI() { mGreetingTextView.setText(mGreeting); mShowAchievementsButton.setEnabled(!mShowSignInButton); mShowLeaderboardsButton.setEnabled(!mShowSignInButton); + mShowFriendsButton.setEnabled(!mShowSignInButton); mSignInBarView.setVisibility(mShowSignInButton ? View.VISIBLE : View.GONE); mSignOutBarView.setVisibility(mShowSignInButton ? View.GONE : View.VISIBLE); } @@ -123,6 +130,9 @@ public void onClick(View view) { case R.id.show_leaderboards_button: mListener.onShowLeaderboardsRequested(); break; + case R.id.show_friends_button: + mListener.onShowFriendsButtonClicked(); + break; case R.id.sign_in_button: mListener.onSignInButtonClicked(); break; diff --git a/TypeANumber/src/main/java/com/google/example/games/tanc/WinFragment.java b/TypeANumber/src/main/java/com/google/example/games/tanc/WinFragment.java index 860eadaf..1d4e03ba 100644 --- a/TypeANumber/src/main/java/com/google/example/games/tanc/WinFragment.java +++ b/TypeANumber/src/main/java/com/google/example/games/tanc/WinFragment.java @@ -17,12 +17,12 @@ package com.google.example.games.tanc; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; /** * Fragment that shows the 'You won' message. Apart from congratulating the user @@ -123,4 +123,4 @@ public void setShowSignInButton(boolean showSignIn) { mShowSignIn = showSignIn; updateUI(); } -} +} \ No newline at end of file diff --git a/TypeANumber/src/main/res/drawable-hdpi/pgs_signed_in_friend.png b/TypeANumber/src/main/res/drawable-hdpi/pgs_signed_in_friend.png new file mode 100644 index 0000000000000000000000000000000000000000..ef8a50849edf79d58119901ea5bc4acdda09a883 GIT binary patch literal 4528 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 + +