mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 977161 - Part 2: Add a clickable footer showing hidden device count. r=rnewman
This is quite delicate, due to the interactions between ExpandableListView and the footer views. At the end of the day, this uses an Adapter that handles header and footer views as separate view-group types. That means working around Android's limitations; see the comments in the code for the details. The alternative would be to implement our own wrapping Adapter; or to boil the specifics of our use case into RemoteTabsExpandableListAdapter. This is certainly quicker than the former; and, I hope, less error prone than the latter.
This commit is contained in:
parent
f5ab0b7ea2
commit
b226800b48
@ -5,7 +5,9 @@
|
||||
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
@ -34,6 +36,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
@ -64,12 +67,19 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
|
||||
// Adapter for the list of remote tabs.
|
||||
private RemoteTabsExpandableListAdapter mAdapter;
|
||||
|
||||
// List of hidden remote clients.
|
||||
// Only accessed from the UI thread.
|
||||
private final ArrayList<RemoteClient> mHiddenClients = new ArrayList<RemoteClient>();
|
||||
|
||||
// The view shown by the fragment.
|
||||
private HomeExpandableListView mList;
|
||||
|
||||
// Reference to the View to display when there are no results.
|
||||
private View mEmptyView;
|
||||
|
||||
// The footer view to display when there are hidden devices not shown.
|
||||
private View mFooterView;
|
||||
|
||||
// Callbacks used for the loader.
|
||||
private CursorLoaderCallbacks mCursorLoaderCallbacks;
|
||||
|
||||
@ -156,9 +166,8 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
|
||||
final RemoteClient client = (RemoteClient) adapter.getGroup(groupPosition);
|
||||
final RemoteTabsClientContextMenuInfo info = new RemoteTabsClientContextMenuInfo(view, position, id, client);
|
||||
return info;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
@ -190,10 +199,36 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
|
||||
sState = new RemoteTabsExpandableListState(GeckoSharedPrefs.forProfile(getActivity()));
|
||||
}
|
||||
|
||||
// There is an unfortunate interaction between ExpandableListViews and
|
||||
// footer onClick handling. The footer view itself appears to not
|
||||
// receive click events. Its children, however, do receive click events.
|
||||
// Therefore, we attach an onClick handler to a child of the footer view
|
||||
// itself.
|
||||
mFooterView = LayoutInflater.from(getActivity()).inflate(R.layout.home_remote_tabs_hidden_devices_footer, mList, false);
|
||||
final View view = mFooterView.findViewById(R.id.hidden_devices);
|
||||
view.setClickable(true);
|
||||
view.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Nothing for now. This will be fleshed out in the next commits.
|
||||
}
|
||||
});
|
||||
|
||||
// There is a delicate interaction, pre-KitKat, between
|
||||
// {add,remove}FooterView and setAdapter. setAdapter wraps the adapter
|
||||
// in a footer/header-managing adapter, which only happens (pre-KitKat)
|
||||
// if a footer/header is present. Therefore, we add our footer before
|
||||
// setting the adapter; and then we remove it afterward. From there on,
|
||||
// we can add/remove it at will.
|
||||
mList.addFooterView(mFooterView, null, true);
|
||||
|
||||
// Intialize adapter
|
||||
mAdapter = new RemoteTabsExpandableListAdapter(R.layout.home_remote_tabs_group, R.layout.home_remote_tabs_child, null);
|
||||
mList.setAdapter(mAdapter);
|
||||
|
||||
// Now the adapter is wrapped; we can remove our footer view.
|
||||
mList.removeFooterView(mFooterView);
|
||||
|
||||
// Create callbacks before the initial loader is started
|
||||
mCursorLoaderCallbacks = new CursorLoaderCallbacks();
|
||||
loadIfVisible();
|
||||
@ -240,17 +275,45 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
|
||||
final int itemId = item.getItemId();
|
||||
if (itemId == R.id.home_remote_tabs_hide_client) {
|
||||
sState.setClientHidden(info.client.guid, true);
|
||||
getLoaderManager().restartLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);
|
||||
return true;
|
||||
} else if (itemId == R.id.home_remote_tabs_show_client) {
|
||||
sState.setClientHidden(info.client.guid, false);
|
||||
getLoaderManager().restartLoader(LOADER_ID_REMOTE_TABS, null, mCursorLoaderCallbacks);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateUiFromClients(List<RemoteClient> clients) {
|
||||
private void updateUiFromClients(List<RemoteClient> clients, List<RemoteClient> hiddenClients) {
|
||||
// We have three states: no clients (including hidden clients) at all;
|
||||
// all clients hidden; some clients hidden. We want to show the empty
|
||||
// list view only when we have no clients at all. This flag
|
||||
// differentiates the first from the latter two states.
|
||||
boolean displayedSomeClients = false;
|
||||
|
||||
if (hiddenClients == null || hiddenClients.isEmpty()) {
|
||||
mList.removeFooterView(mFooterView);
|
||||
} else {
|
||||
displayedSomeClients = true;
|
||||
|
||||
final TextView textView = (TextView) mFooterView.findViewById(R.id.hidden_devices);
|
||||
if (hiddenClients.size() == 1) {
|
||||
textView.setText(getResources().getString(R.string.home_remote_tabs_one_hidden_device));
|
||||
} else {
|
||||
textView.setText(getResources().getString(R.string.home_remote_tabs_many_hidden_devices, hiddenClients.size()));
|
||||
}
|
||||
|
||||
// This is a simple, if not very future-proof, way to determine if
|
||||
// the footer view has already been added to the list view.
|
||||
if (mList.getFooterViewsCount() < 1) {
|
||||
mList.addFooterView(mFooterView);
|
||||
}
|
||||
}
|
||||
|
||||
if (clients != null && !clients.isEmpty()) {
|
||||
displayedSomeClients = true;
|
||||
|
||||
// No sense crashing if we've made an error.
|
||||
int groupCount = Math.min(mList.getExpandableListAdapter().getGroupCount(), clients.size());
|
||||
for (int i = 0; i < groupCount; i++) {
|
||||
@ -261,10 +324,14 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
|
||||
mList.expandGroup(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayedSomeClients) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cursor is empty, so set the empty view if it hasn't been set already.
|
||||
// No clients shown, not even hidden clients. Set the empty view if it
|
||||
// hasn't been set already.
|
||||
if (mEmptyView == null) {
|
||||
// Set empty panel view. We delay this so that the empty view won't flash.
|
||||
final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.home_empty_view_stub);
|
||||
@ -305,8 +372,22 @@ public class RemoteTabsExpandableListFragment extends HomeFragment {
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
|
||||
final List<RemoteClient> clients = TabsAccessor.getClientsFromCursor(c);
|
||||
|
||||
// Filter the hidden clients out of the clients list. The clients
|
||||
// list is updated in place; the hidden clients list is built
|
||||
// incrementally.
|
||||
mHiddenClients.clear();
|
||||
final Iterator<RemoteClient> it = clients.iterator();
|
||||
while (it.hasNext()) {
|
||||
final RemoteClient client = it.next();
|
||||
if (sState.isClientHidden(client.guid)) {
|
||||
it.remove();
|
||||
mHiddenClients.add(client);
|
||||
}
|
||||
}
|
||||
|
||||
mAdapter.replaceClients(clients);
|
||||
updateUiFromClients(clients);
|
||||
updateUiFromClients(clients, mHiddenClients);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -413,6 +413,13 @@ size. -->
|
||||
<!ENTITY home_remote_tabs_trouble_verifying "Trouble verifying your account?">
|
||||
<!ENTITY home_remote_tabs_need_to_verify "Please verify your Firefox Account to start syncing.">
|
||||
|
||||
<!ENTITY home_remote_tabs_one_hidden_device "1 device hidden">
|
||||
<!-- Localization note (home_remote_tabs_many_hidden_devices) : The
|
||||
formatD is replaced with the number of hidden devices. The
|
||||
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">
|
||||
|
||||
<!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.">
|
||||
<!ENTITY private_tabs_panel_learn_more "Want to learn more?">
|
||||
|
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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/.
|
||||
-->
|
||||
|
||||
<!-- This layout is actually necessary because of an interaction
|
||||
between ExpandableListView and onClick handling. We need a child
|
||||
to attach a click listener to. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:gecko="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/Widget.RemoteTabsClientView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/home_remote_tabs_hidden_footer_height"
|
||||
android:gravity="center_vertical" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hidden_devices"
|
||||
style="@style/Widget.Home.ActionItem"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:maxLength="1024" />
|
||||
|
||||
</LinearLayout>
|
@ -38,6 +38,9 @@
|
||||
<!-- Remote Tabs static view top padding. Less in landscape on phones. -->
|
||||
<dimen name="home_remote_tabs_top_padding">48dp</dimen>
|
||||
|
||||
<!-- Remote Tabs Hidden devices row height -->
|
||||
<dimen name="home_remote_tabs_hidden_footer_height">48dp</dimen>
|
||||
|
||||
<!-- Search Engine Row height -->
|
||||
<dimen name="search_row_height">48dp</dimen>
|
||||
|
||||
|
@ -359,6 +359,8 @@
|
||||
<string name="home_remote_tabs_need_to_sign_in">&home_remote_tabs_need_to_sign_in;</string>
|
||||
<string name="home_remote_tabs_trouble_verifying">&home_remote_tabs_trouble_verifying;</string>
|
||||
<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="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>
|
||||
|
Loading…
Reference in New Issue
Block a user