gecko-dev/mobile/android/base/fxa/AccountLoader.java
Nick Alexander 2f89b3cf6d Bug 1026005 - Create Loader for querying/updating Accounts status. r=mcomella
========

3cd96669bc
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Aug 18 17:43:24 2014 -0700

    Bug 1026005 - Review comments.

========

5aefe1f716
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Jun 16 11:16:16 2014 -0700

    Bug 1026005 - Part 2: Add AccountLoader.

========

61bc36bd7d
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Jun 16 11:12:37 2014 -0700

    Bug 1026005 - Part 1: Add and broadcast ACCOUNT_STATE_CHANGED_ACTION.

========

9d80df0aa7
Author: Nick Alexander <nalexander@mozilla.com>
Date:   Mon Aug 18 17:42:11 2014 -0700

    Bug 1026005 - Pre: Allow testing with |mvn integration-test|.

    This avoids errors like:

    W dalvikvm(24082)             Class resolved by unexpected DEX: Lorg/mozilla/gecko/fxa/AccountLoader;(0x42c13358):0x75440000 ref [Landroid/support/v4/content/AsyncTaskLoader;] Landroid/support/v4/content/AsyncTaskLoader;(0x42c13358):0x74a71000
    W dalvikvm(24082)             (Lorg/mozilla/gecko/fxa/AccountLoader; had used a different Landroid/support/v4/content/AsyncTaskLoader; during pre-verification)
    W dalvikvm(24082)             Unable to resolve superclass of Lorg/mozilla/gecko/fxa/AccountLoader; (247)
    W dalvikvm(24082)             Link of class 'Lorg/mozilla/gecko/fxa/AccountLoader;' failed
    E dalvikvm(24082)             Could not find class 'org.mozilla.gecko.fxa.AccountLoader', referenced from method org.mozilla.gecko.background.fxa.TestAccountLoader.testInitialLoad
    W dalvikvm(24082)             VFY: unable to resolve new-instance 1299 (Lorg/mozilla/gecko/fxa/AccountLoader;) in Lorg/mozilla/gecko/background/fxa/TestAccountLoader;
    D dalvikvm(24082)             VFY: replacing opcode 0x22 at 0x0005
    W dalvikvm(24082)             Class resolved by unexpected DEX: Lorg/mozilla/gecko/fxa/AccountLoader;(0x42c13358):0x75440000 ref [Landroid/support/v4/content/AsyncTaskLoader;] Landroid/support/v4/content/AsyncTaskLoader;(0x42c13358):0x74a71000
    W dalvikvm(24082)             (Lorg/mozilla/gecko/fxa/AccountLoader; had used a different Landroid/support/v4/content/AsyncTaskLoader; during pre-verification)
    W dalvikvm(24082)             Unable to resolve superclass of Lorg/mozilla/gecko/fxa/AccountLoader; (247)
    W dalvikvm(24082)             Link of class 'Lorg/mozilla/gecko/fxa/AccountLoader;' failed
    D dalvikvm(24082)             DexOpt: unable to opt direct call 0x1df8 at 0x07 in Lorg/mozilla/gecko/background/fxa/TestAccountLoader;.testInitialLoad
    I TestRunner(24082)           started: testInitialLoad(org.mozilla.gecko.background.fxa.TestAccountLoader)
    I TestRunner(24082)           failed: testInitialLoad(org.mozilla.gecko.background.fxa.TestAccountLoader)
    I TestRunner(24082)           ----- begin exception -----
    I TestRunner(24082)
    I TestRunner(24082)           java.lang.NoClassDefFoundError: org.mozilla.gecko.fxa.AccountLoader
    I TestRunner(24082)           	at org.mozilla.gecko.background.fxa.TestAccountLoader.testInitialLoad(TestAccountLoader.java:145)
    I TestRunner(24082)           	at java.lang.reflect.Method.invokeNative(Native Method)
    I TestRunner(24082)           	at java.lang.reflect.Method.invoke(Method.java:525)
    I TestRunner(24082)           	at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
    I TestRunner(24082)           	at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
    I TestRunner(24082)           	at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)
    I TestRunner(24082)           	at junit.framework.TestCase.runBare(TestCase.java:134)
    I TestRunner(24082)           	at junit.framework.TestResult$1.protect(TestResult.java:115)
    I TestRunner(24082)           	at junit.framework.TestResult.runProtected(TestResult.java:133)
    I TestRunner(24082)           	at junit.framework.TestResult.run(TestResult.java:118)
    I TestRunner(24082)           	at junit.framework.TestCase.run(TestCase.java:124)
    I TestRunner(24082)           	at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
    I TestRunner(24082)           	at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
    I TestRunner(24082)           	at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
    I TestRunner(24082)           	at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1738)
    I TestRunner(24082)           ----- end exception -----
    I TestRunner(24082)           finished: testInitialLoad(org.mozilla.gecko.background.fxa.TestAccountLoader)
