I'm following the following tutorial for Facebook Connect:
https://developers.facebook.com/docs/mobile/android/build/
And attempting to allow my users to login via Facebook. Right now, I'm successfully rendering a permission request / login box, but the language, according to Google Translate, is Indonesian. I can see nowhere in my FB App settings to change this, and nowhere in the FB-Connect source to change this. I've tried not only my Inspire 4g, which has a custom ROM, but also a stock Galaxy Tab.
Has anyone encountered this before?
EDIT: Additionally, the app is attempting SSO (single-sign-on), aka, it's using the installed Facebook application to do the authorization, so I can't just tack on the locale to the end of the API URLs, as it's not using them. I will need to do that if the user doesn't have Facebook installed, but right now I'm trying to get SSO working.
Here is the relevant section of code from the FB API that calls the FB App. I imagine that somewhere within I need to request my specific locale:
private boolean startSingleSignOn( Activity activity, String applicationId, String[] permissions, int activityCode )
{
boolean didSucceed = true;
Intent intent = new Intent();
intent.setClassName( "com.facebook.katana", "com.facebook.katana.ProxyAuth" );
intent.putExtra( "client_id", applicationId );
/** perhaps here, I need to add to the Intent? **/
if( permissions.length > 0 )
{
intent.putExtra( "scope", TextUtils.join( ",", permissions ) );
}
// Verify that the application whose package name is
// com.facebook.katana.ProxyAuth
// has the expected FB app signature.
if( !validateActivityIntent( activity, intent ) )
{
return false;
}
mAuthActivity = activity;
mAuthPermissions = permissions;
mAuthActivityCode = activityCode;
try
{
activity.startActivityForResult( intent, activityCode );
}
catch( ActivityNotFoundException e )
{
didSucceed = false;
}
return didSucceed;
}
This looks like an issue with Facebook:
Facebook Authorize App dialog displayed using incorrect locale
Related
I've been banging my head on my desk the past few days trying to figure out how to setup the sign-in for Google Play Games Services.
I am able to start an activity for an interactive signin, but the window just hangs and loads forever with no error:
Here is the code I have. It checks to see if the user has signed in before, if they have, it attempts to login silently. If not, it brings up interactive signin:
final GoogleSignInOptions signInOption =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN)
// Add the APPFOLDER scope for Snapshot support.
.requestScopes(Games.SCOPE_GAMES_SNAPSHOTS)
.requestIdToken(activity.getString(R.string.debug_client_id))
.requestEmail()
.build();
signInClient = GoogleSignIn.getClient(this, signInOption);
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(this);
if (account != null) {
// The user has signed in before
signInClient.silentSignIn().addOnCompleteListener(new OnCompleteListener < GoogleSignInAccount > () {
#Override
public void onComplete(#NonNull Task < GoogleSignInAccount > task) {
if (task.isSuccessful()) {
// Silent signin successful
onConnected(task.getResult());
} else {
// Player will need to sign-in explicitly using via UI
// Interactive sign-in
activity.startActivityForResult(signInClient.getSignInIntent(), AndroidLauncher.RC_SIGN_IN);
}
}
});
} else {
//The user has never signed in
// Interactive sign-in
this.activity.startActivityForResult(signInClient.getSignInIntent(), AndroidLauncher.RC_SIGN_IN);
}
I also have the onActivityResult() handler:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.isSuccess()) {
signedIn = true;
// The signed in account is stored in the result.
GoogleSignInAccount signedInAccount = result.getSignInAccount();
} else {
signedIn = false;
String message = result.getStatus().getStatusMessage();
if (message == null || message.isEmpty()) {
message = "Sign in error!";
}
new AlertDialog.Builder(this).setMessage(message)
.setNeutralButton(android.R.string.ok, null).show();
}
}
}
What I've Tried/Done
I added the required metadata tags in the AndroidManifest.xml file (with correct values)
I've added the Google Play Games account as a tester on the Google Play Console for the app, for Google Play Services, and on the OAuth consent screen for the API on Google Cloud Platform.
I've checked that the SHA-1 hash of the signature on the app matches the SHA-1 hash of the OAuth credential in use on Google Play Games, and that the client ID is correct and is of the selected OAuth credential.
I have enabled all of the non-sensitive data scopes (which is all I need) on the Google Cloud Platform OAuth consent screen
More Info
If I try to call signInSilently(...) before the interactive signin, I get API exception code 4 (SIGN_IN_REQUIRED). When I execute the interactive signin, it just hangs like above. I actually am able to get the signin to work, but only if I use signin options like so:
final GoogleSignInOptions signInOption = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN).build();
(with no snapshots permissions and no passed client ID. But I need to be able to access snapshots)
The only possibly relevant exception I see in Logcat when running the interactive signin is:
E/ExecutionCriteria: Package unavailable for task: com.google.android.apps.tachyon/com.firebase.jobdispatcher.GooglePlayReceiver{u=0 tag="duo-analytics-summary-report" trigger=window{start=82080s,end=86401s,earliest=-97027894s,latest=-97023574s} requirements=[NET_ANY] attributes=[PERSISTED,RECURRING] scheduled=-97109975s last_run=N/A exec_window_multiplier=1.0000 jid=N/A status=PENDING retries=0 client_lib=FIREBASE_JOB_DISPATCHER-1} [CONTEXT service_id=218 ]
...but it seems like it might be a benign error.
I've been stuck on this for a while; any suggestions/help would be appreciated.
I am a newbie (6 months going or so) and creating an app on Android (Java) that utilizes FireBase Auth with Google Sign In. (with only a few days of NodeJS exposure now) In other words my end user signs in with the Google Account. That part (I think) works pretty well so far. I use the Firestore Database heavily for a lot of things in the app.
So now I've gotten to the point where I want to use (Callable) Cloud Functions with HTTP Triggers. (never having done any of this before) I'm trying to get a proof of concept working at this time. The actual function works and I can trigger it from my app.
It appears that I cannot figure out how to make the function "private" though; as in "adding proper Members" to the Cloud function who have the right to invoke the function.
I have tried a few different things by trial error, but first let me show what I have.
This is the Cloud Function and I'm passing in an arbitrary String as a test, works nicely: (as long as "allUsers" have the role/right to invoke the function; in other words when the function is public.
exports.createTest = functions.https.onCall((data, context) => {
const text = data.text;
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const email = context.auth.token.email || null;
console.log('UID: ', uid);
console.log('Name: ', name);
console.log('Email: ', email);
console.log('Message: ', text);
});
The above function gets triggered in my Android/Java code like this: (I think this code came from Google Doc/Sample/Example
private FirebaseFunctions mFunctions;
...
private void testing() {
mFunctions = FirebaseFunctions.getInstance();
Log.e(LOG_TAG, "Testing executed!");
String testMessage = "Hello Hello Testing 123 Mic Check";
createTest(testMessage)
.addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
if (!task.isSuccessful()) {
Exception e = task.getException();
if (e instanceof FirebaseFunctionsException) {
FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
FirebaseFunctionsException.Code code = ffe.getCode();
Object details = ffe.getDetails();
Log.e(LOG_TAG, "FFE: " + ffe.getMessage() );
Log.e(LOG_TAG, "Code: " + code);
Log.e(LOG_TAG, "Details:" + details);
}
// ...
}
// ...
}
});
}
private Task<String> createTest(String text) {
// Create the arguments to the callable function.
Map<String, Object> data = new HashMap<>();
data.put("text", text);
data.put("push", true);
return mFunctions
.getHttpsCallable("createTest") //this is the function name
.call(data)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
// This continuation runs on either success or failure, but if the task
// has failed then getResult() will throw an Exception which will be
// propagated down.
String result = (String) task.getResult().getData();
if (result != null) {
Log.e(LOG_TAG, "Result: " + result);
}
return result;
}
});
}
Only when I have "allUsers" added with the role/right to "invoke Cloud Function" then I get this working. My understanding of HTTP Requests and such is pretty limited, which is not making things easier.
I tried using the "allAuthenticatedUsers" options, which I figured would do the trick, because I actually authenticate my Users in the app through Firebase/Google Sign In. This Cloud Function shall only be available to either a) authenticated users or b) users of a specific domain. (I have a domain, let's say #testorganization.com) Or if I can identify my particular app (api key?) then that would work, too.
The moment I add a member "allAuthenticatedUsers" with role to invoke the function (and remove "allUsers) nothing happens. I also tried adding the entire domain, but that wouldn't work. (duh) Also tried adding my service account (trial and error at this point) and didn't seem to work.
In my Node JS code I am actually receiving the UID of the authenticated user, so it appears that some kind of user authentication information is already being exchanged.
With that knowledge, I can (successfully tried this) get the UID and cross check that against my database and verify a user that way, but seems unnecessary and I should be able to make the permissions work. (lock the function down entirely) Plus this took a really long time just finish this cross check. Or is this pretty standard procedure to do?
Like this-->
const usersRef = admin.firestore().collection('users').doc(uid)
usersRef.get()
.then((docSnapshot) => {
if (docSnapshot.exists) {
usersRef.onSnapshot((doc) => {
console.log('User Type logged in: ', doc.data().userCategory)
console.log('User Title: ', doc.data().userTitle)
});
} else {
console.log('User does not exist')
}
});
Edit:
So while not having figured out how to shut down the function entirely, I did discover that instead of cross checking my users, I can simple check for auth like this:
if (context.auth){
//user is auth'd
} else {
//no auth
}
That's a little bit better, I guess. (but still doesn't technically prevent access to the function?!)
Thank you so much for any help. Much appreciated.
Edit2:
Here is a screensshot of the area in the cloud console (for cloud function roles/privileges) that I am referring to:
https://imgur.com/cBsjaaL
With a Callable Cloud Function, if you want to ensure that only authenticated users can trigger it's business logic, you actually don't need to configure any extra "cloud function roles/privileges" as shown at the bottom of your question.
By default, with a Callable Cloud Function you will get, when available, "Firebase Authentication and FCM tokens automatically included in requests" and it will "automatically deserializes the request body and validates auth tokens", as explained in the doc.
So you just have to follow the doc and use the context parameter. As you have mentioned in your question, you can check the user is authenticated by doing:
if (context.auth) {
//...
}
If you want to verify the user email, you would do:
exports.addMessage = functions.https.onCall((data, context) => {
const uid = context.auth.uid;
return admin.auth().getUser(uid)
.then(userRecord => {
const userEmail = userRecord.email;
//....
})
.catch(function(error) {
console.log('Error fetching user data:', error);
// Send back an error to the front end
// See https://firebase.google.com/docs/functions/callable#handle_errors
});
});
You will find more examples on how to "work with" users with the Admin SDK here in the doc.
Apply Deep Linking in flutter app to open
a specific page in another app or same app
i want to know to how implement deep links in flutter
or open channel with android native and ios native ?
?
I think it would be the same as in a normal android app. Deeplinking is a configuration thing rather than code.
You need to write some things in your android manifest.
Have a look at the firebase manual for deep linking:
Firebase deeplinking manual
You can use firebase dynamic links for deep linking in a flutter. Refer this link for full implement steps and create and receive a link, https://medium.com/better-programming/deep-linking-in-flutter-with-firebase-dynamic-links-8a4b1981e1eb.
Here is a sample code for receiving a link inside the app and open a new screen.
class MainWidgetState extends State<MainWidget> {
#override
void initState() {
super.initState();
this.initDynamicLinks();
}
initDynamicLinks(BuildContext context) async {
await Future.delayed(Duration(seconds: 3));
var data = await FirebaseDynamicLinks.instance.getInitialLink();
var deepLink = data?.link;
final queryParams = deepLink.queryParameters;
if (queryParams.length > 0) {
var userName = queryParams['userId'];
openNewScreen(userName);
}
FirebaseDynamicLinks.instance.onLink(onSuccess: (dynamicLink)
async {
var deepLink = dynamicLink?.link;
final queryParams = deepLink.queryParameters;
if (queryParams.length > 0) {
var userName = queryParams['userId'];
openNewScreen(userName);
}
debugPrint('DynamicLinks onLink $deepLink');
}, onError: (e) async {
debugPrint('DynamicLinks onError $e');
});
}
openNewScreen(String userName){
Navigator.of(context).pushNamed("routeFormScreen", arguments: {"name": userName});
}
}
I have two app on Google Play. The old and new one. And I would like to use old auth token to the new app to be easier for users.
On the old app, the user has a popup to install the new app on Google Play.
I would like to pass the auth token in parameter to Google Play.
After new app has been installed, I would like to save the token in the new one app.
I tried to use Play Install Referrer Library but it didn't work.
The other way was to use SharedPreferences but MODE_WORLD_READABLE has been deprecated.
Old APP :
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=com.<PACKAGENAME>&token=pokpok&refresh_token=lolol"));
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.<PACKAGENAME>?token=pokpok&refresh_token=lolol")));
}
New APP code :
private fun shouldGetTokenFromOldApp() {
mReferrerClient = InstallReferrerClient.newBuilder(this).build()
mReferrerClient.startConnection(object : InstallReferrerStateListener {
override fun onInstallReferrerSetupFinished(responseCode: Int) {
when (responseCode) {
InstallReferrerClient.InstallReferrerResponse.OK -> {
// Connection established
val response: ReferrerDetails = mReferrerClient.installReferrer
val url = "https://play.google.com/store?${response.installReferrer}"
Log.d("APP", "Token old app 1 : $url")
val uri: Uri = Uri.parse(url)
val token = uri.getQueryParameter("token")
val refreshToken = uri.getQueryParameter("refresh_token")
Log.d("APP", "Token old app 2 : $token - $refreshToken")
mReferrerClient.endConnection()
}
InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED -> {
// API not available on the current Play Store app
}
InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE -> {
// Connection could not be established
}
}
}
override fun onInstallReferrerServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
})
}
This sounds like a nice thing to do for users, but seems very dangerous. You are sending an Auth token - something which if someone has it could allow them to login as that user anywhere off through untrusted systems like world readable files or referral parameters in URLs.
If you really need to do this, I'd suggest using some form of inter-app RPC (IPC) to communicate the token after the app is installed. One option would be a binder to a service that supplies the auth.
I downloaded apk file from url(my server) and save it in sdcard. If user install it from sdcard, I want to know, whether is any notification that app is installed successfully or is app istalled in device. Is there any callback on installed app
try this code :
protected boolean isAppInstalled(String packageName) {
Intent mIntent = getPackageManager().getLaunchIntentForPackage(packageName);
if (mIntent != null) {
return true;
}
else {
return false;
}
}
to get the package name of the app easily : just search your app in the google play website , and then you will take the id parameter ( it is the package name of the app) . Example :
you will search on Youtube app on google play , and you will find it in this url :
https://play.google.com/store/apps/details?id=com.google.android.youtube&feature=search_result#?t=W251bGwsMSwxLDEsImNvbS5nb29nbGUuYW5kcm9pZC55b3V0dWJlIl0.
the package name is the id param, so it is : com.google.android.youtube
And then when you want to test , you will just have :
String packageName = "com.google.android.youtube";
boolean isYoutubeInstalled = isAppInstalled(packageName);
PLUS : if you want to get the list of all installed apps in you device , you can find your answer in this tutorial
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
final List pkgAppsList = context.getPackageManager().queryIntentActivities( mainIntent, 0);
You'll get the list of all installed applications on Android.
Use this to check if an application is installed
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> list = pm.getInstalledApplications(0);
for (int i = 0; i < list.size(); i++) {
if(list.get(i).packageName.equals("com.my package")){
//do what you want
}
}
In Youtube Player API, you can access YoutubeIntents class and use isYoutubeInstalled to verify if device has the Android app or not.