What could be causing SharedPreferences do not update? - android

My app consists of playing simple games and earn some points and then withdraw these points to an external account. I am saving these points in the SharedPreferences to avoid multiple requests to the server (I still validate them when the user wants to withdrawn, but this is not relevant for my problem).
Now, the code goes something like this:
UserActiviy
private fun userClickedToWithdraw() {
startActivityForResult(Intent(this, WithdrawActivity::class.java), RequestCode.RC_WITHDRAW)
}
private fun transactionSuccessfully() {
//set the user points to zero in the SharedPreferences
//and updates the UI accordingly
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RequestCode.RC_WITHDRAW && resultCode == RESULT_OK) transactionSuccessfully()
}
WithdrawActivity
//handles the transaction and then call finishTransaction
private fun finishTransaction() {
setResult(RESULT_OK)
onBackPressed()
}
The user transfers his points, and then the finishTransaction function is called setting the result as OK and finishing the activity. The parent activity handles the onActivityResult and if the result was OK, it updates the SharedPreferences and UI.
This works perfectly on all the devices that I tested, but for some reason, some of my users are having a different outcome. When they transfer successfully, the SharedPreferences and UI are not being updated correctly.
I cannot replicate this behavior so I can fix it. So I was hoping if someone that had some similar problems could lead me here. I am thinking that maybe the resultCode is not RESULT_OK and the transactionSuccessfully is not being called because of that, but as I said, I am not being able to replicate this.
Does anyone has had a similar problem that could help me?

This is just speculation but if possible call
super.onActivityResult(requestCode, resultCode, data)
last. Ideally you want to run it first but I've had issues where it led to unexpected behavior concerning my code inside of my #override.

Related

Time Stamping on snaps

We are using android based mobile phone for taking snaps of utility meters like electricity and natural gas. We need to time stamp each snap as it is the essential requirement for the acceptance of the snaps by the government authority. Kindly indicate how can we time stamp snaps in android phone.
Method 1 :
You can generate and save your timestamp, once you reach in onActivityResult() method after taking the snap, follow the steps.
Step 1: Fire the Camera Intent, so that Camera Activity can open.
Step 2: After the snap(image) has been captured, OnActivityResult() method will be called.
Step 3: Inside this method you can generate the current timeStamp, like this( snippet is in Kotlin ) :
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?{
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CAMERA && resultCode == Activity.RESULT_OK) {
val timeStamp = SimpleDateFormat("your_desired_timestamp_pattern",Locale.ENGLISH).format(Date())
saveCurrentTimeStamp(timeStamp)
}
Step 4: Save this timeStamp in some persistence storage like your DataBase.
I would suggest saving the captured image(snaps) files itself with the name of timeStamp(the moment they were captured).
Although this method doesn't provide the exact interval at which the snap were captured, the actual delay is just of some seconds .
Method 2 :
One other way is to implement a BroadCastReceiver for ACTION_NEW_PICTURE and then inside your onReceive() method you can save the timeStamp.
update - ACTION_NEW_PICTURE was deprecated in Android N, but later they brought it back in Android O but only for registered receivers.

How can I test setResult() in an Android Espresso test?

