Bug 696462: Refresh UX of tabs list UI [r=mfinkle]

This commit is contained in:
Sriram Ramasubramanian 2011-11-02 11:36:44 -07:00
Родитель 38d3ab102c
Коммит 98b4676b51
9 изменённых файлов: 323 добавлений и 216 удалений

Просмотреть файл

@ -137,6 +137,15 @@
android:backgroundDimEnabled="false">
</activity>
<activity android:name="org.mozilla.gecko.TabsTray"
android:theme="@android:style/Theme.Translucent"
android:windowIsTranslucent="true"
android:windowContentOverlay="@null"
android:windowNoTitle="true"
android:windowIsFloating="true"
android:backgroundDimEnabled="false">
</activity>
<activity android:name="org.mozilla.gecko.GeckoPreferences"
android:label="@string/preferences_title"
android:excludeFromRecents="true">

Просмотреть файл

@ -56,6 +56,7 @@ import android.app.*;
import android.text.*;
import android.view.*;
import android.view.inputmethod.*;
import android.view.ViewGroup.LayoutParams;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
@ -97,12 +98,15 @@ abstract public class GeckoApp
private IntentFilter mConnectivityFilter;
private BroadcastReceiver mConnectivityReceiver;
private BrowserToolbar mBrowserToolbar;
private PopupWindow mTabsTray;
private TabsAdapter mTabsAdapter;
public DoorHanger mDoorHanger;
private static boolean sIsTabsTrayShowing;
private static boolean sIsGeckoReady = false;
public interface OnTabsChangedListener {
public void onTabsChanged();
}
private static ArrayList<OnTabsChangedListener> mTabsChangedListeners;
static class ExtraMenuItem implements MenuItem.OnMenuItemClickListener {
String label;
String icon;
@ -611,62 +615,32 @@ abstract public class GeckoApp
}
void showTabs() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int width = metrics.widthPixels;
int height = (int) (metrics.widthPixels * 0.75);
LayoutInflater inflater = (LayoutInflater) mAppContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mTabsTray = new PopupWindow(inflater.inflate(R.layout.tabs_tray, null, false),
width,
height,
true);
mTabsTray.setBackgroundDrawable(new BitmapDrawable());
mTabsTray.setOutsideTouchable(true);
ListView list = (ListView) mTabsTray.getContentView().findViewById(R.id.list);
Button addTab = new Button(this);
addTab.setText(R.string.new_tab);
addTab.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
hideTabs();
addTab();
}
});
list.addFooterView(addTab);
sIsTabsTrayShowing = true;
onTabsChanged();
mTabsTray.showAsDropDown(mBrowserToolbar.findViewById(R.id.tabs));
Intent intent = new Intent(mAppContext, TabsTray.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
}
void hideTabs() {
if (mTabsTray.isShowing()) {
mTabsAdapter = null;
((ListView) mTabsTray.getContentView().findViewById(R.id.list)).invalidateViews();
mTabsTray.dismiss();
sIsTabsTrayShowing = false;
}
public static void registerOnTabsChangedListener(OnTabsChangedListener listener) {
if (mTabsChangedListeners == null)
mTabsChangedListeners = new ArrayList<OnTabsChangedListener>();
mTabsChangedListeners.add(listener);
}
public static void unregisterOnTabsChangedListener(OnTabsChangedListener listener) {
if (mTabsChangedListeners == null)
return;
mTabsChangedListeners.remove(listener);
}
public void onTabsChanged() {
if (mTabsTray == null)
if (mTabsChangedListeners == null)
return;
if (!sIsTabsTrayShowing)
return;
final HashMap<Integer, Tab> tabs = Tabs.getInstance().getTabs();
if (mTabsAdapter != null) {
mTabsAdapter = new TabsAdapter(mAppContext, tabs);
mTabsAdapter.notifyDataSetChanged();
ListView list = (ListView) mTabsTray.getContentView().findViewById(R.id.list);
list.invalidateViews();
list.setAdapter(mTabsAdapter);
} else {
mTabsAdapter = new TabsAdapter(mAppContext, tabs);
ListView list = (ListView) mTabsTray.getContentView().findViewById(R.id.list);
list.setAdapter(mTabsAdapter);
Iterator items = mTabsChangedListeners.iterator();
while (items.hasNext()) {
((OnTabsChangedListener) items.next()).onTabsChanged();
}
}
@ -1246,11 +1220,6 @@ abstract public class GeckoApp
// in onXreExit.
if (isFinishing())
GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_SHUTDOWN));
if (mTabsTray != null && mTabsTray.isShowing()) {
hideTabs();
mTabsTray = null;
}
GeckoAppShell.unregisterGeckoEventListener("DOMContentLoaded", GeckoApp.mAppContext);
GeckoAppShell.unregisterGeckoEventListener("DOMTitleChanged", GeckoApp.mAppContext);
@ -1578,109 +1547,4 @@ abstract public class GeckoApp
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Load", url));
}
}
// Adapter to bind tabs into a list
private class TabsAdapter extends BaseAdapter {
public TabsAdapter(Context context, HashMap<Integer, Tab> tabs) {
mContext = context;
mTabs = new ArrayList<Tab>();
if (tabs != null) {
Iterator keys = tabs.keySet().iterator();
Tab tab;
while (keys.hasNext()) {
tab = tabs.get(keys.next());
mTabs.add(tab);
}
}
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Tab getItem(int position) {
return mTabs.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = mInflater.inflate(R.layout.tabs_row, null);
Tab tab = mTabs.get(position);
LinearLayout info = (LinearLayout) convertView.findViewById(R.id.info);
info.setTag("" + tab.getId());
info.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
hideTabs();
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + v.getTag()));
}
});
ImageView favicon = (ImageView) convertView.findViewById(R.id.favicon);
Drawable faviconImage = tab.getFavicon();
if (faviconImage != null)
favicon.setImageDrawable(faviconImage);
else
favicon.setImageResource(R.drawable.favicon);
TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(tab.getDisplayTitle());
TextView url = (TextView) convertView.findViewById(R.id.url);
url.setText(tab.getURL());
ImageButton close = (ImageButton) convertView.findViewById(R.id.close);
if (mTabs.size() > 1) {
close.setTag("" + tab.getId());
close.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
int tabId = Integer.parseInt("" + v.getTag());
Tabs tabs = Tabs.getInstance();
Tab tab = tabs.getTab(tabId);
if (tabs.isSelectedTab(tab)) {
int index = tabs.getIndexOf(tab);
if (index >= 1)
index--;
else
index = 1;
int id = tabs.getTabAt(index).getId();
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + id));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", "" + v.getTag()));
} else {
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", "" + v.getTag()));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + tabs.getSelectedTabId()));
}
}
});
} else {
close.setVisibility(View.GONE);
}
return convertView;
}
@Override
public void notifyDataSetChanged() {
}
private Context mContext;
private ArrayList<Tab> mTabs;
private LayoutInflater mInflater;
}
}

