bug 701380 - Implement a native about:home (start page) r=mfinkle

--HG--
extra : rebase_source : 3b61498dc4a17fbec21531bd831994f81fb85532
This commit is contained in:
Brad Lassey 2011-11-29 01:38:35 -05:00
parent 58040aed0c
commit 2d71f62306
17 changed files with 466 additions and 914 deletions

View File

@ -45,6 +45,7 @@ include $(DEPTH)/config/autoconf.mk
DIRS = profile/extensions
PREF_JS_EXPORTS = $(srcdir)/mobile.js
DIST_FILES = recommended-addons.json
ifndef LIBXUL_SDK
ifneq (Android,$(OS_TARGET))

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,273 @@
/* -*- 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):
* Brad Lassey <blassey@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 android.app.Activity;
import android.os.Bundle;
import android.widget.*;
import android.database.*;
import android.view.*;
import android.graphics.*;
import android.content.*;
import android.provider.Browser;
import android.util.Log;
import java.util.Date;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.util.Log;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.GridView;
import android.widget.SimpleCursorAdapter;
import java.io.*;
import java.util.zip.*;
import android.os.Handler;
import org.json.*;
import android.util.AttributeSet;
public class AboutHomeContent extends LinearLayout {
public interface UriLoadCallback {
public void callback(String uriSpec);
}
UriLoadCallback mUriLoadCallback = null;
void setUriLoadCallback(UriLoadCallback uriLoadCallback) {
mUriLoadCallback = uriLoadCallback;
}
public AboutHomeContent(Context context, AttributeSet attrs) {
super(context, attrs);
}
private static final String LOGTAG = "GeckoAboutHome";
private static final String TITLE_KEY = "title";
private static final String kAbouthomeWhereClause = Browser.BookmarkColumns.BOOKMARK + " = 1";
private static final int kTileWidth = 122;
private Cursor mCursor;
private Uri mUri;
private String mTitle;
protected ListAdapter mGridAdapter;
protected ArrayAdapter<String> mAddonAdapter;
protected GridView mGrid;
protected ListView mAddonList;
private Handler mHandler = new Handler();
public void onActivityContentChanged(Activity activity) {
mGrid = (GridView)findViewById(R.id.grid);
if (mGrid == null)
return;
mGrid.setOnItemClickListener(mGridOnClickListener);
// we want to do this: mGrid.setNumColumns(GridView.AUTO_FIT); but it doesn't work
Display display = ((WindowManager) activity.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
mGrid.setNumColumns((int) Math.floor(width / kTileWidth));
mAddonList = (ListView)findViewById(R.id.recommended_addon_list);
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() {
mGrid.setAdapter(mGridAdapter);
}
});
}
private AdapterView.OnItemClickListener mGridOnClickListener = new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id)
{
onGridItemClick((GridView)parent, v, position, id);
}
};
void init(final Activity activity) {
LayoutInflater inflater =
(LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.abouthome_content, this);
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
mCursor = activity.managedQuery(Browser.BOOKMARKS_URI,
null, kAbouthomeWhereClause, null, null);
activity.startManagingCursor(mCursor);
onActivityContentChanged(activity);
mAddonAdapter = new ArrayAdapter<String>(activity, R.layout.abouthome_addon_list_item);
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() {
final SimpleCursorAdapter gridAdapter =
new SimpleCursorAdapter(activity, R.layout.abouthome_grid_box, mCursor,
new String[] {Browser.BookmarkColumns.TITLE,
Browser.BookmarkColumns.FAVICON,
Browser.BookmarkColumns.URL,
"thumbnail"},
new int[] {R.id.bookmark_title, R.id.bookmark_icon, R.id.bookmark_url, R.id.screenshot});
mGrid.setAdapter(gridAdapter);
gridAdapter.setViewBinder(new AwesomeCursorViewBinder());
mAddonList.setAdapter(mAddonAdapter);
}
});
readRecommendedAddons(activity);
}
});
}
void readRecommendedAddons(final Activity activity) {
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
try {
File applicationPackage = new File(activity.getApplication().getPackageResourcePath());
byte[] buf = new byte[32768];
ZipFile zip = new ZipFile(applicationPackage);
ZipEntry fileEntry = zip.getEntry("recommended-addons.json");
InputStream fileStream;
fileStream = zip.getInputStream(fileEntry);
StringBuffer jsonString = new StringBuffer();
int read = 0;
while ((read = fileStream.read(buf, 0, 32768)) != -1) {
jsonString.append(new String(buf, 0, read));
}
final JSONArray array = new JSONObject(jsonString.toString()).getJSONArray("addons");
GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
public void run() {
try {
for (int i = 0; i < array.length(); i++) {
JSONObject jsonobj = array.getJSONObject(i);
mAddonAdapter.add(jsonobj.getString("name"));
Log.i("GeckoAddons", "addon #" + i +": " + jsonobj.getString("name"));
}
} catch (Exception e) {
Log.i("GeckoAddons", "error reading json file", e);
}
}
});
} catch (Exception e) {
Log.i("GeckoAddons", "error reading json file", e);
}
}
});
}
protected void onGridItemClick(GridView l, View v, int position, long id) {
mCursor.moveToPosition(position);
String spec = mCursor.getString(mCursor.getColumnIndex(Browser.BookmarkColumns.URL));
Log.i(LOGTAG, "clicked: " + spec);
if (mUriLoadCallback != null)
mUriLoadCallback.callback(spec);
}
}
class AwesomeCursorViewBinder implements SimpleCursorAdapter.ViewBinder {
private boolean updateImage(View view, Cursor cursor, int faviconIndex) {
byte[] b = cursor.getBlob(faviconIndex);
ImageView favicon = (ImageView) view;
if (b == null) {
favicon.setImageResource(R.drawable.favicon);
} else {
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
favicon.setImageBitmap(bitmap);
}
return true;
}
private boolean updateTitle(View view, Cursor cursor, int titleIndex) {
String title = cursor.getString(titleIndex);
TextView titleView = (TextView)view;
// Use the URL instead of an empty title for consistency with the normal URL
// bar view - this is the equivalent of getDisplayTitle() in Tab.java
if (title == null || title.length() == 0) {
int urlIndex = cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.URL);
title = cursor.getString(urlIndex);
}
titleView.setText(title);
return true;
}
private boolean updateUrl(View view, Cursor cursor, int urlIndex) {
String title = cursor.getString(urlIndex);
TextView urlView = (TextView)view;
if (title != null) {
int index;
if ((index = title.indexOf("://")) != -1)
title = title.substring(index + 3);
if (title.startsWith("www."))
title = title.substring(4);
if (title.endsWith("/"))
title = title.substring(0, title.length() -1);
}
urlView.setText(title);
return true;
}
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int faviconIndex = cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.FAVICON);
if (columnIndex == faviconIndex) {
return updateImage(view, cursor, faviconIndex);
}
int titleIndex = cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.TITLE);
if (columnIndex == titleIndex) {
return updateTitle(view, cursor, titleIndex);
}
int urlIndex = cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.URL);
if (columnIndex == urlIndex) {
return updateUrl(view, cursor, urlIndex);
}
int thumbIndex = cursor.getColumnIndexOrThrow("thumbnail");
if (columnIndex == thumbIndex) {
return updateImage(view, cursor, thumbIndex);
}
// Other columns are handled automatically
return false;
}
}

View File

@ -122,6 +122,7 @@ abstract public class GeckoApp
private static LayerController mLayerController;
private static PlaceholderLayerClient mPlaceholderLayerClient;
private static GeckoSoftwareLayerClient mSoftwareLayerClient;
AboutHomeContent mAboutHomeContent;
boolean mUserDefinedProfile = false;
private Vector<View> mPluginViews = new Vector<View>();
@ -888,6 +889,8 @@ abstract public class GeckoApp
mBrowserToolbar.setVisibility(View.VISIBLE);
}
});
} else if (event.equals("AboutHome:Show")) {
showAboutHome();
} else if (event.equals("AgentMode:Changed")) {
Tab.AgentMode agentMode = message.getString("agentMode").equals("mobile") ? Tab.AgentMode.MOBILE : Tab.AgentMode.DESKTOP;
int tabId = message.getInt("tabId");
@ -904,6 +907,40 @@ abstract public class GeckoApp
}
}
public void showAboutHome() {
Runnable r = new AboutHomeRunnable(true);
mMainHandler.postAtFrontOfQueue(r);
}
public void hideAboutHome() {
Runnable r = new AboutHomeRunnable(false);
mMainHandler.postAtFrontOfQueue(r);
}
public class AboutHomeRunnable implements Runnable {
boolean mShow;
AboutHomeRunnable(boolean show) {
mShow = show;
}
public void run() {
if (mAboutHomeContent == null) {
mAboutHomeContent = new AboutHomeContent(GeckoApp.mAppContext, null);
mAboutHomeContent.init(GeckoApp.mAppContext);
mAboutHomeContent.setUriLoadCallback(new AboutHomeContent.UriLoadCallback() {
public void callback(String url) {
mBrowserToolbar.setProgressVisibility(true);
loadUrl(url, AwesomeBar.Type.EDIT);
}
});
mGeckoLayout.addView(mAboutHomeContent);
}
if (mLayerController != null && mLayerController.getView() != null)
mLayerController.getView().setVisibility(mShow ? View.GONE : View.VISIBLE);
mAboutHomeContent.setVisibility(mShow ? View.VISIBLE : View.GONE);
}
}
void updateAgentModeMenuItem(final Tab tab, final Tab.AgentMode agentMode) {
if (sMenu == null)
return;
@ -985,10 +1022,16 @@ abstract public class GeckoApp
}
void handleSelectTab(int tabId) {
Tab selTab = Tabs.getInstance().getSelectedTab();
final Tab tab = Tabs.getInstance().selectTab(tabId);
if (tab == null)
return;
if (selTab.getURL().equals("about:home") && !tab.getURL().equals("about:home"))
hideAboutHome();
else if (tab.getURL().equals("about:home"))
showAboutHome();
updateAgentModeMenuItem(tab, tab.getAgentMode());
mMainHandler.post(new Runnable() {
@ -1328,6 +1371,7 @@ abstract public class GeckoApp
GeckoAppShell.registerGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("AgentMode:Changed", GeckoApp.mAppContext);
GeckoAppShell.registerGeckoEventListener("AboutHome:Show", GeckoApp.mAppContext);
mConnectivityFilter = new IntentFilter();
mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
@ -1543,6 +1587,16 @@ abstract public class GeckoApp
unregisterReceiver(mBatteryReceiver);
}
@Override
public void onContentChanged() {
super.onContentChanged();
if (mAboutHomeContent == null)
return;
mAboutHomeContent = (AboutHomeContent) findViewById(R.id.abouthome_content);
mAboutHomeContent.onActivityContentChanged(this);
}
@Override
public void onConfigurationChanged(android.content.res.Configuration newConfig)
{
@ -1860,6 +1914,7 @@ abstract public class GeckoApp
}
public void loadUrl(String url, AwesomeBar.Type type) {
hideAboutHome();
mBrowserToolbar.setTitle(url);
Log.d(LOGTAG, type.name());
if (type == AwesomeBar.Type.ADD) {

View File

@ -62,6 +62,16 @@ public class GeckoThread extends Thread {
public void run() {
final GeckoApp app = GeckoApp.mAppContext;
Intent intent = mIntent;
String uri = intent.getDataString();
String title = uri;
if (!app.mUserDefinedProfile && (uri == null || uri.length() == 0)) {
SharedPreferences prefs = app.getSharedPreferences("GeckoApp", app.MODE_PRIVATE);
uri = prefs.getString("last-uri", "");
title = prefs.getString("last-title", uri);
}
if (uri == null || uri.equals("") || uri.equals("about:home")) {
app.showAboutHome();
}
File cacheFile = GeckoAppShell.getCacheDir();
File libxulFile = new File(cacheFile, "libxul.so");
@ -93,23 +103,14 @@ public class GeckoThread extends Thread {
Log.w(LOGTAG, "zerdatime " + new Date().getTime() + " - runGecko");
// and then fire us up
try {
String uri = intent.getDataString();
String title = uri;
if (!app.mUserDefinedProfile &&
(uri == null || uri.length() == 0)) {
SharedPreferences prefs = app.getSharedPreferences("GeckoApp", app.MODE_PRIVATE);
uri = prefs.getString("last-uri", "");
title = prefs.getString("last-title", uri);
final String activityTitle = title;
app.mMainHandler.post(new Runnable() {
public void run() {
app.mBrowserToolbar.setTitle(activityTitle);
}
final String awesomeTitle = title;
app.mMainHandler.post(new Runnable() {
public void run() {
app.mBrowserToolbar.setTitle(awesomeTitle);
}
});
});
try {
Log.w(LOGTAG, "RunGecko - URI = " + uri);
GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(),

View File

@ -48,6 +48,7 @@ DIRS = locales
DIST_FILES = package-name.txt
JAVAFILES = \
AboutHomeContent.java \
AlertNotification.java \
AwesomeBar.java \
AwesomeBarTabs.java \
@ -183,6 +184,8 @@ RES_LAYOUT = \
res/layout/tabs_tray.xml \
res/layout/list_item_header.xml \
res/layout/select_dialog_list.xml \
res/layout/abouthome_content.xml \
res/layout/abouthome_grid_box.xml \
$(NULL)
RES_LAYOUT_V11 = \
@ -378,6 +381,8 @@ MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/address_bar_bg.x
mobile/android/base/resources/drawable/tabs_tray_bg.9.png \
mobile/android/base/resources/drawable/checkerboard.png \
mobile/android/base/resources/drawable/shadow.png \
mobile/android/base/resources/drawable/rounded_grey_border.xml \
mobile/android/base/resources/drawable/rounded_grey_box.xml \
$(NULL)

View File

@ -0,0 +1,10 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFFFF" />
<stroke android:width="2dip" android:color="#ffD0D0D0"/>
<padding
android:left="7dp"
android:top="7dp"
android:right="7dp"
android:bottom="7dp" />
<corners android:radius="4dp" />
</shape>

View File

@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFD0D0D0" />
<stroke android:width="2dip" android:color="#FFD0D0D0"/>
<padding android:left="7dp" android:top="7dp"
android:right="7dp" android:bottom="7dp" />
<corners android:radius="4dp" />
</shape>

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/abouthome_content"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:isScrollContainer="true" >
<ImageView
android:layout_width="fill_parent"
android:layout_height="100dip"
android:layout_gravity="center_horizontal"
android:src="@drawable/icon" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:textColor="#FF202020"
android:textStyle="bold"
android:text="Recommended Addons"
android:isScrollContainer="false" />
<ListView
android:id="@+id/recommended_addon_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:divider="@android:color/transparent"
android:isScrollContainer="false"
android:dividerHeight="8dip" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dip"
android:textColor="#FF202020"
android:textStyle="bold"
android:isScrollContainer="false"
android:text="Favorite Sites" />
<GridView
android:id="@+id/grid"
android:layout_width="fill_parent"
android:isScrollContainer="false"
android:layout_height="wrap_content"/>
</LinearLayout>
</merge>

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dip"
android:layout_height="150dip"
android:padding="6dip"
android:gravity="center_horizontal">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/rounded_grey_box" >
<ImageView
android:id="@+id/screenshot"
android:layout_width="fill_parent"
android:layout_height="80dip" />
<LinearLayout
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="fill_parent" >
<ImageView
android:id="@+id/bookmark_icon"
android:layout_width="16dip"
android:layout_height="16dip"
android:layout_marginRight="6dip" />
<TextView
android:id="@+id/bookmark_title"
android:singleLine="true"
android:textColor="#FF202020"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#FF202020"
android:id="@+id/bookmark_url"
android:singleLine="true"
android:ellipsize="marquee" />
</LinearLayout>
</LinearLayout>

View File

@ -4,12 +4,8 @@
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd" >
%globalDTD;
<!ENTITY % preferenceDTD SYSTEM "chrome://browser/locale/preferences.dtd" >
%preferenceDTD;
<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutHome.dtd" >
%aboutDTD;
]>
<!-- ***** BEGIN LICENSE BLOCK *****
@ -53,458 +49,23 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&homepage.default;</title>
<meta name="viewport" content="width=480; initial-scale=.6667; user-scalable=0" />
<link rel="icon" type="image/png" href="chrome://branding/content/favicon32.png" />
<link rel="stylesheet" href="chrome://browser/skin/aboutHome.css" type="text/css"/>
<link rel="icon" type="image/png"
href="chrome://branding/content/favicon32.png" />
</head>
<body dir="&locale.dir;" onload="init();" onunload="uninit();">
<div id="wrapper">
<div id="header"><div id="logo"><img src="chrome://branding/content/logo.png"/></div>&aboutHome.header;</div>
<div id="content">
<div id="recentTabs" class="section-box">
<h1>&aboutHome.recentTabs;</h1>
<div id="loadingTabs" class="loading">
<img src="chrome://browser/skin/images/throbber.png"/>
</div>
</div>
<div id="remoteTabs" class="section-row" onclick="openRemoteTabs();" role="button">
<div>
<img class="favicon" src="chrome://browser/skin/images/remotetabs-32.png"/>
<div>&aboutHome.remoteTabs;</div>
</div>
</div>
<div id="newAddons" class="section-row" onclick="goToAddons(' ');" role="button">
<div>
<img class="favicon" src="chrome://browser/skin/images/addons-32.png"/>
<div>&aboutHome.recommendedAddons2;</div>
</div>
</div>
<div id="newAddonsList" class="section-box" style="display: none;">
</div>
<div id="locale" class="section-row" style="display: none;" onclick="openLocalePicker();" role="button">
<div>&aboutHome.getLocale;</div>
</div>
<div id="footer-wrapper">
<span id="feedback" style="width: &aboutHome.footerWidth;" class="section-row" pref="app.feedbackURL" onclick="openLink(this);" role="button">&aboutHome.giveFeedback;</span
><span id="support" style="width: &aboutHome.footerWidth;" class="section-row" pref="app.support.baseURL" onclick="openLink(this);" role="button">&aboutHome.getHelp;</span>
</div>
<div id="sync-setup">
<a id="syncSetupSync" href="#" onclick="openSetupSyncWizard();">&aboutHome.setupSync;</a>
<a id="syncPairDevice" href="#" onclick="openPairDeviceWizard();">&aboutHome.syncPairDevice;</a>
</div>
</div>
</div>
<!-- l10n hack -->
<div style="display: none">
<span id="text-openalltabs">&aboutHome.openAllTabs;</span>
<span id="text-notabs">&aboutHome.noTabs;</span>
<span id="text-noaddons">&aboutHome.noAddons;</span>
</div>
<!-- Lightbox -->
<div id="lightbox">
<div id="lightbox-background"></div>
<div id="lightbox-wrapper">
<div id="lightbox-container">
<img id="lightbox-logo" src="chrome://browser/skin/images/aurora-lightbox-logo.png"/>
<div id="lightbox-message">
<p class="title">&aboutHome.getInBeforeBeta;</p>
<p class="message">&aboutHome.tryAurora;</p>
</div>
<a id="lightbox-link">
<div id="lightbox-button">
<p class="title">&aboutHome.downloadAurora;</p>
<p class="sub-title">&aboutHome.forAndroid;</p>
</div>
</a>
<img id="lightbox-close" src="chrome://browser/skin/images/aurora-lightbox-close.png" alt="Aurora Close"/>
</div>
</div>
</div>
<body onload="init();" >
<script type="application/javascript;version=1.8"><![CDATA[
let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
let gChromeWin = null;
Cu.import("resource://gre/modules/NetUtil.jsm");
function openLink(aElement) {
try {
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
let url = formatter.formatURLPref(aElement.getAttribute("pref"));
openTabs([url]);
} catch (ex) {}
}
function getChromeWin() {
if (!gChromeWin) {
gChromeWin = window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.QueryInterface(Ci.nsIDOMChromeWindow);
}
return gChromeWin;
}
function openLocalePicker() {
let win = getChromeWin();
win.BrowserUI.showPanel("prefs-container");
win.document.getElementById("prefs-uilanguage-button").click();
}
let Cc = Components.classes;
let Ci = Components.interfaces;
function init() {
initTabs();
initAddons();
initSetupSync();
let prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).QueryInterface(Ci.nsIPrefBranch2);
if (prefs.getBoolPref("browser.firstrun.show.uidiscovery")) {
startDiscovery();
prefs.setBoolPref("browser.firstrun.show.uidiscovery", false);
document.getElementById("locale").style.display = "block";
} else {
endDiscovery();
}
initLightbox();
}
function uninit() {
uninitAddons();
uninitSetupSync();
}
function _readFile(aFile, aCallback) {
let channel = NetUtil.newChannel(aFile);
channel.contentType = "application/json";
NetUtil.asyncFetch(channel, function(aStream, aResult) {
if (!Components.isSuccessCode(aResult)) {
Cu.reportError("AboutHome: Could not read from " + aFile.leafName);
aCallback(null);
return;
}
let fileSize = aStream.available();
let cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
cvstream.init(aStream, "UTF-8", fileSize, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
let content = {};
cvstream.readString(fileSize, content);
cvstream.close();
aStream.close();
aCallback(content.value.replace(/\r\n?/g, "\n"));
});
}
function openTabs(aURLs) {
let BrowserUI = getChromeWin().BrowserUI;
let owner = getChromeWin().Browser.selectedTab;
for (let i=0; i < aURLs.length; i++) {
BrowserUI.newTab(aURLs[i], owner);
}
}
function initTabs() {
function noRecentTabs() {
let placeHolder = document.getElementById("loadingTabs");
placeHolder.innerHTML = "<div class='no-items'>" + document.getElementById("text-notabs").textContent + "</div>";
}
let dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
let session = dirService.get("ProfD", Ci.nsILocalFile);
session.append("sessionstore.bak");
if (!session.exists()) {
noRecentTabs();
return;
}
_readFile(session, function(aContent) {
let data = JSON.parse(aContent);
if (!data || data.windows.length == 0) {
noRecentTabs();
return;
}
let chromeWin = getChromeWin();
let allPageURLs = [];
let list = document.getElementById("recentTabs");
let tabs = data.windows[0].tabs;
for (let i=0; i<tabs.length; i++) {
let tabData = tabs[i];
let tabEntry = tabData.entries[tabData.index - 1];
let url = tabEntry.url;
if (url.indexOf("about:") == 0)
continue;
let title = tabEntry.title;
if (!title)
continue;
let uri = chromeWin.Util.makeURI(url);
let favicon = chromeWin.gFaviconService.getFaviconImageForPage(uri).spec;
let outer = document.createElement("a");
outer.setAttribute("role", "button");
outer.setAttribute("href", url);
allPageURLs.push(url);
let img = document.createElement("img");
img.className = "favicon";
img.setAttribute("src", favicon);
outer.appendChild(img);
let inner = document.createElement("div");
inner.className = "inner";
let titlePart = document.createElement("div");
titlePart.textContent = title;
titlePart.className = "title";
inner.appendChild(titlePart);
outer.appendChild(inner);
list.appendChild(outer);
}
if (allPageURLs.length > 0) {
let loading = document.getElementById("loadingTabs");
loading.parentNode.removeChild(loading);
if (allPageURLs.length > 1) {
let outer = document.createElement("div");
outer.className = "openall";
outer.textContent = document.getElementById("text-openalltabs").textContent;
outer.setAttribute("role", "button");
outer.addEventListener("click", function() {
openTabs(allPageURLs);
}, false);
list.appendChild(outer);
let bridge = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
let obj = { gecko: {
type: "AboutHome:Show"
}
} else {
noRecentTabs();
}
});
};
bridge.handleGeckoMessage(JSON.stringify(obj));
}
function openRemoteTabs() {
getChromeWin().CommandUpdater.doCommand("cmd_remoteTabs");
}
function goToAddons(aSearchString) {
let chromeWin = getChromeWin();
let BrowserUI = chromeWin.BrowserUI;
BrowserUI.showPanel("addons-container");
if (aSearchString) {
// ExtensionsView initialization is delayed, so need to delay triggering
// the search as well
setTimeout(function () {
// do the search
chromeWin.ExtensionsView.doSearch(aSearchString);
}, 0);
}
}
var RecommendedAddons = {
_getFile: function() {
let dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
let file = dirService.get("ProfD", Ci.nsILocalFile);
file.append("recommended-addons.json");
return file;
},
_loadAddons: function(aAddons) {
if (aAddons.length == 0)
return;
// Clear all content before filling the addons
let list = document.getElementById("newAddonsList");
while (list.firstElementChild)
list.removeChild(list.firstElementChild);
for (let i=0; i<aAddons.length; i++) {
let addon = aAddons[i];
let outer = document.createElement("div");
outer.setAttribute("role", "button");
outer.setAttribute("addonID", addon.id);
let addonName = aAddons[i].name;
outer.addEventListener("click", function() {
goToAddons(addonName);
}, false)
let img = document.createElement("img");
img.className = "favicon";
img.setAttribute("src", addon.iconURL);
outer.appendChild(img);
let inner = document.createElement("div");
inner.className = "inner";
let titlePart = document.createElement("span");
titlePart.textContent = addon.name;
titlePart.className = "title";
inner.appendChild(titlePart);
let versionPart = document.createElement("span");
versionPart.textContent = addon.version;
versionPart.className = "version";
inner.appendChild(versionPart);
outer.appendChild(inner);
list.appendChild(outer);
}
list.style.display = "block";
document.getElementById("newAddons").setAttribute("showlist", "true");
},
loadFromCache: function loadFromCache() {
let self = this;
let file = this._getFile();
if (file.exists()) {
_readFile(file, function(aContent) {
let json = JSON.parse(aContent);
if (!json || json.addons.length == 0) {
self._loadAddons([]);
return;
}
self._loadAddons(json.addons);
});
}
}
}
function updateAddons() {
RecommendedAddons.loadFromCache();
}
function initAddons() {
getChromeWin().Services.obs.addObserver(updateAddons, "recommended-addons-cache-updated", false);
updateAddons();
}
function uninitAddons() {
getChromeWin().Services.obs.removeObserver(updateAddons, "recommended-addons-cache-updated");
}
function startDiscovery() {
let win = getChromeWin();
// If a sidebar is completely open, just bail
let [leftWidth, rightWidth] = win.Browser.computeSidebarVisibility();
if (leftWidth == 1 || rightWidth == 1 || win.Util.isTablet()) {
endDiscovery();
return;
}
let doc = win.document;
let broadcaster = doc.getElementById("bcast_uidiscovery");
doc.addEventListener("animationend", endDiscovery, false);
doc.addEventListener("PanBegin", endDiscovery, false);
broadcaster.setAttribute("mode", "discovery");
}
function endDiscovery() {
let doc = getChromeWin().document;
let broadcaster = doc.getElementById("bcast_uidiscovery");
broadcaster.removeAttribute("mode");
doc.removeEventListener("animationend", endDiscovery, false);
doc.removeEventListener("PanBegin", endDiscovery, false);
}
function initSetupSync() {
let services = getChromeWin().Services;
if (services.prefs.prefHasUserValue("services.sync.username")) {
syncConnected();
} else {
syncDisconnected();
}
services.obs.addObserver(syncConnected, "weave:service:setup-complete", false);
services.obs.addObserver(syncDisconnected, "weave:service:start-over", false);
}
function uninitSetupSync() {
let services = getChromeWin().Services;
services.obs.removeObserver(syncConnected, "weave:service:setup-complete");
services.obs.removeObserver(syncDisconnected, "weave:service:start-over");
}
function syncConnected() {
document.getElementById("syncSetupSync").style.display = "none";
document.getElementById("syncPairDevice").style.display = "inline";
}
function syncDisconnected() {
document.getElementById("syncSetupSync").style.display = "inline";
document.getElementById("syncPairDevice").style.display = "none";
}
function openSetupSyncWizard() {
let chromeWin = getChromeWin();
chromeWin.WeaveGlue.open();
}
function openPairDeviceWizard() {
let chromeWin = getChromeWin();
chromeWin.SyncPairDevice.open();
}
function initLightbox() {
let prefs = getChromeWin().Services.prefs;
let channel = prefs.getCharPref("app.update.channel");
let startupCount = 0;
try {
startupCount = prefs.getIntPref("app.promo.aurora");
if(startupCount != 6)
prefs.setIntPref("app.promo.aurora", ++startupCount);
} catch(e) {
prefs.setIntPref("app.promo.aurora", 1);
}
// This should appear only on "beta" channel on the fifth launch.
if ((channel == "beta") && (startupCount == 5)) {
let close = document.getElementById("lightbox-close");
close.addEventListener("click", function() {
document.getElementById("lightbox").style.display = "none";
}, false);
let link = document.getElementById("lightbox-link");
link.addEventListener("click", function(event) {
event.preventDefault();
document.getElementById("lightbox").style.display = "none";
let BrowserUI = getChromeWin().BrowserUI;
let owner = getChromeWin().Browser.selectedTab;
let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
let linkUrl = formatter.formatURLPref("app.channelURL");
BrowserUI.newTab(linkUrl, owner);
}, false);
// Giving a chance for any page transitions to happen, if any, before displaying the lightbox
setTimeout(function() {
document.getElementById("lightbox").style.display = "block";
}, 300);
}
}
]]></script>
]]></script>
</body>
</html>

View File

@ -88,6 +88,7 @@
@BINPATH@/res/drawable
@BINPATH@/res/drawable-hdpi
@BINPATH@/res/layout
@BINPATH@/recommended-addons.json
#endif
#ifdef MOZ_PLATFORM_MAEMO

View File

@ -1,31 +0,0 @@
<!ENTITY aboutHome.header "<span>&brandShortName; </span><span id='header-suffix'>Start</span>">
<!ENTITY aboutHome.recentTabs "Your tabs from last time">
<!ENTITY aboutHome.remoteTabs "Tabs from your other computers">
<!ENTITY aboutHome.recommendedAddons2 "Add-ons for your &brandShortName;">
<!ENTITY aboutHome.giveFeedback "Give Feedback">
<!ENTITY aboutHome.getHelp "Get Help">
<!ENTITY aboutHome.footerWidth "10em">
<!ENTITY aboutHome.openAllTabs "Open all in tabs">
<!ENTITY aboutHome.noTabs "No tabs from last time">
<!ENTITY aboutHome.noAddons "No recommended add-ons">
<!ENTITY aboutHome.getLocale "Change Language">
<!ENTITY aboutHome.getInBeforeBeta "Get in before Beta.">
<!ENTITY aboutHome.tryAurora "Try Firefox Aurora and help shape the future of Firefox.">
<!-- LOCALIZATION NOTE:
(aboutHome.downloadAurora): First line of a multi-line button. Treat as a title.
-->
<!ENTITY aboutHome.downloadAurora "Download Aurora">
<!-- LOCALIZATION NOTE:
(aboutHome.forAndroid): Second line of a multi-line button. Treat as a subtitle.
-->
<!ENTITY aboutHome.forAndroid "for Android">
<!-- LOCALIZATION NOTE:
(aboutHome.setupSync): This string should match the desktop
equivalent, in particular concerning syncBrand.fullName.label.
-->
<!ENTITY aboutHome.setupSync "Set Up Sync">
<!-- LOCALIZATION NOTE:
(aboutHome.syncPairDevice): This string should match the desktop
equivalent, in particular concerning syncBrand.fullName.label.
-->
<!ENTITY aboutHome.syncPairDevice "Pair a Device">

View File

@ -6,7 +6,6 @@
locale/@AB_CD@/browser/aboutAddons.dtd (%chrome/aboutAddons.dtd)
locale/@AB_CD@/browser/aboutAddons.properties (%chrome/aboutAddons.properties)
locale/@AB_CD@/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd)
locale/@AB_CD@/browser/aboutHome.dtd (%chrome/aboutHome.dtd)
locale/@AB_CD@/browser/browser.dtd (%chrome/browser.dtd)
locale/@AB_CD@/browser/browser.properties (%chrome/browser.properties)
locale/@AB_CD@/browser/config.dtd (%chrome/config.dtd)

View File

@ -1,413 +0,0 @@
/* ***** 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 Mobile Browser.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mfinkle@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 ***** */
html {
background: white;
font-family: "Nokia Sans", Tahoma, sans-serif !important;
font-size: 24px;
margin: 0px 20px;
}
#wrapper {
max-width: 600px;
margin: 0 auto;
}
#logo {
float: right;
margin-top: -24px;
-moz-margin-end: -34px;
/* Trick to ensure this is shown above the opacity transform happening to content*/
-moz-transform: translate(0,0);
}
body[dir="rtl"] #logo {
float: left;
}
#header {
font-weight: bold;
color: white;
background: transparent -moz-linear-gradient(top, rgba(57,89,117,1) 0%, rgba(30,66,98,1) 50%, rgba(24,54,82,1) 90%);
border-radius: 6px;
padding: 12px;
padding-bottom: 14px;
margin-bottom: 12px;
margin-top: 20px;
box-shadow: inset rgba(0, 0, 0, 0.2) 0 -3px 0px, rgba(0, 0, 0, 0.1) 0px 2px 0px;
}
#header-suffix {
color: rgb(112,136,156);
}
.section-box {
background-color: white;
padding: 0;
margin-bottom: 8px;
border-radius: 8px;
border: 1px solid rgba(0,0,0,0.3);
box-shadow:
inset rgba(255, 255, 255, 0.5) 0 1px 0px,
inset rgba(0, 0, 0, 0.1) 0 -3px 0px,
rgba(0, 0, 0, 0.1) 0px 2px 0px;
width: 100%;
overflow: hidden;
}
.section-box > h1 {
font-size: 18px;
font-weight: normal;
color: black;
background: transparent -moz-linear-gradient(top, rgba(235,235,235,0.2) 0%, rgba(235,235,235,1) 90%);
margin: 0;
padding: 4px 12px 8px 12px;
border-radius: 8px 8px 0 0;
}
.section-box > div,
.section-box > a {
border-bottom: 1px solid rgba(0,0,0,0.1);
padding: 8px;
padding-bottom: 12px;
position: relative;
}
.section-box > a {
display: block;
text-decoration: none;
}
.section-box > div:last-child,
.section-box > a:last-child {
border-bottom: 0;
}
/* Make room for the image */
.section-box > div > div,
.section-box > a > div,
.section-row > div > div {
-moz-margin-start: 48px;
}
.section-box .title {
color: black;
}
.section-box .openall {
font-size: 18px;
padding: 12px 8px;
color: black;
}
body[dir="ltr"] .section-box .openall {
text-align: right;
}
.section-box .version {
/* The addon title is not localized, so keep the margin on the left side */
margin-left: 12px;
font-size: 18px;
color: gray;
}
.section-box .inner {
pointer-events: none;
}
.section-box .favicon {
border: none;
top: 8px;
width: 32px;
height: 32px;
position: absolute;
pointer-events: none;
}
body[dir="ltr"] .section-box .favicon {
left: 12px;
}
body[dir="ltr"] .section-box .favicon {
right: 12px;
}
body[dir="ltr"] #newAddons > div:not(.loading) {
background: url("images/arrowright-16.png") 99% center no-repeat;
}
body[dir="rtl"] #newAddons > div:not(.loading) {
background: url("images/arrowleft-16.png") 1% center no-repeat;
}
#remoteTabs[disabled=true] {
pointer-events: none;
color: #aaa;
}
#remoteTabs[disabled=true] > div > .favicon {
opacity: 0.5;
}
.section-row {
color: black;
background: transparent -moz-linear-gradient(top, rgba(235,235,235,0.2) 0%, rgba(235,235,235,1) 90%);
padding: 8px;
padding-bottom: 12px;
border-radius: 8px;
border: 1px solid rgba(0,0,0,0.3);
box-shadow:
inset rgba(255, 255, 255, 0.5) 0 1px 0px,
inset rgba(0, 0, 0, 0.1) 0 -3px 0px,
rgba(0, 0, 0, 0.1) 0px 2px 0px;
margin: 24px auto;
}
.section-row > div {
position: relative;
pointer-events: none;
}
.section-row .favicon {
border: none;
top: 0;
width: 32px;
height: 32px;
position: absolute;
}
#newAddons {
margin-bottom: 0;
}
/* Make sure the button and list match up nicely */
#newAddons[showlist] {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
box-shadow: none;
margin: 0 -2px 0 0;
}
#newAddonsList {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
body[dir="ltr"] {
left: 4px;
}
body[dir="rtl"] {
right: 4px;
}
.loading > img {
display: block;
margin: 0 auto;
}
.no-items {
text-align: center;
-moz-margin-start: 0 !important;
}
#footer-wrapper {
font-size: 18px;
margin-top: 24px;
text-align: center;
}
#feedback {
display: inline-block;
margin: 0;
}
#support {
display: inline-block;
margin: 0;
}
#sync-setup {
font-size: 18px;
margin-top: 24px;
text-align: center;
}
#syncSetupSync,
#syncPairDevice {
text-decoration: underline;
color: blue;
}
/* Lightbox for Aurora */
#lightbox {
position: fixed;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
display: none;
}
#lightbox-background {
background-color: #000;
width: 100%;
height: 100%;
opacity: 0.4;
}
#lightbox-wrapper {
background: transparent;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
position: fixed;
}
#lightbox-container {
max-width: 300px;
margin: 40px auto;
background: #000 url("images/aurora-lightbox-bg.jpg") center top no-repeat;
border-radius: 6px;
padding: 26px 0px 0px;
border: 1px solid #000;
box-shadow: rgba(0, 0, 0, 0.75) 0px 0px 10px 2px;
position: relative;
}
#lightbox-logo {
width: 65%;
max-width: 195px;
display: block;
margin: 0px auto;
}
#lightbox-message {
width: 100%;
padding: 34px 0px 18px;
text-align: center;
}
#lightbox-message > p,
#lightbox-button > p {
font-family: Georgia;
margin: 0px;
padding: 0px;
}
#lightbox-message > p.title ,
#lightbox-button > p.title {
font-size: 24px;
line-height: 24px;
color: #fff;
padding-bottom: 10px;
}
#lightbox-message > p.message,
#lightbox-button > p.sub-title {
font-size: 16px;
line-height: 20px;
}
#lightbox-message > p.message {
color: #c0b5b5;
}
#lightbox-container a,
#lightbox-container a:hover,
#lightbox-container a:visited {
outline: 0px;
border: 0px;
text-decoration: none;
display: block;
}
#lightbox-button {
background: -moz-linear-gradient(center top , #84C63C 0%, #489615 100%) repeat scroll 0 0 transparent;
margin: 10px;
text-align: center;
border-radius: 4px;
padding: 18px 0px;
box-shadow: rgba(0, 0, 0, 0) 0px 0px, inset rgba(0, 0, 0, 0.1) 0px -3px;
}
#lightbox-button > p.title,
#lightbox-button > p.sub-title {
text-shadow: rgba(136, 136, 136, 0.39) 0px 0px 0px -1px;
}
#lightbox-button > p.sub-title {
font-style: italic;
color: #fff;
}
#lightbox-close {
position: absolute;
top: 15px;
right: 15px;
width: 16px;
height: 16px;
}
@media (max-width: 399px) {
#support {
margin-top: 8px;
}
}
@media (min-width: 400px) {
body[dir="ltr"] #feedback {
border-radius: 8px 0 0 8px;
}
body[dir="rtl"] #feedback {
border-radius: 0 8px 8px 0;
}
#support {
-moz-border-start: none;
}
body[dir="ltr"] #support {
border-radius: 0 8px 8px 0;
}
body[dir="rtl"] #support {
border-radius: 8px 0 0 8px;
}
}

