mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
2f89b3cf6d
========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)
185 lines
6.5 KiB
Java
185 lines
6.5 KiB
Java
/* 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 Loader’s 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);
|
||
}
|
||
}
|