2014-08-22 11:15:38 -07:00

185 lines
6.5 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.fxa;
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
import org.mozilla.gecko.sync.setup.SyncAccounts;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.support.v4.content.AsyncTaskLoader;
/**
* A Loader that queries and updates based on the existence of Firefox and
* legacy Sync Android Accounts.
*
* The loader returns an Android Account (of either Account type) if an account
* exists, and null to indicate no Account is present.
*
* The loader listens for Accounts added and deleted, and also Accounts being
* updated by Sync or another Activity, via the use of
* {@link AndroidFxAccount#setState(org.mozilla.gecko.fxa.login.State)}.
* Be careful of message loops if you update the account state from an activity
* that uses this loader.
*
* This implementation is based on
* <a href="http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html">http://www.androiddesignpatterns.com/2012/08/implementing-loaders.html</a>.
*/
public class AccountLoader extends AsyncTaskLoader<Account> {
protected Account account = null;
protected BroadcastReceiver broadcastReceiver = null;
public AccountLoader(Context context) {
super(context);
}
// Task that performs the asynchronous load **/
@Override
public Account loadInBackground() {
final Context context = getContext();
Account foundAccount = FirefoxAccounts.getFirefoxAccount(context);
if (foundAccount == null) {
final Account[] syncAccounts = SyncAccounts.syncAccounts(context);
if (syncAccounts != null && syncAccounts.length > 0) {
foundAccount = syncAccounts[0];
}
}
return foundAccount;
}
// Deliver the results to the registered listener.
@Override
public void deliverResult(Account data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
Account oldData = account;
account = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
// The Loaders state-dependent behavior.
@Override
protected void onStartLoading() {
if (account != null) {
// Deliver any previously loaded data immediately.
deliverResult(account);
}
// Begin monitoring the underlying data source.
if (broadcastReceiver == null) {
broadcastReceiver = makeNewObserver();
registerObserver(broadcastReceiver);
}
if (takeContentChanged() || account == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
@Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
@Override
protected void onReset() {
// Ensure the loader has been stopped. In CursorLoader and the template
// this code follows (see the class comment), this is onStopLoading, which
// appears to not set the started flag (see Loader itself).
stopLoading();
// At this point we can release the resources associated with 'mData'.
if (account != null) {
releaseResources(account);
account = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (broadcastReceiver != null) {
final BroadcastReceiver observer = broadcastReceiver;
broadcastReceiver = null;
unregisterObserver(observer);
}
}
@Override
public void onCanceled(Account data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(Account data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
// Observer which receives notifications when the data changes.
protected BroadcastReceiver makeNewObserver() {
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// Must be called on the main thread of the process. We register the
// broadcast receiver with a null Handler (see registerObserver), which
// ensures we're on the main thread when we receive this intent.
onContentChanged();
}
};
return broadcastReceiver;
}
protected void registerObserver(BroadcastReceiver observer) {
final IntentFilter intentFilter = new IntentFilter();
// Android Account added or removed.
intentFilter.addAction(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
// Firefox Account internal state changed.
intentFilter.addAction(FxAccountConstants.ACCOUNT_STATE_CHANGED_ACTION);
// null means: "the main thread of the process will be used." We must call
// onContentChanged on the main thread of the process; this ensures we do.
final Handler handler = null;
getContext().registerReceiver(observer, intentFilter, FxAccountConstants.PER_ACCOUNT_TYPE_PERMISSION, handler);
}
protected void unregisterObserver(BroadcastReceiver observer) {
getContext().unregisterReceiver(observer);
}
}