First post, so please go easy.
I have an app with a handful of tabs, the first is opened on running the app.
One of the tabs is 'My Account' (a ListActivity) showing account options. I switch to this and if the user is not logged in, it, in turn, runs a UserLogon activity using the following:
Intent logonActivity = new Intent(getBaseContext(), UserLogon.class);
startActivityForResult(logonActivity, 0);
To catch the result, I use the following code:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 0){
MainBlock ta = (MainBlock) this.getParent();
TabHost th = ta.getMyTabHost();
th.setCurrentTab(0);
finish();
}
if (requestCode == 100)
{
showAccountOptions();
}
}
In the UserLogon class, I have the usual fare; TextView's and Button's. The intention is that if the user cancels the login, it will revert to the initial tab, or if login is successful, it will show the Account Options. And this is indeed what does happen.
All good so far.
The problem I'm having is that if I cancel the login and return to the first tab, when I select the Accounts tab again, I'm not presented with the UserLogon activity. I was under the impression that finish() would close the UserLogon activity, and the Accounts activity but it would appear not.
So, my question is simply, how do I, in effect, restart the Accounts activity so that the user would be presented with the option to login once more.
We're good people and all willing to help ;-) I'll give it a shot. Still, I'm not quite sure I get that all right.
Basically you have an TabActivity which you setup and where you do something like that:
myTabHost.setOnTabChangedListener(new OnTabChangeListener(){
#Override
public void onTabChanged(String tabId) {
if (tabId.equals("account") && !loggedIn) {
Intent logonActivity = new Intent(getBaseContext(), UserLogon.class);
startActivityForResult(logonActivity, 0);
}
}});
You're basically saying that the first Activity start of UserLogon works, but the second one doesn't, right? Did you debugged to that point to check whether you reach the code which starts the activity again?
Update based on comment
Your UserLogon should always provide a result information, here's a blueprint:
public class UserLogon extends Activity {
public void onCreate(Bundle bundle) {
// ... do something ...
// if activity is canceled this will be the "last" result
setResult(RESULT_CANCELED);
}
public void checkLoginOrSomethingLikeThat() {
Intent result = new Intent();
// put your results in this intent
setResult(RESULT_OK, intent);
// close activity since we have the information we need
finish();
}
}
The parent activity which is waiting for the result should do it like that:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// it's good practice to use a constant for 0 like LOGIN_REQUEST
// otherwise you will ask yourself later "What the hell did 0 stand for?"
if(requestCode == 0){
if (resultCode == RESULT_OK) {
// get needed data from data intent and react on that
} else {
// no success, react on that
}
}
// ... I don't know what your resultCode 100 is but handle itwith the same pattern
}
Related
I have two separate applications written using Xamarin.Android; for the sake of discussion, let's call them "Tristan" and "Isolde". Tristan has some state information that Isolde sometimes needs to know. Complication: Tristan may or may not be running at the moment Isolde develops the need to know his state.
I've got kludge working now where Isolde sends a special launch intent to Tristan, who then uses a broadcast intent to send information back to Isolde. (See my earlier question for details.)
"But wait!" I hear you cry, "Is this not a perfect use case for StartActivityForResult()?" Indeed it is! The code is a whole lot simpler, and everything I've read implies that this is how Android wants you to do stuff like this.
Unfortunately, I can't get it to work (despite trying many variations and reading the dozen-or-so related questions on this very site). My specific problem is that in Isolde's OnActivityResult() callback, the resultCode is always Result.Canceled and the data is always null.
Here is the code for Tristan (where commented-out bits represent variations I've tried):
using Android.App;
using Android.Content;
namespace com.example.Tristan.Android
{
[Activity(Name ="com.example.Tristan.Android.IsoldeQueryActivity")]
public class IsoldeQueryActivity : Activity
{
protected override void OnStart()
{
// base.OnStart();
var rtn = new Intent();
rtn.PutExtra("Test", "test");
//rtn.SetAction("TestAction");
SetResult(Result.Ok, rtn);
Finish();
//FinishActivity(1234);
}
}
}
And here is the relevant code from the Activity where Isolde needs to ask for Tristan's state:
private TaskCompletionSource<bool> TristanStateCompletion;
public async Task GetTristanState()
{
TristanStateCompletion = new TaskCompletionSource<bool>();
var req = new Intent("com.example.Tristan.Android.IsoldeQueryActivity");
//req.PutExtra(Intent.ExtraReturnResult, true);
StartActivityForResult(req, 1234);
var rtn = await TristanStateCompletion.Task;
if (!rtn) bomb("can't get state");
TristanStateCompletion = null;
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if(requestCode == 1234) {
DoStuffWith(data);
TristanStateCompletion?.TrySetResult(true);
}
}
Diagnostics -- or rather, a specific lack of them -- leads me to believe that Tristan's IsoldeQueryActivity.OnStart() is never actually being called.
Ideas, requests for additional information and/or useful experiments to try are all welcome. (If your idea is "Put <thing> in the manifest", remember this is Xamarin.Android and I have to do that by putting <relatedThing> in the attribute decorating the Activity.)
Edited to add: In Isolde's code, DoStuffWith(data) was crashing because data was null. When I changed that method to avoid that, I found that I got a (slightly later) exception thrown in StartActivityForResult():
Android.Content.ActivityNotFoundException No Activity found to handle Intent { act=com.example.Tristan.Android.IsoldeQueryActivity }
This leads me to believe I'm not creating the Intent properly in Isolde. Do I need to be using one of the other Intent constructors? If so, how specifically?
Okay, I think I have this figured out. The code in my original question had three major problems:
I was building the Intent incorrectly in Isolde.
I didn't export the IsoldeQueryActivity in Tristan.
The call to base.OnStart() in Tristan's OnStart override is mandatory.
Here is the working version of Tristan:
using Android.App;
using Android.Content;
namespace com.example.Tristan.Android
{
[Activity(Name ="com.example.Tristan.Android.IsoldeQueryActivity", Exported=true)]
public class IsoldeQueryActivity : Activity
{
protected override void OnStart()
{
base.OnStart();
var rtn = new Intent();
rtn.PutExtra("Test", "test");
SetResult(Result.Ok, rtn);
Finish();
}
}
}
And here is the fixed code from Isolde:
private TaskCompletionSource<bool> TristanStateCompletion;
public async Task GetTristanState()
{
TristanStateCompletion = new TaskCompletionSource<bool>();
var req = new Intent();
req.SetComponent(new ComponentName("com.example.Tristan.Android", "com.example.Tristan.Android.IsoldeQueryActivity"));
StartActivityForResult(req, 1234);
var rtn = await TristanStateCompletion.Task;
if (!rtn) bomb("can't get state");
TristanStateCompletion = null;
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if(requestCode == 1234) {
if(resultCode != Result.Ok) bomb("bad resultCode {0}", resultCode);
if(data == null) bomb("null data from Tristan");
DoStuffWith(data);
TristanStateCompletion?.TrySetResult(true);
}
}
UPDATE
Question 1 Solved
I have solved the issue of profile pic not loading by changing the context of Glide from getApplicationContext to HomeActivity.this and image loaded quickly. :)
But still, I need your help with other two questions. If I am doing it right or wrong and how to avoid that error.
Question 1
So far, I have successfully connected my app for facebook login, but the issue is the profile picture.
What I have done is created 2 activity:-
1. login button of Facebook in Activity 1
2. Home Activity of App.
If the user is logged in, it goes to Activity 2 and if not he goes to activity 1.
On activity 1, I have fetched basic details like dob and email address as name and profile picture doesn't need to be fetched using GraphRequest. (To be able to save on my server)
On activity 2, I have fetched the user name and profile picture. Here's the code:-
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
im = (ImageView) findViewById(R.id.img);
name = (EditText) findViewById(R.id.name);
try {
String url = Profile.getCurrentProfile().getProfilePictureUri(150,150).toString();
name.setText(" "+Profile.getCurrentProfile().getName()+"\n"+Profile.getCurrentProfile().getId());
Glide.with(getApplicationContext()).load(url).into(im);
} catch (Exception e){
Toast.makeText(this, "Error : "+e , Toast.LENGTH_SHORT).show();
}
}
Until 2 days back, profile pic was loading fine but from today it's not loading at all.
Question 2
Another question, I have is that sometimes I get an error message when I log in and get in Home Activity, here's the snapshot of the same:-
Question 3
The last question is how to close the entire app on back pressed from activity 2.
What I have done right now is use handler on Activity 1 so that the above error doesn't come and then use Intent and below that use finish() method on activity 1, and on activity 2:-
public void onBackPressed() {
onStop();
onDestroy();
finish();
}
To Question 3
from Activity 1 create a function onActivityResult
and be sure to use the startActivityForResult to call the Activity 2 instead of startActivity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startActivityForResult(new Intent(this, ActivityTwo.class), 1);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
if (data.getBooleanExtra("EXIT", false)) {
finish();
}
}
}
and to close the entire app from Activity 2 using the onBackPressed.
don't forget to remove the super.onBackPressed()
#Override
public void onBackPressed() {
// super.onBackPressed();
Intent intent = new Intent();
intent.putExtra("EXIT", true);
setResult(RESULT_OK, intent);
finish();
}
I am using a Fragment to start a new Activity using startActivityForResult(), I am getting the result (Bundle) in onActivityResult() method.Since onActivityResult() called before onResume().I want to make sure, I keep/save the Bundle properly so that when Fragment's onResume() gets called, I get the kept/saved result to perform further action.
What are the different ways to achieve this. I tried using getArguments()/setArguments(), but that seems to be not the right way to achieve this.
Try this
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == MY_REQUEST) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
mResultBundle = data.getExtras(); //mResultBundle is in fragment
//scope
}
}
}
#Override
protected void onResume(){
super.onResume();
if(mResultBundle != null){
// process saved bundle from activity result here
// don't forget to set it back to null once you are done
mResultBundle = null;
}
}
I have a ListActivity which displays all the aggregate contacts. When the user clicks one, my code calls startActivityForResult. All this works properly. When the user finishes editing, I want my ListActivity to be displayed again. Instead, the "people" activity gets displayed. Similarly, my onActivityResult function never gets called.
Here is the code handling the click:
#Override
public void onItemClick (AdapterView<?> parent, View view, int position, long id)
{
Cursor cur = ((SimpleCursorAdapter)parent.getAdapter()).getCursor();
cur.moveToPosition (position);
String key = cur.getString (2); // "2" is the col containing the LOOKUP_KEY
System.out.println ("clicked " + key);
// make intent to edit contact
Intent intent = new Intent (Intent.ACTION_EDIT);
intent.setData (Uri.parse (ContactsContract.Contacts.CONTENT_LOOKUP_URI + "/" + key));
startActivityForResult (intent, 2);
}
And I also have an onActivityResult function:
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
System.out.println ("request " + requestCode + ", result " + resultCode);
}
Any suggestions?
I filed a bug to android about this. Someone looked at it and responded that there is an undocumented workaround. From the bug report:
The undocumented workaround is to call putExtra("finishActivityOnSaveCompleted", true); on the ACTION_EDIT Intent.
However, as this is undocumented, I have no idea which Android version(s) will use it.
I tried it and it works for the version of Android I'm using: 4.1.2. See issue 39262 for more info.
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == 2) {
// Make sure the request was successful
if (resultCode == RESULT_OK) {
// The user picked a contact.
// The Intent's data Uri identifies which contact was selected.
// Do something with the contact here (bigger example below)
}
}
}
Replace your onActivity result to this. for request code get after
they will work
Add
super.onActivityResult(requestCode, resultCode, data);
in OnActivtyResult().
Make sure you are calling startActivityForResult from an activity, then only your onActivityResult will be called. For example, if you have the similar code in an fragment, the onActivityResult will never be called.
I am using following code to open Android Contacts
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnContacts = (Button) findViewById(R.id.btn_contacts);
txtContacts = (TextView) findViewById(R.id.txt_contacts);
btnContacts.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
txtContacts.setText("");
Intent intent = new Intent(Intent.ACTION_PICK, People.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT);
}
});
}
#Override
public void onActivityResult(int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data);
switch (reqCode) {
case (PICK_CONTACT):
if (resultCode == Activity.RESULT_OK) {
//display picked contact data.
}
}
}
Now I want to put Button at the top of this Contact activity when opened or add my own Menu in this Activity
Can any one guide me? Is this possible or not? If yes then please tell how to achieve this?
I don't believe that is possible as each Activity in Android is working on its own and by starting the Intent, you are basically giving the new Activity the focus (and control).
One way to do something like this would be to build a custom contact list activity that uses the public data providers to access the contacts and simply lists them then. Then you could add as many custom functions as you like or even add Intents for original actions (like viewing a contact's details).