Просмотреть файл

@ -66,6 +66,7 @@ JAVAFILES = \
SurfaceInfo.java \
Tab.java \
Tabs.java \
TabsTray.java \
$(NULL)
PROCESSEDJAVAFILES = \
@ -194,12 +195,15 @@ MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/addons.png
embedding/android/resources/drawable/reload.png \
embedding/android/resources/drawable/share.png \
embedding/android/resources/drawable/start.png \
embedding/android/resources/drawable/tab_new.png \
embedding/android/resources/drawable/tab_close.png \
embedding/android/resources/drawable/tabs_button.xml \
embedding/android/resources/drawable/tabs_normal.png \
embedding/android/resources/drawable/tabs_pressed.png \
embedding/android/resources/drawable/tabs_off.png \
embedding/android/resources/drawable/tabs_plus.png \
embedding/android/resources/drawable/tabs_menu.png \
embedding/android/resources/drawable/tabs_tray_bg.9.png \
$(NULL)

Просмотреть файл

@ -0,0 +1,215 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Android code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Sriram Ramasubramanian <sriram@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
package org.mozilla.gecko;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import android.app.Activity;
import android.content.Intent;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
public class TabsTray extends Activity implements GeckoApp.OnTabsChangedListener {
private ListView mList;
private TabsAdapter mTabsAdapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.tabs_tray);
mList = (ListView) findViewById(R.id.list);
GeckoApp.registerOnTabsChangedListener(this);
onTabsChanged();
LinearLayout addTab = (LinearLayout) findViewById(R.id.add_tab);
addTab.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
GeckoApp.mAppContext.addTab();
finish();
}
});
LinearLayout container = (LinearLayout) findViewById(R.id.container);
container.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
finish();
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
GeckoApp.unregisterOnTabsChangedListener(this);
}
public void onTabsChanged() {
if (Tabs.getInstance().getCount() == 1)
finish();
HashMap<Integer, Tab> tabs = Tabs.getInstance().getTabs();
mTabsAdapter = new TabsAdapter(this, tabs);
mList.setAdapter(mTabsAdapter);
}
// Adapter to bind tabs into a list
private class TabsAdapter extends BaseAdapter {
public TabsAdapter(Context context, HashMap<Integer, Tab> tabs) {
mContext = context;
mTabs = new ArrayList<Tab>();
if (tabs != null) {
Iterator keys = tabs.keySet().iterator();
Tab tab;
while (keys.hasNext()) {
tab = tabs.get(keys.next());
mTabs.add(tab);
}
}
mInflater = LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return mTabs.size();
}
@Override
public Tab getItem(int position) {
return mTabs.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null)
convertView = mInflater.inflate(R.layout.tabs_row, null);
Tab tab = mTabs.get(position);
RelativeLayout info = (RelativeLayout) convertView.findViewById(R.id.info);
info.setTag("" + tab.getId());
info.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + v.getTag()));
finish();
}
});
ImageView favicon = (ImageView) convertView.findViewById(R.id.favicon);
Drawable faviconImage = tab.getFavicon();
if (faviconImage != null)
favicon.setImageDrawable(faviconImage);
else
favicon.setImageResource(R.drawable.favicon);
TextView title = (TextView) convertView.findViewById(R.id.title);
title.setText(tab.getDisplayTitle());
ImageButton close = (ImageButton) convertView.findViewById(R.id.close);
if (mTabs.size() > 1) {
close.setTag("" + tab.getId());
close.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
int tabId = Integer.parseInt("" + v.getTag());
Tabs tabs = Tabs.getInstance();
Tab tab = tabs.getTab(tabId);
if (tabs.isSelectedTab(tab)) {
int index = tabs.getIndexOf(tab);
if (index >= 1)
index--;
else
index = 1;
int id = tabs.getTabAt(index).getId();
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + id));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", "" + v.getTag()));
} else {
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", "" + v.getTag()));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", "" + tabs.getSelectedTabId()));
}
}
});
} else {
close.setVisibility(View.GONE);
}
return convertView;
}
@Override
public void notifyDataSetChanged() {
}
@Override
public void notifyDataSetInvalidated() {
}
private Context mContext;
private ArrayList<Tab> mTabs;
private LayoutInflater mInflater;
}
}

