mobile/android/base/sync/setup/activities/SendTabActivity.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.sync.setup.activities;
michael@0 6
michael@0 7 import java.util.ArrayList;
michael@0 8 import java.util.Collection;
michael@0 9 import java.util.List;
michael@0 10 import java.util.Map;
michael@0 11 import java.util.Map.Entry;
michael@0 12
michael@0 13 import org.mozilla.gecko.R;
michael@0 14 import org.mozilla.gecko.background.common.log.Logger;
michael@0 15 import org.mozilla.gecko.fxa.FirefoxAccounts;
michael@0 16 import org.mozilla.gecko.fxa.FxAccountConstants;
michael@0 17 import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
michael@0 18 import org.mozilla.gecko.fxa.activities.FxAccountStatusActivity;
michael@0 19 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
michael@0 20 import org.mozilla.gecko.fxa.login.State.Action;
michael@0 21 import org.mozilla.gecko.sync.CommandProcessor;
michael@0 22 import org.mozilla.gecko.sync.CommandRunner;
michael@0 23 import org.mozilla.gecko.sync.GlobalSession;
michael@0 24 import org.mozilla.gecko.sync.SyncConfiguration;
michael@0 25 import org.mozilla.gecko.sync.SyncConstants;
michael@0 26 import org.mozilla.gecko.sync.repositories.NullCursorException;
michael@0 27 import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor;
michael@0 28 import org.mozilla.gecko.sync.repositories.domain.ClientRecord;
michael@0 29 import org.mozilla.gecko.sync.setup.SyncAccounts;
michael@0 30 import org.mozilla.gecko.sync.setup.activities.LocaleAware.LocaleAwareActivity;
michael@0 31 import org.mozilla.gecko.sync.stage.SyncClientsEngineStage;
michael@0 32 import org.mozilla.gecko.sync.syncadapter.SyncAdapter;
michael@0 33
michael@0 34 import android.accounts.Account;
michael@0 35 import android.accounts.AccountManager;
michael@0 36 import android.app.Activity;
michael@0 37 import android.content.Context;
michael@0 38 import android.content.Intent;
michael@0 39 import android.content.SharedPreferences;
michael@0 40 import android.os.AsyncTask;
michael@0 41 import android.os.Bundle;
michael@0 42 import android.view.View;
michael@0 43 import android.widget.ListView;
michael@0 44 import android.widget.TextView;
michael@0 45 import android.widget.Toast;
michael@0 46
michael@0 47 public class SendTabActivity extends LocaleAwareActivity {
michael@0 48 private interface TabSender {
michael@0 49 static final String[] CLIENTS_STAGE = new String[] { SyncClientsEngineStage.COLLECTION_NAME };
michael@0 50
michael@0 51 /**
michael@0 52 * @return Return null if the account isn't correctly initialized. Return
michael@0 53 * the account GUID otherwise.
michael@0 54 */
michael@0 55 String getAccountGUID();
michael@0 56
michael@0 57 /**
michael@0 58 * Sync this account, specifying only clients as the engine to sync.
michael@0 59 */
michael@0 60 void syncClientsStage();
michael@0 61 }
michael@0 62
michael@0 63 private static class FxAccountTabSender implements TabSender {
michael@0 64 private final AndroidFxAccount fxAccount;
michael@0 65
michael@0 66 public FxAccountTabSender(Context context, AndroidFxAccount fxAccount) {
michael@0 67 this.fxAccount = fxAccount;
michael@0 68 }
michael@0 69
michael@0 70 @Override
michael@0 71 public String getAccountGUID() {
michael@0 72 try {
michael@0 73 final SharedPreferences prefs = this.fxAccount.getSyncPrefs();
michael@0 74 return prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null);
michael@0 75 } catch (Exception e) {
michael@0 76 Logger.warn(LOG_TAG, "Could not get Firefox Account parameters or preferences; aborting.");
michael@0 77 return null;
michael@0 78 }
michael@0 79 }
michael@0 80
michael@0 81 @Override
michael@0 82 public void syncClientsStage() {
michael@0 83 fxAccount.requestSync(FirefoxAccounts.FORCE, CLIENTS_STAGE, null);
michael@0 84 }
michael@0 85 }
michael@0 86
michael@0 87 private static class Sync11TabSender implements TabSender {
michael@0 88 private final Account account;
michael@0 89 private final AccountManager accountManager;
michael@0 90 private final Context context;
michael@0 91
michael@0 92 private Sync11TabSender(Context context, Account syncAccount, AccountManager accountManager) {
michael@0 93 this.context = context;
michael@0 94 this.account = syncAccount;
michael@0 95 this.accountManager = accountManager;
michael@0 96 }
michael@0 97
michael@0 98 @Override
michael@0 99 public String getAccountGUID() {
michael@0 100 try {
michael@0 101 final SharedPreferences prefs = SyncAccounts.blockingPrefsFromDefaultProfileV0(this.context, this.accountManager, this.account);
michael@0 102 return prefs.getString(SyncConfiguration.PREF_ACCOUNT_GUID, null);
michael@0 103 } catch (Exception e) {
michael@0 104 Logger.warn(LOG_TAG, "Could not get Sync account parameters or preferences; aborting.");
michael@0 105 return null;
michael@0 106 }
michael@0 107 }
michael@0 108
michael@0 109 @Override
michael@0 110 public void syncClientsStage() {
michael@0 111 SyncAdapter.requestImmediateSync(this.account, CLIENTS_STAGE);
michael@0 112 }
michael@0 113 }
michael@0 114
michael@0 115 public static final String LOG_TAG = "SendTabActivity";
michael@0 116 private ClientRecordArrayAdapter arrayAdapter;
michael@0 117
michael@0 118 private TabSender tabSender;
michael@0 119 private SendTabData sendTabData;
michael@0 120
michael@0 121 @Override
michael@0 122 public void onCreate(Bundle savedInstanceState) {
michael@0 123 super.onCreate(savedInstanceState);
michael@0 124
michael@0 125 try {
michael@0 126 sendTabData = getSendTabData(getIntent());
michael@0 127 } catch (IllegalArgumentException e) {
michael@0 128 notifyAndFinish(false);
michael@0 129 return;
michael@0 130 }
michael@0 131
michael@0 132 setContentView(R.layout.sync_send_tab);
michael@0 133
michael@0 134 final ListView listview = (ListView) findViewById(R.id.device_list);
michael@0 135 listview.setItemsCanFocus(true);
michael@0 136 listview.setTextFilterEnabled(true);
michael@0 137 listview.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
michael@0 138
michael@0 139 arrayAdapter = new ClientRecordArrayAdapter(this, R.layout.sync_list_item);
michael@0 140 listview.setAdapter(arrayAdapter);
michael@0 141
michael@0 142 TextView textView = (TextView) findViewById(R.id.title);
michael@0 143 textView.setText(sendTabData.title);
michael@0 144
michael@0 145 textView = (TextView) findViewById(R.id.uri);
michael@0 146 textView.setText(sendTabData.uri);
michael@0 147
michael@0 148 enableSend(false);
michael@0 149
michael@0 150 // will enableSend if appropriate.
michael@0 151 updateClientList();
michael@0 152 }
michael@0 153
michael@0 154 protected static SendTabData getSendTabData(Intent intent) throws IllegalArgumentException {
michael@0 155 if (intent == null) {
michael@0 156 Logger.warn(LOG_TAG, "intent was null; aborting without sending tab.");
michael@0 157 throw new IllegalArgumentException();
michael@0 158 }
michael@0 159
michael@0 160 Bundle extras = intent.getExtras();
michael@0 161 if (extras == null) {
michael@0 162 Logger.warn(LOG_TAG, "extras was null; aborting without sending tab.");
michael@0 163 throw new IllegalArgumentException();
michael@0 164 }
michael@0 165
michael@0 166 SendTabData sendTabData = SendTabData.fromBundle(extras);
michael@0 167 if (sendTabData == null) {
michael@0 168 Logger.warn(LOG_TAG, "send tab data was null; aborting without sending tab.");
michael@0 169 throw new IllegalArgumentException();
michael@0 170 }
michael@0 171
michael@0 172 if (sendTabData.uri == null) {
michael@0 173 Logger.warn(LOG_TAG, "uri was null; aborting without sending tab.");
michael@0 174 throw new IllegalArgumentException();
michael@0 175 }
michael@0 176
michael@0 177 if (sendTabData.title == null) {
michael@0 178 Logger.warn(LOG_TAG, "title was null; ignoring and sending tab anyway.");
michael@0 179 }
michael@0 180
michael@0 181 return sendTabData;
michael@0 182 }
michael@0 183
michael@0 184 /**
michael@0 185 * Ensure that the view's list of clients is backed by a recently populated
michael@0 186 * array adapter.
michael@0 187 */
michael@0 188 protected synchronized void updateClientList() {
michael@0 189 // Fetching the client list hits the clients database, so we spin this onto
michael@0 190 // a background task.
michael@0 191 new AsyncTask<Void, Void, Collection<ClientRecord>>() {
michael@0 192
michael@0 193 @Override
michael@0 194 protected Collection<ClientRecord> doInBackground(Void... params) {
michael@0 195 return getOtherClients();
michael@0 196 }
michael@0 197
michael@0 198 @Override
michael@0 199 protected void onPostExecute(final Collection<ClientRecord> clientArray) {
michael@0 200 // We're allowed to update the UI from here.
michael@0 201
michael@0 202 Logger.debug(LOG_TAG, "Got " + clientArray.size() + " clients.");
michael@0 203 arrayAdapter.setClientRecordList(clientArray);
michael@0 204 if (clientArray.size() == 1) {
michael@0 205 arrayAdapter.checkItem(0, true);
michael@0 206 }
michael@0 207
michael@0 208 enableSend(arrayAdapter.getNumCheckedGUIDs() > 0);
michael@0 209 }
michael@0 210 }.execute();
michael@0 211 }
michael@0 212
michael@0 213 @Override
michael@0 214 public void onResume() {
michael@0 215 ActivityUtils.prepareLogging();
michael@0 216 Logger.info(LOG_TAG, "Called SendTabActivity.onResume.");
michael@0 217 super.onResume();
michael@0 218
michael@0 219 /*
michael@0 220 * First, decide if we are able to send anything.
michael@0 221 */
michael@0 222 final Context applicationContext = getApplicationContext();
michael@0 223 final AccountManager accountManager = AccountManager.get(applicationContext);
michael@0 224
michael@0 225 final Account[] fxAccounts = accountManager.getAccountsByType(FxAccountConstants.ACCOUNT_TYPE);
michael@0 226 if (fxAccounts.length > 0) {
michael@0 227 final AndroidFxAccount fxAccount = new AndroidFxAccount(applicationContext, fxAccounts[0]);
michael@0 228 if (fxAccount.getState().getNeededAction() != Action.None) {
michael@0 229 // We have a Firefox Account, but it's definitely not able to send a tab
michael@0 230 // right now. Redirect to the status activity.
michael@0 231 Logger.warn(LOG_TAG, "Firefox Account named like " + fxAccount.getObfuscatedEmail() +
michael@0 232 " needs action before it can send a tab; redirecting to status activity.");
michael@0 233 redirectToNewTask(FxAccountStatusActivity.class, false);
michael@0 234 return;
michael@0 235 }
michael@0 236
michael@0 237 this.tabSender = new FxAccountTabSender(applicationContext, fxAccount);
michael@0 238
michael@0 239 Logger.info(LOG_TAG, "Allowing tab send for Firefox Account.");
michael@0 240 registerDisplayURICommand();
michael@0 241 return;
michael@0 242 }
michael@0 243
michael@0 244 final Account[] syncAccounts = accountManager.getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 245 if (syncAccounts.length > 0) {
michael@0 246 this.tabSender = new Sync11TabSender(applicationContext, syncAccounts[0], accountManager);
michael@0 247
michael@0 248 Logger.info(LOG_TAG, "Allowing tab send for Sync account.");
michael@0 249 registerDisplayURICommand();
michael@0 250 return;
michael@0 251 }
michael@0 252
michael@0 253 // Offer to set up a Firefox Account, and finish this activity.
michael@0 254 redirectToNewTask(FxAccountGetStartedActivity.class, false);
michael@0 255 }
michael@0 256
michael@0 257 private static void registerDisplayURICommand() {
michael@0 258 final CommandProcessor processor = CommandProcessor.getProcessor();
michael@0 259 processor.registerCommand("displayURI", new CommandRunner(3) {
michael@0 260 @Override
michael@0 261 public void executeCommand(final GlobalSession session, List<String> args) {
michael@0 262 CommandProcessor.displayURI(args, session.getContext());
michael@0 263 }
michael@0 264 });
michael@0 265 }
michael@0 266
michael@0 267 public void sendClickHandler(View view) {
michael@0 268 Logger.info(LOG_TAG, "Send was clicked.");
michael@0 269 final List<String> remoteClientGuids = arrayAdapter.getCheckedGUIDs();
michael@0 270
michael@0 271 if (remoteClientGuids == null) {
michael@0 272 // Should never happen.
michael@0 273 Logger.warn(LOG_TAG, "guids was null; aborting without sending tab.");
michael@0 274 notifyAndFinish(false);
michael@0 275 return;
michael@0 276 }
michael@0 277
michael@0 278 final TabSender sender = this.tabSender;
michael@0 279 if (sender == null) {
michael@0 280 // This should never happen.
michael@0 281 Logger.warn(LOG_TAG, "tabSender was null; aborting without sending tab.");
michael@0 282 notifyAndFinish(false);
michael@0 283 return;
michael@0 284 }
michael@0 285
michael@0 286 // Fetching local client GUID hits the DB, and we want to update the UI
michael@0 287 // afterward, so we perform the tab sending on another thread.
michael@0 288 new AsyncTask<Void, Void, Boolean>() {
michael@0 289
michael@0 290 @Override
michael@0 291 protected Boolean doInBackground(Void... params) {
michael@0 292 final CommandProcessor processor = CommandProcessor.getProcessor();
michael@0 293
michael@0 294 final String accountGUID = sender.getAccountGUID();
michael@0 295 Logger.debug(LOG_TAG, "Retrieved local account GUID '" + accountGUID + "'.");
michael@0 296 if (accountGUID == null) {
michael@0 297 return false;
michael@0 298 }
michael@0 299
michael@0 300 for (String remoteClientGuid : remoteClientGuids) {
michael@0 301 processor.sendURIToClientForDisplay(sendTabData.uri, remoteClientGuid, sendTabData.title, accountGUID, getApplicationContext());
michael@0 302 }
michael@0 303
michael@0 304 Logger.info(LOG_TAG, "Requesting immediate clients stage sync.");
michael@0 305 sender.syncClientsStage();
michael@0 306
michael@0 307 return true;
michael@0 308 }
michael@0 309
michael@0 310 @Override
michael@0 311 protected void onPostExecute(final Boolean success) {
michael@0 312 // We're allowed to update the UI from here.
michael@0 313 notifyAndFinish(success.booleanValue());
michael@0 314 }
michael@0 315 }.execute();
michael@0 316 }
michael@0 317
michael@0 318 /**
michael@0 319 * Notify the user about sent tabs status and then finish the activity.
michael@0 320 * <p>
michael@0 321 * "Success" is a bit of a misnomer: we wrote "displayURI" commands to the local
michael@0 322 * command database, and they will be sent on next sync. There is no way to
michael@0 323 * verify that the commands were successfully received by the intended remote
michael@0 324 * client, so we lie and say they were sent.
michael@0 325 *
michael@0 326 * @param success true if tab was sent successfully; false otherwise.
michael@0 327 */
michael@0 328 protected void notifyAndFinish(final boolean success) {
michael@0 329 int textId;
michael@0 330 if (success) {
michael@0 331 textId = R.string.sync_text_tab_sent;
michael@0 332 } else {
michael@0 333 textId = R.string.sync_text_tab_not_sent;
michael@0 334 }
michael@0 335
michael@0 336 Toast.makeText(this, textId, Toast.LENGTH_LONG).show();
michael@0 337 finish();
michael@0 338 }
michael@0 339
michael@0 340 public void enableSend(boolean shouldEnable) {
michael@0 341 View sendButton = findViewById(R.id.send_button);
michael@0 342 sendButton.setEnabled(shouldEnable);
michael@0 343 sendButton.setClickable(shouldEnable);
michael@0 344 }
michael@0 345
michael@0 346 /**
michael@0 347 * @return a map from GUID to client record, including our own.
michael@0 348 */
michael@0 349 protected Map<String, ClientRecord> getAllClients() {
michael@0 350 ClientsDatabaseAccessor db = new ClientsDatabaseAccessor(this.getApplicationContext());
michael@0 351 try {
michael@0 352 return db.fetchAllClients();
michael@0 353 } catch (NullCursorException e) {
michael@0 354 Logger.warn(LOG_TAG, "NullCursorException while populating device list.", e);
michael@0 355 return null;
michael@0 356 } finally {
michael@0 357 db.close();
michael@0 358 }
michael@0 359 }
michael@0 360
michael@0 361 /**
michael@0 362 * @return a collection of client records, excluding our own.
michael@0 363 */
michael@0 364 protected Collection<ClientRecord> getOtherClients() {
michael@0 365 final Map<String, ClientRecord> all = getAllClients();
michael@0 366 if (all == null) {
michael@0 367 return new ArrayList<ClientRecord>(0);
michael@0 368 }
michael@0 369
michael@0 370 if (this.tabSender == null) {
michael@0 371 Logger.warn(LOG_TAG, "No tab sender when fetching other client IDs.");
michael@0 372 return new ArrayList<ClientRecord>(0);
michael@0 373 }
michael@0 374
michael@0 375 final String ourGUID = this.tabSender.getAccountGUID();
michael@0 376 if (ourGUID == null) {
michael@0 377 return all.values();
michael@0 378 }
michael@0 379
michael@0 380 final ArrayList<ClientRecord> out = new ArrayList<ClientRecord>(all.size());
michael@0 381 for (Entry<String, ClientRecord> entry : all.entrySet()) {
michael@0 382 if (ourGUID.equals(entry.getKey())) {
michael@0 383 continue;
michael@0 384 }
michael@0 385 out.add(entry.getValue());
michael@0 386 }
michael@0 387 return out;
michael@0 388 }
michael@0 389
michael@0 390 // Adapted from FxAccountAbstractActivity.
michael@0 391 protected void redirectToNewTask(Class<? extends Activity> activityClass, boolean success) {
michael@0 392 Intent intent = new Intent(this, activityClass);
michael@0 393 // Per http://stackoverflow.com/a/8992365, this triggers a known bug with
michael@0 394 // the soft keyboard not being shown for the started activity. Why, Android, why?
michael@0 395 intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
michael@0 396 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
michael@0 397 startActivity(intent);
michael@0 398 notifyAndFinish(success);
michael@0 399 }
michael@0 400 }

mercurial