Bug 977161 - Part 3: Allow unhiding hidden devices. r=rnewman

I'd like to make the DialogFragment show icons, last synced times,
number of tabs, etc; but AlertDialog does not handle multi-choice lists
backed by a custom list adapters.  (It does handle single-choice lists
backed by custom list adapters.)  We can implement our own dialog
fragment if and when we want better looks.

I've made this somewhat general so that we can use it from
ShareDialog/SendTab, which currently use AlertDialog.  Using
DialogFragment has two advantages.  First, it smoothly persists across
configuration changes; AlertDialog does not.  Second, it allow us to use
AlertDialog until we implement the improved UI above.
This commit is contained in:
Nick Alexander 2014-09-16 15:41:14 -07:00
parent b226800b48
commit f9ac46bb66
5 changed files with 159 additions and 2 deletions

View File

@ -0,0 +1,125 @@
/* -*- 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;
import java.util.ArrayList;
import java.util.List;
import org.mozilla.gecko.TabsAccessor.RemoteClient;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.util.SparseBooleanArray;
/**
* A dialog fragment that displays a list of remote clients.
* <p>
* The dialog allows both single (one tap) and multiple (checkbox) selection.
* The dialog's results are communicated via the {@link RemoteClientsListener}
* interface. Either the dialog fragment's <i>target fragment</i> (see
* {@link Fragment#setTargetFragment(Fragment, int)}), or the containing
* <i>activity</i>, must implement that interface. See
* {@link #notifyListener(List)} for details.
*/
public class RemoteClientsDialogFragment extends DialogFragment {
private static final String KEY_TITLE = "title";
private static final String KEY_CHOICE_MODE = "choice_mode";
private static final String KEY_POSITIVE_BUTTON_TEXT = "positive_button_text";
private static final String KEY_CLIENTS = "clients";
public interface RemoteClientsListener {
// Always called on the main UI thread.
public void onClients(List<RemoteClient> clients);
}
public enum ChoiceMode {
SINGLE,
MULTIPLE,
}
public static RemoteClientsDialogFragment newInstance(String title, String positiveButtonText, ChoiceMode choiceMode, ArrayList<RemoteClient> clients) {
final RemoteClientsDialogFragment dialog = new RemoteClientsDialogFragment();
final Bundle args = new Bundle();
args.putString(KEY_TITLE, title);
args.putString(KEY_POSITIVE_BUTTON_TEXT, positiveButtonText);
args.putInt(KEY_CHOICE_MODE, choiceMode.ordinal());
args.putParcelableArrayList(KEY_CLIENTS, clients);
dialog.setArguments(args);
return dialog;
}
public RemoteClientsDialogFragment() {
// Empty constructor is required for DialogFragment.
}
protected void notifyListener(List<RemoteClient> clients) {
RemoteClientsListener listener;
try {
listener = (RemoteClientsListener) getTargetFragment();
} catch (ClassCastException e) {
try {
listener = (RemoteClientsListener) getActivity();
} catch (ClassCastException f) {
throw new ClassCastException(getTargetFragment() + " or " + getActivity()
+ " must implement RemoteClientsListener");
}
}
listener.onClients(clients);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String title = getArguments().getString(KEY_TITLE);
final String positiveButtonText = getArguments().getString(KEY_POSITIVE_BUTTON_TEXT);
final ChoiceMode choiceMode = ChoiceMode.values()[getArguments().getInt(KEY_CHOICE_MODE)];
final ArrayList<RemoteClient> clients = getArguments().getParcelableArrayList(KEY_CLIENTS);
final Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(title);
final String[] clientNames = new String[clients.size()];
for (int i = 0; i < clients.size(); i++) {
clientNames[i] = clients.get(i).name;
}
if (choiceMode == ChoiceMode.MULTIPLE) {
builder.setMultiChoiceItems(clientNames, null, null);
builder.setPositiveButton(positiveButtonText, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int which) {
if (which != Dialog.BUTTON_POSITIVE) {
return;
}
final AlertDialog dialog = (AlertDialog) dialogInterface;
final SparseBooleanArray checkedItemPositions = dialog.getListView().getCheckedItemPositions();
final ArrayList<RemoteClient> checked = new ArrayList<RemoteClient>();
for (int i = 0; i < checkedItemPositions.size(); i++) {
if (checkedItemPositions.get(i)) {
checked.add(clients.get(i));
}
}
notifyListener(checked);
}
});
} else {
builder.setItems(clientNames, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int index) {
final ArrayList<RemoteClient> checked = new ArrayList<RemoteClient>();
checked.add(clients.get(index));
notifyListener(checked);
}
});
}
return builder.create();
}
}