Is there any good way to test the result code and data in an Android Espresso test? I am using Espresso 2.0.
Suppose I have an Activity called BarActivity.class, which upon performing some action, calls setResult(int resultCode, Intent data) with the appropriate payload.
I'd like to write a test case to verify the resultCode and data. However, because setResult() is a final method, I can't override it.
Some options I thought about were:
Define a new method like setActivityResult() and just use that so it can be intercepted, etc...
Write a test-only TestActivity that will call startActivityForResult() on BarActivity and check the result in TestActivity.onActivityResult()
Trying to think what's lesser of the two evils, or if there's any other suggestions on how to test for this. Any suggestions? Thanks!
If meanwhile you switched to the latest Espresso, version 3.0.1, you can simply use an ActivityTestRule and get the Activity result like this:
assertThat(rule.getActivityResult(), hasResultCode(Activity.RESULT_OK));
assertThat(rule.getActivityResult(), hasResultData(IntentMatchers.hasExtraWithKey(PickActivity.EXTRA_PICKED_NUMBER)));
You can find a working example here.
If you are willing to upgrade to 2.1, then take a look at Espresso-Intents:
Using the intending API (cousin of Mockito.when), you can provide a response for activities that are launched with startActivityForResult
This basically means it is possible to build and return any result when a specific activity is launched (in your case the BarActivity class).
Check this example here: https://google.github.io/android-testing-support-library/docs/espresso/intents/index.html#intent-stubbing
And also my answer on a somewhat similar issue (but with the contact picker activity), in which I show how to build a result and send it back to the Activity which called startActivityForResult()
this works for me:
#Test
fun testActivityForResult(){
// Build the result to return when the activity is launched.
val resultData = Intent()
resultData.putExtra(KEY_VALUE_TO_RETURN, true)
// Set up result stubbing when an intent sent to <ActivityB> is seen.
intending(hasComponent("com.xxx.xxxty.ActivityB")) //Path of <ActivityB>
.respondWith(
Instrumentation.ActivityResult(
RESULT_OK,
resultData
)
)
// User action that results in "ActivityB" activity being launched.
onView(withId(R.id.view_id))
.perform(click())
// Assert that the data we set up above is shown.
onView(withId(R.id.another_view_id)).check(matches(matches(isDisplayed())))
}
Assuming a validation like below on onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
data?.getBooleanExtra(KEY_VALUE_TO_RETURN, false)?.let {showView ->
if (showView) {
another_view_id.visibility = View.VISIBLE
}else{
another_view_id.visibility = View.GONE
}
}
}
I follow this guide as reference : https://developer.android.com/training/testing/espresso/intents and also i had to check this links at the end of the above link https://github.com/android/testing-samples/tree/master/ui/espresso/IntentsBasicSample
and
https://github.com/android/testing-samples/tree/master/ui/espresso/IntentsAdvancedSample
If you're using ActivityScenario (or ActivityScenarioRule) as is the current recommendation in the Android Developers documentation (see the Test your app's activities page), the ActivityScenario class offers a getResult() method which you can assert on as follows:
#Test
fun result_code_is_set() {
val activityScenario = ActivityScenario.launch(BarActivity::class.java)
// TODO: execute some view actions which cause setResult() to be called
// TODO: execute a view action which causes the activity to be finished
val activityResult = activityScenario.result
assertEquals(expectedResultCode, activityResult.resultCode)
assertEquals(expectedResultData, activityResult.resultData)
}

Android API IsConnected returning TRUE after Signing Out

I am developing a game for Android using Google Play Game Services, using Xamarin. I am doing my testing using a Genymotion Android Emulator. I have run into an issue that appears to be a bug in either Google Play or Xamarin's implementation.
If I sign out of a Google account, calls to the IGoogleApiClient.IsConnected() continue to return true (even though I have clearly just signed out). If I then attempt to use that API object, I will get exceptions like:
java.lang.SecurityException: Not signed in when calling API
For example, the follow code results in the above exception if executed after signing out:
public void StartNewMatch()
{
if (!mGoogleApiClient.IsConnected)
{
return;
}
Intent intent = GamesClass.TurnBasedMultiplayer.GetSelectOpponentsIntent(mGoogleApiClient, 1, 1, true);
StartActivityForResult(intent, RC_SELECT_PLAYERS);
}
I am signing out in the Google Play Games Inbox (match picker); as shown in the images below.
Anyone run into this before? Am I missing something? Got any work-arounds?
Note: This only occurs if signing out through Google's UI. If I manually sign the user out, with something like mGoogleApiClient.Disconnect(), the issue does not occur; mGoogleApiClient.IsConnected() now returns false (as expected).
In order to keep signed-in state synced up you MUST implement onActivityResult properly.
This should look something as follows:
NOTE: this is java code, I am not sure how this will look exactly using Xamarin, but hopefully you should be able to figure it out :)
#Override
protected void onActivityResult(int requestCode, int responseCode, Intent data) {
// check for "inconsistent state"
if ( responseCode == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED && requestCode == <your_request_code_here> ) {
// force a disconnect to sync up state, ensuring that mClient reports "not connected"
mGoogleApiClient.disconnect();
}
}
NOTE: just make sure to replace in the code with the request code you used. You may need to check for multiple request codes too.
If you are using gameHelper classes from BaseGameUtils library(it is easier to use), you may modify the above code to this:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
gameHelper.onActivityResult(requestCode, resultCode, data);
if (resultCode == GamesActivityResultCodes.RESULT_RECONNECT_REQUIRED){
// force a disconnect to sync up state, ensuring that mClient reports "not connected"
gameHelper.getApiClient().disconnect();
}
}

