I am stuck with a functionality of the Firebase SDK (Auth package) regarding the Scenes and it's integration. Here's how they work:
1st: Loading Scene
Here, I just added the FirebaseInit code EXACTLY as suggested by Patrick, which it's only function is to call the next scene (Login/Register) once everything loads correctly.
2nd: Login/Register Scene
Here I do all the Login AND ALSO the register logic. I set up a button that alternates between the two (Activating the respective parent gameObject within the Canvas). Once the user log's in, the 3rd scene comes into play.
3rd: App's Main Screen Scene
Main Screen of the app, where the user can LOGOUT and return to the Login Scene.
Problem
I added the 'LoadSceneWhenUserAuthenticated.cs' in the 2nd Scene, and it works (kind of).
It actually does what it is supposed to. If I log in, quit the game without loging out, and open it again, it does come back directly to the 3rd scene. BUT some things are happening and they aren't supposed to.
First
When I Sign Up a user, I call the method 'CreateUserWithEmailAndPasswordAsync()'. Once it completes, it should activate the login screen and stay there, waiting for the user to fill in the password, but the 'FirebaseAuth.DefaultInstance.StateChanged' comes into play and forces the 3rd screen to be loaded, skipping several other steps that should be taken (email registration for example).
Second
As I mentioned in the end of number 1 above, if I try to log in to an account that does not have it's email verified, it works! (due to the 'LoadSceneWhenUserAuthenticated.cs' which is added in the scene). Code:
var LoginTask = auth.SignInWithEmailAndPasswordAsync(_email, _password);
LoginTask.ContinueWithOnMainThread(task =>
{
if (task.IsCanceled || task.IsFaulted)
{
Firebase.FirebaseException e =
task.Exception.Flatten().InnerExceptions[0] as Firebase.FirebaseException;
GetErrorMessage((AuthError)e.ErrorCode, warningLoginText);
return;
}
if (task.IsCompleted)
{
User = LoginTask.Result;
if (User.IsEmailVerified == true)
{
UIControllerLogin.instance.MainScreenScene();
}
else
{
warningLoginText.text = Lean.Localization.LeanLocalization.GetTranslationText($"Login/VerifyEmail");
}
I know that it's possible to fix this issue by adding an extra scene just before the login scene (as Patrick does in the youtube video) but it doesn't make any sense in my app. It would actually only harm the UX of it.
Patrick's Video:
https://www.youtube.com/watch?v=52yUcKLMKX0&t=264s
I'm glad my video helped!
My architecture won't work for every game, and I tried to boil it down to the bare minimum to get folks started. You may be able to get the functionality you want by adding an additional check in HandleAuthStateChanged:
private void HandleAuthStateChanged(object sender, EventArgs e)
{
if (_auth.CurrentUser != null && !_auth.CurrentUser.IsAnonymous && _auth.CurrentUser.IsEmailVerified)
{
SceneManager.LoadScene(_sceneToLoad);
}
}
but it does sound like, at this point, you'll want to build out a more robust registration/sign in flow that fits your use case.
If you need more help, I might suggest re-posting on the community mailing list or the subreddit. Those resources may be more better suited to discussing various pros/cons of different architectures or spitballing ideas (and feel free to link to any new posts in a comment so myself or others interested can follow along).
Related
I'm trying to develop an android app for the SDK version 30 that (when a button is clicked) starts listening to what apps are opened on the phone. If it detects the user opening Whatsapp, it is supposed to show a LockScreen activity over Whatsapp that makes you answer a math question before being able to use Whatsapp.
I know this can be done as their are apps like QualityTime or Forest that have similar features to restrict you from using certain apps, but I am a newbie when it comes to programming (probably obvious from my code) and feel totally stuck.
I have already figured out how to detect what app the user opened in the last second with code from stack overflow:
public String getCurrentApp() {
String topPackageName = "None";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
UsageStatsManager mUsageStatsManager = (UsageStatsManager) getSystemService("usagestats");
long time = System.currentTimeMillis();
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1, time);
// Sort the stats by the last time used
if (stats != null) {
SortedMap<Long, UsageStats> mySortedMap = new TreeMap<Long, UsageStats>();
for (UsageStats usageStats : stats) {
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
}
if (!mySortedMap.isEmpty())
{
topPackageName = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
}
}
}
return topPackageName;
}
I have another function that is started when the user clicks the button in my app to "activate" the listening process. This function keeps checking if the user opens Whatsapp and is then supposed to display the Lockscreen activity on top:
public void startListening(View view)
{
System.out.println("Lock activated.");
while (activated) {
String currentlyRunningApp;
currentlyRunningApp = getCurrentApp();
if (currentlyRunningApp.equals("com.whatsapp"))
{
System.out.println("Whatsapp detected. Showing Lockscreen...");
Intent i = new Intent(this,LockScreen.class);
startActivity(i);
}
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
(All of the code I have shown is in my MainActivity btw.)
I have the following permissions granted to my app:
android.permission.PACKAGE_USAGE_STATS (for the getCurrentApp() function)
android.permission.SYSTEM_ALERT_WINDOW (as suggested here)
android.permission.ACTION_MANAGE_OVERLAY_PERMISSION (although I am not sure I even need this one)
My problem is, that instead of showing the Lockscreen activity I created, it only shows a blackscreen for the user. The Lockscreen activity itself works fine if I let the user open it through a button on the mainActivity, so the issue seems to really be that I can not properly show an activity if my app is running in the background and I want to display it on top of Whatsapp.
I have tried to look through similar questions, but all of the posts on here with similar use cases seem to be very old and outdated (i.e. this or this), as the newer versions seem to have way tighter security restrictions.
I also tried to do it with a screen overlay instead of an activity (using this source), but this doesn't even give me a blackscreen - just does nothing...
I am also aware that there are probably better ways to program the whole "listening and checking for whatsapp" part - i.e. with a service instead of a while-loop or something, but I only found out about services while researching this problem and I'd like to fix the blackscreen issue first.
After lots of trial and error I figured out that the issues was indeed caused by a missing permission, but one that I could not find on any stack overflow answer related to black screen problems. On top of that, I believe it's an issue that only occurred because I used a Xiaomi device for testing.
There are currently two separate permissions for displaying screens over other apps that you will need to grant:
Display over other apps, also called Display pop-up window. This is the android.permission.ACTION_MANAGE_OVERLAY_PERMISSION that I wasn't sure was even needed. So to emphasize, I definetly do need this permission.
Display pop-up windows while running in the background. This is the permission I was missing.
After I allowed them both (which you can do under Settings > Apps > Manage Apps > Your App > Other Permissions) everything worked fine.
To direct the user directly to the settings menu where they can allow these permissions, I used the code from this stack overflow answer. This is also where I got the info that it's a xiaomi-specific "issue".
The google demo makes a single API request. I have tried to use this code as the basis for a more real world scenario (multiple different API calls) but keep hitting a brick wall. The demo's structure makes it very difficult.
Consider the code that makes the API request:
private void getResultsFromApi() {
if (! isGooglePlayServicesAvailable()) {
acquireGooglePlayServices();
} else if (mCredential.getSelectedAccountName() == null) {
chooseAccount();
} else if (! isDeviceOnline()) {
mOutputText.setText("No network connection available.");
} else {
new MakeRequestTask(mCredential).execute();
}
}
getResultsFromApi() does a lot of auth stuff before the API request. Should this be done before every API request??
getResultsFromApi() may start an activity (in chooseAccount())which means that the onActivityResult() will (re)call getResultsFromApi() in order to eventually get to the API request.
In fact there are actually 5 calls to getResultsFromApi() in the code which together achieve the auth for the single API request!! It may be necessary but it ends up being spaghetti.
This makes the demo impossible to generalize in its current form. Say I want a 2nd button making some other API request. Cant be done without a major rewrite
Can anyone suggest how I can reorganise the demo code in order to make it useable in the real world?
Thanks
getResultsFromApi does not do "a lot of stuff" it just checks that everything is done correctly as said in the demo project :
Attempt to call the API, after verifying that all the preconditions are
satisfied
If a condition is not satisfied another function will be called to solve this, like to select an account. Then getResultsFromApi will be called again to finish that call that you started with not all the preconditions satisfied.
You are not force to do all the checks, but if a precondition is not satisfied your call will fail.
I'm building a Flutter app where some screens can be shown to anonymous users, and other screens require a user to be logged in.
For the authenticated screens, they should automatically navigate to (push) a login screen if the user is not logged in. A user session can expire at any time and if the user is viewing one of these authenticated screens then the login screen should be shown immediately at that time.
In Flutter, how can I achieve this notion of authenticated screens that automatically navigate to/from a login screen when the user is not authenticated?
Currently, there's nothing that flutter do with Authentification and Authentified routes.
The problem is that dart:mirror is disabled, which prevents from doing a more automated solution.
You could try to :
Put Anonymous routes in MaterialApp's routes property
Put Authentified routes in MaterialApp's onGenerateRoute property
And make sure inside onGenerateRoute that the user is logged. If he is, build that route. If not, build the Login route with the original destination passed as parameter (to later redirect to that page)
A code generator may be a good solution too ; although more complex.
There is a really good package auto_route for flutter.
It gives you a lot of power to implement Route Guards for Authenticated routes.
I'm encouring you to use the latest version of the library (on this time I'm writting the ansewer https://pub.dev/packages/auto_route/versions/1.0.0-beta.8 is the latest one)
The documentation is missleading but https://github.com/Milad-Akarie is actively working on that library and respond on any question under Issues tab on GitHub.
There is an example solution: https://github.com/Milad-Akarie/auto_route_library/tree/master/example
AppRouter.dart
...
AutoRoute(path: RoomPage.path, guards: [AuthGuard],page: RoomPage),
...
AuthGuard.dart
class AuthGuard extends AutoRouteGuard {
#override
Future<bool> canNavigate(
List<PageRouteInfo> pendingRoutes, StackRouter router) async {
bool isAuthenticated = await checkUserAccess();
if (!isAuthenticated) {
router.root.push(LoginRoute(onLoginResult: (success) {
if (success) {
router.replaceAll(pendingRoutes);
}
}));
return false;
}
return true;
}
}
Lately, I have been trying to add static interstitial ads into my Unity game. For some reason, I could not get the system to show anything, or even react to me. After trying to work with the base Chartboost plugin, I tried to match a tutorial that I was following and purchased Prime31's Chartboost plugin and have been using that. However, neither the base plugin, nor Prime31's plugin, seem to be allowing me to show any ads. The code is pretty much done inside a single object, and it seems simple enough.
public class Advertisement : MonoBehaviour {
public string chartboostAppID = "5461129ec909a61e38b1505b";
public string chartboostAppSignature = "672b3b34e3e358e7a003789ddc36bd2bc49ea3b5";
// Use this for initialization
void Start () {
DontDestroyOnLoad(this.gameObject);
ChartboostAndroid.init (chartboostAppID, chartboostAppSignature, true);
ChartboostAndroid.cacheInterstitial(null);
}
void OnLevelWasLoaded(int level) {
ChartboostAndroid.cacheInterstitial(null);
if(Application.loadedLevelName == "Network Lobby") {
showAds();
}
}
public static void showAds() {
Debug.Log("Showing ad");
ChartboostAndroid.showInterstitial(null);
}
}
As you can see, it's pretty straightforward. This object is created at the game's splash screen, which appears only once, and it's never destroyed until the program ends. The goal is, whenever I enter the lobby scene, I want to see an ad before going to the lobby's menus. As it is, I do see the log printing "Showing ad", so I know the function is being called. However, nothing appears. Do I need to disable the GUI system first? Is there a step I'm missing?
I have already performed the following steps:
I have created and registered the app with chartboost, as well as double and triple checked the AppID and App Signature.
I have created a publishing campaign and registered it to the app.
I double-checked the orientation and confirmed that it's correct.
I registered this specific device as a test device.
The tutorial showed a call to ChartBoostAndroid.OnStart(), but there was no function like that for me to call. Perhaps that is from an older version?
I emailed Chartboost support and have not heard from them yet. I do not have that much time on this project, so if anyone can offer help, I'd appreciate it.
there a lot of q&a about how users can rate my app within the app,
but i need just a direct link to review\rate my app to send the user by mail and not to my app page in the market because there he need to cilck review then login and then write the review and this is exhausting and not user friendly.
tnx
In order not to disturb the user with annoying forms you can add a menu item that let the user rate the application through your application site in google play. After the user click in this option, this should not been showed again (even if the user did not rate the app at the end). This solution is quite user friendly, in my opinion.
Add a menu item like this (in res\menu[menu].xml):
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
(other options...)
<item android:id="#+id/MenuRateApp" android:title="#string/menu_Rate_app"
android:icon="#drawable/ic_menu_star"></item>
</menu>
In your main activity add the following in order to hide the option once the user has already rated your app:
#Override
public boolean onPrepareOptionsMenu(Menu menu)
{
MenuItem register = menu.findItem(R.id.MenuRateApp);
if(fApp.isRated()) {
register.setVisible(false);
}
return true;
}
Change the fApp.isRated() for a method or variable that keep a boolean saying if the user already rated the app (write and read this value using the sharedPreferences mechanism).
The code to redirect the user to your app site in Google Play could be like the following:
private boolean MyStartActivity(Intent aIntent) {
try {
startActivity(aIntent);
return true;
} catch (ActivityNotFoundException e) {
return false;
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
(other options code...)
if (item.getItemId() == R.id.MenuRateApp) {
//Try Google play
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id="+getPackageName()));
if (MyStartActivity(intent) == false) {
//Market (Google play) app seems not installed, let's try to open a webbrowser
intent.setData(Uri.parse("https://play.google.com/store/apps/details?id="+getPackageName()));
if (MyStartActivity(intent) == false) {
//Well if this also fails, we have run out of options, inform the user.
Toast.makeText(this, this.getString(R.string.error_no_google_play), Toast.LENGTH_LONG).show();
}
}
//Do not disturb again (even if the user did not rated the app in the end)
fApp.setRated(true);
}
return super.onOptionsItemSelected(item);
}
Hope this solution feets your requirements.
Note: part of the code has been borrowed from this site:
http://martin.cubeactive.com/android-how-to-create-a-rank-this-app-button/
Example:
The premise from where you start, saying that rating an app is exhausting and not user friendly is not applicable because the user should only rate your app when he is willing to "donate" 30 seconds of his life to rate your app. There is a minimal responsibility involved when rating other people work.
The farthest I'd go, since there are also ethics involved, is providing a button in the About section of my app with a link to the Market app screen containing my app, using an Intent to the market (search StackOverflow). Other apps constantly ask a user to rate... I find it bothersome, but at least they are not pushing me right into the Edit and star Views of the Market.
The question you need to ask yourself: do you need to disrupt the user experience of your app by automatically stopping the activity and displaying this "oh-my-gosh-rate-my-app" view in the Market app?
You don't need to push the user into that situation... chances are you will end up with more low ratings than good ratings. I'd take one star just because of that. :-)
Personally, I wouldn't do it and leave the way it is. My 2 cents, of course.
Based on a similar question I posted, the desired answer I was looking for was
https://play.google.com/store/apps/details?id= + your.package.name
This should be what you're looking for if a link is what you have in mind. The first part is the default starter, and the second part will be your package name.