I want to extend a common security check to nearly every view of my application. To do this, I have made this class
public class ProtectedActivity extends ActivityBase {
boolean isAuthenticated = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Thread validationThread = new Thread()
{
#Override
public void run()
{
try
{
isAuthenticated = UserService.validateToken();
}
catch (FTNIServiceException e)
{
//eat it
}
finally
{
if (!isAuthenticated)
{
startActivity(new Intent(ProtectedActivity.this, SignInActivity.class));
finish();
}
}
}
};
validationThread.start();
}
}
The logic is simple. Validate the user against my restful api to make sure they are signed in. If they aren't, show them to the signin page.
This works great, because to add the security check, all I need to do is inherit from my ProtectedActivity.
public class MainMenuActivity extends ProtectedActivity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
The problem is, however, that I periodically receive View not attached to window manager errors. I understand why this is happening. I am starting a new intent in the parent class, and the child lives on. to attempt to alter it's view even though a new intent has started. What is a better way to handle this so that if a user is not authenticated (such as their session expires serverside), it won't error when sending the user to the sign in screen?
Don't you Thread. Use AsyncTask instead which should handle your references to windows correctly.
On a different note, I would change this to a different implementation. Why don't use the Preferences storage on the phone to store some kind token. If the token is not valid then request a new token and all the stuff you are doing currently. This way is better because you don't want to request a REST call every time.
I imagine something like this (pseudo code)
Check if credentials exist in Preference
if(valid) then do nothing
else use AsyncTask and pop up a loader screen "Waiting..."
Related
I want to send a String message to database when user presses a specific button in the LibGDX game I am designing for android. How do I go about doing that? Following is the code I tried. But it does not work.
Net.HttpRequest httpRequest = new Net.HttpRequest();
httpRequest.setMethod("POST");
httpRequest.setUrl("URL is here");
httpRequest.setContent("INSERT INTO `game_table` (`Button`) VALUES ('Button 1 Pressed')");
Net.HttpResponseListener httpResponseListener = new Net.HttpResponseListener() {
#Override
public void handleHttpResponse(Net.HttpResponse httpResponse) {
Gdx.app.log("Log httpResponse", httpResponse.getResultAsString());
}
#Override
public void failed(Throwable t) {
}
#Override
public void cancelled() {
}
};
Gdx.net.sendHttpRequest(httpRequest,httpResponseListener);
Log does not provide anything in android monitor. I also tried using AsyncTask and without AsyncTask to implement this code. But neither works.
Am I missing something? If so could you give me small code snippet that will work?
You don't need to use an AsyncTask, libGDX' HTTPRequest is async out of the box.
You did not log anything if the request fails or is cancelled so probably that's the case.
I am creating a digitsauthconfig like this:
private DigitsAuthConfig createDigitsAuthConfig() {
return new DigitsAuthConfig.Builder()
.withAuthCallBack(createAuthCallback())
.withPhoneNumber("+91")
.withThemeResId(R.style.CustomDigitsTheme)
.build();
}
Where authcallback is returned by:
private AuthCallback createAuthCallback() {
return new AuthCallback() {
#Override
public void success(DigitsSession session, String phoneNumber) {
doIfSuccessfulOtpVerification();
}
#Override
public void failure(DigitsException exception) {
doIfNotSuccessfulOtpVerification();
}
};
}
I initiate the process using a button with event listener:
digitsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Digits.authenticate(createDigitsAuthConfig());
}
});
The problem is, once my phone number is verified, it goes back to the activity where the button is displayed and does nothing. Technically, the authcallback is never called, doesn't matter successful or not. But if I click the button again, the authcallback is called without repeating the verification step. So right now I am required to click the button twice.
What is the way around it?
Finally i got solution for that issue, May it will help you too also.
You need to remove the ActiveSession, before calling the setCallback(authCallback) like mentioned as below.It will remove the existing session(if you already entered your phone number and got an OTP) from digits. This session will will not allows you to make another session to generate an OTP. So, we have to remove it. And it will work if there is no any previous sessions.
DigitsAuthButton digitsButton = (DigitsAuthButton) findViewById(R.id.auth_button);
Digits.getSessionManager().clearActiveSession();
digitsButton.setCallback(((WyzConnectApp) getApplication()).getAuthCallback());
Digits changed the way it reference the AuthCallback passed in the Digits#authenticate call.
Now Digits holds a weak reference (to avoid a memory leak), unless you hold a strong reference, that AuthCallback will be garbage collected and the result of the flow will be never propagated.
You need to define the AuthCallback in the Application context and then use this callback in your activity and it should work.
Please check the documentation on how to do this
I know its late but may be helpful for a beginner.To verify that the session is already active, please put this code in your onCreate of that activity in which Digits phone number verification button is initialised:
TwitterAuthConfig authConfig = new TwitterAuthConfig(TWITTER_KEY, TWITTER_SECRET);
Fabric.with(this, new TwitterCore(authConfig), new Digits.Builder().build());
if (Digits.getActiveSession() != null) {
Intent ss = new Intent(CurrentActivity.this, SecondActivity.class);
startActivity(ss);
finish();
}
Please assist in this. I can't seem to create a suitable test for this method:
protected void startInterfacing() {
mLiveAuthClient.login(mView.context(), Arrays.asList(SCOPES), new LiveAuthListener() {
#Override
public void onAuthComplete(final LiveStatus liveStatus, final LiveConnectSession liveConnectSession,
Object o) {
// Login successful and user consented, now retrieve user ID and connect with backend server
getUserIdAndConnectWithBackendServer(liveConnectSession, mLiveAuthClient);
}
#Override
public void onAuthError(LiveAuthException e, Object o) {
// We failed to authenticate with auth service... show error
if (e.getError().equals("access_denied") ||
e.getMessage().equals("The user cancelled the login operation.")) {
// When user cancels in either the login or consent page, we need to log the user out to enable
// the login screen again when trying to connect later on
logUserOut(mLiveAuthClient, false);
} else {
onErrorOccured();
}
}
});
}
I'll explain abit what goes on here:
I'm trying to authenticate my client and log into OneDrive.
The method starts with a call to the Live SDK's login method. That SDK object is given to me from outside this class. So I can basically mock it.
Here's what I'm struggling with:
I do not need to test the call to the login method because it is not mine. I do need to test the call to getUserIdAndConnectWithBackendServer() inside onAuthComplete. But this method requires a liveConnectSession object. How do I provide that? It is given to me on the onAuthComplete method.
How do I mock the calls to onAuthComplete and onAuthError? I read about ArgumentCaptor but when I use that, I need to provide the arguments to those methods when I call the actual method.
For instance, argument.getValue().onAuthComplete() requires me to add arguments to this call. What do I actually provide here?
Here is the next method which is roughly the same but has its own issues:
protected void getUserIdAndConnectWithBackendServer(final LiveConnectSession liveConnectSession, final LiveAuthClient
authClient) {
final LiveConnectClient connectClient = new LiveConnectClient(liveConnectSession);
connectClient.getAsync("me", new LiveOperationListener() {
#Override
public void onComplete(LiveOperation liveOperation) {
// We got a result. Check for errors...
JSONObject result = liveOperation.getResult();
if (result.has(ERROR)) {
JSONObject error = result.optJSONObject(ERROR);
String code = error.optString(CODE);
String message = error.optString(MESSAGE);
onErrorOccured();
} else {
connectWithBackend(result, liveConnectSession, authClient);
}
}
#Override
public void onError(LiveOperationException e, LiveOperation liveOperation) {
// We failed to retrieve user information.... show error
onErrorOccured();
logUserOut(authClient, false);
}
});
}
In here I would like to mock the JSONObject for instance. But how do I call the onComplete method, or the onError method. And what would I provide as the arguments the methods provide me with. LiveOperation for instance?
Thank you!!
The solution I eventually used was to use mockito's doAnswer() structure.
This enabled me to get the callback argument and call one of its methods.
Another solution was to use an ArgumentCator.
On my Activity :
public class MainActivity extends Activity {
private mDbxAccountManager mDbxAccountManager = null;
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
mDbxAccountManager = DbxAccountManager.getInstance(getApplicationContext(), getString(R.string.dbx_app_key), getString(R.string.dbx_app_secret));
...
}
...
public void buttonOnClick(View view) {
if(mDbxAccountManager.hasLinkedAccount()) {
//Do something
}
else {
mDbxAccountManager.startLink(this, 0);
}
...
}
}
And on my Remote Service :
public class CloudService extends Service {
private mDbxAccountManager mDbxAccountManager = null;
#Override
public void onCreate() {
...
mDbxAccountManager = DbxAccountManager.getInstance(getApplicationContext(), getString(R.string.dbx_app_key), getString(R.string.dbx_app_secret));
if(!mDbxAccountManager.hasLinkedAccount()) {
return;
stopSelf();
}
...
}
}
The result is, after I link my app with dropbox using installed dropbox client, the hasLinkedAccount() on my Activity return true, meanwhile the same code on my Remote Service always return false.
I also check the logcat and it showed that my app already linked with dropbox.
My suspect is that the dropbox API create some SharedPreferences when it successfully link with my app, but my Remote Service can't access that or get a cached version of that SharedPreferences... I don't know...
Please help...
Thank you
Edited :
If I reinstall the app, then the result is as expected and hasLinkedAccount() return true, but if I uninstall and install again which cause clearing the user-data, then I link my app again with Dropbox, then the same strange behaviour appear again.
What I'm doing wrong? I'm turning my head almost 24-hours....
Solved!!!
After trying and trying...
I get conclusion that the Service that runs before the app linked with dropbox will always get DbxAccountManager.hasLinkedAccount() return false.
I try to kill the process by calling Process.killProcess(myservicePid) after I link my app with Dropbox and start the service again and it work.
So... I solved it by not starting the service before the app was linked with dropbox and start the service only if it already linked, because stopSelf() on the service doesn't kill the process.
I think this issue have something to do with the Context getApplicationContext() which is passed to DbxAccountManager.getInstance(), and I don't know why looks like the Context is not updated when the dropbox was link with the app.
Thank you.
I'm trying to use the APK Expansion extension from Google to download expansion files I have hosted with them. I'm also using the code from the SampleDownloadActivity to do this, albeit slightly modified to fit in my app.
My problem is that the download is never initiated. In my class that implements IDownloadClient, onStart() is called, but onServiceConnected() is not.
I have traced this down to this line in DownloaderClientMarshaller:
if( c.bindService(bindIntent, mConnection, Context.BIND_DEBUG_UNBIND) ) {
This always returns false, and therefore the service is not bound.
I'm using the calling activity within a TabHost, which has caused problems for other people. They were saying that you must not pass the TabHost context, rather that the Application context to the connect function. I've changed this by doing:
mDownloaderClientStub.connect(getApplicationContext());
instead of:
mDownloaderClientStub.connect(this);
but it doesn't help, I still get false. I'm doing all my testing on the Emulator if that makes a difference.
I'm really pulling my hair out on this one. If anyone has any ideas, I'd be extremely grateful!
In most cases, bindService() method returns false if the service was not declared in the application's Manifest file.
In my case, the problem was that I had given the wrong class object to the DownloaderClientMarshaller.CreateStub() method. I accidentally used DownloaderService.class instead of MyDownloaderService.class.
When using the downloader API, make sure to pass the correct class object that extends the base DownloaderService.
I recommend using the updated Downloader Library included in Better APK Expansion package. It has this and other issues fixed and also provides simplified API that minimizes chances to shoot yourself in the foot.
To receive the download progress, you will just have to extend the BroadcastDownloaderClient.
public class SampleDownloaderActivity extends AppCompatActivity {
private final DownloaderClient mClient = new DownloaderClient(this);
// ...
#Override
protected void onStart() {
super.onStart();
mClient.register(this);
}
#Override
protected void onStop() {
mClient.unregister(this);
super.onStop();
}
// ...
class DownloaderClient extends BroadcastDownloaderClient {
#Override
public void onDownloadStateChanged(int newState) {
if (newState == STATE_COMPLETED) {
// downloaded successfully...
} else if (newState >= 15) {
// failed
int message = Helpers.getDownloaderStringResourceIDFromState(newState);
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onDownloadProgress(DownloadProgressInfo progress) {
if (progress.mOverallTotal > 0) {
// receive the download progress
// you can then display the progress in your activity
String progress = Helpers.getDownloadProgressPercent(
progress.mOverallProgress, progress.mOverallTotal);
Log.i("SampleDownloaderActivity", "downloading progress: " + progress);
}
}
}
}
Check the full documentation on the library's page.