Configuring the length of utterance and pauses in Android's speech recognizer

I have android's Speech To Text API to speak something to the phone and convert it into text. By default, if one stops speaking to the microphone, the API assumes that the user is done talking and returns the text from the input speech.
For my application, the user might have long pauses between her consecutive sentences. How can I configure Android's speech to text API to consider the end of the speech only when I ask it to and not do that as soon as the speaker takes a small pause between sentences? Thanks!
Here is my current implementation which simply converts speech to text as soon as the user takes a small pause between sentences:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RESULT_SPEECH: {
if (resultCode == RESULT_OK && null != data) {
ArrayList<String> text = data
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
txtText.setText(text.get(0));
}
break;
}
}
}
The API has 3 EXTRAs for that
EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS
EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS
EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS
But note that the API also says that "depending on the recognizer implementation, these values may have no effect", so you just have to test with the implementation that you are using if they have any effect or not. (I haven't done this test myself, so it would be great if you added a comment to this answer reporting your test results.)
Prior to Android 4.1 (or users of the Google Search/Now app) this will work for you:
int someValue = 5;
intent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, Long.valueOf(someValue * 1000L));
Unfortunately later versions no longer react to this parameter, a great shame as it makes lengthy note taking or email composing impossible....
I have brought the issue to their attention.

How do I use voice search and VoiceRecognition on Android?

I want to use VoiceRecognition in my application, but this application needs to install voice search.
I don't want the user to have to install another other application then return to my application to run it. I want voice search to be installed from my application, or alternatively I'd like to find a tutorial to on how to add Voice Search capability to my application.
What can I do?
Use the RecognizerIntent to fire the speech recognizer installed on your device
This can be done in a few simple steps:
Create some sort of button in your activity, and place the following code in its OnClickListener:
// Define MY_REQUEST_CODE as an int constant in your activity...I use ints in the 10000s
startVoiceRecognitionActivity(MY_REQUEST_CODE, "Say something.");
Override the onActivityResult() method in your activity. In the implementation, place a switch block or if statement to run some logic when the requestCode argument matches your MY_REQUEST_CODE constant. Logic similar to the following will get you the list of results the speech recognition activity thought it heard:
ArrayList keywordMatches = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
You may get 0 or many matches from the recognizer. Be sure to handle all cases.
In some cases, the speech recognizer may not even be on the device. Try to handle that where you call startVoiceRecognitionActivity().
I found this tutorial :
http://www.jameselsey.co.uk/blogs/techblog/android-how-to-implement-voice-recognition-a-nice-easy-tutorial/
hope this helps.
Android Open Source VoiceRecognition Example
Here is a Simple way to Handle Voice Search
Step 1 Call this method on button click
public void startVoiceRecognition() {
Intent intent = new Intent("android.speech.action.RECOGNIZE_SPEECH");
intent.putExtra("android.speech.extra.LANGUAGE_MODEL", "free_form");
intent.putExtra("android.speech.extra.PROMPT", "Speak Now");
this.mContainerActivity.startActivityForResult(intent, 3012);
}
Step 2 Override onActivityResult method
# Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 3012 && resultCode == RESULT_OK) {
ArrayList < String > matches = data.getStringArrayListExtra("android.speech.extra.RESULTS");
String result= matches.get(0);
//Consume result
edittext.setText(result);
}
}
Thats all, DONE

Categories

Resources