First off, I'm sorry if this has been asked before.
So my partner and I are creating a Firebase linked app for android. The Firebase looks somewhat like this:
+ projects
+ <Some project>
+name: <name>
+users
+<a user>
+<another user>
+ <Another project>
+ users
+ <Some user>
+ <Another user>
The problem is that in our app, the user logins into Firebase, then they are found in the users child, and then there is a ListView that is populated with only the projects that they are a part of (i.e. the projects' users contains the user that is logging in.)
The login works fine, and the ListView works fine when we hard code in the projects, but when we have it find the projects, it displays an empty list, because the data isn't returned before the ListView is created.
Here is the code we have so far: This is our Login Activity code:
public class LoginActivity extends Activity implements LoaderCallbacks<Cursor> {
/**
* Keep track of the login task to ensure we can cancel it if requested.
*/
// private UserLoginTask mAuthTask = null;
private ProgressDialog mAuthProgressDialog;
/* A reference to the firebase */
private Firebase ref = new Firebase(<URL>);
/* Data from the authenticated user */
private AuthData authData;
/* A tag that is used for logging statements */
private static final String TAG = "LoginDemo";
// UI references.
private AutoCompleteTextView mEmailView;
private EditText mPasswordView;
private View mProgressView;
private View mLoginFormView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Firebase.setAndroidContext(this);
setContentView(R.layout.activity_login);
mAuthProgressDialog = new ProgressDialog(this);
mAuthProgressDialog.setTitle("Loading");
mAuthProgressDialog.setMessage("Authenticating with Firebase...");
mAuthProgressDialog.setCancelable(false);
// Set up the login form.
mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
populateAutoComplete();
mPasswordView = (EditText) findViewById(R.id.password);
mPasswordView
.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id,
KeyEvent keyEvent) {
if (id == R.id.login || id == EditorInfo.IME_NULL) {
attemptLogin();
return true;
}
return false;
}
});
Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
mEmailSignInButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
attemptLogin();
}
});
mLoginFormView = findViewById(R.id.login_form);
mProgressView = findViewById(R.id.login_progress);
}
private void populateAutoComplete() {
getLoaderManager().initLoader(0, null, this);
}
/**
* Attempts to sign in or register the account specified by the login form.
* If there are form errors (invalid email, missing fields, etc.), the
* errors are presented and no actual login attempt is made.
*/
public void attemptLogin() {
// if (mAuthTask != null) {
// return;
// }
// Reset errors.
mEmailView.setError(null);
mPasswordView.setError(null);
// Store values at the time of the login attempt.
String email = mEmailView.getText().toString();
String password = mPasswordView.getText().toString();
boolean cancel = false;
View focusView = null;
//Email and Password validity checking
//I.E. does email have an @ and is password longer than 4 characters?
if (cancel) {
// There was an error; don't attempt login and focus the first
// form field with an error.
focusView.requestFocus();
} else {
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
showProgress(true);
mAuthProgressDialog.show();
loginWithPassword(email, password);
showProgress(false);
}
}
public void openProjectListView(AuthData authdata) {
ArrayList<Project> projects = new ArrayList<Project>();
User user = new User(
<URL>+"users/"
+ authData.getUid());
Map<Project, Role> projectMap = user.getProjects();
for (Project p : projectMap.keySet()) {
projects.add(p);
}
//ArrayAdapter<Project> arrayAdapter = new ArrayAdapter<Project>(this,
// android.R.layout.simple_list_item_activated_1,
// android.R.id.text1, projects);
// setListAdapter(arrayAdapter);
//ListChangeNotifier<Project> lcn = new ListChangeNotifier<Project>(
// arrayAdapter);
//for (Project project : projects) {
// project.setListChangeNotifier(lcn);
//}
mAuthProgressDialog.hide();
Intent intent = new Intent(this, ProjectListActivity.class);
intent.putParcelableArrayListExtra("projects", projects);
this.startActivity(intent);
}
private void setAuthenticatedUser(AuthData authData) {
if (authData != null) {
String name = authData.getUid();
this.authData = authData;
openProjectListView(this.authData);
}
}
public void loginWithPassword(String username, String password) {
ref.authWithPassword(username, password, new AuthResultHandler(
"password"));
}
private class AuthResultHandler implements Firebase.AuthResultHandler {
private final String provider;
public AuthResultHandler(String provider) {
this.provider = provider;
}
@Override
public void onAuthenticated(AuthData authData) {
setAuthenticatedUser(authData);
}
@Override
public void onAuthenticationError(FirebaseError firebaseError) {
showErrorDialog(firebaseError.toString());
}
}
private void showErrorDialog(String message) {
new AlertDialog.Builder(this).setTitle("Error").setMessage(message)
.setPositiveButton(android.R.string.ok, null)
.setIcon(android.R.drawable.ic_dialog_alert).show();
}
This is our Project List Activity Code:
public class ProjectListActivity extends Activity implements
ProjectListFragment.Callbacks {
public User user;
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
/**
* The list of projects
*/
private ArrayList<Project> projects;
/**
* A selected Project
*/
private Project projectItem;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.projects = this.getIntent()
.getParcelableArrayListExtra("projects");
boolean tabletSize = (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
if (!tabletSize) {
setContentView(R.layout.activity_project_list);
} else {
setContentView(R.layout.activity_project_twopane);
}
if (findViewById(R.id.project_detail_container) != null) {
// The detail container view will be present only in the
// large-screen layouts (res/values-large and
// res/values-sw600dp). If this view is present, then the
// activity should be in two-pane mode.
this.mTwoPane = true;
// In two-pane mode, list items should be given the
// 'activated' state when touched.
((ProjectListFragment) getFragmentManager().findFragmentById(
R.id.project_list)).setActivateOnItemClick(true);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
/**
*
* @return
*/
public ArrayList<Project> getProjects() {
return projects;
}
/**
* Callback method from {@link ProjectListFragment.Callbacks} indicating
* that the item with the given ID was selected.
*/
public void onItemSelected(Project p) {
this.projectItem = p;
if (this.mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
// arguments.putString(ProjectDetailFragment.ARG_ITEM_ID, id);
arguments.putParcelable("Project", p);
ProjectDetailFragment fragment = new ProjectDetailFragment();
fragment.setArguments(arguments);
getFragmentManager().beginTransaction()
.replace(R.id.project_detail_container, fragment).commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, ProjectDetailActivity.class);
// detailIntent.putExtra(ProjectDetailFragment.ARG_ITEM_ID, id);
detailIntent.putExtra("Project", p);
startActivity(detailIntent);
}
}
}
And lastly, here is our ListChangeNotifier Code:
public class ListChangeNotifier<T> {
private ArrayAdapter<T> adapter;
public ListChangeNotifier(ArrayAdapter<T> adapter) {
this.adapter = adapter;
}
/**
*
* The method that will be triggered when data is changed in the object.
*
*/
public void onChange() {
this.adapter.notifyDataSetChanged();
}
}
I'm sorry to include so much code, I'm just not sure what people would need to see.
Thanks for the help!
EDIT:
Here's the code from Project List Fragment that deals with the adapter:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//this.projects = this.getArguments().getParcelableArrayList("Projects");
this.projects = ((ProjectListActivity)this.getActivity()).getProjects();
// Done: replace with a real list adapter.
ArrayAdapter<Project> arrayAdapter = new ArrayAdapter<Project>(getActivity(),
android.R.layout.simple_list_item_activated_1,
android.R.id.text1, this.projects);
setListAdapter(arrayAdapter);
ListChangeNotifier<Project> lcn = new ListChangeNotifier<Project>(arrayAdapter);
for (Project project : this.projects) {
project.setListChangeNotifier(lcn);
}
}