View File

@ -12,6 +12,9 @@ import java.util.List;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.R;
import org.mozilla.gecko.RemoteClientsDialogFragment;
import org.mozilla.gecko.RemoteClientsDialogFragment.ChoiceMode;
import org.mozilla.gecko.RemoteClientsDialogFragment.RemoteClientsListener;
import org.mozilla.gecko.RemoteTabsExpandableListAdapter;
import org.mozilla.gecko.TabsAccessor;
import org.mozilla.gecko.TabsAccessor.RemoteClient;
@ -51,13 +54,16 @@ import android.widget.TextView;
* <p>
* This is intended to be used on phones, and possibly in portrait mode on tablets.
*/
public class RemoteTabsExpandableListFragment extends HomeFragment {
public class RemoteTabsExpandableListFragment extends HomeFragment implements RemoteClientsListener {
// Logging tag name.
private static final String LOGTAG = "GeckoRemoteTabsExpList";
// Cursor loader ID.
private static final int LOADER_ID_REMOTE_TABS = 0;
// Dialog fragment TAG.
private static final String DIALOG_TAG_REMOTE_TABS = "dialog_tag_remote_tabs";
private static final String[] STAGES_TO_SYNC_ON_REFRESH = new String[] { "clients", "tabs" };
// Maintain group collapsed and hidden state.
@ -210,7 +216,12 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Nothing for now. This will be fleshed out in the next commits.
final RemoteClientsDialogFragment dialog = RemoteClientsDialogFragment.newInstance(
getResources().getString(R.string.home_remote_tabs_hidden_devices_title),
getResources().getString(R.string.home_remote_tabs_unhide_selected_devices),
ChoiceMode.MULTIPLE, new ArrayList<RemoteClient>(mHiddenClients));
dialog.setTargetFragment(RemoteTabsExpandableListFragment.this, 0);
dialog.show(getActivity().getSupportFragmentManager(), DIALOG_TAG_REMOTE_TABS);
}
});
@ -347,6 +358,18 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
}
}
public void onClients(List<RemoteClient> clients) {
// The clients listed were hidden and have been checked by the user. We
// interpret that as "show these clients now".
for (RemoteClient client : clients) {
sState.setClientHidden(client.guid, false);
// There's no particular need to do this, but if you want to see it,
// let's show it all.
sState.setClientCollapsed(client.guid, false);
}
getLoaderManager().restartLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);
}
@Override
protected void load() {
getLoaderManager().initLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);

View File

@ -419,6 +419,12 @@ size. -->
number of hidden devices is always more than one. We can't use
Android plural forms, sadly. See Bug #753859. -->
<!ENTITY home_remote_tabs_many_hidden_devices "&formatD; devices hidden">
<!-- Localization note (home_remote_tabs_hidden_devices_title) : This is the
title of a dialog; we expect more than one device. -->
<!ENTITY home_remote_tabs_hidden_devices_title "Hidden devices">
<!-- Localization note (home_remote_tabs_unhide_selected_devices) : This is
the text of a button; we expect more than one device. -->
<!ENTITY home_remote_tabs_unhide_selected_devices "Unhide selected devices">
<!ENTITY private_browsing_title "Private Browsing">
<!ENTITY private_tabs_panel_empty_desc "Your private tabs will show up here. While we don\'t keep any of your browsing history or cookies, bookmarks and files that you download will still be saved on your device.">

View File

@ -369,6 +369,7 @@ gbjar.sources += [
'prompts/PromptService.java',
'prompts/TabInput.java',
'ReaderModeUtils.java',
'RemoteClientsDialogFragment.java',
'RemoteTabsExpandableListAdapter.java',
'Restarter.java',
'RestrictedProfiles.java',

View File

@ -361,6 +361,8 @@
<string name="home_remote_tabs_need_to_verify">&home_remote_tabs_need_to_verify;</string>
<string name="home_remote_tabs_one_hidden_device">&home_remote_tabs_one_hidden_device;</string>
<string name="home_remote_tabs_many_hidden_devices">&home_remote_tabs_many_hidden_devices;</string>
<string name="home_remote_tabs_hidden_devices_title">&home_remote_tabs_hidden_devices_title;</string>
<string name="home_remote_tabs_unhide_selected_devices">&home_remote_tabs_unhide_selected_devices;</string>
<string name="private_browsing_title">&private_browsing_title;</string>
<string name="private_tabs_panel_empty_desc">&private_tabs_panel_empty_desc;</string>
<string name="private_tabs_panel_learn_more">&private_tabs_panel_learn_more;</string>