mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
244 lines
10 KiB
Java
244 lines
10 KiB
Java
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
|
* 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.home;
|
|
|
|
import org.mozilla.gecko.GeckoScreenOrientation;
|
|
import org.mozilla.gecko.R;
|
|
import org.mozilla.gecko.fxa.AccountLoader;
|
|
import org.mozilla.gecko.fxa.FirefoxAccounts;
|
|
import org.mozilla.gecko.fxa.FxAccountConstants;
|
|
import org.mozilla.gecko.fxa.login.State;
|
|
import org.mozilla.gecko.fxa.login.State.Action;
|
|
import org.mozilla.gecko.sync.SyncConstants;
|
|
import org.mozilla.gecko.util.HardwareUtils;
|
|
|
|
import android.accounts.Account;
|
|
import android.content.res.Configuration;
|
|
import android.os.Bundle;
|
|
import android.support.v4.app.Fragment;
|
|
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
|
import android.support.v4.content.Loader;
|
|
import android.support.v4.util.Pair;
|
|
import android.util.Log;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
|
|
/**
|
|
* A <code>HomeFragment</code> that, depending on the state of accounts on the
|
|
* device:
|
|
* <ul>
|
|
* <li>displays remote tabs from other devices;</li>
|
|
* <li>offers to re-connect a Firefox Account;</li>
|
|
* <li>offers to create a new Firefox Account.</li>
|
|
* </ul>
|
|
*/
|
|
public class RemoteTabsPanel extends HomeFragment {
|
|
private static final String LOGTAG = "GeckoRemoteTabsPanel";
|
|
|
|
// Loader ID for Android Account loader.
|
|
private static final int LOADER_ID_ACCOUNT = 0;
|
|
private static final String FRAGMENT_ACTION = "FRAGMENT_ACTION";
|
|
private static final String FRAGMENT_ORIENTATION = "FRAGMENT_ORIENTATION";
|
|
private static final String FRAGMENT_TAG = "FRAGMENT_TAG";
|
|
private static final String NO_ACCOUNT = "NO_ACCOUNT";
|
|
|
|
// Callback for loaders.
|
|
private AccountLoaderCallbacks mAccountLoaderCallbacks;
|
|
|
|
@Override
|
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
return inflater.inflate(R.layout.home_remote_tabs_panel, container, false);
|
|
}
|
|
|
|
@Override
|
|
public void onActivityCreated(Bundle savedInstanceState) {
|
|
super.onActivityCreated(savedInstanceState);
|
|
|
|
// Create callbacks before the initial loader is started.
|
|
mAccountLoaderCallbacks = new AccountLoaderCallbacks();
|
|
loadIfVisible();
|
|
}
|
|
|
|
@Override
|
|
protected void loadIfVisible() {
|
|
// Force reload fragment only in tablets when there is valid account and the orientation has changed.
|
|
Pair<String, Integer> actionOrientationPair;
|
|
if (canLoad() && HardwareUtils.isTablet() && (actionOrientationPair = getActionAndOrientationForFragmentInBackStack()) != null) {
|
|
if (actionOrientationPair.first.equals(Action.None.name()) && actionOrientationPair.second != GeckoScreenOrientation.getInstance().getAndroidOrientation()) {
|
|
// As the fragment becomes visible only after onStart callback, we can safely remove it from the back-stack.
|
|
// If a portrait fragment is in the back-stack and then a landscape fragment should be shown, there can
|
|
// be a brief flash as the fragment as replaced.
|
|
getChildFragmentManager()
|
|
.beginTransaction()
|
|
.addToBackStack(null)
|
|
.remove(getChildFragmentManager().findFragmentByTag(FRAGMENT_TAG))
|
|
.commitAllowingStateLoss();
|
|
getChildFragmentManager().executePendingTransactions();
|
|
|
|
load();
|
|
return;
|
|
}
|
|
}
|
|
super.loadIfVisible();
|
|
}
|
|
|
|
@Override
|
|
public void load() {
|
|
getLoaderManager().initLoader(LOADER_ID_ACCOUNT, null, mAccountLoaderCallbacks);
|
|
}
|
|
|
|
private void showSubPanel(Account account) {
|
|
final Action actionNeeded = getActionNeeded(account);
|
|
final String actionString = actionNeeded != null ? actionNeeded.name() : NO_ACCOUNT;
|
|
final int orientation = HardwareUtils.isTablet() ? GeckoScreenOrientation.getInstance().getAndroidOrientation()
|
|
: Configuration.ORIENTATION_UNDEFINED;
|
|
|
|
// Check if fragment for given action and orientation is in the back-stack.
|
|
final Pair<String, Integer> actionOrientationPair = getActionAndOrientationForFragmentInBackStack();
|
|
if (actionOrientationPair != null && actionOrientationPair.first.equals(actionString) && (actionOrientationPair.second == orientation)) {
|
|
return;
|
|
}
|
|
|
|
// Instantiate the fragment for the action and update the arguments.
|
|
Fragment subPanel = makeFragmentForAction(actionNeeded);
|
|
final Bundle args = new Bundle();
|
|
args.putBoolean(HomePager.CAN_LOAD_ARG, getCanLoadHint());
|
|
args.putString(FRAGMENT_ACTION, actionString);
|
|
args.putInt(FRAGMENT_ORIENTATION, orientation);
|
|
subPanel.setArguments(args);
|
|
|
|
// Add the fragment to the back-stack.
|
|
getChildFragmentManager()
|
|
.beginTransaction()
|
|
.addToBackStack(null)
|
|
.replace(R.id.remote_tabs_container, subPanel, FRAGMENT_TAG)
|
|
.commitAllowingStateLoss();
|
|
}
|
|
|
|
private Pair<String, Integer> getActionAndOrientationForFragmentInBackStack() {
|
|
final Fragment currentFragment = getChildFragmentManager().findFragmentByTag(FRAGMENT_TAG);
|
|
if (currentFragment != null && currentFragment.getArguments() != null) {
|
|
final String fragmentAction = currentFragment.getArguments().getString(FRAGMENT_ACTION);
|
|
final int fragmentOrientation = currentFragment.getArguments().getInt(FRAGMENT_ORIENTATION);
|
|
return Pair.create(fragmentAction, fragmentOrientation);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get whatever <code>Action</code> is required to continue healthy syncing
|
|
* of Remote Tabs.
|
|
* <p>
|
|
* A Firefox Account can be in many states, from healthy to requiring a
|
|
* Fennec upgrade to continue use. If we have a Firefox Account, but the
|
|
* state seems corrupt, the best we can do is ask for a password, which
|
|
* resets most of the Account state. The health of a Sync account is
|
|
* essentially opaque in this respect.
|
|
* <p>
|
|
* A null Account means there is no Account (Sync or Firefox) on the device.
|
|
*
|
|
* @param account
|
|
* Android Account (Sync or Firefox); may be null.
|
|
*/
|
|
private Action getActionNeeded(Account account) {
|
|
if (account == null) {
|
|
return null;
|
|
}
|
|
|
|
if (SyncConstants.ACCOUNTTYPE_SYNC.equals(account.type)) {
|
|
return Action.None;
|
|
}
|
|
|
|
if (!FxAccountConstants.ACCOUNT_TYPE.equals(account.type)) {
|
|
Log.wtf(LOGTAG, "Non Sync, non Firefox Android Account returned by AccountLoader; returning null.");
|
|
return null;
|
|
}
|
|
|
|
final State state = FirefoxAccounts.getFirefoxAccountState(getActivity());
|
|
if (state == null) {
|
|
Log.wtf(LOGTAG, "Firefox Account with null state found; offering needs password.");
|
|
return Action.NeedsPassword;
|
|
}
|
|
|
|
final Action actionNeeded = state.getNeededAction();
|
|
if (actionNeeded == null) {
|
|
Log.wtf(LOGTAG, "Firefox Account with non-null state but null action needed; offering needs password.");
|
|
return Action.NeedsPassword;
|
|
}
|
|
|
|
return actionNeeded;
|
|
}
|
|
|
|
private Fragment makeFragmentForAction(Action action) {
|
|
if (action == null) {
|
|
// This corresponds to no Account: neither Sync nor Firefox.
|
|
return RemoteTabsStaticFragment.newInstance(R.layout.remote_tabs_setup);
|
|
}
|
|
|
|
switch (action) {
|
|
case None:
|
|
if (HardwareUtils.isTablet() && GeckoScreenOrientation.getInstance().getAndroidOrientation() == Configuration.ORIENTATION_LANDSCAPE) {
|
|
return new RemoteTabsSplitPlaneFragment();
|
|
} else {
|
|
return new RemoteTabsExpandableListFragment();
|
|
}
|
|
case NeedsVerification:
|
|
return RemoteTabsStaticFragment.newInstance(R.layout.remote_tabs_needs_verification);
|
|
case NeedsPassword:
|
|
return RemoteTabsStaticFragment.newInstance(R.layout.remote_tabs_needs_password);
|
|
case NeedsUpgrade:
|
|
return RemoteTabsStaticFragment.newInstance(R.layout.remote_tabs_needs_upgrade);
|
|
case NeedsFinishMigrating:
|
|
return RemoteTabsStaticFragment.newInstance(R.layout.remote_tabs_needs_finish_migrating);
|
|
default:
|
|
// This should never happen, but we're confident we have a Firefox
|
|
// Account at this point, so let's show the needs password screen.
|
|
// That's our best hope of righting the ship.
|
|
Log.wtf(LOGTAG, "Got unexpected action needed; offering needs password.");
|
|
return RemoteTabsStaticFragment.newInstance(R.layout.remote_tabs_needs_password);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the UI to reflect the given <code>Account</code> and its state.
|
|
* <p>
|
|
* A null Account means there is no Account (Sync or Firefox) on the device.
|
|
*
|
|
* @param account
|
|
* Android Account (Sync or Firefox); may be null.
|
|
*/
|
|
protected void updateUiFromAccount(Account account) {
|
|
if (getView() == null) {
|
|
// Early abort. When the fragment is detached, we get a loader
|
|
// reset, which calls this with a null account parameter. A null
|
|
// account is valid (it means there is no account, either Sync or
|
|
// Firefox), and so we start to offer the setup flow. But this all
|
|
// happens after the view has been destroyed, which means inserting
|
|
// the setup flow fails. In this case, just abort.
|
|
return;
|
|
}
|
|
showSubPanel(account);
|
|
}
|
|
|
|
private class AccountLoaderCallbacks implements LoaderCallbacks<Account> {
|
|
@Override
|
|
public Loader<Account> onCreateLoader(int id, Bundle args) {
|
|
return new AccountLoader(getActivity());
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<Account> loader, Account account) {
|
|
updateUiFromAccount(account);
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<Account> loader) {
|
|
updateUiFromAccount(null);
|
|
}
|
|
}
|
|
}
|