View File

@ -9,7 +9,6 @@ chrome.jar:
skin/aboutPage.css (aboutPage.css)
skin/about.css (about.css)
skin/aboutAddons.css (aboutAddons.css)
skin/aboutHome.css (aboutHome.css)
* skin/browser.css (browser.css)
* skin/content.css (content.css)
skin/config.css (config.css)
@ -90,7 +89,6 @@ chrome.jar:
skin/gingerbread/aboutPage.css (aboutPage.css)
skin/gingerbread/about.css (about.css)
skin/gingerbread/aboutAddons.css (aboutAddons.css)
skin/gingerbread/aboutHome.css (aboutHome.css)
* skin/gingerbread/browser.css (gingerbread/browser.css)
* skin/gingerbread/content.css (gingerbread/content.css)
skin/gingerbread/config.css (config.css)
@ -168,7 +166,6 @@ chrome.jar:
skin/honeycomb/aboutPage.css (aboutPage.css)
skin/honeycomb/about.css (about.css)
skin/honeycomb/aboutAddons.css (aboutAddons.css)
skin/honeycomb/aboutHome.css (aboutHome.css)
* skin/honeycomb/browser.css (honeycomb/browser.css)
* skin/honeycomb/content.css (content.css)
skin/honeycomb/config.css (config.css)

View File

@ -298,6 +298,7 @@ DIST_FILES = \
chrome.manifest \
update.locale \
removed-files \
recommended-addons.json \
$(NULL)
NON_DIST_FILES = \