For my Android app I use Google Play Services for things like: Chromecast, Maps and analytics.
In my build.gradle file I compile version 12.0.0:
implementation "com.google.android.gms:play-services-cast-framework:12.0.0"
implementation "com.google.android.gms:play-services-cast:12.0.0"
implementation "com.google.android.gms:play-services-base:12.0.0"
implementation "com.google.android.gms:play-services-maps:12.0.0"
implementation "com.google.android.gms:play-services-analytics:12.0.0"
implementation "com.google.android.gms:play-services-vision:12.0.0"
implementation "com.google.android.gms:play-services-gcm:12.0.0"
The problem is that the app crashes when people don't have version 12 or newer installed on their device.
So what I did is the following:
In AndroidManifest.xml:
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version"/>
And a small class to check if play services is up-to-date or installed:
public boolean checkIfInstalled(Context applicationContext) {
try {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(applicationContext);
// Version is out of date
if (resultCode != ConnectionResult.SUCCESS) {
final AlertDialog.Builder builder = new AlertDialog.Builder(applicationContext);
builder.setMessage("Google Play Services is required to use this app. Install Play Services or update to the latest version to continue.");
builder.setPositiveButton("Go to Play store", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Log.i("PlayServicesCheck", "clicked on the button");
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
return false;
}
return resultCode == ConnectionResult.SUCCESS;
} catch (Exception err) {
return false;
}
}
In onCreate (when the app launches) I call this function to check if everything is alright. The function returns false when it's outdated. So my code works at this point.
#Override
protected void onCreate(Bundle savedInstanceState) {
boolean isInstalled = checkIfInstalled(getApplicationContext());
// this works, I get the right value back from the checkIfInstalled function
if (isInstalled) {
Log.i("PlayServicesCheck", "Installed");
// Do nothing
} else {
Log.i("PlayServicesCheck", "Not Installed");
// Give alert, stop everything else
}
super.onCreate(savedInstanceState);
}
But how do I stop the app from launching when this happens? Right now it says: "version outdated" but it will still continue to start the app which leads to a crash.
Thanks
You can call finishAndRemoveTask.
The problem with checking from the store that it takes time for Google to deploy the version to all the servers, therefore, it can happen that you're forced to update but when you enter into the store you still don't see the new version.
In any case, it would be better if you would have a server where you return the minimum version. This will let you fine tune on which changes you want to force the user to download and on which you don't. It can sometimes be disturbing for the user if each version is forced.
Related
Similar questions to this have been trivial at best for answers. I am using a faily up-to-date Google Play Game Services apk 'com.google.android.gms:play-services:7.0.0' and I have onInvitationReceived() and onConnected() implemented.
However, onInvitationReceived() doesn't seem to be called when the invitee accepts the game invite in-game. And while I'm fairly certain onConnected() is called no matter what when the player connects via callback after mGoogleClient.connect() , it seems like the invitation is dropped or something because the player is not redirected to the game screen as I specified if the Bundle contains an invitation (I'm assuming this is called when the app is closed but either way, only a status bar notification is shown for the invite).
Any insight into this would be appreciated. Here are the relevant methods:
onConnected() is from a class, that extends AndroidGame, and AndroidGame extends Activity (I've tried it in both classes, now I just have it overridden in the child class of AndroidGame):
#Override
public void onConnected(Bundle connectionHint) {
// Connected to Google Play services!
// The good stuff goes here.
Games.Invitations.registerInvitationListener(mGoogleClient, Network.getInstance().mListener);
if (connectionHint != null) {
Invitation inv =
connectionHint.getParcelable(Multiplayer.EXTRA_INVITATION);
if (inv != null) {
// accept invitation
RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(Network.getInstance());
roomConfigBuilder.setMessageReceivedListener(Network.getInstance());
roomConfigBuilder.setRoomStatusUpdateListener(Network.getInstance());
roomConfigBuilder.setInvitationIdToAccept(inv.getInvitationId());
RoomConfig roomConfig = roomConfigBuilder.build();
Games.RealTimeMultiplayer.join(mGoogleClient, roomConfig);
// prevent screen from sleeping during handshake
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// go to game screen
setScreen(new CharSelectScreen(this));
}
}
}
onInvitationReceived is from my Network class, which is basically a singleton which handles other listeners as well. onInvitationReceived() is both overridden in the Network class and in the anonymous inner class, just to cover my assets:
#Override
public void onInvitationReceived(Invitation invitation) {
Intent intent = Games.Invitations.getInvitationInboxIntent(game.getGoogleClient());
game.startActivityForResult(intent, RC_INVITATION_INBOX);
mIncomingInvitationId = invitation.getInvitationId();
AlertDialog.Builder builder = new AlertDialog.Builder(game.getApplicationContext());
builder.setMessage("Join Game?")
.setTitle("Bit Game Invitation!");
builder.setPositiveButton("Join", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if(!game.getGoogleClient().isConnected()) {
game.getGoogleClient().connect();
}
// User clicked OK button
Bundle am = RoomConfig.createAutoMatchCriteria(1, 3, 0);
RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(Network.getInstance());
roomConfigBuilder.setMessageReceivedListener(Network.getInstance());
roomConfigBuilder.setRoomStatusUpdateListener(Network.getInstance());
roomConfigBuilder.setAutoMatchCriteria(am);
roomConfigBuilder.setInvitationIdToAccept(mIncomingInvitationId);
RoomConfig roomConfig = roomConfigBuilder.build();
Games.RealTimeMultiplayer.join(game.getGoogleClient(), roomConfig);
// prevent screen from sleeping during handshake
game.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
dialog.dismiss();
// go to game screen
game.setScreen(new CharSelectScreen(game));
}
});
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
dialog.dismiss();
}
});
AlertDialog dialog = builder.create();
dialog.show();
I solved it, with the short answer being I did not follow the Android Realtime Multiplayer recipe (guidelines) as closely as I should have.
The guidelines calls for easy access to Google Play sign-in, either automatically when starting the app or I'd imagine preferably, a button to sign in. When I added this, my callback onConnected() code worked, the invitation allowed the user to proceed to the designated game screen with a default waiting room pop-up (implementation shown here). I am still debugging the onInvitationReceived() callback, but I suspect that when the invitation is received when the game is open, the game crash means there is something wrong with my implementation.
All in all, I think it was a bit of misunderstanding on my end, the documentation is fairly intuitive. Thank you for helping, and I hope this will help future Google Play Game Services developers.
With Google Play Game Services samples, If you use a Gingerbread device that has multiple Google Accounts, you cannot sign in. GameHelper fails to sign-in and you get stuck in a never ending "sign-in screen" loop.
If you open GameHelper.java and comment out line 417: mExpectingResolution = false;
..then you can actually log in. But this also causes my game to crash.
Google Play Services and a new class GameHelper with an example ButtonClicker2000 on android 2.3 can not log in to your account. When a dialog box (sign-in dialog) appears with the user's choice, the method onStop().
/** Call this method from your Activity's onStop(). */
public void onStop() {
debugLog("onStop");
assertConfigured("onStop");
if (mGoogleApiClient.isConnected()) {
debugLog("Disconnecting client due to onStop");
mGoogleApiClient.disconnect();
} else {
debugLog("Client already disconnected when we got onStop.");
}
mConnecting = false;
mExpectingResolution = false;
// let go of the Activity reference
mActivity = null;
}
After selecting the user calls the onStart(). However, there is no sign-in. Instead Development dialog box (sign-in dialog) reappears with the user's choice. And so indefinitely.
/** Call this method from your Activity's onStart(). */
public void onStart(Activity act) {
mActivity = act;
mAppContext = act.getApplicationContext();
debugLog("onStart");
assertConfigured("onStart");
if (mConnectOnStart) {
if (mGoogleApiClient.isConnected()) {
Log.w(TAG,
"GameHelper: client was already connected on onStart()");
} else {
debugLog("Connecting client.");
mConnecting = true;
mGoogleApiClient.connect();
}
} else {
debugLog("Not attempting to connect becase mConnectOnStart=false");
debugLog("Instead, reporting a sign-in failure.");
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
notifyListener(false);
}
}, 1000);
}
}
Is there any fix for the log-in loop bug in GameHelper?
The source is here: https://github.com/playgameservices/android-basic-samples
ImageView connect = (ImageView) findViewById(R.id.fconnect);
connect.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
facebook.authorize(SignIn.this, new String[] {"offline_access", "email", "read_friendlists","publish_stream" },new DialogListener() {
#Override
public void onComplete(Bundle values) {
String AccessToken = facebook.getAccessToken();
LoginDirect = "Loading Home....";
LoginProcessChkUserStatus();
}
#Override
public void onFacebookError(FacebookError error) {
}
#Override
public void onError(DialogError e) {
}
#Override
public void onCancel() {
}
});
}else{
progress = true;
LoginProcessChkUserStatus();
}
}
});
this the facebook api....that i use for loading in my application...this works fine...when i click login button...after authorizing it comes to oncomplete stage... now the problem comes when i installed separtely Facebook.apk in my phone taken from Facebook SDK....the view becomes this....also when i click login button it never excutes the above code....what shall i do...???
I have also faced some similar issue when I have integrated facebook with my application. when I am clicking the facebook icon in my application native facebook app was launched and when I uninstalled that native facebook app all were working correctly. I figured a way out to overcome this issue by the following method and I have posted it on below link on stackoverflow: "An error's occurred" when authenticating with Facebook's android sdk . Actually my problem was, when I used the debug key, the Key Hash value I entered was wrong in the facebook app register. When I corrected the key hash according to what I have posted in the above link my issue was solved. Please try this also.
The changing of screens is normal. If you have the Facebook app installed, the SDK uses this to log in. If not, it uses a WebView for authentication (as seen on your first screenshot).
And why does this not work? The Facebook app uses result codes from Androids activity mechanism.
I dont see onActivityResult() in your code. Make sure to have that implemented in your activity. It should look like this:
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
facebook.authorizeCallback(requestCode, resultCode, data);
}
After that, your code should work as intended. :)
I've run into a bit of a strange problem. I'm trying to display an AlertDialog with a list when the user presses a button. The following code works perfectly when I execute the app on a device or emulator. However, if I run the app under the debugger on either a device or emulator, the code causes an exception and we break into the debugger on builder.show();.
This is an issue because I'm trying to debug a branch that comes after selecting something from my list. The debugger brings up ViewGroup.class when it breaks, though I can't tell you the specific line because I don't think I quite have the right android source.
Any thoughts on why this is crashing in debug?
Callback:
public View.OnClickListener selectCategoryClick = new View.OnClickListener() {
final String[] categories = new String [] {"A","B","C"};
#Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(MyApp.this);
builder.setTitle("Select a category");
builder.setItems(categories, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//Do something with which
}
});
builder.show();
}
};
I finally got the right source code to align with the SDK version that I was using (thanks to http://blog.michael-forster.de/2008/12/view-android-source-code-in-eclipse.html?showComment=1299971635442#c3854025611409009224). Once I found the line causing the issue the cause became immediately clear:
public View getChildAt(int index) {
try {
return mChildren[index]; <---- Crash line
} catch (IndexOutOfBoundsException ex) {
return null;
}
}
I had set a break point on all ArrayIndexOutOfBoundsExceptions and forgot to remove it. Now the code runs fine.
My stupid mistake, thanks guys for the comments.
I'm trying to implement the LVL (Licensing Verification Library for Android), but running into a problem when the license check fails. I pop up a Dialog which explains that the license check failed and presents them with two buttons. One button is "Retry" which I want to immediately do another check or "Purchase App" which redirects them to buy the app on the App Market.
When I tap the Retry button, I can see from logging messages that my licensing check is called, I receive another "dont allow" callback and I try to show the above failure dialog again (I see onPrepareDialog() get called again).
The problem is the second dialog doesn't actually show. So the user can use the app even though the license check failed. Why isn't the dialog popping up again and again?
I believe this is all of the relevant code:
private void checkLicense() {
Log.d(TAG, "Checking License...");
this.licenseChecker.checkAccess(this.licenseCheckerCallback);
}
private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow() { }
public void dontAllow() {
Log.d(TAG, "License resposne: Don't Allow");
if (isFinishing())
return; // Don't update UI if Activity is finishing.
// Should not allow access. In most cases, the app should assume
// the user has access unless it encounters this. If it does,
// the app should inform the user of their unlicensed ways
// and then either shut down the app or limit the user to a
// restricted set of features.
// In this example, we show a dialog that takes the user to Market.
Log.d(TAG, "Showing No License Dialog");
showDialog(DIALOG_NO_LICENSE_ID);
}
public void applicationError(ApplicationErrorCode errorCode) {
if (isFinishing())
return; // Don't update UI if Activity is finishing.
// This is a polite way of saying the developer made a mistake
// while setting up or calling the license checker library.
showDialog(DIALOG_APPLICATION_ERROR_ID);
}
}
protected Dialog onCreateDialog(int id) {
Log.d(TAG, "Creating Dialog "+id);
switch(id) {
case DIALOG_NO_LICENSE_ID:
return this.makeRetryPurchaseDialog(getString(R.string.no_license));
case DIALOG_APPLICATION_ERROR_ID:
return this.makeRetryPurchaseDialog(this.applicationErrorMessageForDialog);
}
return null;
}
private Dialog makeRetryPurchaseDialog(String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(CalculatorTabActivity.this);
builder.setMessage(message).setCancelable(false)
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
CalculatorTabActivity.this.checkLicense();
}
}).setNegativeButton("Purchase App", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
CalculatorTabActivity.this.openAppMarket();
}
});
return builder.create();
}
Just as an idea, try to explicitly call for dialog dismissing:
...
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dissmissDialog(DIALOG_NO_LICENSE_ID);
CalculatorTabActivity.this.checkLicense();
}
})
Also, if that will not work, try using removeDialog(DIALOG_NO_LICENSE_ID) instead of dissmissDialog(DIALOG_NO_LICENSE_ID).
Finally what code (if at all) do you have in onPrepareDialog() ?