Двоичные данные
embedding/android/resources/drawable/tab_close.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.0 KiB

Двоичные данные
embedding/android/resources/drawable/tab_new.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Двоичные данные
embedding/android/resources/drawable/tabs_tray_bg.9.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Просмотреть файл

@ -1,54 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/info"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#fefefe"
android:padding="6dip"
android:orientation="horizontal">
<LinearLayout android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
android:layout_height="48dip"
android:minHeight="48dip"
android:background="@android:drawable/list_selector_background">
<ImageView android:id="@+id/favicon"
android:layout_width="32dip"
android:layout_height="match_parent"
android:layout_marginRight="6dip"
android:layout_gravity="center_vertical"
android:layout_width="24dip"
android:layout_height="24dip"
android:layout_marginLeft="12dip"
android:layout_marginRight="8dip"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:scaleType="fitCenter"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="6dip"
android:layout_marginRight="6dip"
android:orientation="vertical">
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/favicon"
android:layout_marginRight="48dip"
android:layout_centerVertical="true"
android:textAppearance="?android:attr/textAppearanceMediumInverse"
android:textColor="?android:attr/textColorPrimaryInverse"
android:singleLine="true"
android:ellipsize="middle"/>
<TextView android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMediumInverse"
android:textColor="#000"
android:singleLine="true"
android:ellipsize="middle"/>
<TextView android:id="@+id/url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:textColor="?android:attr/textColorSecondaryInverse"
android:layout_marginTop="2dip"
android:includeFontPadding="false"
android:singleLine="true"
android:ellipsize="middle"/>
</LinearLayout>
</LinearLayout>
<ImageButton android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="#00000000"
android:src="@drawable/quit"/>
android:layout_width="48dip"
android:layout_height="48dip"
android:layout_alignParentRight="true"
android:background="@android:drawable/list_selector_background"
android:padding="15dip"
android:scaleType="centerInside"
android:src="@drawable/tab_close"/>
</LinearLayout>
</RelativeLayout>

Просмотреть файл

@ -1,9 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
android:cacheColorHint="#fff"
android:divider="#666666"
android:dividerHeight="1dp"/>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="40dip">
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/tabs_tray_bg"
android:orientation="vertical">
<ListView android:id="@+id/list"
style="@style/AwesomeBarList"/>
<LinearLayout android:id="@+id/add_tab"
android:layout_width="match_parent"
android:layout_height="48dip"
android:background="@android:drawable/list_selector_background"
android:gravity="center">
<ImageView android:layout_width="32dip"
android:layout_height="32dip"
android:layout_marginRight="12dip"
android:src="@drawable/tab_new"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMediumInverse"
android:textColor="?android:attr/textColorPrimaryInverse"
android:text="@string/new_tab"
android:singleLine="true"
android:ellipsize="middle"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>