merge birch to mozilla-central

--HG--
rename : embedding/android/GeckoBatteryManager.java => mobile/android/base/GeckoBatteryManager.java
This commit is contained in:
Brad Lassey 2011-12-06 13:55:36 -05:00
Родитель 0042c62308 4ec5158529
Коммит e30512cbdb
803 изменённых файлов: 46179 добавлений и 3139 удалений

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

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

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

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

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

@ -1,336 +0,0 @@
/* -*- Mode: Java; 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) 2010
* 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 ***** */
#filter substitution
package @ANDROID_PACKAGE_NAME@;
import android.app.*;
import android.content.*;
import android.os.*;
import android.util.*;
import android.view.*;
import android.view.View.*;
import android.widget.*;
import org.mozilla.gecko.*;
import java.util.*;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
public class CrashReporter extends Activity
{
static final String kMiniDumpPathKey = "upload_file_minidump";
static final String kPageURLKey = "URL";
static final String kNotesKey = "Notes";
Handler mHandler = null;
ProgressDialog mProgressDialog;
File mPendingMinidumpFile;
File mPendingExtrasFile;
HashMap<String, String> mExtrasStringMap;
boolean moveFile(File inFile, File outFile)
{
Log.i("GeckoCrashReporter", "moving " + inFile + " to " + outFile);
if (inFile.renameTo(outFile))
return true;
try {
outFile.createNewFile();
Log.i("GeckoCrashReporter", "couldn't rename minidump file");
// so copy it instead
FileChannel inChannel = new FileInputStream(inFile).getChannel();
FileChannel outChannel = new FileOutputStream(outFile).getChannel();
long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
if (transferred > 0)
inFile.delete();
} catch (Exception e) {
Log.e("GeckoCrashReporter",
"exception while copying minidump file: ", e);
return false;
}
return true;
}
void doFinish() {
if (mHandler != null) {
mHandler.post(new Runnable(){
public void run() {
finish();
}});
}
}
@Override
public void finish()
{
mProgressDialog.dismiss();
super.finish();
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// mHandler is created here so runnables can be run on the main thread
mHandler = new Handler();
setContentView(R.layout.crash_reporter);
mProgressDialog = new ProgressDialog(CrashReporter.this);
mProgressDialog.setMessage(getString(R.string.sending_crash_report));
final Button restartButton = (Button) findViewById(R.id.restart);
final Button closeButton = (Button) findViewById(R.id.close);
String passedMinidumpPath = getIntent().getStringExtra("minidumpPath");
File passedMinidumpFile = new File(passedMinidumpPath);
File pendingDir =
new File("/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/pending");
pendingDir.mkdirs();
mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
moveFile(passedMinidumpFile, mPendingMinidumpFile);
File extrasFile = new File(passedMinidumpPath.replaceAll(".dmp", ".extra"));
mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
moveFile(extrasFile, mPendingExtrasFile);
mExtrasStringMap = new HashMap<String, String>();
readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
}
void backgroundSendReport()
{
final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
if (!sendReportCheckbox.isChecked()) {
doFinish();
return;
}
mProgressDialog.show();
new Thread(new Runnable() {
public void run() {
sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
}}).start();
}
public void onCloseClick(View v)
{
backgroundSendReport();
}
public void onRestartClick(View v)
{
doRestart();
backgroundSendReport();
}
boolean readStringsFromFile(String filePath, Map stringMap)
{
try {
BufferedReader reader = new BufferedReader(
new FileReader(filePath));
return readStringsFromReader(reader, stringMap);
} catch (Exception e) {
Log.e("GeckoCrashReporter", "exception while reading strings: ", e);
return false;
}
}
boolean readStringsFromReader(BufferedReader reader, Map stringMap)
throws java.io.IOException
{
String line;
while ((line = reader.readLine()) != null) {
int equalsPos = -1;
if ((equalsPos = line.indexOf('=')) != -1) {
String key = line.substring(0, equalsPos);
String val = unescape(line.substring(equalsPos + 1));
stringMap.put(key, val);
}
}
reader.close();
return true;
}
String generateBoundary()
{
// Generate some random numbers to fill out the boundary
int r0 = (int)((double)Integer.MAX_VALUE * Math.random());
int r1 = (int)((double)Integer.MAX_VALUE * Math.random());
return String.format("---------------------------%08X%08X", r0, r1);
}
void sendPart(OutputStream os, String boundary, String name, String data)
{
try {
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" +
name + "\"\r\n\r\n" +
data + "\r\n").getBytes());
} catch (Exception ex) {
Log.e("GeckoCrashReporter", "Exception when sending \"" + name + "\"", ex);
}
}
void sendFile(OutputStream os, String boundary, String name, File file)
throws IOException
{
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; " +
"name=\"" + name + "\"; " +
"filename=\"" + file.getName() + "\"\r\n" +
"Content-Type: application/octet-stream\r\n" +
"\r\n").getBytes());
FileChannel fc =
new FileInputStream(file).getChannel();
fc.transferTo(0, fc.size(), Channels.newChannel(os));
fc.close();
}
void sendReport(File minidumpFile, Map<String, String> extras,
File extrasFile)
{
Log.i("GeckoCrashReport", "sendReport: " + minidumpFile.getPath());
final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
String spec = extras.get("ServerURL");
if (spec == null)
doFinish();
Log.i("GeckoCrashReport", "server url: " + spec);
try {
URL url = new URL(spec);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
String boundary = generateBoundary();
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
OutputStream os = conn.getOutputStream();
Iterator<String> keys = extras.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
if (key.equals(kPageURLKey)) {
if (includeURLCheckbox.isChecked())
sendPart(os, boundary, key, extras.get(key));
} else if (!key.equals("ServerURL") && !key.equals(kNotesKey)) {
sendPart(os, boundary, key, extras.get(key));
}
}
// Add some extra information to notes so its displayed by
// crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
String notes = extras.containsKey(kNotesKey) ? extras.get(kNotesKey) +
"\n" : "";
if (@MOZ_MIN_CPU_VERSION@ < 7)
notes += "nothumb Build\n";
notes += Build.MANUFACTURER + " ";
notes += Build.MODEL + "\n";
notes += Build.FINGERPRINT;
sendPart(os, boundary, kNotesKey, notes);
sendPart(os, boundary, "Min_ARM_Version", "@MOZ_MIN_CPU_VERSION@");
sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
sendPart(os, boundary, "Android_Model", Build.MODEL);
sendPart(os, boundary, "Android_Board", Build.BOARD);
sendPart(os, boundary, "Android_Brand", Build.BRAND);
sendPart(os, boundary, "Android_Device", Build.DEVICE);
sendPart(os, boundary, "Android_Display", Build.DISPLAY);
sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
if (Build.VERSION.SDK_INT >= 8) {
try {
sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
} catch (Exception ex) {
Log.e("GeckoCrashReporter", "Exception while sending SDK version 8 keys", ex);
}
}
sendPart(os, boundary, "Android_Version", Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
sendFile(os, boundary, kMiniDumpPathKey, minidumpFile);
os.write(("\r\n--" + boundary + "--\r\n").getBytes());
os.flush();
os.close();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
HashMap<String, String> responseMap = new HashMap<String, String>();
readStringsFromReader(br, responseMap);
if (conn.getResponseCode() == conn.HTTP_OK) {
File submittedDir = new File(
"/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/submitted");
submittedDir.mkdirs();
minidumpFile.delete();
extrasFile.delete();
String crashid = responseMap.get("CrashID");
File file = new File(submittedDir, crashid + ".txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write("Crash ID: ".getBytes());
fos.write(crashid.getBytes());
fos.close();
}
} catch (IOException e) {
Log.e("GeckoCrashReporter", "exception during send: ", e);
}
doFinish();
}
void doRestart()
{
try {
String action = "android.intent.action.MAIN";
Intent intent = new Intent(action);
intent.setClassName("@ANDROID_PACKAGE_NAME@",
"@ANDROID_PACKAGE_NAME@.App");
Log.i("GeckoCrashReporter", intent.toString());
startActivity(intent);
} catch (Exception e) {
Log.e("GeckoCrashReporter", "error while trying to restart", e);
}
}
public String unescape(String string)
{
return string.replaceAll("\\\\", "\\").replaceAll("\\n", "\n")
.replaceAll("\\t", "\t");
}
}

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

@ -1,881 +0,0 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com>
* Matt Brubeck <mbrubeck@mozilla.com>
* Vivien Nicolas <vnicolas@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.io.*;
import java.util.*;
import java.util.zip.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.concurrent.*;
import java.lang.reflect.*;
import android.os.*;
import android.app.*;
import android.text.*;
import android.view.*;
import android.view.inputmethod.*;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.widget.*;
import android.hardware.*;
import android.util.*;
import android.net.*;
import android.database.*;
import android.provider.*;
import android.content.pm.*;
import android.content.pm.PackageManager.*;
import dalvik.system.*;
abstract public class GeckoApp
extends Activity
{
private static final String LOG_FILE_NAME = "GeckoApp";
public static final String ACTION_ALERT_CLICK = "org.mozilla.gecko.ACTION_ALERT_CLICK";
public static final String ACTION_ALERT_CLEAR = "org.mozilla.gecko.ACTION_ALERT_CLEAR";
public static final String ACTION_WEBAPP = "org.mozilla.gecko.WEBAPP";
public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
public static final String ACTION_BOOKMARK = "org.mozilla.gecko.BOOKMARK";
public static AbsoluteLayout mainLayout;
public static GeckoSurfaceView surfaceView;
public static SurfaceView cameraView;
public static GeckoApp mAppContext;
public static boolean mFullscreen = false;
public static File sGREDir = null;
static Thread mLibLoadThread = null;
public Handler mMainHandler;
private IntentFilter mConnectivityFilter;
private BroadcastReceiver mConnectivityReceiver;
private BroadcastReceiver mBatteryReceiver;
private BroadcastReceiver mSmsReceiver;
enum LaunchState {PreLaunch, Launching, WaitForDebugger,
Launched, GeckoRunning, GeckoExiting};
private static LaunchState sLaunchState = LaunchState.PreLaunch;
private static boolean sTryCatchAttached = false;
static boolean checkLaunchState(LaunchState checkState) {
synchronized(sLaunchState) {
return sLaunchState == checkState;
}
}
static void setLaunchState(LaunchState setState) {
synchronized(sLaunchState) {
sLaunchState = setState;
}
}
// if mLaunchState is equal to checkState this sets mLaunchState to setState
// and return true. Otherwise we return false.
static boolean checkAndSetLaunchState(LaunchState checkState, LaunchState setState) {
synchronized(sLaunchState) {
if (sLaunchState != checkState)
return false;
sLaunchState = setState;
return true;
}
}
void showErrorDialog(String message)
{
new AlertDialog.Builder(this)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(R.string.exit_label,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int id)
{
GeckoApp.this.finish();
System.exit(0);
}
}).show();
}
public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
/**
* A plugin that wish to be loaded in the WebView must provide this permission
* in their AndroidManifest.xml.
*/
public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
private static final String LOGTAG = "PluginManager";
private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/";
private static final String PLUGIN_TYPE = "type";
private static final String TYPE_NATIVE = "native";
public ArrayList<PackageInfo> mPackageInfoCache = new ArrayList<PackageInfo>();
String[] getPluginDirectories() {
ArrayList<String> directories = new ArrayList<String>();
PackageManager pm = this.mAppContext.getPackageManager();
List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(PLUGIN_ACTION),
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
synchronized(mPackageInfoCache) {
// clear the list of existing packageInfo objects
mPackageInfoCache.clear();
for (ResolveInfo info : plugins) {
// retrieve the plugin's service information
ServiceInfo serviceInfo = info.serviceInfo;
if (serviceInfo == null) {
Log.w(LOGTAG, "Ignore bad plugin");
continue;
}
Log.w(LOGTAG, "Loading plugin: " + serviceInfo.packageName);
// retrieve information from the plugin's manifest
PackageInfo pkgInfo;
try {
pkgInfo = pm.getPackageInfo(serviceInfo.packageName,
PackageManager.GET_PERMISSIONS
| PackageManager.GET_SIGNATURES);
} catch (Exception e) {
Log.w(LOGTAG, "Can't find plugin: " + serviceInfo.packageName);
continue;
}
if (pkgInfo == null) {
Log.w(LOGTAG, "Loading plugin: " + serviceInfo.packageName + ". Could not load package information.");
continue;
}
/*
* find the location of the plugin's shared library. The default
* is to assume the app is either a user installed app or an
* updated system app. In both of these cases the library is
* stored in the app's data directory.
*/
String directory = pkgInfo.applicationInfo.dataDir + "/lib";
final int appFlags = pkgInfo.applicationInfo.flags;
final int updatedSystemFlags = ApplicationInfo.FLAG_SYSTEM |
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
// preloaded system app with no user updates
if ((appFlags & updatedSystemFlags) == ApplicationInfo.FLAG_SYSTEM) {
directory = PLUGIN_SYSTEM_LIB + pkgInfo.packageName;
}
// check if the plugin has the required permissions
String permissions[] = pkgInfo.requestedPermissions;
if (permissions == null) {
Log.w(LOGTAG, "Loading plugin: " + serviceInfo.packageName + ". Does not have required permission.");
continue;
}
boolean permissionOk = false;
for (String permit : permissions) {
if (PLUGIN_PERMISSION.equals(permit)) {
permissionOk = true;
break;
}
}
if (!permissionOk) {
Log.w(LOGTAG, "Loading plugin: " + serviceInfo.packageName + ". Does not have required permission (2).");
continue;
}
// check to ensure the plugin is properly signed
Signature signatures[] = pkgInfo.signatures;
if (signatures == null) {
Log.w(LOGTAG, "Loading plugin: " + serviceInfo.packageName + ". Not signed.");
continue;
}
// determine the type of plugin from the manifest
if (serviceInfo.metaData == null) {
Log.e(LOGTAG, "The plugin '" + serviceInfo.name + "' has no type defined");
continue;
}
String pluginType = serviceInfo.metaData.getString(PLUGIN_TYPE);
if (!TYPE_NATIVE.equals(pluginType)) {
Log.e(LOGTAG, "Unrecognized plugin type: " + pluginType);
continue;
}
try {
Class<?> cls = getPluginClass(serviceInfo.packageName, serviceInfo.name);
//TODO implement any requirements of the plugin class here!
boolean classFound = true;
if (!classFound) {
Log.e(LOGTAG, "The plugin's class' " + serviceInfo.name + "' does not extend the appropriate class.");
continue;
}
} catch (NameNotFoundException e) {
Log.e(LOGTAG, "Can't find plugin: " + serviceInfo.packageName);
continue;
} catch (ClassNotFoundException e) {
Log.e(LOGTAG, "Can't find plugin's class: " + serviceInfo.name);
continue;
}
// if all checks have passed then make the plugin available
mPackageInfoCache.add(pkgInfo);
directories.add(directory);
}
}
return directories.toArray(new String[directories.size()]);
}
Class<?> getPluginClass(String packageName, String className)
throws NameNotFoundException, ClassNotFoundException {
Context pluginContext = this.mAppContext.createPackageContext(packageName,
Context.CONTEXT_INCLUDE_CODE |
Context.CONTEXT_IGNORE_SECURITY);
ClassLoader pluginCL = pluginContext.getClassLoader();
return pluginCL.loadClass(className);
}
// Returns true when the intent is going to be handled by gecko launch
boolean launch(Intent intent)
{
if (!checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched))
return false;
if (intent == null)
intent = getIntent();
final Intent i = intent;
new Thread() {
public void run() {
try {
if (mLibLoadThread != null)
mLibLoadThread.join();
} catch (InterruptedException ie) {}
// Show the URL we are about to load, if the intent has one
if (Intent.ACTION_VIEW.equals(i.getAction())) {
surfaceView.mSplashURL = i.getDataString();
}
surfaceView.drawSplashScreen();
// unpack files in the components directory
try {
unpackComponents();
} catch (FileNotFoundException fnfe) {
Log.e(LOG_FILE_NAME, "error unpacking components", fnfe);
Looper.prepare();
showErrorDialog(getString(R.string.error_loading_file));
Looper.loop();
return;
} catch (IOException ie) {
Log.e(LOG_FILE_NAME, "error unpacking components", ie);
String msg = ie.getMessage();
Looper.prepare();
if (msg != null && msg.equalsIgnoreCase("No space left on device"))
showErrorDialog(getString(R.string.no_space_to_start_error));
else
showErrorDialog(getString(R.string.error_loading_file));
Looper.loop();
return;
}
// and then fire us up
try {
String env = i.getStringExtra("env0");
GeckoAppShell.runGecko(getApplication().getPackageResourcePath(),
i.getStringExtra("args"),
i.getDataString());
} catch (Exception e) {
Log.e(LOG_FILE_NAME, "top level exception", e);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
GeckoAppShell.reportJavaCrash(sw.toString());
}
}
}.start();
return true;
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
mAppContext = this;
mMainHandler = new Handler();
if (!sTryCatchAttached) {
sTryCatchAttached = true;
mMainHandler.post(new Runnable() {
public void run() {
try {
Looper.loop();
} catch (Exception e) {
Log.e(LOG_FILE_NAME, "top level exception", e);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
GeckoAppShell.reportJavaCrash(sw.toString());
}
// resetting this is kinda pointless, but oh well
sTryCatchAttached = false;
}
});
}
SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
String localeCode = settings.getString(getPackageName() + ".locale", "");
if (localeCode != null && localeCode.length() > 0)
GeckoAppShell.setSelectedLocale(localeCode);
Log.i(LOG_FILE_NAME, "create");
super.onCreate(savedInstanceState);
if (sGREDir == null)
sGREDir = new File(this.getApplicationInfo().dataDir);
getWindow().setFlags(mFullscreen ?
WindowManager.LayoutParams.FLAG_FULLSCREEN : 0,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
if (cameraView == null) {
cameraView = new SurfaceView(this);
cameraView.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
if (surfaceView == null)
surfaceView = new GeckoSurfaceView(this);
else
mainLayout.removeAllViews();
mainLayout = new AbsoluteLayout(this);
mainLayout.addView(surfaceView,
new AbsoluteLayout.LayoutParams(AbsoluteLayout.LayoutParams.MATCH_PARENT, // level 8
AbsoluteLayout.LayoutParams.MATCH_PARENT,
0,
0));
setContentView(mainLayout,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
mConnectivityFilter = new IntentFilter();
mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mConnectivityReceiver = new GeckoConnectivityReceiver();
IntentFilter batteryFilter = new IntentFilter();
batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
mBatteryReceiver = new GeckoBatteryManager();
registerReceiver(mBatteryReceiver, batteryFilter);
IntentFilter smsFilter = new IntentFilter();
smsFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
mSmsReceiver = new GeckoSmsManager();
registerReceiver(mSmsReceiver, smsFilter);
if (!checkAndSetLaunchState(LaunchState.PreLaunch,
LaunchState.Launching))
return;
checkAndLaunchUpdate();
mLibLoadThread = new Thread(new Runnable() {
public void run() {
// At some point while loading the gecko libs our default locale gets set
// so just save it to locale here and reset it as default after the join
Locale locale = Locale.getDefault();
GeckoAppShell.loadGeckoLibs(
getApplication().getPackageResourcePath());
Locale.setDefault(locale);
Resources res = getBaseContext().getResources();
Configuration config = res.getConfiguration();
config.locale = locale;
res.updateConfiguration(config, res.getDisplayMetrics());
}});
mLibLoadThread.start();
}
public void enableCameraView() {
// Some phones (eg. nexus S) need at least a 8x16 preview size
mainLayout.addView(cameraView, new AbsoluteLayout.LayoutParams(8, 16, 0, 0));
}
public void disableCameraView() {
mainLayout.removeView(cameraView);
}
@Override
protected void onNewIntent(Intent intent) {
if (checkLaunchState(LaunchState.GeckoExiting)) {
// We're exiting and shouldn't try to do anything else just incase
// we're hung for some reason we'll force the process to exit
System.exit(0);
return;
}
final String action = intent.getAction();
if (ACTION_DEBUG.equals(action) &&
checkAndSetLaunchState(LaunchState.Launching, LaunchState.WaitForDebugger)) {
mMainHandler.postDelayed(new Runnable() {
public void run() {
Log.i(LOG_FILE_NAME, "Launching from debug intent after 5s wait");
setLaunchState(LaunchState.Launching);
launch(null);
}
}, 1000 * 5 /* 5 seconds */);
Log.i(LOG_FILE_NAME, "Intent : ACTION_DEBUG - waiting 5s before launching");
return;
}
if (checkLaunchState(LaunchState.WaitForDebugger) || launch(intent))
return;
if (Intent.ACTION_MAIN.equals(action)) {
Log.i(LOG_FILE_NAME, "Intent : ACTION_MAIN");
GeckoAppShell.sendEventToGecko(new GeckoEvent(""));
}
else if (Intent.ACTION_VIEW.equals(action)) {
String uri = intent.getDataString();
GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
Log.i(LOG_FILE_NAME,"onNewIntent: "+uri);
}
else if (ACTION_WEBAPP.equals(action)) {
String uri = intent.getStringExtra("args");
GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
Log.i(LOG_FILE_NAME,"Intent : WEBAPP - " + uri);
}
else if (ACTION_BOOKMARK.equals(action)) {
String args = intent.getStringExtra("args");
GeckoAppShell.sendEventToGecko(new GeckoEvent(args));
Log.i(LOG_FILE_NAME,"Intent : BOOKMARK - " + args);
}
}
@Override
public void onPause()
{
Log.i(LOG_FILE_NAME, "pause");
GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_PAUSING));
// The user is navigating away from this activity, but nothing
// has come to the foreground yet; for Gecko, we may want to
// stop repainting, for example.
// Whatever we do here should be fast, because we're blocking
// the next activity from showing up until we finish.
// onPause will be followed by either onResume or onStop.
super.onPause();
unregisterReceiver(mConnectivityReceiver);
}
@Override
public void onResume()
{
Log.i(LOG_FILE_NAME, "resume");
if (checkLaunchState(LaunchState.GeckoRunning))
GeckoAppShell.onResume();
// After an onPause, the activity is back in the foreground.
// Undo whatever we did in onPause.
super.onResume();
// Just in case. Normally we start in onNewIntent
if (checkLaunchState(LaunchState.PreLaunch) ||
checkLaunchState(LaunchState.Launching))
onNewIntent(getIntent());
registerReceiver(mConnectivityReceiver, mConnectivityFilter);
}
@Override
public void onStop()
{
Log.i(LOG_FILE_NAME, "stop");
// We're about to be stopped, potentially in preparation for
// being destroyed. We're killable after this point -- as I
// understand it, in extreme cases the process can be terminated
// without going through onDestroy.
//
// We might also get an onRestart after this; not sure what
// that would mean for Gecko if we were to kill it here.
// Instead, what we should do here is save prefs, session,
// etc., and generally mark the profile as 'clean', and then
// dirty it again if we get an onResume.
GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_STOPPING));
super.onStop();
GeckoAppShell.putChildInBackground();
}
@Override
public void onRestart()
{
Log.i(LOG_FILE_NAME, "restart");
GeckoAppShell.putChildInForeground();
super.onRestart();
}
@Override
public void onStart()
{
Log.i(LOG_FILE_NAME, "start");
GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_START));
super.onStart();
}
@Override
public void onDestroy()
{
Log.i(LOG_FILE_NAME, "destroy");
// Tell Gecko to shutting down; we'll end up calling System.exit()
// in onXreExit.
if (isFinishing())
GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_SHUTDOWN));
super.onDestroy();
unregisterReceiver(mSmsReceiver);
unregisterReceiver(mBatteryReceiver);
}
@Override
public void onConfigurationChanged(android.content.res.Configuration newConfig)
{
Log.i(LOG_FILE_NAME, "configuration changed");
// nothing, just ignore
super.onConfigurationChanged(newConfig);
}
@Override
public void onLowMemory()
{
Log.e(LOG_FILE_NAME, "low memory");
if (checkLaunchState(LaunchState.GeckoRunning))
GeckoAppShell.onLowMemory();
super.onLowMemory();
}
abstract public String getPackageName();
abstract public String getContentProcessName();
protected void unpackComponents()
throws IOException, FileNotFoundException
{
File applicationPackage = new File(getApplication().getPackageResourcePath());
File componentsDir = new File(sGREDir, "components");
if (componentsDir.lastModified() == applicationPackage.lastModified())
return;
componentsDir.mkdir();
componentsDir.setLastModified(applicationPackage.lastModified());
GeckoAppShell.killAnyZombies();
ZipFile zip = new ZipFile(applicationPackage);
byte[] buf = new byte[32768];
try {
if (unpackFile(zip, buf, null, "removed-files"))
removeFiles();
} catch (Exception ex) {
// This file may not be there, so just log any errors and move on
Log.w(LOG_FILE_NAME, "error removing files", ex);
}
// copy any .xpi file into an extensions/ directory
Enumeration<? extends ZipEntry> zipEntries = zip.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry entry = zipEntries.nextElement();
if (entry.getName().startsWith("extensions/") && entry.getName().endsWith(".xpi")) {
Log.i("GeckoAppJava", "installing extension : " + entry.getName());
unpackFile(zip, buf, entry, entry.getName());
}
}
}
void removeFiles() throws IOException {
BufferedReader reader = new BufferedReader(
new FileReader(new File(sGREDir, "removed-files")));
try {
for (String removedFileName = reader.readLine();
removedFileName != null; removedFileName = reader.readLine()) {
File removedFile = new File(sGREDir, removedFileName);
if (removedFile.exists())
removedFile.delete();
}
} finally {
reader.close();
}
}
private boolean unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry,
String name)
throws IOException, FileNotFoundException
{
if (fileEntry == null)
fileEntry = zip.getEntry(name);
if (fileEntry == null)
throw new FileNotFoundException("Can't find " + name + " in " +
zip.getName());
File outFile = new File(sGREDir, name);
if (outFile.lastModified() == fileEntry.getTime() &&
outFile.length() == fileEntry.getSize())
return false;
File dir = outFile.getParentFile();
if (!dir.exists())
dir.mkdirs();
InputStream fileStream;
fileStream = zip.getInputStream(fileEntry);
OutputStream outStream = new FileOutputStream(outFile);
while (fileStream.available() > 0) {
int read = fileStream.read(buf, 0, buf.length);
outStream.write(buf, 0, read);
}
fileStream.close();
outStream.close();
outFile.setLastModified(fileEntry.getTime());
return true;
}
public void addEnvToIntent(Intent intent) {
Map<String,String> envMap = System.getenv();
Set<Map.Entry<String,String>> envSet = envMap.entrySet();
Iterator<Map.Entry<String,String>> envIter = envSet.iterator();
StringBuffer envstr = new StringBuffer();
int c = 0;
while (envIter.hasNext()) {
Map.Entry<String,String> entry = envIter.next();
intent.putExtra("env" + c, entry.getKey() + "="
+ entry.getValue());
c++;
}
}
public void doRestart() {
try {
String action = "org.mozilla.gecko.restart";
Intent intent = new Intent(action);
intent.setClassName(getPackageName(),
getPackageName() + ".Restarter");
addEnvToIntent(intent);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
Log.i(LOG_FILE_NAME, intent.toString());
GeckoAppShell.killAnyZombies();
startActivity(intent);
} catch (Exception e) {
Log.i(LOG_FILE_NAME, "error doing restart", e);
}
finish();
// Give the restart process time to start before we die
GeckoAppShell.waitForAnotherGeckoProc();
}
public void handleNotification(String action, String alertName, String alertCookie) {
GeckoAppShell.handleNotification(action, alertName, alertCookie);
}
private void checkAndLaunchUpdate() {
Log.i(LOG_FILE_NAME, "Checking for an update");
int statusCode = 8; // UNEXPECTED_ERROR
File baseUpdateDir = null;
if (Build.VERSION.SDK_INT >= 8)
baseUpdateDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
else
baseUpdateDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
File updateDir = new File(new File(baseUpdateDir, "updates"),"0");
File updateFile = new File(updateDir, "update.apk");
File statusFile = new File(updateDir, "update.status");
if (!statusFile.exists() || !readUpdateStatus(statusFile).equals("pending"))
return;
if (!updateFile.exists())
return;
Log.i(LOG_FILE_NAME, "Update is available!");
// Launch APK
File updateFileToRun = new File(updateDir, getPackageName() + "-update.apk");
try {
if (updateFile.renameTo(updateFileToRun)) {
String amCmd = "/system/bin/am start -a android.intent.action.VIEW " +
"-n com.android.packageinstaller/.PackageInstallerActivity -d file://" +
updateFileToRun.getPath();
Log.i(LOG_FILE_NAME, amCmd);
Runtime.getRuntime().exec(amCmd);
statusCode = 0; // OK
} else {
Log.i(LOG_FILE_NAME, "Cannot rename the update file!");
statusCode = 7; // WRITE_ERROR
}
} catch (Exception e) {
Log.i(LOG_FILE_NAME, "error launching installer to update", e);
}
// Update the status file
String status = statusCode == 0 ? "succeeded\n" : "failed: "+ statusCode + "\n";
OutputStream outStream;
try {
byte[] buf = status.getBytes("UTF-8");
outStream = new FileOutputStream(statusFile);
outStream.write(buf, 0, buf.length);
outStream.close();
} catch (Exception e) {
Log.i(LOG_FILE_NAME, "error writing status file", e);
}
if (statusCode == 0)
System.exit(0);
}
private String readUpdateStatus(File statusFile) {
String status = "";
try {
BufferedReader reader = new BufferedReader(new FileReader(statusFile));
status = reader.readLine();
reader.close();
} catch (Exception e) {
Log.i(LOG_FILE_NAME, "error reading update status", e);
}
return status;
}
static final int FILE_PICKER_REQUEST = 1;
private SynchronousQueue<String> mFilePickerResult = new SynchronousQueue();
public String showFilePicker(String aMimeType) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(aMimeType);
GeckoApp.this.
startActivityForResult(
Intent.createChooser(intent, getString(R.string.choose_file)),
FILE_PICKER_REQUEST);
String filePickerResult = "";
try {
while (null == (filePickerResult = mFilePickerResult.poll(1, TimeUnit.MILLISECONDS))) {
Log.i("GeckoApp", "processing events from showFilePicker ");
GeckoAppShell.processNextNativeEvent();
}
} catch (InterruptedException e) {
Log.i(LOG_FILE_NAME, "showing file picker ", e);
}
return filePickerResult;
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
String filePickerResult = "";
if (data != null && resultCode == RESULT_OK) {
try {
ContentResolver cr = getContentResolver();
Uri uri = data.getData();
Cursor cursor = GeckoApp.mAppContext.getContentResolver().query(
uri,
new String[] { OpenableColumns.DISPLAY_NAME },
null,
null,
null);
String name = null;
if (cursor != null) {
try {
if (cursor.moveToNext()) {
name = cursor.getString(0);
}
} finally {
cursor.close();
}
}
String fileName = "tmp_";
String fileExt = null;
int period;
if (name == null || (period = name.lastIndexOf('.')) == -1) {
String mimeType = cr.getType(uri);
fileExt = "." + GeckoAppShell.getExtensionFromMimeType(mimeType);
} else {
fileExt = name.substring(period);
fileName = name.substring(0, period);
}
File file = File.createTempFile(fileName, fileExt, sGREDir);
FileOutputStream fos = new FileOutputStream(file);
InputStream is = cr.openInputStream(uri);
byte[] buf = new byte[4096];
int len = is.read(buf);
while (len != -1) {
fos.write(buf, 0, len);
len = is.read(buf);
}
fos.close();
filePickerResult = file.getAbsolutePath();
}catch (Exception e) {
Log.e(LOG_FILE_NAME, "showing file picker", e);
}
}
try {
mFilePickerResult.put(filePickerResult);
} catch (InterruptedException e) {
Log.i(LOG_FILE_NAME, "error returning file picker result", e);
}
}
}

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

@ -1,833 +0,0 @@
/* -*- 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) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.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.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
import java.util.zip.*;
import java.nio.*;
import android.os.*;
import android.app.*;
import android.text.*;
import android.text.method.*;
import android.view.*;
import android.view.inputmethod.*;
import android.content.*;
import android.graphics.*;
import android.widget.*;
import android.hardware.*;
import android.location.*;
import android.graphics.drawable.*;
import android.content.res.*;
import android.util.*;
/*
* GeckoSurfaceView implements a GL surface view,
* similar to GLSurfaceView. However, since we
* already have a thread for Gecko, we don't really want
* a separate renderer thread that GLSurfaceView provides.
*/
class GeckoSurfaceView
extends SurfaceView
implements SurfaceHolder.Callback, SensorEventListener, LocationListener
{
private static final String LOG_FILE_NAME = "GeckoSurfaceView";
public GeckoSurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
inputConnection = new GeckoInputConnection(this);
setFocusable(true);
setFocusableInTouchMode(true);
DisplayMetrics metrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().
getDefaultDisplay().getMetrics(metrics);
mWidth = metrics.widthPixels;
mHeight = metrics.heightPixels;
mBufferWidth = 0;
mBufferHeight = 0;
mSurfaceLock = new ReentrantLock();
mEditableFactory = Editable.Factory.getInstance();
initEditable("");
mIMEState = IME_STATE_DISABLED;
mIMETypeHint = "";
mIMEActionHint = "";
}
protected void finalize() throws Throwable {
super.finalize();
}
void drawSplashScreen() {
this.drawSplashScreen(getHolder(), mWidth, mHeight);
}
void drawSplashScreen(SurfaceHolder holder, int width, int height) {
// No splash screen for Honeycomb or greater
if (Build.VERSION.SDK_INT >= 11) {
Log.i(LOG_FILE_NAME, "skipping splash screen");
return;
}
Canvas c = holder.lockCanvas();
if (c == null) {
Log.i(LOG_FILE_NAME, "canvas is null");
return;
}
Resources res = getResources();
File watchDir = new File(GeckoApp.sGREDir, "components");
if (watchDir.exists() == false) {
// Just show the simple splash screen for "new profile" startup
c.drawColor(res.getColor(R.color.splash_background));
Drawable drawable = res.getDrawable(R.drawable.splash);
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
int x = (width - w) / 2;
int y = (height - h) / 2 - 16;
drawable.setBounds(x, y, x + w, y + h);
drawable.draw(c);
Paint p = new Paint();
p.setTextAlign(Paint.Align.CENTER);
p.setTextSize(32f);
p.setAntiAlias(true);
p.setColor(res.getColor(R.color.splash_msgfont));
c.drawText(res.getString(R.string.splash_firstrun), width / 2, y + h + 16, p);
} else {
// Show the static UI for normal startup
DisplayMetrics metrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
// Default to DENSITY_HIGH sizes
int toolbarHeight = 80;
int faviconOffset = 25;
float urlHeight = 24f;
int urlOffsetX = 80;
int urlOffsetY = 48;
if (metrics.densityDpi == DisplayMetrics.DENSITY_MEDIUM) {
toolbarHeight = 53;
faviconOffset = 10;
urlHeight = 16f;
urlOffsetX = 53;
urlOffsetY = 32;
}
c.drawColor(res.getColor(R.color.splash_content));
Drawable toolbar = res.getDrawable(Build.VERSION.SDK_INT > 8 ?
R.drawable.splash_v9 :
R.drawable.splash_v8);
toolbar.setBounds(0, 0, width, toolbarHeight);
toolbar.draw(c);
// XUL/CSS always uses 32px width and height for favicon
Drawable favicon = res.getDrawable(R.drawable.favicon32);
favicon.setBounds(faviconOffset, faviconOffset, 32 + faviconOffset, 32 + faviconOffset);
favicon.draw(c);
if (GeckoSurfaceView.mSplashURL != "") {
TextPaint p = new TextPaint();
p.setTextAlign(Paint.Align.LEFT);
p.setTextSize(urlHeight);
p.setAntiAlias(true);
p.setColor(res.getColor(R.color.splash_urlfont));
String url = TextUtils.ellipsize(GeckoSurfaceView.mSplashURL, p, width - urlOffsetX * 2, TextUtils.TruncateAt.END).toString();
c.drawText(url, urlOffsetX, urlOffsetY, p);
}
}
holder.unlockCanvasAndPost(c);
}
/*
* Called on main thread
*/
public void draw(SurfaceHolder holder, ByteBuffer buffer) {
if (buffer == null || buffer.capacity() != (mWidth * mHeight * 2))
return;
synchronized (mSoftwareBuffer) {
if (buffer != mSoftwareBuffer || mSoftwareBufferCopy == null)
return;
Canvas c = holder.lockCanvas();
if (c == null)
return;
mSoftwareBufferCopy.copyPixelsFromBuffer(buffer);
c.drawBitmap(mSoftwareBufferCopy, 0, 0, null);
holder.unlockCanvasAndPost(c);
}
}
public void draw(SurfaceHolder holder, Bitmap bitmap) {
if (bitmap == null ||
bitmap.getWidth() != mWidth || bitmap.getHeight() != mHeight)
return;
synchronized (mSoftwareBitmap) {
if (bitmap != mSoftwareBitmap)
return;
Canvas c = holder.lockCanvas();
if (c == null)
return;
c.drawBitmap(bitmap, 0, 0, null);
holder.unlockCanvasAndPost(c);
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// On pre-Honeycomb, force exactly one frame of the previous size
// to render because the surface change is only seen by GLES after we
// have swapped the back buffer (i.e. the buffer size only changes
// after the next swap buffer). We need to make sure Gecko's view
// resizes when Android's buffer resizes.
// In Honeycomb, the buffer size changes immediately, so rendering a
// frame of the previous size is unnecessary (and wrong).
if (mDrawMode == DRAW_GLES_2 &&
(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)) {
// When we get a surfaceChange event, we have 0 to n paint events
// waiting in the Gecko event queue. We will make the first
// succeed and the abort the others.
mDrawSingleFrame = true;
if (!mInDrawing) {
// Queue at least one paint event in case none are queued.
GeckoAppShell.scheduleRedraw();
}
GeckoAppShell.geckoEventSync();
mDrawSingleFrame = false;
mAbortDraw = false;
}
if (mShowingSplashScreen)
drawSplashScreen(holder, width, height);
mSurfaceLock.lock();
if (mInDrawing) {
Log.w(LOG_FILE_NAME, "surfaceChanged while mInDrawing is true!");
}
boolean invalidSize;
if (width == 0 || height == 0) {
mSoftwareBitmap = null;
mSoftwareBuffer = null;
mSoftwareBufferCopy = null;
invalidSize = true;
} else {
invalidSize = false;
}
boolean doSyncDraw =
mDrawMode == DRAW_2D &&
!invalidSize &&
GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning);
mSyncDraw = doSyncDraw;
mFormat = format;
mWidth = width;
mHeight = height;
mSurfaceValid = true;
Log.i(LOG_FILE_NAME, "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
try {
DisplayMetrics metrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height,
metrics.widthPixels, metrics.heightPixels);
GeckoAppShell.sendEventToGecko(e);
} finally {
mSurfaceLock.unlock();
}
if (doSyncDraw) {
GeckoAppShell.scheduleRedraw();
Object syncDrawObject = null;
try {
syncDrawObject = mSyncDraws.take();
} catch (InterruptedException ie) {
Log.e(LOG_FILE_NAME, "Threw exception while getting sync draw bitmap/buffer: ", ie);
}
if (syncDrawObject != null) {
if (syncDrawObject instanceof Bitmap)
draw(holder, (Bitmap)syncDrawObject);
else
draw(holder, (ByteBuffer)syncDrawObject);
} else {
Log.e("GeckoSurfaceViewJava", "Synchronised draw object is null");
}
} else if (!mShowingSplashScreen) {
// Make sure a frame is drawn before we return
// otherwise we see artifacts or a black screen
GeckoAppShell.scheduleRedraw();
GeckoAppShell.geckoEventSync();
}
}
public void surfaceCreated(SurfaceHolder holder) {
Log.i(LOG_FILE_NAME, "surface created");
GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_CREATED);
GeckoAppShell.sendEventToGecko(e);
if (mShowingSplashScreen)
drawSplashScreen();
}
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i(LOG_FILE_NAME, "surface destroyed");
mSurfaceValid = false;
mSoftwareBuffer = null;
mSoftwareBufferCopy = null;
mSoftwareBitmap = null;
GeckoEvent e = new GeckoEvent(GeckoEvent.SURFACE_DESTROYED);
if (mDrawMode == DRAW_GLES_2) {
// Ensure GL cleanup occurs before we return.
GeckoAppShell.sendEventToGeckoSync(e);
} else {
GeckoAppShell.sendEventToGecko(e);
}
}
public Bitmap getSoftwareDrawBitmap() {
if (mSoftwareBitmap == null ||
mSoftwareBitmap.getHeight() != mHeight ||
mSoftwareBitmap.getWidth() != mWidth) {
mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
}
mDrawMode = DRAW_2D;
return mSoftwareBitmap;
}
public ByteBuffer getSoftwareDrawBuffer() {
// We store pixels in 565 format, so two bytes per pixel (explaining
// the * 2 in the following check/allocation)
if (mSoftwareBuffer == null ||
mSoftwareBuffer.capacity() != (mWidth * mHeight * 2)) {
mSoftwareBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
}
if (mSoftwareBufferCopy == null ||
mSoftwareBufferCopy.getHeight() != mHeight ||
mSoftwareBufferCopy.getWidth() != mWidth) {
mSoftwareBufferCopy = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.RGB_565);
}
mDrawMode = DRAW_2D;
return mSoftwareBuffer;
}
public Surface getSurface() {
return getHolder().getSurface();
}
/*
* Called on Gecko thread
*/
public static final int DRAW_ERROR = 0;
public static final int DRAW_GLES_2 = 1;
public static final int DRAW_2D = 2;
// Drawing is disable when the surface buffer
// has changed size but we haven't yet processed the
// resize event.
public static final int DRAW_DISABLED = 3;
public int beginDrawing() {
if (mInDrawing) {
Log.e(LOG_FILE_NAME, "Recursive beginDrawing call!");
return DRAW_ERROR;
}
// Once we drawn our first frame after resize we can ignore
// the other draw events until we handle the resize events.
if (mAbortDraw) {
return DRAW_DISABLED;
}
/* Grab the lock, which we'll hold while we're drawing.
* It gets released in endDrawing(), and is also used in surfaceChanged
* to make sure that we don't change our surface details while
* we're in the middle of drawing (and especially in the middle of
* executing beginDrawing/endDrawing).
*
* We might not need to hold this lock in between
* beginDrawing/endDrawing, and might just be able to make
* surfaceChanged, beginDrawing, and endDrawing synchronized,
* but this way is safer for now.
*/
mSurfaceLock.lock();
if (!mSurfaceValid) {
Log.e(LOG_FILE_NAME, "Surface not valid");
mSurfaceLock.unlock();
return DRAW_ERROR;
}
mInDrawing = true;
mDrawMode = DRAW_GLES_2;
return DRAW_GLES_2;
}
public void endDrawing() {
if (!mInDrawing) {
Log.e(LOG_FILE_NAME, "endDrawing without beginDrawing!");
return;
}
if (mDrawSingleFrame)
mAbortDraw = true;
try {
if (!mSurfaceValid) {
Log.e(LOG_FILE_NAME, "endDrawing with false mSurfaceValid");
return;
}
} finally {
mInDrawing = false;
if (!mSurfaceLock.isHeldByCurrentThread())
Log.e(LOG_FILE_NAME, "endDrawing while mSurfaceLock not held by current thread!");
mSurfaceLock.unlock();
}
}
/* How this works:
* Whenever we want to draw, we want to be sure that we do not lock
* the canvas unless we're sure we can draw. Locking the canvas clears
* the canvas to black in most cases, causing a black flash.
* At the same time, the surface can resize/disappear at any moment
* unless the canvas is locked.
* Draws originate from a different thread so the surface could change
* at any moment while we try to draw until we lock the canvas.
*
* Also, never try to lock the canvas while holding the surface lock
* unless you're in SurfaceChanged, in which case the canvas was already
* locked. Surface lock -> Canvas lock will lead to AB-BA deadlocks.
*/
public void draw2D(Bitmap bitmap, int width, int height) {
// mSurfaceLock ensures that we get mSyncDraw/mSoftwareBitmap/etc.
// set correctly before determining whether we should do a sync draw
mSurfaceLock.lock();
try {
if (mSyncDraw) {
if (bitmap != mSoftwareBitmap || width != mWidth || height != mHeight)
return;
mSyncDraw = false;
try {
mSyncDraws.put(bitmap);
} catch (InterruptedException ie) {
Log.e(LOG_FILE_NAME, "Threw exception while getting sync draws queue: ", ie);
}
return;
}
} finally {
mSurfaceLock.unlock();
}
draw(getHolder(), bitmap);
}
public void draw2D(ByteBuffer buffer, int stride) {
mSurfaceLock.lock();
try {
if (mSyncDraw) {
if (buffer != mSoftwareBuffer || stride != (mWidth * 2))
return;
mSyncDraw = false;
try {
mSyncDraws.put(buffer);
} catch (InterruptedException ie) {
Log.e(LOG_FILE_NAME, "Threw exception while getting sync bitmaps queue: ", ie);
}
return;
}
} finally {
mSurfaceLock.unlock();
}
draw(getHolder(), buffer);
}
@Override
public boolean onCheckIsTextEditor () {
return false;
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
outAttrs.actionLabel = null;
mKeyListener = TextKeyListener.getInstance();
if (mIMEState == IME_STATE_PASSWORD)
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
else if (mIMETypeHint.equalsIgnoreCase("url"))
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
else if (mIMETypeHint.equalsIgnoreCase("email"))
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
else if (mIMETypeHint.equalsIgnoreCase("search"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
else if (mIMETypeHint.equalsIgnoreCase("tel"))
outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
else if (mIMETypeHint.equalsIgnoreCase("number") ||
mIMETypeHint.equalsIgnoreCase("range"))
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
else if (mIMETypeHint.equalsIgnoreCase("datetime") ||
mIMETypeHint.equalsIgnoreCase("datetime-local"))
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
InputType.TYPE_DATETIME_VARIATION_NORMAL;
else if (mIMETypeHint.equalsIgnoreCase("date"))
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
InputType.TYPE_DATETIME_VARIATION_DATE;
else if (mIMETypeHint.equalsIgnoreCase("time"))
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
InputType.TYPE_DATETIME_VARIATION_TIME;
if (mIMEActionHint.equalsIgnoreCase("go"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
else if (mIMEActionHint.equalsIgnoreCase("done"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
else if (mIMEActionHint.equalsIgnoreCase("next"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
else if (mIMEActionHint.equalsIgnoreCase("search"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
else if (mIMEActionHint.equalsIgnoreCase("send"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
else if (mIMEActionHint != null && mIMEActionHint.length() != 0)
outAttrs.actionLabel = mIMEActionHint;
if (mIMELandscapeFS == false)
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
inputConnection.reset();
return inputConnection;
}
public void setEditable(String contents)
{
mEditable.removeSpan(inputConnection);
mEditable.replace(0, mEditable.length(), contents);
mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
Selection.setSelection(mEditable, contents.length());
}
public void initEditable(String contents)
{
mEditable = mEditableFactory.newEditable(contents);
mEditable.setSpan(inputConnection, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
Selection.setSelection(mEditable, contents.length());
}
// accelerometer
public void onAccuracyChanged(Sensor sensor, int accuracy)
{
}
public void onSensorChanged(SensorEvent event)
{
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
}
private class GeocoderTask extends AsyncTask<Location, Void, Void> {
protected Void doInBackground(Location... location) {
try {
List<Address> addresses = mGeocoder.getFromLocation(location[0].getLatitude(),
location[0].getLongitude(), 1);
// grab the first address. in the future,
// may want to expose multiple, or filter
// for best.
mLastGeoAddress = addresses.get(0);
GeckoAppShell.sendEventToGecko(new GeckoEvent(location[0], mLastGeoAddress));
} catch (Exception e) {
Log.w(LOG_FILE_NAME, "GeocoderTask "+e);
}
return null;
}
}
// geolocation
public void onLocationChanged(Location location)
{
if (mGeocoder == null)
mGeocoder = new Geocoder(getContext(), Locale.getDefault());
if (mLastGeoAddress == null) {
new GeocoderTask().execute(location);
}
else {
float[] results = new float[1];
Location.distanceBetween(location.getLatitude(),
location.getLongitude(),
mLastGeoAddress.getLatitude(),
mLastGeoAddress.getLongitude(),
results);
// pfm value. don't want to slam the
// geocoder with very similar values, so
// only call after about 100m
if (results[0] > 100)
new GeocoderTask().execute(location);
}
GeckoAppShell.sendEventToGecko(new GeckoEvent(location, mLastGeoAddress));
}
public void onProviderDisabled(String provider)
{
}
public void onProviderEnabled(String provider)
{
}
public void onStatusChanged(String provider, int status, Bundle extras)
{
}
// event stuff
public boolean onTouchEvent(MotionEvent event) {
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (event.isSystem())
return super.onKeyPreIme(keyCode, event);
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
return processKeyDown(keyCode, event, true);
case KeyEvent.ACTION_UP:
return processKeyUp(keyCode, event, true);
case KeyEvent.ACTION_MULTIPLE:
return onKeyMultiple(keyCode, event.getRepeatCount(), event);
}
return super.onKeyPreIme(keyCode, event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return processKeyDown(keyCode, event, false);
}
private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (event.getRepeatCount() == 0) {
event.startTracking();
return true;
} else {
return false;
}
case KeyEvent.KEYCODE_MENU:
if (event.getRepeatCount() == 0) {
event.startTracking();
break;
} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
break;
}
// Ignore repeats for KEYCODE_MENU; they confuse the widget code.
return false;
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_SEARCH:
return false;
case KeyEvent.KEYCODE_DEL:
// See comments in GeckoInputConnection.onKeyDel
if (inputConnection != null &&
inputConnection.onKeyDel()) {
return true;
}
break;
case KeyEvent.KEYCODE_ENTER:
if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
mIMEActionHint.equalsIgnoreCase("next"))
event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB);
break;
default:
break;
}
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
(event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
// Let active IME process pre-IME key events
return false;
// KeyListener returns true if it handled the event for us.
if (mIMEState == IME_STATE_DISABLED ||
keyCode == KeyEvent.KEYCODE_ENTER ||
keyCode == KeyEvent.KEYCODE_DEL ||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
!mKeyListener.onKeyDown(this, mEditable, keyCode, event))
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return processKeyUp(keyCode, event, false);
}
private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
if (!event.isTracking() || event.isCanceled())
return false;
break;
default:
break;
}
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
(event.getMetaState() & KeyEvent.META_ALT_ON) == 0)
// Let active IME process pre-IME key events
return false;
if (mIMEState == IME_STATE_DISABLED ||
keyCode == KeyEvent.KEYCODE_ENTER ||
keyCode == KeyEvent.KEYCODE_DEL ||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
!mKeyListener.onKeyUp(this, mEditable, keyCode, event))
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
case KeyEvent.KEYCODE_MENU:
InputMethodManager imm = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInputFromWindow(getWindowToken(),
imm.SHOW_FORCED, 0);
return true;
default:
break;
}
return false;
}
// Is this surface valid for drawing into?
boolean mSurfaceValid;
// Are we actively between beginDrawing/endDrawing?
boolean mInDrawing;
// Used to finish the current buffer before changing the surface size
boolean mDrawSingleFrame = false;
boolean mAbortDraw = false;
// Are we waiting for a buffer to draw in surfaceChanged?
boolean mSyncDraw;
// True if gecko requests a buffer
int mDrawMode;
static boolean mShowingSplashScreen = true;
static String mSplashURL = "";
// let's not change stuff around while we're in the middle of
// starting drawing, ending drawing, or changing surface
// characteristics
ReentrantLock mSurfaceLock;
// Surface format, from surfaceChanged. Largely
// useless.
int mFormat;
// the dimensions of the surface
int mWidth;
int mHeight;
// the dimensions of the buffer we're using for drawing,
// that is the software buffer or the EGLSurface
int mBufferWidth;
int mBufferHeight;
// IME stuff
public static final int IME_STATE_DISABLED = 0;
public static final int IME_STATE_ENABLED = 1;
public static final int IME_STATE_PASSWORD = 2;
public static final int IME_STATE_PLUGIN = 3;
GeckoInputConnection inputConnection;
KeyListener mKeyListener;
Editable mEditable;
Editable.Factory mEditableFactory;
int mIMEState;
String mIMETypeHint;
String mIMEActionHint;
boolean mIMELandscapeFS;
// Software rendering
Bitmap mSoftwareBitmap;
ByteBuffer mSoftwareBuffer;
Bitmap mSoftwareBufferCopy;
Geocoder mGeocoder;
Address mLastGeoAddress;
final SynchronousQueue<Object> mSyncDraws = new SynchronousQueue<Object>();
}

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

@ -1,220 +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 the Mozilla browser.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2009-2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Vladimir Vukicevic <vladimir@pobox.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 *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/ipc/app/defs.mk
DIRS = locales
JAVAFILES = \
GeckoApp.java \
GeckoAppShell.java \
GeckoConnectivityReceiver.java \
GeckoEvent.java \
GeckoSurfaceView.java \
GeckoInputConnection.java \
AlertNotification.java \
SurfaceInfo.java \
GeckoBatteryManager.java \
GeckoSmsManager.java \
$(NULL)
PROCESSEDJAVAFILES = \
App.java \
Restarter.java \
NotificationHandler.java \
LauncherShortcuts.java \
$(NULL)
ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
MIN_CPU_VERSION=7
else
MIN_CPU_VERSION=5
endif
ifeq (,$(ANDROID_VERSION_CODE))
ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10)
endif
DEFINES += \
-DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
-DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \
-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
-DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
-DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) \
-DMOZ_MIN_CPU_VERSION=$(MIN_CPU_VERSION) \
-DMOZ_CRASHREPORTER=$(MOZ_CRASHREPORTER) \
-DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
-DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
$(NULL)
GARBAGE += \
AndroidManifest.xml \
classes.dex \
$(PROCESSEDJAVAFILES) \
gecko.ap_ \
res/values/strings.xml \
R.java \
$(NULL)
GARBAGE_DIRS += classes res
# Bug 567884 - Need a way to find appropriate icons during packaging
ifeq ($(MOZ_APP_NAME),fennec)
ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_48x48.png
ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png
# we released these builds to the public with shared IDs and need to keep them
ifeq (org.mozilla.firefox,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.firefox.sharedID"
else ifeq (org.mozilla.firefox_beta,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.firefox.sharedID"
else ifeq (org.mozilla.fennec_aurora,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.fennec.sharedID"
else ifeq (org.mozilla.fennec,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.fennec.sharedID"
endif
else
ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon48.png
ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png
DEFINES += -DMOZ_ANDROID_SHARED_ID="$(ANDROID_PACKAGE_NAME).sharedID"
endif
RES_LAYOUT = \
res/layout/notification_progress.xml \
res/layout/notification_progress_text.xml \
res/layout/notification_icon_text.xml \
res/layout/launch_app_list.xml \
res/layout/launch_app_listitem.xml \
$(NULL)
RES_VALUES = res/values/colors.xml res/values/themes.xml
AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/)
JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
DEFAULT_BRANDPATH = $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/en-US/brand.dtd
DEFAULT_STRINGSPATH = locales/en-US/android_strings.dtd
LOCALIZED_BRANDPATH = $(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd
LOCALIZED_STRINGSPATH = $(DEPTH)/dist/bin/chrome/android-res/res/values-$(AB_CD)/android_strings.dtd
ifdef MOZ_CRASHREPORTER
PROCESSEDJAVAFILES += CrashReporter.java
MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/crash_reporter.png
RES_LAYOUT += res/layout/crash_reporter.xml
endif
MOZ_ANDROID_DRAWABLES += embedding/android/resources/drawable/desktop_notification.png
MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' '; fi)
include $(topsrcdir)/config/rules.mk
ifneq ($(AB_CD),en-US)
LOCALIZED_STRINGS_XML = res/values-$(AB_rCD)/strings.xml
endif
# Override the Java settings with some specific android settings
include $(topsrcdir)/config/android-common.mk
# Note that we're going to set up a dependency directly between embed_android.dex and the java files
# Instead of on the .class files, since more than one .class file might be produced per .java file
classes.dex: $(JAVAFILES) $(PROCESSEDJAVAFILES) R.java
$(NSINSTALL) -D classes
$(JAVAC) $(JAVAC_FLAGS) -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(PROCESSEDJAVAFILES) R.java
$(DX) --dex --output=$@ classes
AndroidManifest.xml $(PROCESSEDJAVAFILES): % : %.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
res/drawable/icon.png: $(MOZ_APP_ICON)
$(NSINSTALL) -D res/drawable
cp $(ICON_PATH) $@
res/drawable-hdpi/icon.png: $(MOZ_APP_ICON)
$(NSINSTALL) -D res/drawable-hdpi
cp $(ICON_PATH_HDPI) $@
RES_DRAWABLE = $(addprefix res/drawable/,$(notdir $(MOZ_ANDROID_DRAWABLES)))
$(RES_DRAWABLE): $(addprefix $(topsrcdir)/,$(MOZ_ANDROID_DRAWABLES))
$(NSINSTALL) -D res/drawable
$(NSINSTALL) $^ res/drawable/
$(RES_LAYOUT): $(subst res/,$(srcdir)/resources/,$(RES_LAYOUT))
$(NSINSTALL) -D res/layout
$(NSINSTALL) $(srcdir)/resources/layout/* res/layout/
$(RES_VALUES): $(subst res/,$(srcdir)/resources/,$(RES_VALUES))
$(NSINSTALL) -D res/values
$(NSINSTALL) $(srcdir)/resources/values/* res/values/
R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/drawable/icon.png res/drawable-hdpi/icon.png res/values/strings.xml AndroidManifest.xml
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko
gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/values/strings.xml FORCE
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@
res/values/strings.xml: $(DEFAULT_BRANDPATH) $(DEFAULT_STRINGSPATH)
mkdir -p res/values
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(DEFAULT_BRANDPATH)" \
-DSTRINGSPATH="$(DEFAULT_STRINGSPATH)" \
$(srcdir)/strings.xml.in \
> $@
res/values-$(AB_rCD)/strings.xml: $(LOCALIZED_BRANDPATH) $(LOCALIZED_STRINGSPATH)
mkdir -p res/values-$(AB_rCD)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(call core_abspath,$(LOCALIZED_BRANDPATH))" \
-DSTRINGSPATH="$(call core_abspath,$(LOCALIZED_STRINGSPATH))" \
$(srcdir)/strings.xml.in \
> $@
chrome:: $(LOCALIZED_STRINGS_XML)
libs:: classes.dex
$(INSTALL) classes.dex $(FINAL_TARGET)

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

@ -1,7 +0,0 @@
package org.mozilla.gecko;
public class SurfaceInfo {
public int format;
public int width;
public int height;
}

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

@ -1,21 +0,0 @@
<!ENTITY splash_firstrun "Setting up &brandShortName;\u2026">
<!ENTITY no_space_to_start_error "There is not enough space available for &brandShortName; to start.">
<!ENTITY error_loading_file "An error occurred when trying to load files required to run &brandShortName;">
<!ENTITY crash_reporter_title "&brandShortName; Crash Reporter">
<!ENTITY crash_message "&brandShortName; has crashed. Your tabs should be listed on the &brandShortName; Start page when you restart.">
<!ENTITY crash_help_message "Please help us fix this problem!">
<!ENTITY crash_send_report_message "Send Mozilla a crash report">
<!ENTITY crash_include_url "Include page address">
<!ENTITY crash_close_label "Close">
<!ENTITY crash_restart_label "Restart &brandShortName;">
<!ENTITY sending_crash_report "Sending crash report\u2026">
<!ENTITY exit_label "Exit">
<!ENTITY continue_label "Continue">
<!ENTITY launcher_shortcuts_title "&brandShortName; Web Apps">
<!ENTITY launcher_shortcuts_empty "No web apps were found">
<!ENTITY choose_file "Choose File">

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

@ -1,48 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="10px" >
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10px"
android:textStyle="bold"
android:text="@string/crash_message"/>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10px"
android:text="@string/crash_help_message"/>
<CheckBox android:id="@+id/send_report"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/crash_send_report_message" />
<CheckBox android:id="@+id/include_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/crash_include_url" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10px"
android:gravity="center_horizontal" >
<Button android:id="@+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10px"
android:minWidth="120sp"
android:onClick="onCloseClick"
android:text="@string/crash_close_label" />
<Button android:id="@+id/restart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10px"
android:minWidth="120sp"
android:onClick="onRestartClick"
android:text="@string/crash_restart_label" />
</LinearLayout>
</LinearLayout>

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

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="3dip"
android:orientation="vertical"
android:windowIsFloating="true">
</LinearLayout>

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

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingLeft="16dip"
android:paddingRight="16dip"
android:orientation="horizontal"
android:gravity="left">
<ImageView
android:id="@+id/favicon"
android:layout_width="48dip"
android:layout_height="48dip"
android:layout_marginRight="12dip"
android:layout_gravity="center_vertical"
android:adjustViewBounds="true"
android:scaleType="fitCenter"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceLargeInverse"
android:ellipsize="marquee"
android:fadingEdge="horizontal"/>
</LinearLayout>

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

@ -1,34 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:paddingLeft="5dp"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<ImageView android:id="@+id/notificationImage"
android:layout_width="25dp"
android:layout_height="25dp"
android:scaleType="fitCenter" />
<TextView android:id="@+id/notificationTitle"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:paddingLeft="4dp"
/>
</LinearLayout>
<TextView android:id="@+id/notificationText"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dp"
/>
</LinearLayout>

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

@ -1,53 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:paddingTop="7dp"
android:paddingLeft="5dp"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<ImageView android:id="@+id/notificationImage"
android:layout_width="25dp"
android:layout_height="25dp"
android:scaleType="fitCenter" />
<TextView android:id="@+id/notificationTitle"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:paddingLeft="10dp"
/>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<TextView android:id="@+id/notificationText"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="3dp"
/>
<ProgressBar android:id="@+id/notificationProgressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="1dip"
android:layout_marginBottom="1dip"
android:layout_marginLeft="4dip"
android:layout_marginRight="10dip"
android:layout_centerHorizontal="true" />
</LinearLayout>
</LinearLayout>

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

@ -1,46 +0,0 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:paddingLeft="5dp"
>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<ImageView android:id="@+id/notificationImage"
android:layout_width="25dp"
android:layout_height="25dp"
android:scaleType="fitCenter" />
<TextView android:id="@+id/notificationTitle"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:paddingLeft="4dp"
/>
</LinearLayout>
<ProgressBar android:id="@+id/notificationProgressbar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="16dip"
android:layout_marginTop="1dip"
android:layout_marginBottom="1dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_centerHorizontal="true" />
<TextView android:id="@+id/notificationText"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="4dp"
/>
</LinearLayout>

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

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="GreyTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item>
</style>
</resources>

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

@ -1,27 +0,0 @@
#filter substitution
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
#includesubst @BRANDPATH@
#includesubst @STRINGSPATH@
]>
<resources>
<string name="splash_firstrun">&splash_firstrun;</string>
<string name="no_space_to_start_error">&no_space_to_start_error;</string>
<string name="error_loading_file">&error_loading_file;</string>
<string name="crash_reporter_title">&crash_reporter_title;</string>
<string name="crash_message">&crash_message;</string>
<string name="crash_help_message">&crash_help_message;</string>
<string name="crash_send_report_message">&crash_send_report_message;</string>
<string name="crash_include_url">&crash_include_url;</string>
<string name="crash_close_label">&crash_close_label;</string>
<string name="crash_restart_label">&crash_restart_label;</string>
<string name="sending_crash_report">&sending_crash_report;</string>
<string name="exit_label">&exit_label;</string>
<string name="continue_label">&continue_label;</string>
<string name="launcher_shortcuts_title">&launcher_shortcuts_title;</string>
<string name="launcher_shortcuts_empty">&launcher_shortcuts_empty;</string>
<string name="choose_file">&choose_file;</string>
</resources>

1333
mobile/android/LICENSE Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,58 @@
# ***** 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.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2007
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Mark Finkle <mfinkle@mozilla.com>
# Joel Maher <jmaher@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 *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = base chrome locales components modules themes/core app
ifndef LIBXUL_SDK
PARALLEL_DIRS += $(DEPTH)/xulrunner/tools/redit
endif
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/testing/testsuite-targets.mk
package-mobile-tests:
$(MAKE) stage-mochitest DIST_BIN=$(DEPTH)/$(DIST)/bin/xulrunner
$(NSINSTALL) -D $(DIST)/$(PKG_PATH)
@(cd $(PKG_STAGE) && tar $(TAR_CREATE_FLAGS) - *) | bzip2 -f > $(DIST)/$(PKG_PATH)$(TEST_PACKAGE)

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

@ -0,0 +1,171 @@
# ***** 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.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2007
# 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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
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))
PROGRAM=$(MOZ_APP_NAME)$(BIN_SUFFIX)
LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre
LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base
LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
LOCAL_INCLUDES += -I$(DEPTH)/build
DEFINES += -DXPCOM_GLUE
STL_FLAGS=
LIBS += \
$(EXTRA_DSO_LIBS) \
$(XPCOM_STANDALONE_GLUE_LDOPTS) \
$(NULL)
ifeq ($(MOZ_PLATFORM_MAEMO),6)
LIBS += \
$(LIBXUL_DIST)/../widget/src/qt/faststartupqt/$(LIB_PREFIX)faststartupqt.$(LIB_SUFFIX) \
$(MOZ_QT_LIBS) \
$(NULL)
LOCAL_INCLUDES += -I$(topsrcdir)/widget/src/qt/faststartupqt $(TK_CFLAGS)
endif
ifeq ($(OS_ARCH),WINNT)
OS_LIBS += $(call EXPAND_LIBNAME,version)
endif
ifdef _MSC_VER
# Always enter a Windows program through wmain, whether or not we're
# a console application.
WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
endif
endif
endif #LIBXUL_SDK
# Make sure the standalone glue doesn't try to get libxpcom.so from mobile/app.
NSDISTMODE = copy
include $(topsrcdir)/config/rules.mk
APP_ICON = mobile
DEFINES += \
-DAPP_NAME=$(MOZ_APP_NAME) \
-DAPP_VERSION=$(MOZ_APP_VERSION) \
-DMOZ_UPDATER=$(MOZ_UPDATER) \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
REDIT_PATH = $(LIBXUL_DIST)/bin
endif
APP_BINARY = $(MOZ_APP_NAME)$(BIN_SUFFIX)
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
APP_NAME = $(MOZ_APP_DISPLAYNAME)
APP_VERSION = $(MOZ_APP_VERSION)
ifdef MOZ_DEBUG
APP_NAME := $(APP_NAME)Debug
endif
AB_CD = $(MOZ_UI_LOCALE)
AB := $(firstword $(subst -, ,$(AB_CD)))
clean clobber repackage::
rm -rf $(DIST)/$(APP_NAME).app
ifdef LIBXUL_SDK
APPFILES = Resources
else
APPFILES = MacOS
endif
libs repackage::
mkdir -p $(DIST)/$(APP_NAME).app/Contents/MacOS
rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents $(DIST)/$(APP_NAME).app --exclude English.lproj
mkdir -p $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj
rsync -a --exclude "*.in" $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj
sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" -e "s/%APP_BINARY%/$(APP_BINARY)/" $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/$(APP_NAME).app/Contents/Info.plist
sed -e "s/%APP_VERSION%/$(APP_VERSION)/" -e "s/%APP_NAME%/$(APP_NAME)/" $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(DIST)/$(APP_NAME).app/Contents/Resources/$(AB).lproj/InfoPlist.strings
rsync -a $(DIST)/bin/ $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)
$(RM) $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/mangle $(DIST)/$(APP_NAME).app/Contents/$(APPFILES)/shlibsign
ifdef LIBXUL_SDK
cp $(LIBXUL_DIST)/bin/xulrunner$(BIN_SUFFIX) $(DIST)/$(APP_NAME).app/Contents/MacOS/$(APP_BINARY)
rsync -a --exclude nsinstall --copy-unsafe-links $(LIBXUL_DIST)/XUL.framework $(DIST)/$(APP_NAME).app/Contents/Frameworks
else
rm -f $(DIST)/$(APP_NAME).app/Contents/MacOS/$(PROGRAM)
rsync -aL $(PROGRAM) $(DIST)/$(APP_NAME).app/Contents/MacOS
endif
printf "APPLMOZB" > $(DIST)/$(APP_NAME).app/Contents/PkgInfo
else # MOZ_WIDGET_TOOLKIT != cocoa
libs::
ifdef LIBXUL_SDK
cp $(LIBXUL_DIST)/bin/xulrunner-stub$(BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY)
endif
ifndef SKIP_COPY_XULRUNNER
ifdef LIBXUL_SDK
$(NSINSTALL) -D $(DIST)/bin/xulrunner
(cd $(LIBXUL_SDK)/bin && tar $(TAR_CREATE_FLAGS) - .) | (cd $(DIST)/bin/xulrunner && tar -xf -)
endif
endif # SKIP_COPY_XULRUNNER
ifeq ($(MOZ_PLATFORM_MAEMO),6)
$(NSINSTALL) -D $(DIST)/bin/res/drawable
cp $(topsrcdir)/mobile/app/maemo/* $(DIST)/bin/res/drawable/
cp $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/favicon32.png $(DIST)/bin/res/drawable/
endif
$(NSINSTALL) -D $(DIST)/bin/chrome/icons/default
ifeq ($(OS_ARCH),WINNT)
cp $(srcdir)/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/$(APP_ICON).ico
$(REDIT_PATH)/redit$(HOST_BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY) $(srcdir)/$(APP_ICON).ico
endif
endif

Двоичные данные
mobile/android/app/android/drawable-hdpi/alertaddons.png Normal file

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

После

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

Двоичные данные
mobile/android/app/android/drawable-hdpi/alertdownloads.png Normal file

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

После

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

Двоичные данные
mobile/android/app/android/drawable/alertaddons.png Normal file

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

После

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

Двоичные данные
mobile/android/app/android/drawable/alertdownloads.png Normal file

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

После

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

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

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>html</string>
<string>htm</string>
<string>shtml</string>
<string>xht</string>
<string>xhtml</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>document.icns</string>
<key>CFBundleTypeName</key>
<string>HTML Document</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>HTML</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>text</string>
<string>txt</string>
<string>js</string>
<string>log</string>
<string>css</string>
<string>xul</string>
<string>rdf</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>document.icns</string>
<key>CFBundleTypeName</key>
<string>Text Document</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>TEXT</string>
<string>utxt</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>jpeg</string>
<string>jpg</string>
<string>png</string>
<string>gif</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>fileBookmark.icns</string>
<key>CFBundleTypeName</key>
<string>document.icns</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>GIFf</string>
<string>JPEG</string>
<string>PNGf</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>fennec</string>
<key>CFBundleGetInfoString</key>
<string>%APP_NAME% %APP_VERSION%</string>
<key>CFBundleIconFile</key>
<string>fennec</string>
<key>CFBundleIdentifier</key>
<string>org.mozilla.fennec</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>%APP_NAME%</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>%APP_VERSION%</string>
<key>CFBundleSignature</key>
<string>MOZB</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLIconFile</key>
<string>document.icns</string>
<key>CFBundleURLName</key>
<string>http URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>http</string>
</array>
</dict>
<dict>
<key>CFBundleURLIconFile</key>
<string>document.icns</string>
<key>CFBundleURLName</key>
<string>https URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>https</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>ftp URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ftp</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>file URL</string>
<key>CFBundleURLSchemes</key>
<array>
<string>file</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>%APP_VERSION%</string>
<key>NSAppleScriptEnabled</key>
<true/>
<key>CGDisableCoalescedUpdates</key>
<true/>
</dict>
</plist>

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

@ -0,0 +1 @@
CFBundleName = "%APP_NAME%";

Двоичные данные
mobile/android/app/maemo/toolbar_splash.png Normal file

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

После

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

Двоичные данные
mobile/android/app/mobile.ico Normal file

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

После

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

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

@ -0,0 +1,691 @@
/* ***** 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):
* Matt Brubeck <mbrubeck@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 ***** */
#filter substitution
// For browser.xml binding
//
// cacheRatio* is a ratio that determines the amount of pixels to cache. The
// ratio is multiplied by the viewport width or height to get the displayport's
// width or height, respectively.
//
// (divide integer value by 1000 to get the ratio)
//
// For instance: cachePercentageWidth is 1500
// viewport height is 500
// => display port height will be 500 * 1.5 = 750
//
pref("toolkit.browser.cacheRatioWidth", 2000);
pref("toolkit.browser.cacheRatioHeight", 3000);
// How long before a content view (a handle to a remote scrollable object)
// expires.
pref("toolkit.browser.contentViewExpire", 3000);
pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul");
pref("general.useragent.compatMode.firefox", true);
pref("browser.chromeURL", "chrome://browser/content/");
pref("browser.tabs.warnOnClose", true);
pref("browser.tabs.remote", true);
pref("toolkit.screen.lock", false);
// From libpref/src/init/all.js, extended to allow a slightly wider zoom range.
pref("zoom.minPercent", 20);
pref("zoom.maxPercent", 400);
pref("toolkit.zoomManager.zoomValues", ".2,.3,.5,.67,.8,.9,1,1.1,1.2,1.33,1.5,1.7,2,2.4,3,4");
// Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
pref("browser.viewport.scaleRatio", -1);
pref("browser.viewport.desktopWidth", 980);
#ifndef ANDROID
#ifndef MOZ_PLATFORM_MAEMO
// On desktop builds, simulate an MDPI tablet by default.
pref("layout.css.dpi", 160);
#else
// Maemo X11 lies about its dpi
pref("layout.css.dpi", 240);
#endif
#endif
/* allow scrollbars to float above chrome ui */
pref("ui.scrollbarsCanOverlapContent", 1);
/* cache prefs */
pref("browser.cache.disk.enable", true);
pref("browser.cache.disk.capacity", 10240); // kilobytes
pref("browser.cache.disk.smart_size.enabled", false);
pref("browser.cache.disk.smart_size.first_run", false);
pref("browser.cache.memory.enable", true);
pref("browser.cache.memory.capacity", 1024); // kilobytes
/* image cache prefs */
pref("image.cache.size", 1048576); // bytes
/* offline cache prefs */
pref("browser.offline-apps.notify", true);
pref("browser.cache.offline.enable", true);
pref("browser.cache.offline.capacity", 5120); // kilobytes
pref("offline-apps.quota.max", 2048); // kilobytes
pref("offline-apps.quota.warn", 1024); // kilobytes
/* protocol warning prefs */
pref("network.protocol-handler.warn-external.tel", false);
pref("network.protocol-handler.warn-external.mailto", false);
pref("network.protocol-handler.warn-external.vnd.youtube", false);
/* http prefs */
pref("network.http.pipelining", true);
pref("network.http.pipelining.ssl", true);
pref("network.http.proxy.pipelining", true);
pref("network.http.pipelining.maxrequests" , 6);
pref("network.http.keep-alive.timeout", 600);
pref("network.http.max-connections", 6);
pref("network.http.max-connections-per-server", 4);
pref("network.http.max-persistent-connections-per-server", 4);
pref("network.http.max-persistent-connections-per-proxy", 4);
#ifdef MOZ_PLATFORM_MAEMO
pref("network.autodial-helper.enabled", true);
#endif
// See bug 545869 for details on why these are set the way they are
pref("network.buffer.cache.count", 24);
pref("network.buffer.cache.size", 16384);
/* history max results display */
pref("browser.display.history.maxresults", 100);
/* How many times should have passed before the remote tabs list is refreshed */
pref("browser.display.remotetabs.timeout", 10);
/* session history */
pref("browser.sessionhistory.max_total_viewers", 1);
pref("browser.sessionhistory.max_entries", 50);
/* session store */
pref("browser.sessionstore.resume_session_once", false);
pref("browser.sessionstore.resume_from_crash", true);
pref("browser.sessionstore.resume_from_crash_timeout", 60); // minutes
pref("browser.sessionstore.interval", 10000); // milliseconds
pref("browser.sessionstore.max_tabs_undo", 1);
/* these should help performance */
pref("mozilla.widget.force-24bpp", true);
pref("mozilla.widget.use-buffer-pixmap", true);
pref("mozilla.widget.disable-native-theme", true);
pref("layout.reflow.synthMouseMove", false);
/* download manager (don't show the window or alert) */
pref("browser.download.useDownloadDir", true);
pref("browser.download.folderList", 1); // Default to ~/Downloads
pref("browser.download.manager.showAlertOnComplete", false);
pref("browser.download.manager.showAlertInterval", 2000);
pref("browser.download.manager.retention", 2);
pref("browser.download.manager.showWhenStarting", false);
pref("browser.download.manager.closeWhenDone", true);
pref("browser.download.manager.openDelay", 0);
pref("browser.download.manager.focusWhenStarting", false);
pref("browser.download.manager.flashCount", 2);
pref("browser.download.manager.displayedHistoryDays", 7);
/* download alerts (disabled above) */
pref("alerts.slideIncrement", 1);
pref("alerts.slideIncrementTime", 10);
pref("alerts.totalOpenTime", 6000);
pref("alerts.height", 50);
/* download helper */
pref("browser.helperApps.deleteTempFileOnExit", false);
/* password manager */
pref("signon.rememberSignons", true);
pref("signon.expireMasterPassword", false);
pref("signon.SignonFileName", "signons.txt");
pref("signon.debug", false);
/* form helper */
// 0 = disabled, 1 = enabled, 2 = dynamic depending on screen size
pref("formhelper.mode", 2);
pref("formhelper.autozoom", true);
pref("formhelper.autozoom.caret", true);
pref("formhelper.restore", false);
/* find helper */
pref("findhelper.autozoom", true);
/* autocomplete */
pref("browser.formfill.enable", true);
/* spellcheck */
pref("layout.spellcheckDefault", 0);
/* extension manager and xpinstall */
pref("xpinstall.whitelist.add", "addons.mozilla.org");
pref("extensions.enabledScopes", 1);
pref("extensions.autoupdate.enabled", true);
pref("extensions.autoupdate.interval", 86400);
pref("extensions.update.enabled", false);
pref("extensions.update.interval", 86400);
pref("extensions.dss.enabled", false);
pref("extensions.dss.switchPending", false);
pref("extensions.ignoreMTimeChanges", false);
pref("extensions.logging.enabled", false);
pref("extensions.hideInstallButton", true);
pref("extensions.showMismatchUI", false);
pref("extensions.hideUpdateButton", false);
pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%");
/* preferences for the Get Add-ons pane */
pref("extensions.getAddons.cache.enabled", true);
pref("extensions.getAddons.maxResults", 15);
pref("extensions.getAddons.recommended.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/recommended/");
pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/list/featured/all/%MAX_RESULTS%/%OS%/%VERSION%");
pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/search?q=%TERMS%");
pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%");
pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/mobile/");
pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
/* preference for the locale picker */
pref("extensions.getLocales.get.url", "");
pref("extensions.compatability.locales.buildid", "0");
/* blocklist preferences */
pref("extensions.blocklist.enabled", true);
pref("extensions.blocklist.interval", 86400);
pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
/* block popups by default, and notify the user about blocked popups */
pref("dom.disable_open_during_load", true);
pref("privacy.popups.showBrowserMessage", true);
pref("keyword.enabled", true);
pref("keyword.URL", "http://www.google.com/m?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q=");
pref("accessibility.typeaheadfind", false);
pref("accessibility.typeaheadfind.timeout", 5000);
pref("accessibility.typeaheadfind.flashBar", 1);
pref("accessibility.typeaheadfind.linksonly", false);
pref("accessibility.typeaheadfind.casesensitive", 0);
// zoom key(F7) conflicts with caret browsing on maemo
pref("accessibility.browsewithcaret_shortcut.enabled", false);
// Whether or not we show a dialog box informing the user that the update was
// successfully applied.
pref("app.update.showInstalledUI", false);
// Whether the character encoding menu is under the main Firefox button. This
// preference is a string so that localizers can alter it.
pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
pref("intl.charsetmenu.browser.static", "chrome://browser/locale/browser.properties");
// pointer to the default engine name
pref("browser.search.defaultenginename", "chrome://browser/locale/region.properties");
// SSL error page behaviour
pref("browser.ssl_override_behavior", 2);
pref("browser.xul.error_pages.expert_bad_cert", false);
// disable logging for the search service by default
pref("browser.search.log", false);
// ordering of search engines in the engine list.
pref("browser.search.order.1", "chrome://browser/locale/region.properties");
pref("browser.search.order.2", "chrome://browser/locale/region.properties");
// disable updating
pref("browser.search.update", false);
pref("browser.search.update.log", false);
pref("browser.search.updateinterval", 6);
// enable search suggestions by default
pref("browser.search.suggest.enabled", true);
// Tell the search service to load search plugins from the locale JAR
pref("browser.search.loadFromJars", true);
pref("browser.search.jarURIs", "chrome://browser/locale/searchplugins/");
// tell the search service that we don't really expose the "current engine"
pref("browser.search.noCurrentEngine", true);
// enable xul error pages
pref("browser.xul.error_pages.enabled", true);
// Specify emptyRestriction = 0 so that bookmarks appear in the list by default
pref("browser.urlbar.default.behavior", 0);
pref("browser.urlbar.default.behavior.emptyRestriction", 0);
// Let the faviconservice know that we display favicons as 32x32px so that it
// uses the right size when optimizing favicons
pref("places.favicons.optimizeToDimension", 32);
// various and sundry awesomebar prefs (should remove/re-evaluate
// these once bug 447900 is fixed)
pref("browser.urlbar.clickSelectsAll", true);
pref("browser.urlbar.doubleClickSelectsAll", true);
pref("browser.urlbar.autoFill", false);
pref("browser.urlbar.matchOnlyTyped", false);
pref("browser.urlbar.matchBehavior", 1);
pref("browser.urlbar.filter.javascript", true);
pref("browser.urlbar.maxRichResults", 24); // increased so we see more results when portrait
pref("browser.urlbar.search.chunkSize", 1000);
pref("browser.urlbar.search.timeout", 100);
pref("browser.urlbar.restrict.history", "^");
pref("browser.urlbar.restrict.bookmark", "*");
pref("browser.urlbar.restrict.tag", "+");
pref("browser.urlbar.match.title", "#");
pref("browser.urlbar.match.url", "@");
pref("browser.urlbar.autocomplete.search_threshold", 5);
pref("browser.history.grouping", "day");
pref("browser.history.showSessions", false);
pref("browser.sessionhistory.max_entries", 50);
pref("browser.history_expire_days", 180);
pref("browser.history_expire_days_min", 90);
pref("browser.history_expire_sites", 40000);
pref("browser.places.migratePostDataAnnotations", true);
pref("browser.places.updateRecentTagsUri", true);
pref("places.frecency.numVisits", 10);
pref("places.frecency.numCalcOnIdle", 50);
pref("places.frecency.numCalcOnMigrate", 50);
pref("places.frecency.updateIdleTime", 60000);
pref("places.frecency.firstBucketCutoff", 4);
pref("places.frecency.secondBucketCutoff", 14);
pref("places.frecency.thirdBucketCutoff", 31);
pref("places.frecency.fourthBucketCutoff", 90);
pref("places.frecency.firstBucketWeight", 100);
pref("places.frecency.secondBucketWeight", 70);
pref("places.frecency.thirdBucketWeight", 50);
pref("places.frecency.fourthBucketWeight", 30);
pref("places.frecency.defaultBucketWeight", 10);
pref("places.frecency.embedVisitBonus", 0);
pref("places.frecency.linkVisitBonus", 100);
pref("places.frecency.typedVisitBonus", 2000);
pref("places.frecency.bookmarkVisitBonus", 150);
pref("places.frecency.downloadVisitBonus", 0);
pref("places.frecency.permRedirectVisitBonus", 0);
pref("places.frecency.tempRedirectVisitBonus", 0);
pref("places.frecency.defaultVisitBonus", 0);
pref("places.frecency.unvisitedBookmarkBonus", 140);
pref("places.frecency.unvisitedTypedBonus", 200);
// disable color management
pref("gfx.color_management.mode", 0);
// don't allow JS to move and resize existing windows
pref("dom.disable_window_move_resize", true);
// prevent click image resizing for nsImageDocument
pref("browser.enable_click_image_resizing", false);
// open in tab preferences
// 0=default window, 1=current window/tab, 2=new window, 3=new tab in most window
pref("browser.link.open_external", 3);
pref("browser.link.open_newwindow", 3);
// 0=force all new windows to tabs, 1=don't force, 2=only force those with no features set
pref("browser.link.open_newwindow.restriction", 0);
// controls which bits of private data to clear. by default we clear them all.
pref("privacy.item.cache", true);
pref("privacy.item.cookies", true);
pref("privacy.item.offlineApps", true);
pref("privacy.item.history", true);
pref("privacy.item.formdata", true);
pref("privacy.item.downloads", true);
pref("privacy.item.passwords", true);
pref("privacy.item.sessions", true);
pref("privacy.item.geolocation", true);
pref("privacy.item.siteSettings", true);
pref("privacy.item.syncAccount", true);
#ifdef MOZ_PLATFORM_MAEMO
pref("plugins.force.wmode", "opaque");
#endif
// URL to the Learn More link XXX this is the firefox one. Bug 495578 fixes this.
pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/geolocation/");
// enable geo
pref("geo.enabled", true);
// content sink control -- controls responsiveness during page load
// see https://bugzilla.mozilla.org/show_bug.cgi?id=481566#c9
//pref("content.sink.enable_perf_mode", 2); // 0 - switch, 1 - interactive, 2 - perf
//pref("content.sink.pending_event_mode", 0);
//pref("content.sink.perf_deflect_count", 1000000);
//pref("content.sink.perf_parse_time", 50000000);
// Disable methodjit in chrome to save memory
pref("javascript.options.methodjit.chrome", false);
pref("javascript.options.mem.high_water_mark", 32);
// Disable the JS engine's gc on memory pressure, since we do one in the mobile
// browser (bug 669346).
pref("javascript.options.gc_on_memory_pressure", false);
pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome
pref("dom.max_script_run_time", 20);
// JS error console
pref("devtools.errorconsole.enabled", false);
pref("browser.ui.layout.tablet", -1); // on: 1, off: 0, auto: -1
// kinetic tweakables
pref("browser.ui.kinetic.updateInterval", 16);
pref("browser.ui.kinetic.exponentialC", 1400);
pref("browser.ui.kinetic.polynomialC", 100);
pref("browser.ui.kinetic.swipeLength", 160);
// zooming
pref("browser.ui.zoom.pageFitGranularity", 9); // don't zoom to fit by less than 1/9 (11%)
pref("browser.ui.zoom.animationDuration", 200); // ms duration of double-tap zoom animation
pref("browser.ui.zoom.reflow", false); // Change text wrapping on double-tap
pref("browser.ui.zoom.reflow.fontSize", 720);
pref("font.size.inflation.minTwips", 120);
// pinch gesture
pref("browser.ui.pinch.maxGrowth", 150); // max pinch distance growth
pref("browser.ui.pinch.maxShrink", 200); // max pinch distance shrinkage
pref("browser.ui.pinch.scalingFactor", 500); // scaling factor for above pinch limits
// Touch radius (area around the touch location to look for target elements),
// in 1/240-inch pixels:
pref("browser.ui.touch.left", 8);
pref("browser.ui.touch.right", 8);
pref("browser.ui.touch.top", 12);
pref("browser.ui.touch.bottom", 4);
pref("browser.ui.touch.weight.visited", 120); // percentage
// plugins
#if MOZ_PLATFORM_MAEMO == 6
pref("plugin.disable", false);
pref("dom.ipc.plugins.enabled", true);
#elifdef ANDROID
pref("plugin.disable", false);
pref("dom.ipc.plugins.enabled", false);
#else
pref("plugin.disable", true);
pref("dom.ipc.plugins.enabled", true);
#endif
// process priority
// higher values give content process less CPU time
#if MOZ_PLATFORM_MAEMO == 5
pref("dom.ipc.content.nice", 10);
#else
pref("dom.ipc.content.nice", 1);
#endif
// product URLs
// The breakpad report server to link to in about:crashes
pref("breakpad.reportURL", "http://crash-stats.mozilla.com/report/index/");
pref("app.releaseNotesURL", "http://www.mozilla.com/%LOCALE%/mobile/%VERSION%/releasenotes/");
pref("app.sync.tutorialURL", "https://support.mozilla.com/kb/sync-firefox-between-desktop-and-mobile");
pref("app.support.baseURL", "http://support.mozilla.com/mobile");
pref("app.feedbackURL", "http://input.mozilla.com/feedback/");
pref("app.privacyURL", "http://www.mozilla.com/%LOCALE%/m/privacy.html");
pref("app.creditsURL", "http://www.mozilla.org/credits/");
pref("app.channelURL", "http://www.mozilla.org/%LOCALE%/firefox/channel/");
#if MOZ_UPDATE_CHANNEL == beta
pref("app.featuresURL", "http://www.mozilla.com/%LOCALE%/mobile/beta/features/");
pref("app.faqURL", "http://www.mozilla.com/%LOCALE%/mobile/beta/faq/");
#else
pref("app.featuresURL", "http://www.mozilla.com/%LOCALE%/mobile/features/");
pref("app.faqURL", "http://www.mozilla.com/%LOCALE%/mobile/faq/");
#endif
// Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror)
pref("security.alternate_certificate_error_page", "certerror");
pref("security.warn_viewing_mixed", false); // Warning is disabled. See Bug 616712.
// Override some named colors to avoid inverse OS themes
pref("ui.-moz-dialog", "#efebe7");
pref("ui.-moz-dialogtext", "#101010");
pref("ui.-moz-field", "#fff");
pref("ui.-moz-fieldtext", "#1a1a1a");
pref("ui.-moz-buttonhoverface", "#f3f0ed");
pref("ui.-moz-buttonhovertext", "#101010");
pref("ui.-moz-combobox", "#fff");
pref("ui.-moz-comboboxtext", "#101010");
pref("ui.buttonface", "#ece7e2");
pref("ui.buttonhighlight", "#fff");
pref("ui.buttonshadow", "#aea194");
pref("ui.buttontext", "#101010");
pref("ui.captiontext", "#101010");
pref("ui.graytext", "#b1a598");
pref("ui.highlight", "#fad184");
pref("ui.highlighttext", "#1a1a1a");
pref("ui.infobackground", "#f5f5b5");
pref("ui.infotext", "#000");
pref("ui.menu", "#f7f5f3");
pref("ui.menutext", "#101010");
pref("ui.threeddarkshadow", "#000");
pref("ui.threedface", "#ece7e2");
pref("ui.threedhighlight", "#fff");
pref("ui.threedlightshadow", "#ece7e2");
pref("ui.threedshadow", "#aea194");
pref("ui.window", "#efebe7");
pref("ui.windowtext", "#101010");
pref("ui.windowframe", "#efebe7");
#ifdef MOZ_OFFICIAL_BRANDING
pref("browser.search.param.yahoo-fr", "moz35");
pref("browser.search.param.yahoo-fr-cjkt", "moz35");
pref("browser.search.param.yahoo-fr-ja", "mozff");
#endif
/* app update prefs */
pref("app.update.timer", 60000); // milliseconds (1 min)
#ifdef MOZ_UPDATER
pref("app.update.enabled", true);
pref("app.update.timerFirstInterval", 20000); // milliseconds
pref("app.update.auto", false);
pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@");
pref("app.update.mode", 1);
pref("app.update.silent", false);
pref("app.update.url", "https://aus2.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PLATFORM_VERSION%/update.xml");
pref("app.update.promptWaitTime", 43200);
pref("app.update.idletime", 60);
pref("app.update.showInstalledUI", false);
pref("app.update.incompatible.mode", 0);
pref("app.update.download.backgroundInterval", 0);
#ifdef MOZ_OFFICIAL_BRANDING
pref("app.update.interval", 86400);
pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/m/");
pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/releases/");
#else
pref("app.update.interval", 28800);
pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/mobile/");
pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/");
#endif
#endif
// replace newlines with spaces on paste into single-line text boxes
pref("editor.singleLine.pasteNewlines", 2);
#ifdef MOZ_PLATFORM_MAEMO
// update fonts for better readability
pref("font.default.x-baltic", "SwissA");
pref("font.default.x-central-euro", "SwissA");
pref("font.default.x-cyrillic", "SwissA");
pref("font.default.x-unicode", "SwissA");
pref("font.default.x-user-def", "SwissA");
pref("font.default.x-western", "SwissA");
#endif
#ifdef MOZ_SERVICES_SYNC
// sync service
pref("services.sync.client.type", "mobile");
pref("services.sync.registerEngines", "Tab,Bookmarks,Form,History,Password,Prefs");
pref("services.sync.autoconnectDelay", 5);
// prefs to sync by default
pref("services.sync.prefs.sync.browser.startup.homepage.title", true);
pref("services.sync.prefs.sync.browser.startup.homepage", true);
pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
pref("services.sync.prefs.sync.browser.ui.zoom.reflow", true);
pref("services.sync.prefs.sync.devtools.errorconsole.enabled", true);
pref("services.sync.prefs.sync.javascript.enabled", true);
pref("services.sync.prefs.sync.lightweightThemes.isThemeSelected", true);
pref("services.sync.prefs.sync.lightweightThemes.usedThemes", true);
pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true);
pref("services.sync.prefs.sync.permissions.default.image", true);
pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
pref("services.sync.prefs.sync.signon.rememberSignons", true);
#endif
// threshold where a tap becomes a drag, in 1/240" reference pixels
// The names of the preferences are to be in sync with nsEventStateManager.cpp
pref("ui.dragThresholdX", 25);
pref("ui.dragThresholdY", 25);
#if MOZ_PLATFORM_MAEMO == 6
pref("layers.acceleration.disabled", false);
#elifdef ANDROID
pref("layers.acceleration.disabled", false);
#else
pref("layers.acceleration.disabled", true);
#endif
pref("notification.feature.enabled", true);
// prevent tooltips from showing up
pref("browser.chrome.toolbar_tips", false);
pref("indexedDB.feature.enabled", true);
pref("dom.indexedDB.warningQuota", 5);
// prevent video elements from preloading too much data
pref("media.preload.default", 1); // default to preload none
pref("media.preload.auto", 2); // preload metadata if preload=auto
// 0: don't show fullscreen keyboard
// 1: always show fullscreen keyboard
// -1: show fullscreen keyboard based on threshold pref
pref("widget.ime.android.landscape_fullscreen", -1);
pref("widget.ime.android.fullscreen_threshold", 250); // in hundreths of inches
// optimize images memory usage
pref("image.mem.decodeondraw", true);
pref("content.image.allow_locking", false);
pref("image.mem.min_discard_timeout_ms", 10000);
// enable touch events interfaces
pref("dom.w3c_touch_events.enabled", true);
pref("dom.w3c_touch_events.safetyX", 5); // escape borders in units of 1/240"
pref("dom.w3c_touch_events.safetyY", 20); // escape borders in units of 1/240"
#ifdef MOZ_SAFE_BROWSING
// Safe browsing does nothing unless this pref is set
pref("browser.safebrowsing.enabled", true);
// Prevent loading of pages identified as malware
pref("browser.safebrowsing.malware.enabled", true);
// Non-enhanced mode (local url lists) URL list to check for updates
pref("browser.safebrowsing.provider.0.updateURL", "http://safebrowsing.clients.google.com/safebrowsing/downloads?client={moz:client}&appver={moz:version}&pver=2.2");
pref("browser.safebrowsing.dataProvider", 0);
// Does the provider name need to be localizable?
pref("browser.safebrowsing.provider.0.name", "Google");
pref("browser.safebrowsing.provider.0.keyURL", "https://sb-ssl.google.com/safebrowsing/newkey?client={moz:client}&appver={moz:version}&pver=2.2");
pref("browser.safebrowsing.provider.0.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/report?");
pref("browser.safebrowsing.provider.0.gethashURL", "http://safebrowsing.clients.google.com/safebrowsing/gethash?client={moz:client}&appver={moz:version}&pver=2.2");
// HTML report pages
pref("browser.safebrowsing.provider.0.reportGenericURL", "http://{moz:locale}.phish-generic.mozilla.com/?hl={moz:locale}");
pref("browser.safebrowsing.provider.0.reportErrorURL", "http://{moz:locale}.phish-error.mozilla.com/?hl={moz:locale}");
pref("browser.safebrowsing.provider.0.reportPhishURL", "http://{moz:locale}.phish-report.mozilla.com/?hl={moz:locale}");
pref("browser.safebrowsing.provider.0.reportMalwareURL", "http://{moz:locale}.malware-report.mozilla.com/?hl={moz:locale}");
pref("browser.safebrowsing.provider.0.reportMalwareErrorURL", "http://{moz:locale}.malware-error.mozilla.com/?hl={moz:locale}");
// FAQ URLs
pref("browser.safebrowsing.warning.infoURL", "http://www.mozilla.com/%LOCALE%/%APP%/phishing-protection/");
pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/%APP%/geolocation/");
// Name of the about: page contributed by safebrowsing to handle display of error
// pages on phishing/malware hits. (bug 399233)
pref("urlclassifier.alternate_error_page", "blocked");
// The number of random entries to send with a gethash request.
pref("urlclassifier.gethashnoise", 4);
// The list of tables that use the gethash request to confirm partial results.
pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
// If an urlclassifier table has not been updated in this number of seconds,
// a gethash request will be forced to check that the result is still in
// the database.
pref("urlclassifier.confirm-age", 2700);
// Maximum size of the sqlite3 cache during an update, in bytes
pref("urlclassifier.updatecachemax", 4194304);
// URL for checking the reason for a malware warning.
pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
#endif
// True if this is the first time we are showing about:firstrun
pref("browser.firstrun.show.uidiscovery", true);
pref("browser.firstrun.show.localepicker", false);
// True if you always want dump() to work
//
// On Android, you also need to do the following for the output
// to show up in logcat:
//
// $ adb shell stop
// $ adb shell setprop log.redirect-stdio true
// $ adb shell start
pref("browser.dom.window.dump.enabled", true);
// controls if we want camera support
pref("device.camera.enabled", true);
pref("media.realtime_decoder.enabled", true);
pref("dom.report_all_js_exceptions", true);
pref("javascript.options.showInConsole", true);
pref("full-screen-api.enabled", true);

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

@ -0,0 +1,67 @@
# ***** 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.
#
# The Initial Developer of the Original Code is
# the Mozilla Foundation <http://www.mozilla.org/>.
# Portions created by the Initial Developer are Copyright (C) 2010
# 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 *****
DEPTH = ../../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
EXTENSIONS_DIR = $(call core_abspath,$(DIST))/bin/extensions
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
ifneq (,$(filter nightly aurora beta,$(MOZ_UPDATE_CHANNEL)))
EXTENSIONS = \
feedback@mobile.mozilla.org \
$(NULL)
define _INSTALL_EXTENSION
$(NSINSTALL) -D $(dir) && \
$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(srcdir)/$(dir)/install.rdf.in > $(dir)/install.rdf && \
cd $(dir) && \
$(ZIP) -r9XD $(EXTENSIONS_DIR)/$(dir).xpi install.rdf && \
cd $(srcdir)/$(dir) && \
$(ZIP) -r9XD $(EXTENSIONS_DIR)/$(dir).xpi * -x install.rdf.in
endef # do not remove the blank line!
libs::
$(NSINSTALL) -D $(EXTENSIONS_DIR)
$(foreach dir,$(EXTENSIONS),$(_INSTALL_EXTENSION))
endif

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

@ -0,0 +1,5 @@
content feedback content/
skin feedback classic/1.0 skin/
locale feedback en-US locale/en-US/
overlay chrome://browser/content/browser.xul chrome://feedback/content/overlay.xul

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

@ -0,0 +1,33 @@
function populateFeedback(aMessage) {
let json = aMessage.json;
let referrer = json.referrer;
let URLElem = content.document.getElementById("id_url");
if (URLElem)
URLElem.value = referrer;
let URLElems = content.document.getElementsByClassName("url");
for (let index=0; index<URLElems.length; index++)
URLElems[index].value = referrer;
let device = json.device || "";
let deviceElem = content.document.getElementById("id_device");
if (deviceElem)
deviceElem.value = device;
let deviceElems = content.document.getElementsByClassName("device");
for (let index=0; index<deviceElems.length; index++)
deviceElems[index].value = device;
let manufacturer = json.manufacturer || "";
let manufacturerElem = content.document.getElementById("id_manufacturer");
if (manufacturerElem)
manufacturerElem.value = manufacturer;
let manufacturerElems = content.document.getElementsByClassName("manufacturer");
for (let index=0; index<manufacturerElems.length; index++)
manufacturerElems[index].value = manufacturer;
}
addMessageListener("Feedback:InitPage", populateFeedback);

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

@ -0,0 +1,148 @@
/* ***** 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 Feedback.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mark.finkle@gmail.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 ***** */
var Feedback = {
_prefs: [],
_device: "",
_manufacturer: "",
init: function(aEvent) {
// Delay the widget initialization during startup.
let panel = document.getElementById("feedback-container");
panel.addEventListener("ToolPanelShown", function delayedInit(aEvent) {
panel.removeEventListener("ToolPanelShown", delayedInit, false);
// A simple frame script to fill in the referrer page and device info
messageManager.loadFrameScript("chrome://feedback/content/content.js", true);
let setting = document.getElementById("feedback-checkCompatibility");
setting.setAttribute("pref", Feedback.compatibilityPref);
setting.preferenceChanged();
document.getElementById("feedback-container").hidden = false;
let feedbackPrefs = document.getElementById("feedback-tools").childNodes;
for (let i = 0; i < feedbackPrefs.length; i++) {
let pref = feedbackPrefs[i].getAttribute("pref");
if (!pref)
continue;
let value = Services.prefs.getPrefType(pref) == Ci.nsIPrefBranch.PREF_INVALID ? false : Services.prefs.getBoolPref(pref);
Feedback._prefs.push({ "name": pref, "value": value });
}
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
Feedback._device = sysInfo.get("device");
Feedback._manufacturer = sysInfo.get("manufacturer");
}, false);
},
get compatibilityPref() {
let result = "extensions.checkCompatibility.";
let channel = Services.prefs.getCharPref("app.update.channel");
if (channel == "nightly") {
result += "nightly";
} else {
// Copied from toolkit/mozapps/extensions/XPIProvider.jsm
const BRANCH_REGEXP = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
result += Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
}
delete this.compatibilityPref;
return this.compatibilityPref = result;
},
openFeedback: function(aName) {
let pref = "extensions.feedback.url." + aName;
let url = Services.prefs.getPrefType(pref) == Ci.nsIPrefBranch.PREF_INVALID ? "" : Services.prefs.getCharPref(pref);
if (!url)
return;
let currentURL = Browser.selectedBrowser.currentURI.spec;
let newTab = BrowserUI.newTab(url, Browser.selectedTab);
// Tell the feedback page to fill in the referrer URL
newTab.browser.messageManager.addMessageListener("DOMContentLoaded", function() {
newTab.browser.messageManager.removeMessageListener("DOMContentLoaded", arguments.callee, true);
newTab.browser.messageManager.sendAsyncMessage("Feedback:InitPage", { referrer: currentURL, device: Feedback._device, manufacturer: Feedback._manufacturer });
});
},
updateRestart: function updateRestart() {
let msg = document.getElementById("feedback-messages");
if (msg) {
let value = "restart-app";
let notification = msg.getNotificationWithValue(value);
if (notification) {
// Check if the prefs are back to the initial state dismiss the restart
// notification because if does not make sense anymore
for each (let pref in this._prefs) {
let value = Services.prefs.getPrefType(pref.name) == Ci.nsIPrefBranch.PREF_INVALID ? false : Services.prefs.getBoolPref(pref.name);
if (value != pref.value)
return;
}
notification.close();
return;
}
let restartCallback = function(aNotification, aDescription) {
// Notify all windows that an application quit has been requested
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
// If nothing aborted, quit the app
if (cancelQuit.data == false) {
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
}
};
let strings = Strings.browser;
let buttons = [ {
label: strings.GetStringFromName("notificationRestart.button"),
accessKey: "",
callback: restartCallback
} ];
let message = strings.GetStringFromName("notificationRestart.normal");
msg.appendNotification(message, value, "", msg.PRIORITY_WARNING_LOW, buttons);
}
}
};
window.addEventListener("load", Feedback.init, false);

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

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- ***** 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 Feedback.
-
- The Initial Developer of the Original Code is
- Mozilla Corporation.
- Portions created by the Initial Developer are Copyright (C) 2010
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
- Mark Finkle <mark.finkle@gmail.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 LGPL or the GPL. 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 ***** -->
<?xml-stylesheet href="chrome://feedback/skin/overlay.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % feedbackDTD SYSTEM "chrome://browser/locale/feedback.dtd">
%feedbackDTD;
]>
<overlay id="feedback-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript" src="chrome://feedback/content/overlay.js"/>
<box id="panel-controls">
<toolbarbutton id="tool-feedback" class="panel-row-button" autocheck="true" type="radio" label="&feedbackHeader2.label;" group="1" linkedpanel="feedback-container" insertafter="tool-addons"/>
</box>
<deck id="panel-items">
<vbox id="feedback-container" flex="1" hidden="true">
<notificationbox id="feedback-messages" flex="1">
<richlistbox id="feedback-list" flex="1" onselect="this.ensureSelectedElementIsVisible()">
<label id="feedback-list-header" class="panel-header" value="&feedbackHeader2.label;"/>
<settings id="feedback-communicate" label="&feedback.communicate.title;">
<setting title="&feedback.feedback.title;" type="control">
<button id="feedback-feedback-happy" oncommand="Feedback.openFeedback('happy');"/>
<button id="feedback-feedback-sad" oncommand="Feedback.openFeedback('sad');"/>
</setting>
</settings>
<settings id="feedback-tools" label="&feedback.tools.title;">
<setting pref="toolkit.telemetry.enabled" title="&feedback.allowTelemetry.title;" type="bool"/>
<setting id="feedback-checkCompatibility" title="&feedback.forceCompat.title;" type="bool" inverted="true" oninputchanged="Feedback.updateRestart();"/>
<setting pref="devtools.errorconsole.enabled" title="&feedback.errorConsole.title;" type="bool"/>
</settings>
</richlistbox>
</notificationbox>
</vbox>
</deck>
</overlay>

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

@ -0,0 +1,2 @@
pref("extensions.feedback.url.happy", "http://input.mozilla.com/happy");
pref("extensions.feedback.url.sad", "http://input.mozilla.com/sad");

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

@ -0,0 +1,28 @@
<?xml version="1.0"?>
#filter substitution
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>feedback@mobile.mozilla.org</em:id>
<em:version>1.0.1</em:version>
<em:type>2</em:type>
<!-- Target Application this extension can install into,
with minimum and maximum supported versions. -->
<em:targetApplication>
<Description>
<em:id>{a23983c0-fd0e-11dc-95ff-0800200c9a66}</em:id>
<em:minVersion>4.0</em:minVersion>
<em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Feedback</em:name>
<em:description>Help make Firefox better by giving feedback.</em:description>
<em:creator>Mozilla Corporation</em:creator>
<em:iconURL>chrome://feedback/skin/dino-32.png</em:iconURL>
</Description>
</RDF>

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

После

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

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

После

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

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

После

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

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

@ -0,0 +1,11 @@
#tool-feedback {
list-style-image: url("chrome://feedback/skin/beta-hdpi.png");
}
#feedback-feedback-happy {
list-style-image: url("chrome://feedback/skin/happy-32.png");
}
#feedback-feedback-sad {
list-style-image: url("chrome://feedback/skin/sad-32.png");
}

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

После

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

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -0,0 +1,293 @@
/* -*- 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);
}
});
}
InputStream getProfileRecommendedAddonsStream() {
try {
File profileDir = GeckoApp.mAppContext.getProfileDir();
if (profileDir == null)
return null;
File recommendedAddonsFile = new File(profileDir, "recommended-addons.json");
if (!recommendedAddonsFile.exists())
return null;
return new FileInputStream(recommendedAddonsFile);
} catch (FileNotFoundException fnfe) {
// ignore
}
return null;
}
InputStream getRecommendedAddonsStream(Activity activity) throws Exception{
InputStream is = getProfileRecommendedAddonsStream();
if (is != null)
return is;
File applicationPackage = new File(activity.getApplication().getPackageResourcePath());
ZipFile zip = new ZipFile(applicationPackage);
ZipEntry fileEntry = zip.getEntry("recommended-addons.json");
return zip.getInputStream(fileEntry);
}
void readRecommendedAddons(final Activity activity) {
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
try {
byte[] buf = new byte[32768];
InputStream fileStream = getRecommendedAddonsStream(activity);
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;
}
}

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

@ -50,30 +50,31 @@ import java.text.NumberFormat;
public class AlertNotification
extends Notification
{
Context mContext;
int mId;
int mIcon;
String mTitle;
String mText;
boolean mProgressStyle;
NotificationManager mNotificationManager;
double mPrevPercent = -1;
String mPrevAlertText = "";
static final double UPDATE_THRESHOLD = .01;
private static final String LOGTAG = "GeckoAlertNotification";
private final int mId;
private final int mIcon;
private final String mTitle;
private final String mText;
private final NotificationManager mNotificationManager;
private boolean mProgressStyle; // = false
private double mPrevPercent = -1;
private String mPrevAlertText = "";
private static final double UPDATE_THRESHOLD = .01;
public AlertNotification(Context aContext, int aNotificationId, int aIcon,
String aTitle, String aText, long aWhen) {
super(aIcon, (aText.length() > 0) ? aText : aTitle, aWhen);
mContext = aContext;
mIcon = aIcon;
mTitle = aTitle;
mText = aText;
mProgressStyle = false;
mId = aNotificationId;
mNotificationManager = (NotificationManager)
mContext.getSystemService(Context.NOTIFICATION_SERVICE);
aContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
public boolean isProgressStyle() {
@ -94,15 +95,15 @@ public class AlertNotification
try {
URL url = new URL(aIconUri.toString());
Bitmap bm = BitmapFactory.decodeStream(url.openStream());
view.setImageViewBitmap(R.id.notificationImage, bm);
view.setTextViewText(R.id.notificationTitle, mTitle);
view.setImageViewBitmap(R.id.notification_image, bm);
view.setTextViewText(R.id.notification_title, mTitle);
if (mText.length() > 0) {
view.setTextViewText(R.id.notificationText, mText);
view.setTextViewText(R.id.notification_text, mText);
}
contentView = view;
mNotificationManager.notify(mId, this);
} catch(Exception ex) {
Log.e("GeckoAlert", "failed to create bitmap", ex);
Log.e(LOGTAG, "failed to create bitmap", ex);
}
}
@ -112,8 +113,8 @@ public class AlertNotification
int layout = aAlertText.length() > 0 ? R.layout.notification_progress_text : R.layout.notification_progress;
RemoteViews view = new RemoteViews(GeckoApp.mAppContext.getPackageName(), layout);
view.setImageViewResource(R.id.notificationImage, mIcon);
view.setTextViewText(R.id.notificationTitle, mTitle);
view.setImageViewResource(R.id.notification_image, mIcon);
view.setTextViewText(R.id.notification_title, mTitle);
contentView = view;
flags |= FLAG_ONGOING_EVENT | FLAG_ONLY_ALERT_ONCE;
@ -133,8 +134,8 @@ public class AlertNotification
if (mPrevAlertText.equals(text) && Math.abs(mPrevPercent - percent) < UPDATE_THRESHOLD)
return;
contentView.setTextViewText(R.id.notificationText, text);
contentView.setProgressBar(R.id.notificationProgressbar, (int)aProgressMax, (int)aProgress, false);
contentView.setTextViewText(R.id.notification_text, text);
contentView.setProgressBar(R.id.notification_progressbar, (int)aProgressMax, (int)aProgress, false);
// Update the notification
mNotificationManager.notify(mId, this);

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

@ -21,9 +21,8 @@
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<!-- WebSMS -->
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
<uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/>
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
@ -43,10 +42,10 @@
<activity android:name="App"
android:label="@MOZ_APP_DISPLAYNAME@"
android:configChanges="keyboard|keyboardHidden|mcc|mnc"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:launchMode="singleTask"
android:theme="@style/GreyTheme">
android:theme="@style/Gecko">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@ -98,7 +97,7 @@
<activity android:name="Restarter"
android:process="@ANDROID_PACKAGE_NAME@Restarter"
android:theme="@style/GreyTheme"
android:theme="@style/Gecko"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="org.mozilla.gecko.restart"/>
@ -109,6 +108,7 @@
<activity android:name="CrashReporter"
android:label="@string/crash_reporter_title"
android:icon="@drawable/crash_reporter"
android:theme="@style/Gecko"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="org.mozilla.gecko.reportCrash" />
@ -118,12 +118,26 @@
<activity android:name="LauncherShortcuts"
android:label="@string/launcher_shortcuts_title"
android:theme="@android:style/Theme.Translucent">
android:theme="@style/Gecko.Translucent">
<!-- This intent-filter allows your shortcuts to be created in the launcher. -->
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="org.mozilla.gecko.AwesomeBar"
android:theme="@style/Gecko.Light.NoActionBar"
android:configChanges="orientation"
android:windowSoftInputMode="stateAlwaysVisible|adjustResize"/>
<activity android:name="org.mozilla.gecko.TabsTray"
android:theme="@style/Gecko.Translucent"/>
<activity android:name="org.mozilla.gecko.GeckoPreferences"
android:theme="@style/Gecko.TitleBar"
android:label="@string/preferences_title"
android:excludeFromRecents="true"/>
</application>
</manifest>

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

@ -44,6 +44,7 @@ public class App extends GeckoApp {
public String getPackageName() {
return "@ANDROID_PACKAGE_NAME@";
}
public String getContentProcessName() {
return "@MOZ_CHILD_PROCESS_NAME@";
}

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

@ -0,0 +1,290 @@
/* -*- 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):
* Vladimir Vukicevic <vladimir@pobox.com>
* Wes Johnston <wjohnston@mozilla.com>
* 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 ***** */
package org.mozilla.gecko;
import java.io.File;
import android.app.Activity;
import android.content.Intent;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Configuration;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
public class AwesomeBar extends Activity {
private static final String LOGTAG = "GeckoAwesomeBar";
static final String URL_KEY = "url";
static final String TITLE_KEY = "title";
static final String CURRENT_URL_KEY = "currenturl";
static final String TYPE_KEY = "type";
static enum Type { ADD, EDIT };
private String mType;
private AwesomeBarTabs mAwesomeTabs;
private AwesomeBarEditText mText;
private ImageButton mGoButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(LOGTAG, "creating awesomebar");
setContentView(R.layout.awesomebar_search);
mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs);
mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() {
public void onUrlOpen(AwesomeBarTabs tabs, String url) {
openUrlAndFinish(url);
}
});
mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
mGoButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
openUrlAndFinish(mText.getText().toString());
}
});
mText = (AwesomeBarEditText) findViewById(R.id.awesomebar_text);
Resources resources = getResources();
int padding[] = { mText.getPaddingLeft(),
mText.getPaddingTop(),
mText.getPaddingRight(),
mText.getPaddingBottom() };
GeckoStateListDrawable states = new GeckoStateListDrawable();
states.initializeFilter(GeckoApp.mBrowserToolbar.getHighlightColor());
states.addState(new int[] { android.R.attr.state_focused }, resources.getDrawable(R.drawable.address_bar_url_pressed));
states.addState(new int[] { android.R.attr.state_pressed }, resources.getDrawable(R.drawable.address_bar_url_pressed));
states.addState(new int[] { }, resources.getDrawable(R.drawable.address_bar_url_default));
mText.setBackgroundDrawable(states);
mText.setPadding(padding[0], padding[1], padding[2], padding[3]);
Intent intent = getIntent();
String currentUrl = intent.getStringExtra(CURRENT_URL_KEY);
mType = intent.getStringExtra(TYPE_KEY);
if (currentUrl != null) {
mText.setText(currentUrl);
mText.selectAll();
}
mText.setOnKeyPreImeListener(new AwesomeBarEditText.OnKeyPreImeListener() {
public boolean onKeyPreIme(View v, int keyCode, KeyEvent event) {
InputMethodManager imm =
(InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
// If input method is in fullscreen mode, we want to dismiss
// it instead of closing awesomebar straight away.
if (!imm.isFullscreenMode() && keyCode == KeyEvent.KEYCODE_BACK) {
cancelAndFinish();
return true;
}
return false;
}
});
mText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
// do nothing
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// do nothing
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
String text = s.toString();
mAwesomeTabs.filter(text);
updateGoButton(text);
}
});
mText.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
if (event.getAction() != KeyEvent.ACTION_DOWN)
return true;
openUrlAndFinish(mText.getText().toString());
return true;
} else {
return false;
}
}
});
mText.setOnFocusChangeListener(new View.OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
});
}
@Override
public void onConfigurationChanged(Configuration newConfiguration) {
super.onConfigurationChanged(newConfiguration);
}
@Override
public boolean onSearchRequested() {
cancelAndFinish();
return true;
}
private void updateGoButton(String text) {
if (text.length() == 0) {
mGoButton.setVisibility(View.GONE);
return;
}
mGoButton.setVisibility(View.VISIBLE);
int imageResource = R.drawable.ic_awesomebar_go;
if (!GeckoAppShell.canCreateFixupURI(text)) {
imageResource = R.drawable.ic_awesomebar_search;
}
mGoButton.setImageResource(imageResource);
}
private void cancelAndFinish() {
setResult(Activity.RESULT_CANCELED);
finish();
}
private void openUrlAndFinish(String url) {
Intent resultIntent = new Intent();
resultIntent.putExtra(URL_KEY, url);
resultIntent.putExtra(TYPE_KEY, mType);
setResult(Activity.RESULT_OK, resultIntent);
finish();
overridePendingTransition(0, 0);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// This method is called only if the key event was not handled
// by any of the views, which usually means the edit box lost focus
if (keyCode == KeyEvent.KEYCODE_BACK ||
keyCode == KeyEvent.KEYCODE_MENU ||
keyCode == KeyEvent.KEYCODE_SEARCH ||
keyCode == KeyEvent.KEYCODE_DPAD_UP ||
keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
keyCode == KeyEvent.KEYCODE_DPAD_LEFT ||
keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
keyCode == KeyEvent.KEYCODE_DPAD_CENTER ||
keyCode == KeyEvent.KEYCODE_DEL) {
return super.onKeyDown(keyCode, event);
} else {
int selStart = -1;
int selEnd = -1;
if (mText.hasSelection()) {
selStart = mText.getSelectionStart();
selEnd = mText.getSelectionEnd();
}
// Return focus to the edit box, and dispatch the event to it
mText.requestFocusFromTouch();
if (selStart >= 0) {
// Restore the selection, which gets lost due to the focus switch
mText.setSelection(selStart, selEnd);
}
mText.dispatchKeyEvent(event);
return true;
}
}
@Override
public void onDestroy() {
super.onDestroy();
mAwesomeTabs.destroy();
}
public static class AwesomeBarEditText extends EditText {
OnKeyPreImeListener mOnKeyPreImeListener;
public interface OnKeyPreImeListener {
public boolean onKeyPreIme(View v, int keyCode, KeyEvent event);
}
public AwesomeBarEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mOnKeyPreImeListener = null;
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (mOnKeyPreImeListener != null)
return mOnKeyPreImeListener.onKeyPreIme(this, keyCode, event);
return false;
}
public void setOnKeyPreImeListener(OnKeyPreImeListener listener) {
mOnKeyPreImeListener = listener;
}
}
}

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

@ -0,0 +1,633 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Lucas Rocha <lucasr@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.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.provider.Browser;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ExpandableListView;
import android.widget.FilterQueryProvider;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.SimpleExpandableListAdapter;
import android.widget.TabHost;
import android.widget.TextView;
import java.lang.ref.WeakReference;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class AwesomeBarTabs extends TabHost {
private static final String LOGTAG = "GeckoAwesomeBarTabs";
private static final String ALL_PAGES_TAB = "all";
private static final String BOOKMARKS_TAB = "bookmarks";
private static final String HISTORY_TAB = "history";
private static enum HistorySection { TODAY, YESTERDAY, WEEK, OLDER };
private Context mContext;
private OnUrlOpenListener mUrlOpenListener;
private View.OnTouchListener mListTouchListener;
private SimpleCursorAdapter mAllPagesAdapter;
private SimpleCursorAdapter mBookmarksAdapter;
private SimpleExpandableListAdapter mHistoryAdapter;
// FIXME: This value should probably come from a
// prefs key (just like XUL-based fennec)
private static final int MAX_RESULTS = 100;
public interface OnUrlOpenListener {
public abstract void onUrlOpen(AwesomeBarTabs tabs, String url);
}
private class HistoryListAdapter extends SimpleExpandableListAdapter {
public HistoryListAdapter(Context context, List<? extends Map<String, ?>> groupData,
int groupLayout, String[] groupFrom, int[] groupTo,
List<? extends List<? extends Map<String, ?>>> childData,
int childLayout, String[] childFrom, int[] childTo) {
super(context, groupData, groupLayout, groupFrom, groupTo,
childData, childLayout, childFrom, childTo);
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
View childView =
super.getChildView(groupPosition, childPosition, isLastChild, convertView, parent);
@SuppressWarnings("unchecked")
Map<String,Object> historyItem =
(Map<String,Object>) mHistoryAdapter.getChild(groupPosition, childPosition);
byte[] b = (byte[]) historyItem.get(Browser.BookmarkColumns.FAVICON);
ImageView favicon = (ImageView) childView.findViewById(R.id.favicon);
if (b == null) {
favicon.setImageResource(R.drawable.favicon);
} else {
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
favicon.setImageBitmap(bitmap);
}
return childView;
}
}
private class AwesomeCursorViewBinder implements SimpleCursorAdapter.ViewBinder {
private boolean updateFavicon(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;
}
@Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
int faviconIndex = cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.FAVICON);
if (columnIndex == faviconIndex) {
return updateFavicon(view, cursor, faviconIndex);
}
int titleIndex = cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.TITLE);
if (columnIndex == titleIndex) {
return updateTitle(view, cursor, titleIndex);
}
// Other columns are handled automatically
return false;
}
}
private class BookmarksQueryTask extends AsyncTask<Void, Void, Cursor> {
protected Cursor doInBackground(Void... arg0) {
ContentResolver resolver = mContext.getContentResolver();
return resolver.query(Browser.BOOKMARKS_URI,
null,
// Select all bookmarks with a non-empty URL. When the history
// is empty there appears to be a dummy entry in the database
// which has a title of "Bookmarks" and no URL; the length restriction
// is to avoid picking that up specifically.
Browser.BookmarkColumns.BOOKMARK + " = 1 AND LENGTH(" + Browser.BookmarkColumns.URL + ") > 0",
null,
Browser.BookmarkColumns.TITLE);
}
protected void onPostExecute(Cursor cursor) {
// Load the list using a custom adapter so we can create the bitmaps
mBookmarksAdapter = new SimpleCursorAdapter(
mContext,
R.layout.awesomebar_row,
cursor,
new String[] { AwesomeBar.TITLE_KEY,
AwesomeBar.URL_KEY,
Browser.BookmarkColumns.FAVICON },
new int[] { R.id.title, R.id.url, R.id.favicon }
);
mBookmarksAdapter.setViewBinder(new AwesomeCursorViewBinder());
final ListView bookmarksList = (ListView) findViewById(R.id.bookmarks_list);
bookmarksList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
handleItemClick(bookmarksList, position);
}
});
bookmarksList.setAdapter(mBookmarksAdapter);
}
}
private class HistoryQueryTask extends AsyncTask<Void, Void, Pair<List,List>> {
private static final long MS_PER_DAY = 86400000;
private static final long MS_PER_WEEK = MS_PER_DAY * 7;
protected Pair<List,List> doInBackground(Void... arg0) {
Pair<List,List> result = null;
ContentResolver resolver = mContext.getContentResolver();
Cursor cursor =
resolver.query(Browser.BOOKMARKS_URI,
null,
// Bookmarks that have not been visited have a date value
// of 0, so don't pick them up in the history view.
Browser.BookmarkColumns.DATE + " > 0",
null,
Browser.BookmarkColumns.DATE + " DESC LIMIT " + MAX_RESULTS);
Date now = new Date();
now.setHours(0);
now.setMinutes(0);
now.setSeconds(0);
long today = now.getTime();
// Split the list of urls into separate date range groups
// and show it in an expandable list view.
List<List<Map<String,?>>> childrenLists = null;
List<Map<String,?>> children = null;
List<Map<String,?>> groups = null;
HistorySection section = null;
// Move cursor before the first row in preparation
// for the iteration.
cursor.moveToPosition(-1);
// Split the history query results into adapters per time
// section (today, yesterday, week, older). Queries on content
// Browser content provider don't support limitting the number
// of returned rows so we limit it here.
while (cursor.moveToNext()) {
long time = cursor.getLong(cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.DATE));
HistorySection itemSection = getSectionForTime(time, today);
if (groups == null)
groups = new LinkedList<Map<String,?>>();
if (childrenLists == null)
childrenLists = new LinkedList<List<Map<String,?>>>();
if (section != itemSection) {
if (section != null) {
groups.add(createGroupItem(section));
childrenLists.add(children);
}
section = itemSection;
children = new LinkedList<Map<String,?>>();
}
children.add(createHistoryItem(cursor));
}
// Add any remaining section to the list if it hasn't
// been added to the list after the loop.
if (section != null && children != null) {
groups.add(createGroupItem(section));
childrenLists.add(children);
}
// Close the query cursor as we won't use it anymore
cursor.close();
if (groups != null && childrenLists != null) {
result = Pair.create((List) groups, (List) childrenLists);
}
return result;
}
public Map<String,?> createHistoryItem(Cursor cursor) {
Map<String,Object> historyItem = new HashMap<String,Object>();
String url = cursor.getString(cursor.getColumnIndexOrThrow(AwesomeBar.URL_KEY));
String title = cursor.getString(cursor.getColumnIndexOrThrow(AwesomeBar.TITLE_KEY));
byte[] favicon = cursor.getBlob(cursor.getColumnIndexOrThrow(Browser.BookmarkColumns.FAVICON));
// 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)
title = url;
historyItem.put(AwesomeBar.URL_KEY, url);
historyItem.put(AwesomeBar.TITLE_KEY, title);
if (favicon != null)
historyItem.put(Browser.BookmarkColumns.FAVICON, favicon);
return historyItem;
}
public Map<String,?> createGroupItem(HistorySection section) {
Map<String,String> groupItem = new HashMap<String,String>();
groupItem.put(AwesomeBar.TITLE_KEY, getSectionName(section));
return groupItem;
}
private String getSectionName(HistorySection section) {
Resources resources = mContext.getResources();
switch (section) {
case TODAY:
return resources.getString(R.string.history_today_section);
case YESTERDAY:
return resources.getString(R.string.history_yesterday_section);
case WEEK:
return resources.getString(R.string.history_week_section);
case OLDER:
return resources.getString(R.string.history_older_section);
}
return null;
}
private void expandAllGroups(ExpandableListView historyList) {
int groupCount = mHistoryAdapter.getGroupCount();
for (int i = 0; i < groupCount; i++) {
historyList.expandGroup(i);
}
}
private HistorySection getSectionForTime(long time, long today) {
long delta = today - time;
if (delta < 0) {
return HistorySection.TODAY;
} else if (delta > 0 && delta < MS_PER_DAY) {
return HistorySection.YESTERDAY;
} else if (delta > MS_PER_DAY && delta < MS_PER_WEEK) {
return HistorySection.WEEK;
}
return HistorySection.OLDER;
}
@SuppressWarnings("unchecked")
protected void onPostExecute(Pair<List,List> result) {
// FIXME: display some sort of message when there's no history
if (result == null)
return;
mHistoryAdapter = new HistoryListAdapter(
mContext,
result.first,
R.layout.awesomebar_header_row,
new String[] { AwesomeBar.TITLE_KEY },
new int[] { R.id.title },
result.second,
R.layout.awesomebar_row,
new String[] { AwesomeBar.TITLE_KEY, AwesomeBar.URL_KEY },
new int[] { R.id.title, R.id.url }
);
final ExpandableListView historyList =
(ExpandableListView) findViewById(R.id.history_list);
historyList.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
public boolean onChildClick(ExpandableListView parent, View view,
int groupPosition, int childPosition, long id) {
handleHistoryItemClick(groupPosition, childPosition);
return true;
}
});
// This is to disallow collapsing the expandable groups in the
// history expandable list view to mimic simpler sections. We should
// Remove this if we decide to allow expanding/collapsing groups.
historyList.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {
return true;
}
});
historyList.setAdapter(mHistoryAdapter);
expandAllGroups(historyList);
}
}
public AwesomeBarTabs(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(LOGTAG, "Creating AwesomeBarTabs");
mContext = context;
// Load layout into the custom view
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.awesomebar_tabs, this);
// This should be called before adding any tabs
// to the TabHost.
setup();
mListTouchListener = new View.OnTouchListener() {
public boolean onTouch(View view, MotionEvent event) {
hideSoftInput(view);
return false;
}
};
addAllPagesTab();
addBookmarksTab();
addHistoryTab();
setOnTabChangedListener(new TabHost.OnTabChangeListener() {
@Override
public void onTabChanged(String tabId) {
boolean hideSoftInput = true;
// Lazy load bookmarks and history lists. Only query the database
// if those lists requested by user.
if (tabId.equals(BOOKMARKS_TAB) && mBookmarksAdapter == null) {
new BookmarksQueryTask().execute();
} else if (tabId.equals(HISTORY_TAB) && mHistoryAdapter == null) {
new HistoryQueryTask().execute();
} else {
hideSoftInput = false;
}
// Always dismiss SKB when changing to lazy-loaded tabs
if (hideSoftInput) {
View tabView = getCurrentTabView();
hideSoftInput(tabView);
}
}
});
// Initialize "App Pages" list with no filter
filter("");
}
private TabSpec addAwesomeTab(String id, int titleId, int contentId) {
TabSpec tab = newTabSpec(id);
LayoutInflater inflater =
(LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View indicatorView = inflater.inflate(R.layout.awesomebar_tab_indicator, null);
Drawable background = indicatorView.getBackground();
try {
background.setColorFilter(new LightingColorFilter(Color.WHITE, GeckoApp.mBrowserToolbar.getHighlightColor()));
} catch (Exception e) {
Log.d(LOGTAG, "background.setColorFilter failed " + e);
}
TextView title = (TextView) indicatorView.findViewById(R.id.title);
title.setText(titleId);
tab.setIndicator(indicatorView);
tab.setContent(contentId);
addTab(tab);
return tab;
}
private void addAllPagesTab() {
Log.d(LOGTAG, "Creating All Pages tab");
addAwesomeTab(ALL_PAGES_TAB,
R.string.awesomebar_all_pages_title,
R.id.all_pages_list);
// Load the list using a custom adapter so we can create the bitmaps
mAllPagesAdapter = new SimpleCursorAdapter(
mContext,
R.layout.awesomebar_row,
null,
new String[] { AwesomeBar.TITLE_KEY,
AwesomeBar.URL_KEY,
Browser.BookmarkColumns.FAVICON },
new int[] { R.id.title, R.id.url, R.id.favicon }
);
mAllPagesAdapter.setViewBinder(new AwesomeCursorViewBinder());
mAllPagesAdapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
ContentResolver resolver = mContext.getContentResolver();
return resolver.query(Browser.BOOKMARKS_URI,
null,
// The length restriction on URL is for the same reason as in the general bookmark query
// (see comment earlier in this file).
"(" + Browser.BookmarkColumns.URL + " LIKE ? OR " + Browser.BookmarkColumns.TITLE + " LIKE ?)"
+ " AND LENGTH(" + Browser.BookmarkColumns.URL + ") > 0",
new String[] {"%" + constraint.toString() + "%", "%" + constraint.toString() + "%",},
// ORDER BY is number of visits times a multiplier from 1 - 120 of how recently the site
// was accessed with a site accessed today getting 120 and a site accessed 119 or more
// days ago getting 1
Browser.BookmarkColumns.VISITS + " * MAX(1, (" +
Browser.BookmarkColumns.DATE + " - " + new Date().getTime() + ") / 86400000 + 120) DESC LIMIT " + MAX_RESULTS);
}
});
final ListView allPagesList = (ListView) findViewById(R.id.all_pages_list);
allPagesList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
handleItemClick(allPagesList, position);
}
});
allPagesList.setAdapter(mAllPagesAdapter);
allPagesList.setOnTouchListener(mListTouchListener);
}
private void addBookmarksTab() {
Log.d(LOGTAG, "Creating Bookmarks tab");
addAwesomeTab(BOOKMARKS_TAB,
R.string.awesomebar_bookmarks_title,
R.id.bookmarks_list);
ListView bookmarksList = (ListView) findViewById(R.id.bookmarks_list);
bookmarksList.setOnTouchListener(mListTouchListener);
// Only load bookmark list when tab is actually used.
// See OnTabChangeListener above.
}
private void addHistoryTab() {
Log.d(LOGTAG, "Creating History tab");
addAwesomeTab(HISTORY_TAB,
R.string.awesomebar_history_title,
R.id.history_list);
ListView historyList = (ListView) findViewById(R.id.history_list);
historyList.setOnTouchListener(mListTouchListener);
// Only load history list when tab is actually used.
// See OnTabChangeListener above.
}
private void hideSoftInput(View view) {
InputMethodManager imm =
(InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
private void handleHistoryItemClick(int groupPosition, int childPosition) {
@SuppressWarnings("unchecked")
Map<String,Object> historyItem =
(Map<String,Object>) mHistoryAdapter.getChild(groupPosition, childPosition);
String url = (String) historyItem.get(AwesomeBar.URL_KEY);
if (mUrlOpenListener != null)
mUrlOpenListener.onUrlOpen(this, url);
}
private void handleItemClick(ListView list, int position) {
Cursor cursor = (Cursor) list.getItemAtPosition(position);
String url = cursor.getString(cursor.getColumnIndexOrThrow(AwesomeBar.URL_KEY));
if (mUrlOpenListener != null)
mUrlOpenListener.onUrlOpen(this, url);
}
public void setOnUrlOpenListener(OnUrlOpenListener listener) {
mUrlOpenListener = listener;
}
public void destroy() {
Cursor allPagesCursor = mAllPagesAdapter.getCursor();
if (allPagesCursor != null)
allPagesCursor.close();
if (mBookmarksAdapter != null) {
Cursor bookmarksCursor = mBookmarksAdapter.getCursor();
if (bookmarksCursor != null)
bookmarksCursor.close();
}
}
public void filter(String searchTerm) {
// Don't let the tab's content steal focus on tab switch
setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
// Ensure the 'All Pages' tab is selected
setCurrentTabByTag(ALL_PAGES_TAB);
// Restore normal focus behavior on tab host
setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
// The tabs should only be visible if there's no on-going search
int tabsVisibility = (searchTerm.length() == 0 ? View.VISIBLE : View.GONE);
getTabWidget().setVisibility(tabsVisibility);
// Perform the actual search
mAllPagesAdapter.getFilter().filter(searchTerm);
}
}

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

@ -0,0 +1,269 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Vladimir Vukicevic <vladimir@pobox.com>
* Matt Brubeck <mbrubeck@mozilla.com>
* Vivien Nicolas <vnicolas@mozilla.com>
* Lucas Rocha <lucasr@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.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.animation.TranslateAnimation;
import android.view.Gravity;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.TextSwitcher;
import android.widget.ViewSwitcher.ViewFactory;
public class BrowserToolbar extends LinearLayout {
final private Button mAwesomeBar;
final private ImageButton mTabs;
final public ImageButton mFavicon;
final public ImageButton mStop;
final public ImageButton mSiteSecurity;
final private AnimationDrawable mProgressSpinner;
final private TextSwitcher mTabsCount;
final private Context mContext;
final private Handler mHandler;
final private int mColor;
final private int mCounterColor;
final private int mDuration;
final private TranslateAnimation mSlideUpIn;
final private TranslateAnimation mSlideUpOut;
final private TranslateAnimation mSlideDownIn;
final private TranslateAnimation mSlideDownOut;
private int mCount;
public BrowserToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// Get the device's highlight color
ContextThemeWrapper wrapper = new ContextThemeWrapper(mContext, android.R.style.TextAppearance);
TypedArray typedArray = wrapper.getTheme().obtainStyledAttributes(new int[] { android.R.attr.textColorHighlight });
mColor = typedArray.getColor(typedArray.getIndex(0), 0);
// Load layout into the custom view
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.browser_toolbar, this);
mAwesomeBar = (Button) findViewById(R.id.awesome_bar);
mAwesomeBar.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
onAwesomeBarSearch();
}
});
Resources resources = getResources();
int padding[] = { mAwesomeBar.getPaddingLeft(),
mAwesomeBar.getPaddingTop(),
mAwesomeBar.getPaddingRight(),
mAwesomeBar.getPaddingBottom() };
GeckoStateListDrawable states = new GeckoStateListDrawable();
states.initializeFilter(mColor);
states.addState(new int[] { android.R.attr.state_pressed }, resources.getDrawable(R.drawable.address_bar_url_pressed));
states.addState(new int[] { }, resources.getDrawable(R.drawable.address_bar_url_default));
mAwesomeBar.setBackgroundDrawable(states);
mAwesomeBar.setPadding(padding[0], padding[1], padding[2], padding[3]);
mTabs = (ImageButton) findViewById(R.id.tabs);
mTabs.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
if (Tabs.getInstance().getCount() > 1)
showTabs();
else
addTab();
}
});
mTabs.setImageLevel(1);
mCounterColor = 0x99ffffff;
mTabsCount = (TextSwitcher) findViewById(R.id.tabs_count);
mTabsCount.setFactory(new ViewFactory() {
public View makeView() {
TextView text = new TextView(mContext);
text.setGravity(Gravity.CENTER);
text.setTextSize(16);
text.setTextColor(mCounterColor);
text.setTypeface(text.getTypeface(), Typeface.BOLD);
return text;
}
});
mCount = 0;
mTabsCount.setText("0");
mFavicon = (ImageButton) findViewById(R.id.favicon);
mSiteSecurity = (ImageButton) findViewById(R.id.site_security);
mProgressSpinner = (AnimationDrawable) resources.getDrawable(R.drawable.progress_spinner);
mStop = (ImageButton) findViewById(R.id.stop);
mStop.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
doStop();
}
});
mHandler = new Handler();
mSlideUpIn = new TranslateAnimation(0, 0, 30, 0);
mSlideUpOut = new TranslateAnimation(0, 0, 0, -30);
mSlideDownIn = new TranslateAnimation(0, 0, -30, 0);
mSlideDownOut = new TranslateAnimation(0, 0, 0, 30);
mDuration = 750;
mSlideUpIn.setDuration(mDuration);
mSlideUpOut.setDuration(mDuration);
mSlideDownIn.setDuration(mDuration);
mSlideDownOut.setDuration(mDuration);
}
private void onAwesomeBarSearch() {
GeckoApp.mAppContext.onEditRequested();
}
private void addTab() {
GeckoApp.mAppContext.addTab();
}
private void showTabs() {
GeckoApp.mAppContext.showTabs();
}
private void doStop() {
GeckoApp.mAppContext.doStop();
}
public int getHighlightColor() {
return mColor;
}
public void updateTabs(int count) {
if (mCount > count) {
mTabsCount.setInAnimation(mSlideDownIn);
mTabsCount.setOutAnimation(mSlideDownOut);
} else if (mCount < count) {
mTabsCount.setInAnimation(mSlideUpIn);
mTabsCount.setOutAnimation(mSlideUpOut);
}
if (count > 1)
mTabs.setImageLevel(count);
else
mTabs.setImageLevel(0);
mTabsCount.setVisibility(View.VISIBLE);
mTabsCount.setText(String.valueOf(count));
mCount = count;
mHandler.postDelayed(new Runnable() {
public void run() {
((TextView) mTabsCount.getCurrentView()).setTextColor(mColor);
}
}, mDuration);
mHandler.postDelayed(new Runnable() {
public void run() {
if (Tabs.getInstance().getCount() == 1) {
mTabs.setImageLevel(1);
mTabsCount.setVisibility(View.GONE);
((TextView) mTabsCount.getCurrentView()).setTextColor(mCounterColor);
} else {
((TextView) mTabsCount.getCurrentView()).setTextColor(mCounterColor);
}
}
}, 2 * mDuration);
}
public void setProgressVisibility(boolean visible) {
if (visible) {
mFavicon.setImageDrawable(mProgressSpinner);
mProgressSpinner.start();
setStopVisibility(true);
} else {
mProgressSpinner.stop();
setStopVisibility(false);
setFavicon(Tabs.getInstance().getSelectedTab().getFavicon());
}
}
public void setStopVisibility(boolean visible) {
mStop.setVisibility(visible ? View.VISIBLE : View.GONE);
mSiteSecurity.setVisibility(visible ? View.GONE : View.VISIBLE);
}
public void setTitle(CharSequence title) {
mAwesomeBar.setText(title);
}
public void setFavicon(Drawable image) {
if (Tabs.getInstance().getSelectedTab().isLoading())
return;
if (image != null)
mFavicon.setImageDrawable(image);
else
mFavicon.setImageResource(R.drawable.favicon);
}
public void setSecurityMode(String mode) {
if (mode.equals("identified") || mode.equals("verified"))
mSiteSecurity.setImageLevel(1);
else
mSiteSecurity.setImageLevel(0);
}
}

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

@ -0,0 +1,79 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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) 2009-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.content.Context;
import android.preference.DialogPreference;
import android.provider.Browser;
import android.util.AttributeSet;
import android.util.Log;
class ConfirmPreference extends DialogPreference {
private static final String LOGTAG = "GeckoConfirmPreference";
private String mAction = null;
private Context mContext = null;
public ConfirmPreference(Context context, AttributeSet attrs) {
super(context, attrs);
mAction = attrs.getAttributeValue(null, "action");
mContext = context;
}
public ConfirmPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mAction = attrs.getAttributeValue(null, "action");
mContext = context;
}
protected void onDialogClosed(boolean positiveResult) {
if (!positiveResult)
return;
if ("clear_history".equalsIgnoreCase(mAction)) {
GeckoAppShell.getHandler().post(new Runnable(){
public void run() {
Browser.clearHistory(mContext.getContentResolver());
}
});
} else if ("clear_private_data".equalsIgnoreCase(mAction)) {
GeckoAppShell.getHandler().post(new Runnable(){
public void run() {
GeckoAppShell.sendEventToGecko(new GeckoEvent("Sanitize:ClearAll", null));
}
});
}
Log.i(LOGTAG, "action: " + mAction);
}
}

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

@ -0,0 +1,336 @@
/* -*- Mode: Java; 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) 2010
* 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 ***** */
#filter substitution
package @ANDROID_PACKAGE_NAME@;
import java.util.HashMap;
import java.util.Map;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import org.mozilla.gecko.R;
public class CrashReporter extends Activity
{
private static final String LOGTAG = "GeckoCrashReporter";
private static final String PASSED_MINI_DUMP_KEY = "minidumpPath";
private static final String MINI_DUMP_PATH_KEY = "upload_file_minidump";
private static final String PAGE_URL_KEY = "URL";
private static final String NOTES_KEY = "Notes";
private static final String SERVER_URL_KEY = "ServerURL";
private static final String CRASH_REPORT_DIR = "/data/data/@ANDROID_PACKAGE_NAME@/files/mozilla/Crash Reports/";
private static final String PENDING_DIR = CRASH_REPORT_DIR + "pending";
private static final String SUBMITTED_DIR = CRASH_REPORT_DIR + "submitted";
private Handler mHandler;
private ProgressDialog mProgressDialog;
private File mPendingMinidumpFile;
private File mPendingExtrasFile;
private HashMap<String, String> mExtrasStringMap;
private boolean moveFile(File inFile, File outFile) {
Log.i(LOGTAG, "moving " + inFile + " to " + outFile);
if (inFile.renameTo(outFile))
return true;
try {
outFile.createNewFile();
Log.i(LOGTAG, "couldn't rename minidump file");
// so copy it instead
FileChannel inChannel = new FileInputStream(inFile).getChannel();
FileChannel outChannel = new FileOutputStream(outFile).getChannel();
long transferred = inChannel.transferTo(0, inChannel.size(), outChannel);
inChannel.close();
outChannel.close();
if (transferred > 0)
inFile.delete();
} catch (Exception e) {
Log.e(LOGTAG, "exception while copying minidump file: ", e);
return false;
}
return true;
}
private void doFinish() {
if (mHandler != null) {
mHandler.post(new Runnable() {
public void run() {
finish();
}
});
}
}
@Override
public void finish() {
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
super.finish();
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// mHandler is created here so runnables can be run on the main thread
mHandler = new Handler();
setContentView(R.layout.crash_reporter);
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setMessage(getString(R.string.sending_crash_report));
final Button restartButton = (Button) findViewById(R.id.restart);
final Button closeButton = (Button) findViewById(R.id.close);
String passedMinidumpPath = getIntent().getStringExtra(PASSED_MINI_DUMP_KEY);
File passedMinidumpFile = new File(passedMinidumpPath);
File pendingDir = new File(PENDING_DIR);
pendingDir.mkdirs();
mPendingMinidumpFile = new File(pendingDir, passedMinidumpFile.getName());
moveFile(passedMinidumpFile, mPendingMinidumpFile);
File extrasFile = new File(passedMinidumpPath.replaceAll(".dmp", ".extra"));
mPendingExtrasFile = new File(pendingDir, extrasFile.getName());
moveFile(extrasFile, mPendingExtrasFile);
mExtrasStringMap = new HashMap<String, String>();
readStringsFromFile(mPendingExtrasFile.getPath(), mExtrasStringMap);
}
private void backgroundSendReport() {
final CheckBox sendReportCheckbox = (CheckBox) findViewById(R.id.send_report);
if (!sendReportCheckbox.isChecked()) {
doFinish();
return;
}
mProgressDialog.show();
new Thread(new Runnable() {
public void run() {
sendReport(mPendingMinidumpFile, mExtrasStringMap, mPendingExtrasFile);
}
}).start();
}
public void onCloseClick(View v) { // bound via crash_reporter.xml
backgroundSendReport();
}
public void onRestartClick(View v) { // bound via crash_reporter.xml
doRestart();
backgroundSendReport();
}
private boolean readStringsFromFile(String filePath, Map<String, String> stringMap) {
try {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
return readStringsFromReader(reader, stringMap);
} catch (Exception e) {
Log.e(LOGTAG, "exception while reading strings: ", e);
return false;
}
}
private boolean readStringsFromReader(BufferedReader reader, Map<String, String> stringMap) throws IOException {
String line;
while ((line = reader.readLine()) != null) {
int equalsPos = -1;
if ((equalsPos = line.indexOf('=')) != -1) {
String key = line.substring(0, equalsPos);
String val = unescape(line.substring(equalsPos + 1));
stringMap.put(key, val);
}
}
reader.close();
return true;
}
private String generateBoundary() {
// Generate some random numbers to fill out the boundary
int r0 = (int)((double)Integer.MAX_VALUE * Math.random());
int r1 = (int)((double)Integer.MAX_VALUE * Math.random());
return String.format("---------------------------%08X%08X", r0, r1);
}
private void sendPart(OutputStream os, String boundary, String name, String data) {
try {
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" + name + "\"\r\n" +
"\r\n" +
data + "\r\n"
).getBytes());
} catch (Exception ex) {
Log.e(LOGTAG, "Exception when sending \"" + name + "\"", ex);
}
}
private void sendFile(OutputStream os, String boundary, String name, File file) throws IOException {
os.write(("--" + boundary + "\r\n" +
"Content-Disposition: form-data; name=\"" + name + "\"; " +
"filename=\"" + file.getName() + "\"\r\n" +
"Content-Type: application/octet-stream\r\n" +
"\r\n"
).getBytes());
FileChannel fc = new FileInputStream(file).getChannel();
fc.transferTo(0, fc.size(), Channels.newChannel(os));
fc.close();
}
private void sendReport(File minidumpFile, Map<String, String> extras, File extrasFile) {
Log.i(LOGTAG, "sendReport: " + minidumpFile.getPath());
final CheckBox includeURLCheckbox = (CheckBox) findViewById(R.id.include_url);
String spec = extras.get(SERVER_URL_KEY);
if (spec == null)
doFinish();
Log.i(LOGTAG, "server url: " + spec);
try {
URL url = new URL(spec);
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("POST");
String boundary = generateBoundary();
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
OutputStream os = conn.getOutputStream();
for (String key : extras.keySet()) {
if (key.equals(PAGE_URL_KEY)) {
if (includeURLCheckbox.isChecked())
sendPart(os, boundary, key, extras.get(key));
} else if (!key.equals(SERVER_URL_KEY) && !key.equals(NOTES_KEY)) {
sendPart(os, boundary, key, extras.get(key));
}
}
// Add some extra information to notes so its displayed by
// crash-stats.mozilla.org. Remove this when bug 607942 is fixed.
StringBuffer sb = new StringBuffer();
sb.append(extras.containsKey(NOTES_KEY) ? extras.get(NOTES_KEY) + "\n" : "");
if (@MOZ_MIN_CPU_VERSION@ < 7)
sb.append("nothumb Build\n");
sb.append(Build.MANUFACTURER).append(' ')
.append(Build.MODEL).append('\n')
.append(Build.FINGERPRINT);
sendPart(os, boundary, NOTES_KEY, sb.toString());
sendPart(os, boundary, "Min_ARM_Version", "@MOZ_MIN_CPU_VERSION@");
sendPart(os, boundary, "Android_Manufacturer", Build.MANUFACTURER);
sendPart(os, boundary, "Android_Model", Build.MODEL);
sendPart(os, boundary, "Android_Board", Build.BOARD);
sendPart(os, boundary, "Android_Brand", Build.BRAND);
sendPart(os, boundary, "Android_Device", Build.DEVICE);
sendPart(os, boundary, "Android_Display", Build.DISPLAY);
sendPart(os, boundary, "Android_Fingerprint", Build.FINGERPRINT);
sendPart(os, boundary, "Android_CPU_ABI", Build.CPU_ABI);
if (Build.VERSION.SDK_INT >= 8) {
try {
sendPart(os, boundary, "Android_CPU_ABI2", Build.CPU_ABI2);
sendPart(os, boundary, "Android_Hardware", Build.HARDWARE);
} catch (Exception ex) {
Log.e(LOGTAG, "Exception while sending SDK version 8 keys", ex);
}
}
sendPart(os, boundary, "Android_Version", Build.VERSION.SDK_INT + " (" + Build.VERSION.CODENAME + ")");
sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile);
os.write(("\r\n--" + boundary + "--\r\n").getBytes());
os.flush();
os.close();
BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
HashMap<String, String> responseMap = new HashMap<String, String>();
readStringsFromReader(br, responseMap);
if (conn.getResponseCode() == conn.HTTP_OK) {
File submittedDir = new File(SUBMITTED_DIR);
submittedDir.mkdirs();
minidumpFile.delete();
extrasFile.delete();
String crashid = responseMap.get("CrashID");
File file = new File(submittedDir, crashid + ".txt");
FileOutputStream fos = new FileOutputStream(file);
fos.write("Crash ID: ".getBytes());
fos.write(crashid.getBytes());
fos.close();
}
} catch (IOException e) {
Log.e(LOGTAG, "exception during send: ", e);
}
doFinish();
}
private void doRestart() {
try {
String action = "android.intent.action.MAIN";
Intent intent = new Intent(action);
intent.setClassName("@ANDROID_PACKAGE_NAME@",
"@ANDROID_PACKAGE_NAME@.App");
Log.i(LOGTAG, intent.toString());
startActivity(intent);
} catch (Exception e) {
Log.e(LOGTAG, "error while trying to restart", e);
}
}
private String unescape(String string) {
return string.replaceAll("\\\\", "\\").replaceAll("\\n", "\n").replaceAll("\\t", "\t");
}
}

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

@ -0,0 +1,162 @@
/* -*- 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):
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
* 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.Date;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
import org.json.JSONObject;
import org.json.JSONException;
public class DoorHanger extends LinearLayout implements Button.OnClickListener {
private Context mContext;
private LinearLayout mChoicesLayout;
private TextView mTextView;
private Button mButton;
private LayoutParams mLayoutParams;
public Tab mTab;
// value used to identify the notification
private String mValue;
private int mPersistence = 0;
private long mTimeout = 0;
public DoorHanger(Context aContext, String aValue) {
super(aContext);
mContext = aContext;
mValue = aValue;
setOrientation(VERTICAL);
setBackgroundResource(R.drawable.doorhanger_shadow_bg);
// Load layout into the custom view
LayoutInflater inflater =
(LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.doorhanger, this);
hide();
mTextView = (TextView) findViewById(R.id.doorhanger_title);
mChoicesLayout = (LinearLayout) findViewById(R.id.doorhanger_choices);
mLayoutParams = new LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT,
1.0f);
}
public void addButton(String aText, int aCallback) {
Button mButton = new Button(mContext);
mButton.setText(aText);
mButton.setTag(Integer.toString(aCallback));
mButton.setOnClickListener(this);
mChoicesLayout.addView(mButton, mLayoutParams);
}
public void onClick(View v) {
GeckoEvent e = new GeckoEvent("Doorhanger:Reply", v.getTag().toString());
GeckoAppShell.sendEventToGecko(e);
mTab.removeDoorHanger(mValue);
// This will hide the doorhanger (and hide the popup if there are no
// more doorhangers to show)
GeckoApp.mDoorHangerPopup.updatePopup();
}
public void show() {
setVisibility(View.VISIBLE);
}
public void hide() {
setVisibility(View.GONE);
}
public boolean isVisible() {
return getVisibility() == View.VISIBLE;
}
public String getValue() {
return mValue;
}
public void setText(String aText) {
mTextView.setText(aText);
}
public Tab getTab() {
return mTab;
}
public void setTab(Tab tab) {
mTab = tab;
}
public void setOptions(JSONObject options) {
try {
mPersistence = options.getInt("persistence");
} catch (JSONException e) { }
try {
mTimeout = options.getLong("timeout");
} catch (JSONException e) { }
}
// This method checks with persistence and timeout options to see if
// it's okay to remove a doorhanger.
public boolean shouldRemove() {
// If persistence is set to -1, the doorhanger will never be
// automatically removed.
if (mPersistence != 0) {
mPersistence--;
return false;
}
if (new Date().getTime() <= mTimeout) {
return false;
}
return true;
}
}

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

@ -0,0 +1,168 @@
/* -*- 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):
* Gian-Carlo Pascutto <gpascutto@mozilla.com>
* 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.HashMap;
import java.util.Iterator;
import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.PopupWindow;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.RelativeLayout;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
public class DoorHangerPopup extends PopupWindow {
private static final String LOGTAG = "GeckoDoorHangerPopup";
private Context mContext;
private LinearLayout mContent;
public DoorHangerPopup(Context aContext) {
super(aContext);
mContext = aContext;
setBackgroundDrawable(new BitmapDrawable());
setOutsideTouchable(true);
setWindowLayoutMode(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
RelativeLayout layout = (RelativeLayout) inflater.inflate(R.layout.doorhangerpopup, null);
mContent = (LinearLayout) layout.findViewById(R.id.doorhanger_container);
setContentView(layout);
}
public void addDoorHanger(String message, String value, JSONArray buttons,
Tab tab, JSONObject options) {
Log.i(LOGTAG, "Adding a DoorHanger to Tab: " + tab.getId());
// Replace the doorhanger if it already exists
DoorHanger dh = tab.getDoorHanger(value);
if (dh != null) {
tab.removeDoorHanger(value);
}
dh = new DoorHanger(mContent.getContext(), value);
// Set the doorhanger text and buttons
dh.setText(message);
for (int i = 0; i < buttons.length(); i++) {
try {
JSONObject buttonObject = buttons.getJSONObject(i);
String label = buttonObject.getString("label");
int callBackId = buttonObject.getInt("callback");
dh.addButton(label, callBackId);
} catch (JSONException e) {
Log.i(LOGTAG, "JSON throws " + e);
}
}
dh.setOptions(options);
dh.setTab(tab);
tab.addDoorHanger(value, dh);
mContent.addView(dh);
updatePopup();
}
// Updates popup contents to show doorhangers for the selected tab
public void updatePopup() {
Tab tab = Tabs.getInstance().getSelectedTab();
if (tab == null) {
hidePopup();
return;
}
Log.i(LOGTAG, "Showing all doorhangers for tab: " + tab.getId());
HashMap<String, DoorHanger> doorHangers = tab.getDoorHangers();
// Hide the popup if there aren't any doorhangers to show
if (doorHangers == null || doorHangers.size() == 0) {
hidePopup();
return;
}
// Hide old doorhangers
for (int i = 0; i < mContent.getChildCount(); i++) {
DoorHanger dh = (DoorHanger) mContent.getChildAt(i);
dh.hide();
}
// Show the doorhangers for the tab
for (DoorHanger dh : doorHangers.values()) {
dh.show();
}
showPopup();
}
public void hidePopup() {
if (isShowing()) {
Log.i(LOGTAG, "Hiding the DoorHangerPopup");
dismiss();
}
}
public void showPopup() {
Log.i(LOGTAG, "Showing the DoorHangerPopup");
fixBackgroundForFirst();
if (isShowing())
update();
else
showAsDropDown(GeckoApp.mBrowserToolbar.mFavicon);
}
private void fixBackgroundForFirst() {
for (int i=0; i < mContent.getChildCount(); i++) {
DoorHanger dh = (DoorHanger) mContent.getChildAt(i);
if (dh.isVisible()) {
dh.setBackgroundResource(R.drawable.doorhanger_bg);
break;
}
}
}
}

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

@ -0,0 +1,424 @@
/* -*- 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):
* Lucas Rocha <lucasr@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.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.provider.Browser;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Favicons {
private static final String LOGTAG = "GeckoFavicons";
public static final long NOT_LOADING = 0;
private Context mContext;
private DatabaseHelper mDbHelper;
private Map<Long,LoadFaviconTask> mLoadTasks;
private long mNextFaviconLoadId;
public interface OnFaviconLoadedListener {
public void onFaviconLoaded(String url, Drawable favicon);
}
private class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "favicon_urls.db";
private static final String TABLE_NAME = "favicon_urls";
private static final int DATABASE_VERSION = 1;
private static final String COLUMN_ID = "_id";
private static final String COLUMN_FAVICON_URL = "favicon_url";
private static final String COLUMN_PAGE_URL = "page_url";
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
Log.d(LOGTAG, "Creating DatabaseHelper");
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.d(LOGTAG, "Creating database for favicon URLs");
db.execSQL("CREATE TABLE " + TABLE_NAME + " (" +
COLUMN_ID + " INTEGER PRIMARY KEY," +
COLUMN_FAVICON_URL + " TEXT NOT NULL," +
COLUMN_PAGE_URL + " TEXT UNIQUE NOT NULL" +
");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(LOGTAG, "Upgrading favicon URLs database from version " +
oldVersion + " to " + newVersion + ", which will destroy all old data");
// Drop table completely
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
// Recreate database
onCreate(db);
}
public String getFaviconUrlForPageUrl(String pageUrl) {
Log.d(LOGTAG, "Calling getFaviconUrlForPageUrl() for " + pageUrl);
SQLiteDatabase db = mDbHelper.getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(TABLE_NAME);
Cursor c = qb.query(
db,
new String[] { COLUMN_FAVICON_URL },
COLUMN_PAGE_URL + " = ?",
new String[] { pageUrl },
null, null, null
);
if (!c.moveToFirst()) {
c.close();
return null;
}
String url = c.getString(c.getColumnIndexOrThrow(COLUMN_FAVICON_URL));
c.close();
return url;
}
public void setFaviconUrlForPageUrl(String pageUrl, String faviconUrl) {
Log.d(LOGTAG, "Calling setFaviconUrlForPageUrl() for " + pageUrl);
SQLiteDatabase db = mDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_FAVICON_URL, faviconUrl);
values.put(COLUMN_PAGE_URL, pageUrl);
db.replace(TABLE_NAME, null, values);
}
}
public Favicons(Context context) {
Log.d(LOGTAG, "Creating Favicons instance");
mContext = context;
mDbHelper = new DatabaseHelper(context);
mLoadTasks = new HashMap<Long,LoadFaviconTask>();
mNextFaviconLoadId = 0;
}
public long loadFavicon(String pageUrl, String faviconUrl,
OnFaviconLoadedListener listener) {
// Handle the case where page url is empty
if (pageUrl == null || pageUrl.length() == 0) {
if (listener != null)
listener.onFaviconLoaded(null, null);
}
LoadFaviconTask task = new LoadFaviconTask(pageUrl, faviconUrl, listener);
long taskId = task.getId();
mLoadTasks.put(taskId, task);
task.execute();
Log.d(LOGTAG, "Calling loadFavicon() with URL = " + pageUrl +
" and favicon URL = " + faviconUrl +
" (" + taskId + ")");
return taskId;
}
public boolean cancelFaviconLoad(long taskId) {
Log.d(LOGTAG, "Requesting cancelation of favicon load (" + taskId + ")");
if (!mLoadTasks.containsKey(taskId))
return false;
Log.d(LOGTAG, "Cancelling favicon load (" + taskId + ")");
LoadFaviconTask task = mLoadTasks.get(taskId);
return task.cancel(false);
}
public void close() {
Log.d(LOGTAG, "Closing Favicons database");
mDbHelper.close();
// Cancel any pending tasks
Set<Long> taskIds = mLoadTasks.keySet();
Iterator iter = taskIds.iterator();
while (iter.hasNext()) {
long taskId = (Long) iter.next();
cancelFaviconLoad(taskId);
}
}
private class LoadFaviconTask extends AsyncTask<Void, Void, BitmapDrawable> {
private long mId;
private String mPageUrl;
private String mFaviconUrl;
private OnFaviconLoadedListener mListener;
public LoadFaviconTask(String pageUrl, String faviconUrl, OnFaviconLoadedListener listener) {
mId = ++mNextFaviconLoadId;
mPageUrl = pageUrl;
mFaviconUrl = faviconUrl;
mListener = listener;
Log.d(LOGTAG, "Creating LoadFaviconTask with URL = " + pageUrl +
" and favicon URL = " + faviconUrl);
}
// Runs in background thread
private BitmapDrawable loadFaviconFromDb() {
Log.d(LOGTAG, "Loading favicon from DB for URL = " + mPageUrl);
ContentResolver resolver = mContext.getContentResolver();
Cursor c = resolver.query(Browser.BOOKMARKS_URI,
new String[] { Browser.BookmarkColumns.FAVICON },
Browser.BookmarkColumns.URL + " = ?",
new String[] { mPageUrl },
null);
if (!c.moveToFirst()) {
c.close();
return null;
}
int faviconIndex = c.getColumnIndexOrThrow(Browser.BookmarkColumns.FAVICON);
byte[] b = c.getBlob(faviconIndex);
c.close();
if (b == null)
return null;
Bitmap bitmap = BitmapFactory.decodeByteArray(b, 0, b.length);
Log.d(LOGTAG, "Loaded favicon from DB successfully for URL = " + mPageUrl);
return new BitmapDrawable(bitmap);
}
// Runs in background thread
private void saveFaviconToDb(BitmapDrawable favicon) {
Bitmap bitmap = favicon.getBitmap();
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.FAVICON, stream.toByteArray());
values.put(Browser.BookmarkColumns.URL, mPageUrl);
ContentResolver resolver = mContext.getContentResolver();
Log.d(LOGTAG, "Saving favicon on browser database for URL = " + mPageUrl);
resolver.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { mPageUrl });
Log.d(LOGTAG, "Saving favicon URL for URL = " + mPageUrl);
mDbHelper.setFaviconUrlForPageUrl(mPageUrl, mFaviconUrl);
}
// Runs in background thread
private BitmapDrawable downloadFavicon(URL faviconUrl) {
Log.d(LOGTAG, "Downloading favicon for URL = " + mPageUrl +
" with favicon URL = " + mFaviconUrl);
// due to android bug 6066, we must download the entire image before using it
// http://code.google.com/p/android/issues/detail?id=6066
HttpURLConnection urlConnection = null;
BufferedInputStream contentStream = null;
ByteArrayInputStream byteStream = null;
BitmapDrawable image = null;
try {
urlConnection = (HttpURLConnection) faviconUrl.openConnection();
int length = urlConnection.getContentLength();
contentStream = new BufferedInputStream(urlConnection.getInputStream(), length);
byte[] bytes = new byte[length];
int pos = 0;
int offset = 0;
while ((pos = contentStream.read(bytes, offset, length - offset)) > 0)
offset += pos;
if (length == offset) {
byteStream = new ByteArrayInputStream(bytes);
image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src");
}
} catch (Exception e) {
Log.d(LOGTAG, "Error downloading favicon: " + e);
} finally {
if (urlConnection != null)
urlConnection.disconnect();
try {
if (contentStream != null)
contentStream.close();
if (byteStream != null)
byteStream.close();
} catch (IOException e) {
Log.d(LOGTAG, "error closing favicon stream");
}
}
if (image != null) {
Log.d(LOGTAG, "Downloaded favicon successfully for URL = " + mPageUrl);
saveFaviconToDb(image);
}
return image;
}
@Override
protected BitmapDrawable doInBackground(Void... unused) {
BitmapDrawable image = null;
URL pageUrl = null;
if (isCancelled())
return null;
// Handle the case of malformed URL
try {
pageUrl = new URL(mPageUrl);
} catch (MalformedURLException e) {
Log.d(LOGTAG, "The provided URL is not valid: " + e);
return null;
}
URL faviconUrl = null;
// Handle the case of malformed favicon URL
try {
// If favicon is empty, fallback to default favicon URI
if (mFaviconUrl == null || mFaviconUrl.length() == 0) {
faviconUrl = new URL(pageUrl.getProtocol(), pageUrl.getAuthority(), "/favicon.ico");
mFaviconUrl = faviconUrl.toString();
} else {
faviconUrl = new URL(mFaviconUrl);
}
} catch (MalformedURLException e) {
Log.d(LOGTAG, "The provided favicon URL is not valid: " + e);
return null;
}
Log.d(LOGTAG, "Favicon URL is now: " + mFaviconUrl);
if (isCancelled())
return null;
String storedFaviconUrl = mDbHelper.getFaviconUrlForPageUrl(mPageUrl);
if (storedFaviconUrl != null && storedFaviconUrl.equals(mFaviconUrl)) {
image = loadFaviconFromDb();
if (isCancelled())
return null;
// If favicon URL is defined but the favicon image is not
// stored in the database for some reason, we force download.
if (image == null) {
image = downloadFavicon(faviconUrl);
}
} else {
image = downloadFavicon(faviconUrl);
}
return image;
}
@Override
protected void onPostExecute(final BitmapDrawable image) {
Log.d(LOGTAG, "LoadFaviconTask finished for URL = " + mPageUrl +
" (" + mId + ")");
mLoadTasks.remove(mId);
if (mListener != null) {
// We want to always run the listener on UI thread
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
public void run() {
mListener.onFaviconLoaded(mPageUrl, image);
}
});
}
}
@Override
protected void onCancelled() {
Log.d(LOGTAG, "LoadFaviconTask cancelled for URL = " + mPageUrl +
" (" + mId + ")");
mLoadTasks.remove(mId);
// Note that we don't call the listener callback if the
// favicon load is cancelled.
}
public long getId() {
return mId;
}
}
}

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

@ -0,0 +1,46 @@
/* -*- 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):
* Chris Lord <chrislord.net@gmail.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.lang.Math;
public final class FloatUtils {
public static boolean fuzzyEquals(float a, float b) {
return (Math.abs(a - b) < 1e-6);
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,57 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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;
// AsyncTask runs onPostExecute on the thread it is constructed on
// We construct these off of the main thread, and we want that to run
// on the main UI thread, so this is a convenience class to do that
public abstract class GeckoAsyncTask<Params, Progress, Result> {
public void execute(final Params... params) {
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
final Result result = doInBackground(params);
GeckoApp.mAppContext.runOnUiThread(new Runnable() {
public void run() {
onPostExecute(result);
}});
}});
}
protected abstract Result doInBackground(Params... params);
protected abstract void onPostExecute(Result result);
}

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

@ -51,6 +51,8 @@ import android.os.BatteryManager;
public class GeckoBatteryManager
extends BroadcastReceiver
{
private static final String LOGTAG = "GeckoBatteryManager";
// Those constants should be keep in sync with the ones in:
// dom/battery/Constants.h
private final static double kDefaultLevel = 1.0;
@ -67,7 +69,7 @@ public class GeckoBatteryManager
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
Log.e("GeckoBatteryManager", "Got an unexpected intent!");
Log.e(LOGTAG, "Got an unexpected intent!");
return;
}
@ -78,7 +80,7 @@ public class GeckoBatteryManager
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
if (plugged == -1) {
sCharging = kDefaultCharging;
Log.e("GeckoBatteryManager", "Failed to get the plugged status!");
Log.e(LOGTAG, "Failed to get the plugged status!");
} else {
// Likely, if plugged > 0, it's likely plugged and charging but the doc
// isn't clear about that.
@ -96,7 +98,7 @@ public class GeckoBatteryManager
double current = (double)intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
double max = (double)intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
if (current == -1 || max == -1) {
Log.e("GeckoBatteryManager", "Failed to get battery level!");
Log.e(LOGTAG, "Failed to get battery level!");
sLevel = kDefaultLevel;
} else {
sLevel = current / max;
@ -113,14 +115,14 @@ public class GeckoBatteryManager
if (sCharging) {
if (dLevel < 0) {
Log.w("GeckoBatteryManager", "When charging, level should increase!");
Log.w(LOGTAG, "When charging, level should increase!");
sRemainingTime = kUnknownRemainingTime;
} else {
sRemainingTime = Math.round(dt / dLevel * (1.0 - sLevel));
}
} else {
if (dLevel > 0) {
Log.w("GeckoBatteryManager", "When discharging, level should decrease!");
Log.w(LOGTAG, "When discharging, level should decrease!");
sRemainingTime = kUnknownRemainingTime;
} else {
sRemainingTime = Math.round(dt / -dLevel * sLevel);

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

@ -37,12 +37,23 @@
package org.mozilla.gecko;
import android.content.*;
import android.net.*;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
public class GeckoConnectivityReceiver
extends BroadcastReceiver
{
/*
* Keep the below constants in sync with
* http://mxr.mozilla.org/mozilla-central/source/netwerk/base/public/nsINetworkLinkService.idl
*/
private static final String LINK_DATA_UP = "up";
private static final String LINK_DATA_DOWN = "down";
private static final String LINK_DATA_UNKNOWN = "unknown";
@Override
public void onReceive(Context context, Intent intent) {
String status;
@ -50,11 +61,11 @@ public class GeckoConnectivityReceiver
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info == null)
status = "unknown";
status = LINK_DATA_UNKNOWN;
else if (!info.isConnected())
status = "down";
status = LINK_DATA_DOWN;
else
status = "up";
status = LINK_DATA_UP;
if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
GeckoAppShell.onChangeNetworkLinkStatus(status);

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

@ -55,6 +55,8 @@ import android.util.Log;
*/
public class GeckoEvent {
private static final String LOGTAG = "GeckoEvent";
public static final int INVALID = -1;
public static final int NATIVE_POKE = 0;
public static final int KEY_EVENT = 1;
@ -73,7 +75,6 @@ public class GeckoEvent {
public static final int SURFACE_DESTROYED = 14;
public static final int GECKO_EVENT_SYNC = 15;
public static final int ACTIVITY_START = 17;
public static final int SAVE_STATE = 18;
public static final int BROADCAST = 19;
public static final int IME_COMPOSITION_END = 0;
@ -157,7 +158,7 @@ public class GeckoEvent {
mAlpha = -s.values[0];
mBeta = -s.values[1];
mGamma = -s.values[2];
Log.i("GeckoEvent", "SensorEvent type = " + s.sensor.getType() + " " + s.sensor.getName() + " " + mAlpha + " " + mBeta + " " + mGamma );
Log.i(LOGTAG, "SensorEvent type = " + s.sensor.getType() + " " + s.sensor.getName() + " " + mAlpha + " " + mBeta + " " + mGamma );
}
}
@ -203,14 +204,14 @@ public class GeckoEvent {
rangeForeColor, rangeBackColor);
}
public GeckoEvent(int etype, Rect dirty) {
public GeckoEvent(int etype, Rect rect) {
if (etype != DRAW) {
mType = INVALID;
return;
}
mType = etype;
mRect = dirty;
mRect = rect;
}
public GeckoEvent(int etype, int w, int h, int screenw, int screenh) {
@ -235,4 +236,5 @@ public class GeckoEvent {
mType = LOAD_URI;
mCharacters = uri;
}
}

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

@ -0,0 +1,44 @@
/* -*- 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) 2009-2010
* 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 org.json.JSONObject;
public interface GeckoEventListener {
public void handleMessage(String event, JSONObject message);
}

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

@ -42,6 +42,10 @@ import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import org.mozilla.gecko.gfx.InputConnectionHandler;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.os.*;
import android.app.*;
import android.text.*;
@ -51,12 +55,18 @@ import android.view.inputmethod.*;
import android.content.*;
import android.R;
import android.text.method.TextKeyListener;
import android.text.method.KeyListener;
import android.util.*;
public class GeckoInputConnection
extends BaseInputConnection
implements TextWatcher
implements TextWatcher, InputConnectionHandler
{
private static final String LOGTAG = "GeckoInputConnection";
private class ChangeNotification {
public String mText;
public int mStart;
@ -81,25 +91,31 @@ public class GeckoInputConnection
public GeckoInputConnection (View targetView) {
super(targetView, true);
mQueryResult = new SynchronousQueue<String>();
mEditableFactory = Editable.Factory.getInstance();
initEditable("");
mIMEState = IME_STATE_DISABLED;
mIMETypeHint = "";
mIMEActionHint = "";
}
@Override
public boolean beginBatchEdit() {
//Log.d("GeckoAppJava", "IME: beginBatchEdit");
Log.d(LOGTAG, "IME: beginBatchEdit");
mBatchMode = true;
return true;
}
@Override
public boolean commitCompletion(CompletionInfo text) {
//Log.d("GeckoAppJava", "IME: commitCompletion");
Log.d(LOGTAG, "IME: commitCompletion");
return commitText(text.getText(), 1);
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
//Log.d("GeckoAppJava", "IME: commitText");
Log.d(LOGTAG, "IME: commitText");
setComposingText(text, newCursorPosition);
finishComposingText();
@ -109,7 +125,7 @@ public class GeckoInputConnection
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
//Log.d("GeckoAppJava", "IME: deleteSurroundingText");
Log.d(LOGTAG, "IME: deleteSurroundingText");
if (leftLength == 0 && rightLength == 0)
return true;
@ -132,7 +148,7 @@ public class GeckoInputConnection
try {
mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: deleteSurroundingText interrupted", e);
Log.e(LOGTAG, "IME: deleteSurroundingText interrupted", e);
return false;
}
delStart = mSelectionStart > leftLength ?
@ -157,18 +173,24 @@ public class GeckoInputConnection
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0));
}
// Temporarily disable text change notifications which confuse some IMEs (SlideIT, for example)
// in the middle of text update.
// They will be re-enabled on the next setComposingText
disableChangeNotifications();
return true;
}
@Override
public boolean endBatchEdit() {
//Log.d("GeckoAppJava", "IME: endBatchEdit");
Log.d(LOGTAG, "IME: endBatchEdit");
mBatchMode = false;
if (!mBatchChanges.isEmpty()) {
InputMethodManager imm = (InputMethodManager)
GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
for (ChangeNotification n : mBatchChanges) {
if (n.mText != null)
@ -184,7 +206,7 @@ public class GeckoInputConnection
@Override
public boolean finishComposingText() {
//Log.d("GeckoAppJava", "IME: finishComposingText");
Log.d(LOGTAG, "IME: finishComposingText");
if (mComposing) {
// Set style to none
@ -209,14 +231,14 @@ public class GeckoInputConnection
@Override
public int getCursorCapsMode(int reqModes) {
//Log.d("GeckoAppJava", "IME: getCursorCapsMode");
Log.d(LOGTAG, "IME: getCursorCapsMode");
return 0;
}
@Override
public Editable getEditable() {
Log.w("GeckoAppJava", "IME: getEditable called from " +
Log.w(LOGTAG, "IME: getEditable called from " +
Thread.currentThread().getStackTrace()[0].toString());
return null;
@ -224,7 +246,7 @@ public class GeckoInputConnection
@Override
public boolean performContextMenuAction(int id) {
//Log.d("GeckoAppJava", "IME: performContextMenuAction");
Log.d(LOGTAG, "IME: performContextMenuAction");
// First we need to ask Gecko to tell us the full contents of the
// text field we're about to operate on.
@ -234,7 +256,7 @@ public class GeckoInputConnection
try {
text = mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e);
Log.e(LOGTAG, "IME: performContextMenuAction interrupted", e);
return false;
}
@ -264,7 +286,7 @@ public class GeckoInputConnection
try {
text = mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e);
Log.e(LOGTAG, "IME: performContextMenuAction interrupted", e);
return false;
}
}
@ -284,7 +306,7 @@ public class GeckoInputConnection
if (!GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
return null;
//Log.d("GeckoAppJava", "IME: getExtractedText");
Log.d(LOGTAG, "IME: getExtractedText");
ExtractedText extract = new ExtractedText();
extract.flags = 0;
@ -296,7 +318,7 @@ public class GeckoInputConnection
try {
mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e);
Log.e(LOGTAG, "IME: getExtractedText interrupted", e);
return null;
}
extract.selectionStart = mSelectionStart;
@ -329,21 +351,21 @@ public class GeckoInputConnection
return extract;
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e);
Log.e(LOGTAG, "IME: getExtractedText interrupted", e);
return null;
}
}
@Override
public CharSequence getTextAfterCursor(int length, int flags) {
//Log.d("GeckoAppJava", "IME: getTextAfterCursor");
Log.d(LOGTAG, "IME: getTextAfterCursor");
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
try {
mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor interrupted", e);
Log.e(LOGTAG, "IME: getTextBefore/AfterCursor interrupted", e);
return null;
}
@ -366,27 +388,43 @@ public class GeckoInputConnection
try {
return mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor: Interrupted!", e);
Log.e(LOGTAG, "IME: getTextBefore/AfterCursor: Interrupted!", e);
return null;
}
}
@Override
public CharSequence getTextBeforeCursor(int length, int flags) {
//Log.d("GeckoAppJava", "IME: getTextBeforeCursor");
Log.d(LOGTAG, "IME: getTextBeforeCursor");
return getTextAfterCursor(-length, flags);
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
//Log.d("GeckoAppJava", "IME: setComposingText");
Log.d(LOGTAG, "IME: setComposingText");
enableChangeNotifications();
// Set new composing text
mComposingText = text != null ? text.toString() : "";
if (!mComposing) {
// Get current selection
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
try {
mQueryResult.take();
} catch (InterruptedException e) {
Log.e(LOGTAG, "IME: setComposingText interrupted", e);
return false;
}
if (mComposingText.length() == 0) {
// Empty composing text is usually sent by IME to delete the selection (for example, ezKeyboard)
if (mSelectionLength > 0)
GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0));
// Some IMEs such as iWnn sometimes call with empty composing
// text. (See bug 664364)
// If composing text is empty, ignore this and don't start
@ -394,15 +432,6 @@ public class GeckoInputConnection
return true;
}
// Get current selection
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
try {
mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: setComposingText interrupted", e);
return false;
}
// Make sure we are in a composition
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_COMPOSITION_BEGIN, 0, 0));
@ -507,7 +536,7 @@ public class GeckoInputConnection
@Override
public boolean setComposingRegion(int start, int end) {
//Log.d("GeckoAppJava", "IME: setComposingRegion(start=" + start + ", end=" + end + ")");
Log.d(LOGTAG, "IME: setComposingRegion(start=" + start + ", end=" + end + ")");
if (start < 0 || end < start)
return true;
@ -525,7 +554,7 @@ public class GeckoInputConnection
try {
text = mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: setComposingRegion interrupted", e);
Log.e(LOGTAG, "IME: setComposingRegion interrupted", e);
return false;
}
}
@ -541,7 +570,7 @@ public class GeckoInputConnection
@Override
public boolean setSelection(int start, int end) {
//Log.d("GeckoAppJava", "IME: setSelection");
Log.d(LOGTAG, "IME: setSelection");
if (mComposing) {
/* Translate to fake selection positions */
@ -589,8 +618,10 @@ public class GeckoInputConnection
public void notifyTextChange(InputMethodManager imm, String text,
int start, int oldEnd, int newEnd) {
// Log.d("GeckoAppShell", String.format("IME: notifyTextChange: text=%s s=%d ne=%d oe=%d",
// text, start, newEnd, oldEnd));
Log.d(LOGTAG, String.format("IME: notifyTextChange: text=%s s=%d ne=%d oe=%d",
text, start, newEnd, oldEnd));
if (!mChangeNotificationsEnabled)
return;
if (mBatchMode) {
mBatchChanges.add(new ChangeNotification(text, start, oldEnd, newEnd));
@ -601,8 +632,10 @@ public class GeckoInputConnection
// If there are pending changes, that means this text is not the most up-to-date version
// and we'll step on ourselves if we change the editable right now.
if (mNumPendingChanges == 0 && !text.contentEquals(GeckoApp.surfaceView.mEditable))
GeckoApp.surfaceView.setEditable(text);
View v = GeckoApp.mAppContext.getLayerController().getView();
if (mNumPendingChanges == 0 && !text.contentEquals(mEditable))
setEditable(text);
if (mUpdateRequest == null)
return;
@ -621,34 +654,38 @@ public class GeckoInputConnection
mUpdateExtract.text = text.substring(0, newEnd);
mUpdateExtract.startOffset = 0;
imm.updateExtractedText(GeckoApp.surfaceView,
mUpdateRequest.token, mUpdateExtract);
imm.updateExtractedText(v, mUpdateRequest.token, mUpdateExtract);
}
public void notifySelectionChange(InputMethodManager imm,
int start, int end) {
// Log.d("GeckoAppJava", String.format("IME: notifySelectionChange: s=%d e=%d", start, end));
Log.d(LOGTAG, String.format("IME: notifySelectionChange: s=%d e=%d", start, end));
if (!mChangeNotificationsEnabled)
return;
if (mBatchMode) {
mBatchChanges.add(new ChangeNotification(start, end));
return;
}
View v = GeckoApp.mAppContext.getLayerController().getView();
if (mComposing)
imm.updateSelection(GeckoApp.surfaceView,
mCompositionStart + mCompositionSelStart,
mCompositionStart + mCompositionSelStart + mCompositionSelLen,
mCompositionStart,
mCompositionStart + mComposingText.length());
imm.updateSelection(v,
mCompositionStart + mCompositionSelStart,
mCompositionStart + mCompositionSelStart + mCompositionSelLen,
mCompositionStart,
mCompositionStart + mComposingText.length());
else
imm.updateSelection(GeckoApp.surfaceView, start, end, -1, -1);
imm.updateSelection(v, start, end, -1, -1);
// We only change the selection if we are relatively sure that the text we have is
// up-to-date. Bail out if we are stil expecting changes.
if (mNumPendingChanges > 0)
return;
int maxLen = GeckoApp.surfaceView.mEditable.length();
Selection.setSelection(GeckoApp.surfaceView.mEditable,
int maxLen = mEditable.length();
Selection.setSelection(mEditable,
Math.min(start, maxLen),
Math.min(end, maxLen));
}
@ -665,8 +702,8 @@ public class GeckoInputConnection
// TextWatcher
public void onTextChanged(CharSequence s, int start, int before, int count)
{
// Log.d("GeckoAppShell", String.format("IME: onTextChanged: t=%s s=%d b=%d l=%d",
// s, start, before, count));
Log.d(LOGTAG, String.format("IME: onTextChanged: t=%s s=%d b=%d l=%d",
s, start, before, count));
mNumPendingChanges++;
GeckoAppShell.sendEventToGecko(
@ -705,6 +742,340 @@ public class GeckoInputConnection
{
}
private void disableChangeNotifications() {
mChangeNotificationsEnabled = false;
}
private void enableChangeNotifications() {
mChangeNotificationsEnabled = true;
}
public InputConnection onCreateInputConnection(EditorInfo outAttrs)
{
Log.d(LOGTAG, "IME: handleCreateInputConnection called");
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
outAttrs.actionLabel = null;
mKeyListener = TextKeyListener.getInstance();
if (mIMEState == IME_STATE_PASSWORD)
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
else if (mIMETypeHint.equalsIgnoreCase("url"))
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_URI;
else if (mIMETypeHint.equalsIgnoreCase("email"))
outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
else if (mIMETypeHint.equalsIgnoreCase("search"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
else if (mIMETypeHint.equalsIgnoreCase("tel"))
outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
else if (mIMETypeHint.equalsIgnoreCase("number") ||
mIMETypeHint.equalsIgnoreCase("range"))
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
else if (mIMETypeHint.equalsIgnoreCase("datetime") ||
mIMETypeHint.equalsIgnoreCase("datetime-local"))
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
InputType.TYPE_DATETIME_VARIATION_NORMAL;
else if (mIMETypeHint.equalsIgnoreCase("date"))
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
InputType.TYPE_DATETIME_VARIATION_DATE;
else if (mIMETypeHint.equalsIgnoreCase("time"))
outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
InputType.TYPE_DATETIME_VARIATION_TIME;
if (mIMEActionHint.equalsIgnoreCase("go"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
else if (mIMEActionHint.equalsIgnoreCase("done"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
else if (mIMEActionHint.equalsIgnoreCase("next"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
else if (mIMEActionHint.equalsIgnoreCase("search"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
else if (mIMEActionHint.equalsIgnoreCase("send"))
outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
else if (mIMEActionHint != null && mIMEActionHint.length() != 0)
outAttrs.actionLabel = mIMEActionHint;
if (mIMELandscapeFS == false)
outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
reset();
return this;
}
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
return processKeyDown(keyCode, event, true);
case KeyEvent.ACTION_UP:
return processKeyUp(keyCode, event, true);
case KeyEvent.ACTION_MULTIPLE:
return onKeyMultiple(keyCode, event.getRepeatCount(), event);
}
return false;
}
public boolean onKeyDown(int keyCode, KeyEvent event) {
return processKeyDown(keyCode, event, false);
}
private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) {
switch (keyCode) {
case KeyEvent.KEYCODE_MENU:
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_SEARCH:
return false;
case KeyEvent.KEYCODE_DEL:
// See comments in GeckoInputConnection.onKeyDel
if (onKeyDel()) {
return true;
}
break;
case KeyEvent.KEYCODE_ENTER:
if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
mIMEActionHint.equalsIgnoreCase("next"))
event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB);
break;
default:
break;
}
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
(event.getMetaState() & KeyEvent.META_ALT_ON) != 0)
// Let active IME process pre-IME key events
return false;
View v = GeckoApp.mAppContext.getLayerController().getView();
// KeyListener returns true if it handled the event for us.
if (mIMEState == IME_STATE_DISABLED ||
keyCode == KeyEvent.KEYCODE_ENTER ||
keyCode == KeyEvent.KEYCODE_DEL ||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
!mKeyListener.onKeyDown(v, mEditable, keyCode, event))
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
public boolean onKeyUp(int keyCode, KeyEvent event) {
return processKeyUp(keyCode, event, false);
}
private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_SEARCH:
case KeyEvent.KEYCODE_MENU:
return false;
default:
break;
}
if (isPreIme && mIMEState != IME_STATE_DISABLED &&
(event.getMetaState() & KeyEvent.META_ALT_ON) != 0)
// Let active IME process pre-IME key events
return false;
View v = GeckoApp.mAppContext.getLayerController().getView();
if (mIMEState == IME_STATE_DISABLED ||
keyCode == KeyEvent.KEYCODE_ENTER ||
keyCode == KeyEvent.KEYCODE_DEL ||
(event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 ||
!mKeyListener.onKeyUp(v, mEditable, keyCode, event))
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
return true;
}
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
View v = GeckoApp.mAppContext.getLayerController().getView();
switch (keyCode) {
case KeyEvent.KEYCODE_MENU:
InputMethodManager imm = (InputMethodManager)
v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInputFromWindow(v.getWindowToken(),
imm.SHOW_FORCED, 0);
return true;
default:
break;
}
return false;
}
public void notifyIME(int type, int state) {
View v = GeckoApp.mAppContext.getLayerController().getView();
Log.d(LOGTAG, "notifyIME");
if (v == null)
return;
Log.d(LOGTAG, "notifyIME v!= null");
switch (type) {
case NOTIFY_IME_RESETINPUTSTATE:
Log.d(LOGTAG, "notifyIME = reset");
// Composition event is already fired from widget.
// So reset IME flags.
reset();
// Don't use IMEStateUpdater for reset.
// Because IME may not work showSoftInput()
// after calling restartInput() immediately.
// So we have to call showSoftInput() delay.
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
// no way to reset IME status directly
IMEStateUpdater.resetIME();
} else {
imm.restartInput(v);
}
// keep current enabled state
IMEStateUpdater.enableIME();
break;
case NOTIFY_IME_CANCELCOMPOSITION:
Log.d(LOGTAG, "notifyIME = cancel");
IMEStateUpdater.resetIME();
break;
case NOTIFY_IME_FOCUSCHANGE:
Log.d(LOGTAG, "notifyIME = focus");
IMEStateUpdater.resetIME();
break;
}
}
public void notifyIMEEnabled(int state, String typeHint,
String actionHint, boolean landscapeFS)
{
View v = GeckoApp.mAppContext.getLayerController().getView();
if (v == null)
return;
/* When IME is 'disabled', IME processing is disabled.
In addition, the IME UI is hidden */
mIMEState = state;
mIMETypeHint = typeHint;
mIMEActionHint = actionHint;
mIMELandscapeFS = landscapeFS;
IMEStateUpdater.enableIME();
}
public void notifyIMEChange(String text, int start, int end, int newEnd) {
View v = GeckoApp.mAppContext.getLayerController().getView();
if (v == null)
return;
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm == null)
return;
Log.d(LOGTAG, String.format("IME: notifyIMEChange: t=%s s=%d ne=%d oe=%d",
text, start, newEnd, end));
if (newEnd < 0)
notifySelectionChange(imm, start, end);
else
notifyTextChange(imm, text, start, end, newEnd);
}
public void returnIMEQueryResult(String result, int selectionStart, int selectionLength) {
mSelectionStart = selectionStart;
mSelectionLength = selectionLength;
try {
mQueryResult.put(result);
} catch (InterruptedException e) {}
}
static private final Timer mIMETimer = new Timer();
static private final int NOTIFY_IME_RESETINPUTSTATE = 0;
static private final int NOTIFY_IME_SETOPENSTATE = 1;
static private final int NOTIFY_IME_CANCELCOMPOSITION = 2;
static private final int NOTIFY_IME_FOCUSCHANGE = 3;
/* Delay updating IME states (see bug 573800) */
private static final class IMEStateUpdater extends TimerTask
{
static private IMEStateUpdater instance;
private boolean mEnable, mReset;
static private IMEStateUpdater getInstance() {
if (instance == null) {
instance = new IMEStateUpdater();
mIMETimer.schedule(instance, 200);
}
return instance;
}
static public synchronized void enableIME() {
getInstance().mEnable = true;
}
static public synchronized void resetIME() {
getInstance().mReset = true;
}
public void run() {
Log.d(LOGTAG, "IME: run()");
synchronized(IMEStateUpdater.class) {
instance = null;
}
View v = GeckoApp.mAppContext.getLayerController().getView();
Log.d(LOGTAG, "IME: v="+v);
InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm == null)
return;
if (mReset)
imm.restartInput(v);
if (!mEnable)
return;
if (mIMEState != IME_STATE_DISABLED &&
mIMEState != IME_STATE_PLUGIN)
imm.showSoftInput(v, 0);
else
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
}
public void setEditable(String contents)
{
mEditable.removeSpan(this);
mEditable.replace(0, mEditable.length(), contents);
mEditable.setSpan(this, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
Selection.setSelection(mEditable, contents.length());
}
public void initEditable(String contents)
{
mEditable = mEditableFactory.newEditable(contents);
mEditable.setSpan(this, 0, contents.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
Selection.setSelection(mEditable, contents.length());
}
// Is a composition active?
boolean mComposing;
// Composition text when a composition is active
@ -720,7 +1091,23 @@ public class GeckoInputConnection
// Number of in flight changes
int mNumPendingChanges;
boolean mBatchMode;
// IME stuff
public static final int IME_STATE_DISABLED = 0;
public static final int IME_STATE_ENABLED = 1;
public static final int IME_STATE_PASSWORD = 2;
public static final int IME_STATE_PLUGIN = 3;
KeyListener mKeyListener;
Editable mEditable;
Editable.Factory mEditableFactory;
static int mIMEState;
static String mIMETypeHint;
static String mIMEActionHint;
static boolean mIMELandscapeFS;
private boolean mBatchMode;
private boolean mChangeNotificationsEnabled = true;
private CopyOnWriteArrayList<ChangeNotification> mBatchChanges =
new CopyOnWriteArrayList<ChangeNotification>();

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

@ -0,0 +1,231 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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 Corporation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Pakhotin <alexp@mozilla.com>
* Brian Nicholson <bnicholson@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 android.os.Build;
import android.os.Bundle;
import android.content.res.Resources;
import android.content.Context;
import android.preference.*;
import android.preference.Preference.*;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class GeckoPreferences
extends PreferenceActivity
implements OnPreferenceChangeListener, GeckoEventListener
{
private static final String LOGTAG = "GeckoPreferences";
private ArrayList<String> mPreferencesList = new ArrayList<String>();
private PreferenceScreen mPreferenceScreen;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 11)
getActionBar().setDisplayHomeAsUpEnabled(true);
addPreferencesFromResource(R.xml.preferences);
mPreferenceScreen = getPreferenceScreen();
GeckoAppShell.registerGeckoEventListener("Preferences:Data", this);
initGroups(mPreferenceScreen);
initValues();
}
@Override
protected void onDestroy() {
super.onDestroy();
GeckoAppShell.unregisterGeckoEventListener("Preferences:Data", this);
}
public void handleMessage(String event, JSONObject message) {
try {
if (event.equals("Preferences:Data")) {
JSONArray jsonPrefs = message.getJSONArray("preferences");
refresh(jsonPrefs);
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
}
}
// Initialize preferences by sending the "Preferences:Get" command to Gecko
private void initValues() {
JSONArray jsonPrefs = new JSONArray(mPreferencesList);
GeckoEvent event = new GeckoEvent("Preferences:Get", jsonPrefs.toString());
GeckoAppShell.sendEventToGecko(event);
}
private void initGroups(PreferenceGroup preferences) {
final int count = preferences.getPreferenceCount();
for (int i = 0; i < count; i++) {
Preference pref = preferences.getPreference(i);
if (pref instanceof PreferenceGroup)
initGroups((PreferenceGroup)pref);
else {
pref.setOnPreferenceChangeListener(this);
mPreferencesList.add(pref.getKey());
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
String prefName = preference.getKey();
setPreference(prefName, newValue);
if (preference instanceof ListPreference)
((ListPreference)preference).setSummary((String)newValue);
return true;
}
private void refresh(JSONArray jsonPrefs) {
// enable all preferences once we have them from gecko
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
mPreferenceScreen.setEnabled(true);
}
});
try {
if (mPreferenceScreen == null)
return;
// set the current page URL for the "Home page" preference
final String[] homepageValues = getResources().getStringArray(R.array.pref_homepage_values);
final Preference homepagePref = mPreferenceScreen.findPreference("browser.startup.homepage");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
Tab tab = Tabs.getInstance().getSelectedTab();
homepageValues[2] = tab.getURL();
((ListPreference)homepagePref).setEntryValues(homepageValues);
}
});
final int length = jsonPrefs.length();
for (int i = 0; i < length; i++) {
JSONObject jPref = jsonPrefs.getJSONObject(i);
final String prefName = jPref.getString("name");
final String prefType = jPref.getString("type");
final Preference pref = mPreferenceScreen.findPreference(prefName);
if (prefName.equals("browser.startup.homepage")) {
final String value = jPref.getString("value");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
pref.setSummary(value);
}
});
}
if (pref instanceof CheckBoxPreference && "bool".equals(prefType)) {
final boolean value = jPref.getBoolean("value");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
if (((CheckBoxPreference)pref).isChecked() != value)
((CheckBoxPreference)pref).setChecked(value);
}
});
} else if (pref instanceof EditTextPreference && "string".equals(prefType)) {
final String value = jPref.getString("value");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
((EditTextPreference)pref).setText(value);
}
});
} else if (pref instanceof ListPreference && "string".equals(prefType)) {
final String value = jPref.getString("value");
GeckoAppShell.getMainHandler().post(new Runnable() {
public void run() {
((ListPreference)pref).setValue(value);
}
});
}
}
} catch (JSONException e) {
Log.e(LOGTAG, "Problem parsing preferences response: ", e);
}
}
// send the Preferences:Set message to Gecko
public static void setPreference(String pref, Object value) {
try {
JSONObject jsonPref = new JSONObject();
jsonPref.put("name", pref);
if (value instanceof Boolean) {
jsonPref.put("type", "bool");
jsonPref.put("value", ((Boolean)value).booleanValue());
}
else if (value instanceof Integer) {
jsonPref.put("type", "int");
jsonPref.put("value", ((Integer)value).intValue());
}
else {
jsonPref.put("type", "string");
jsonPref.put("value", String.valueOf(value));
}
GeckoEvent event = new GeckoEvent("Preferences:Set", jsonPref.toString());
GeckoAppShell.sendEventToGecko(event);
} catch (JSONException e) {
Log.e(LOGTAG, "JSON exception: ", e);
}
}
}

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

@ -0,0 +1,64 @@
/* -*- 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) 2009-2010
* 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 android.graphics.Color;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
import android.graphics.LightingColorFilter;
public class GeckoStateListDrawable extends StateListDrawable {
private LightingColorFilter mFilter;
public void initializeFilter(int color) {
mFilter = new LightingColorFilter(Color.WHITE, color);
}
@Override
protected boolean onStateChange(int[] stateSet) {
for (int state: stateSet) {
if (state == android.R.attr.state_pressed || state == android.R.attr.state_focused) {
super.onStateChange(stateSet);
((LayerDrawable) getCurrent()).getDrawable(0).setColorFilter(mFilter);
return true;
}
}
return super.onStateChange(stateSet);
}
}

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

@ -0,0 +1,130 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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.content.Intent;
import android.content.res.Resources;
import android.content.res.Configuration;
import android.util.Log;
import android.widget.AbsoluteLayout;
import java.io.File;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Date;
import java.util.Locale;
public class GeckoThread extends Thread {
private static final String LOGTAG = "GeckoThread";
Intent mIntent;
String mUri;
String mTitle;
GeckoThread (Intent intent, String uri, String title) {
mIntent = intent;
mUri = uri;
mTitle = title;
}
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)) {
uri = mUri;
title = mTitle;
}
if (uri == null || uri.equals("") || uri.equals("about:home")) {
app.showAboutHome();
}
File cacheFile = GeckoAppShell.getCacheDir();
File libxulFile = new File(cacheFile, "libxul.so");
if ((!libxulFile.exists() ||
new File(app.getApplication().getPackageResourcePath()).lastModified() >= libxulFile.lastModified())) {
File[] libs = cacheFile.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.endsWith(".so");
}
});
if (libs != null) {
for (int i = 0; i < libs.length; i++) {
libs[i].delete();
}
}
}
// At some point while loading the gecko libs our default locale gets set
// so just save it to locale here and reset it as default after the join
Locale locale = Locale.getDefault();
GeckoAppShell.loadGeckoLibs(
app.getApplication().getPackageResourcePath());
Locale.setDefault(locale);
Resources res = app.getBaseContext().getResources();
Configuration config = res.getConfiguration();
config.locale = locale;
res.updateConfiguration(config, res.getDisplayMetrics());
Log.w(LOGTAG, "zerdatime " + new Date().getTime() + " - runGecko");
// and then fire us up
final String activityTitle = title;
app.mMainHandler.post(new Runnable() {
public void run() {
app.mBrowserToolbar.setTitle(activityTitle);
}
});
try {
Log.w(LOGTAG, "RunGecko - URI = " + uri);
GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(),
intent.getStringExtra("args"),
uri);
} catch (Exception e) {
Log.e(LOGTAG, "top level exception", e);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
GeckoAppShell.reportJavaCrash(sw.toString());
}
}
}

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

@ -0,0 +1,149 @@
/* -*- 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):
* Kartikaya Gupta <kgupta@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.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.lang.ref.SoftReference;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Handler;
import android.provider.Browser;
import android.util.Log;
class GlobalHistory {
private static final String LOGTAG = "GeckoGlobalHistory";
private static GlobalHistory sInstance = new GlobalHistory();
static GlobalHistory getInstance() {
return sInstance;
}
// this is the delay between receiving a URI check request and processing it.
// this allows batching together multiple requests and processing them together,
// which is more efficient.
private static final long BATCHING_DELAY_MS = 100;
private final Handler mHandler; // a background thread on which we can process requests
private final Queue<String> mPendingUris; // URIs that need to be checked
private SoftReference<Set<String>> mVisitedCache; // cache of the visited URI list
private final Runnable mNotifierRunnable; // off-thread runnable used to check URIs
private boolean mProcessing; // = false // whether or not the runnable is queued/working
private GlobalHistory() {
mHandler = GeckoAppShell.getHandler();
mPendingUris = new LinkedList<String>();
mVisitedCache = new SoftReference<Set<String>>(null);
mNotifierRunnable = new Runnable() {
public void run() {
Set<String> visitedSet = mVisitedCache.get();
if (visitedSet == null) {
// the cache was wiped away, repopulate it
Log.w(LOGTAG, "Rebuilding visited link set...");
visitedSet = new HashSet<String>();
Cursor c = GeckoApp.mAppContext.getContentResolver().query(Browser.BOOKMARKS_URI,
new String[] { Browser.BookmarkColumns.URL },
Browser.BookmarkColumns.BOOKMARK + "=0 AND " +
Browser.BookmarkColumns.VISITS + ">0",
null,
null);
if (c.moveToFirst()) {
do {
visitedSet.add(c.getString(0));
} while (c.moveToNext());
}
mVisitedCache = new SoftReference<Set<String>>(visitedSet);
c.close();
}
// this runs on the same handler thread as the checkUriVisited code,
// so no synchronization needed
while (true) {
String uri = mPendingUris.poll();
if (uri == null) {
break;
}
if (visitedSet.contains(uri)) {
GeckoAppShell.notifyUriVisited(uri);
}
}
mProcessing = false;
}
};
}
public void add(String uri) {
Browser.updateVisitedHistory(GeckoApp.mAppContext.getContentResolver(), uri, true);
Set<String> visitedSet = mVisitedCache.get();
if (visitedSet != null) {
visitedSet.add(uri);
}
GeckoAppShell.notifyUriVisited(uri);
}
public void update(String uri, String title) {
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.TITLE, title);
GeckoApp.mAppContext.getContentResolver().update(
Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { uri });
}
public void checkUriVisited(final String uri) {
mHandler.post(new Runnable() {
public void run() {
// this runs on the same handler thread as the processing loop,
// so synchronization needed
mPendingUris.add(uri);
if (mProcessing) {
// there's already a runnable queued up or working away, so
// no need to post another
return;
}
mProcessing = true;
mHandler.postDelayed(mNotifierRunnable, BATCHING_DELAY_MS);
}
});
}
}

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

@ -195,7 +195,7 @@ public class LauncherShortcuts extends Activity {
try {
JSONObject webApps = (JSONObject) new JSONTokener(webappsJson).nextValue();
Iterator<Object> appKeys = webApps.keys();
Iterator appKeys = webApps.keys();
HashMap<String, String> map;
while (appKeys.hasNext()) {
@ -235,7 +235,6 @@ public class LauncherShortcuts extends Activity {
new String[] { "favicon", "title" },
new int[] { R.id.favicon, R.id.title }
), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.dismiss();
onListItemClick(id);
@ -244,7 +243,6 @@ public class LauncherShortcuts extends Activity {
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
finish();

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

@ -0,0 +1,509 @@
# ***** 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 the Mozilla browser.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation
# Portions created by the Initial Developer are Copyright (C) 2009-2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Vladimir Vukicevic <vladimir@pobox.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 *****
DEPTH = ../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/ipc/app/defs.mk
DIRS = locales
DIST_FILES = package-name.txt
JAVAFILES = \
AboutHomeContent.java \
AlertNotification.java \
AwesomeBar.java \
AwesomeBarTabs.java \
BrowserToolbar.java \
ConfirmPreference.java \
DoorHanger.java \
DoorHangerPopup.java \
Favicons.java \
FloatUtils.java \
GeckoApp.java \
GeckoAppShell.java \
GeckoAsyncTask.java \
GeckoBatteryManager.java \
GeckoConnectivityReceiver.java \
GeckoEvent.java \
GeckoEventListener.java \
GeckoInputConnection.java \
GeckoPreferences.java \
GeckoSmsManager.java \
GeckoStateListDrawable.java \
GeckoThread.java \
GlobalHistory.java \
PromptService.java \
SurfaceLockInfo.java \
Tab.java \
Tabs.java \
TabsTray.java \
gfx/BufferedCairoImage.java \
gfx/CairoGLInfo.java \
gfx/CairoImage.java \
gfx/CairoUtils.java \
gfx/FloatSize.java \
gfx/GeckoSoftwareLayerClient.java \
gfx/InputConnectionHandler.java \
gfx/IntSize.java \
gfx/Layer.java \
gfx/LayerClient.java \
gfx/LayerController.java \
gfx/LayerRenderer.java \
gfx/LayerView.java \
gfx/NinePatchTileLayer.java \
gfx/PlaceholderLayerClient.java \
gfx/PointUtils.java \
gfx/RectUtils.java \
gfx/ScrollbarLayer.java \
gfx/SingleTileLayer.java \
gfx/TextLayer.java \
gfx/TextureReaper.java \
gfx/TileLayer.java \
gfx/ViewportMetrics.java \
ui/PanZoomController.java \
$(NULL)
PROCESSEDJAVAFILES = \
App.java \
LauncherShortcuts.java \
NotificationHandler.java \
Restarter.java \
$(NULL)
ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
MIN_CPU_VERSION=7
else
MIN_CPU_VERSION=5
endif
ifeq (,$(ANDROID_VERSION_CODE))
ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10)
endif
DEFINES += \
-DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
-DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \
-DMOZ_APP_NAME=$(MOZ_APP_NAME) \
-DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
-DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) \
-DMOZ_MIN_CPU_VERSION=$(MIN_CPU_VERSION) \
-DMOZ_CRASHREPORTER=$(MOZ_CRASHREPORTER) \
-DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
-DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
$(NULL)
GARBAGE += \
AndroidManifest.xml \
classes.dex \
$(PROCESSEDJAVAFILES) \
gecko.ap_ \
res/values/strings.xml \
R.java \
$(NULL)
GARBAGE_DIRS += classes res
# Bug 567884 - Need a way to find appropriate icons during packaging
ifeq ($(MOZ_APP_NAME),fennec)
ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_48x48.png
ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png
# we released these builds to the public with shared IDs and need to keep them
ifeq (org.mozilla.firefox,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.firefox.sharedID"
else ifeq (org.mozilla.firefox_beta,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.firefox.sharedID"
else ifeq (org.mozilla.fennec_aurora,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.fennec.sharedID"
else ifeq (org.mozilla.fennec,$(ANDROID_PACKAGE_NAME))
DEFINES += -DMOZ_ANDROID_SHARED_ID="org.mozilla.fennec.sharedID"
endif
else
ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon48.png
ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png
DEFINES += -DMOZ_ANDROID_SHARED_ID="$(ANDROID_PACKAGE_NAME).sharedID"
endif
RES_LAYOUT = \
res/layout/awesomebar_header_row.xml \
res/layout/awesomebar_row.xml \
res/layout/awesomebar_search.xml \
res/layout/awesomebar_tab_indicator.xml \
res/layout/awesomebar_tabs.xml \
res/layout/browser_toolbar.xml \
res/layout/doorhangerpopup.xml \
res/layout/doorhanger.xml \
res/layout/gecko_app.xml \
res/layout/gecko_menu.xml \
res/layout/launch_app_list.xml \
res/layout/launch_app_listitem.xml \
res/layout/notification_icon_text.xml \
res/layout/notification_progress.xml \
res/layout/notification_progress_text.xml \
res/layout/tabs_row.xml \
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 \
res/layout/abouthome_addon_list_item.xml \
$(NULL)
RES_LAYOUT_V11 = \
res/layout-v11/awesomebar_search.xml \
res/layout-v11/gecko_app.xml \
res/layout-v11/gecko_app_actionbar.xml \
$(NULL)
RES_VALUES = \
res/values/arrays.xml \
res/values/colors.xml \
res/values/styles.xml \
res/values/themes.xml \
$(NULL)
RES_VALUES_V11 = \
res/values-v11/styles.xml \
res/values-v11/themes.xml \
$(NULL)
RES_XML = res/xml/preferences.xml
RES_ANIM = \
res/anim/grow_fade_in.xml \
res/anim/shrink_fade_out.xml \
$(NULL)
RES_DRAWABLE_MDPI_V8 = \
res/drawable-mdpi-v8/ic_awesomebar_go.png \
res/drawable-mdpi-v8/ic_awesomebar_search.png \
res/drawable-mdpi-v8/ic_menu_bookmark_add.png \
res/drawable-mdpi-v8/ic_menu_bookmark_remove.png \
res/drawable-mdpi-v8/ic_menu_find_in_page.png \
res/drawable-mdpi-v8/ic_menu_reload.png \
res/drawable-mdpi-v8/ic_menu_save_as_pdf.png \
res/drawable-mdpi-v8/ic_menu_share.png \
res/drawable-mdpi-v8/tabs_normal.png \
res/drawable-mdpi-v8/tabs_pressed.png \
res/drawable-mdpi-v8/tabs_more.png \
res/drawable-mdpi-v8/tabs_plus.png \
res/drawable-mdpi-v8/address_bar_url_bg.9.png \
res/drawable-mdpi-v8/doorhanger_arrow.png \
res/drawable-mdpi-v8/doorhanger_bg.9.png \
res/drawable-mdpi-v8/doorhanger_shadow_bg.9.png \
res/drawable-mdpi-v8/doorhanger_popup_bg.9.png \
res/drawable-mdpi-v8/site_security_lock.png \
res/drawable-mdpi-v8/urlbar_stop.png \
$(NULL)
RES_DRAWABLE_HDPI_V8 = \
res/drawable-hdpi-v8/ic_awesomebar_go.png \
res/drawable-hdpi-v8/ic_awesomebar_search.png \
res/drawable-hdpi-v8/ic_menu_bookmark_add.png \
res/drawable-hdpi-v8/ic_menu_bookmark_remove.png \
res/drawable-hdpi-v8/ic_menu_find_in_page.png \
res/drawable-hdpi-v8/ic_menu_reload.png \
res/drawable-hdpi-v8/ic_menu_save_as_pdf.png \
res/drawable-hdpi-v8/ic_menu_share.png \
res/drawable-hdpi-v8/tabs_normal.png \
res/drawable-hdpi-v8/tabs_pressed.png \
res/drawable-hdpi-v8/tabs_more.png \
res/drawable-hdpi-v8/tabs_plus.png \
res/drawable-hdpi-v8/address_bar_url_bg.9.png \
res/drawable-hdpi-v8/doorhanger_arrow.png \
res/drawable-hdpi-v8/doorhanger_bg.9.png \
res/drawable-hdpi-v8/doorhanger_shadow_bg.9.png \
res/drawable-hdpi-v8/doorhanger_popup_bg.9.png \
res/drawable-hdpi-v8/site_security_lock.png \
res/drawable-hdpi-v8/urlbar_stop.png \
$(NULL)
RES_DRAWABLE_MDPI_V9 = \
res/drawable-mdpi-v9/ic_menu_bookmark_add.png \
res/drawable-mdpi-v9/ic_menu_bookmark_remove.png \
res/drawable-mdpi-v9/ic_menu_find_in_page.png \
res/drawable-mdpi-v9/ic_menu_reload.png \
res/drawable-mdpi-v9/ic_menu_save_as_pdf.png \
res/drawable-mdpi-v9/ic_menu_share.png \
$(NULL)
RES_DRAWABLE_HDPI_V9 = \
res/drawable-hdpi-v9/ic_menu_bookmark_add.png \
res/drawable-hdpi-v9/ic_menu_bookmark_remove.png \
res/drawable-hdpi-v9/ic_menu_find_in_page.png \
res/drawable-hdpi-v9/ic_menu_reload.png \
res/drawable-hdpi-v9/ic_menu_save_as_pdf.png \
res/drawable-hdpi-v9/ic_menu_share.png \
$(NULL)
RES_DRAWABLE_MDPI_V11 = \
res/drawable-mdpi-v11/ic_awesomebar_go.png \
res/drawable-mdpi-v11/ic_awesomebar_search.png \
res/drawable-mdpi-v11/ic_menu_bookmark_add.png \
res/drawable-mdpi-v11/ic_menu_bookmark_remove.png \
res/drawable-mdpi-v11/ic_menu_find_in_page.png \
res/drawable-mdpi-v11/ic_menu_reload.png \
res/drawable-mdpi-v11/ic_menu_save_as_pdf.png \
res/drawable-mdpi-v11/ic_menu_share.png \
res/drawable-mdpi-v11/tabs_normal.png \
res/drawable-mdpi-v11/tabs_pressed.png \
res/drawable-mdpi-v11/tabs_more.png \
res/drawable-mdpi-v11/tabs_plus.png \
res/drawable-mdpi-v11/address_bar_url_bg.9.png \
$(NULL)
RES_DRAWABLE_HDPI_V11 = \
res/drawable-hdpi-v11/ic_awesomebar_go.png \
res/drawable-hdpi-v11/ic_awesomebar_search.png \
res/drawable-hdpi-v11/ic_menu_bookmark_add.png \
res/drawable-hdpi-v11/ic_menu_bookmark_remove.png \
res/drawable-hdpi-v11/ic_menu_find_in_page.png \
res/drawable-hdpi-v11/ic_menu_reload.png \
res/drawable-hdpi-v11/ic_menu_save_as_pdf.png \
res/drawable-hdpi-v11/ic_menu_share.png \
res/drawable-hdpi-v11/tabs_normal.png \
res/drawable-hdpi-v11/tabs_pressed.png \
res/drawable-hdpi-v11/tabs_more.png \
res/drawable-hdpi-v11/tabs_plus.png \
res/drawable-hdpi-v11/address_bar_url_bg.9.png \
$(NULL)
RES_DRAWABLE_XHDPI_V11 = \
res/drawable-xhdpi-v11/ic_awesomebar_go.png \
res/drawable-xhdpi-v11/ic_awesomebar_search.png \
res/drawable-xhdpi-v11/ic_menu_bookmark_add.png \
res/drawable-xhdpi-v11/ic_menu_bookmark_remove.png \
res/drawable-xhdpi-v11/ic_menu_find_in_page.png \
res/drawable-xhdpi-v11/ic_menu_reload.png \
res/drawable-xhdpi-v11/ic_menu_save_as_pdf.png \
res/drawable-xhdpi-v11/ic_menu_share.png \
res/drawable-xhdpi-v11/tabs_normal.png \
res/drawable-xhdpi-v11/tabs_pressed.png \
res/drawable-xhdpi-v11/tabs_more.png \
res/drawable-xhdpi-v11/tabs_plus.png \
res/drawable-xhdpi-v11/address_bar_url_bg.9.png \
$(NULL)
RES_COLOR = \
res/color/awesomebar_tab_text.xml
AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/)
JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
DEFAULT_BRANDPATH = $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/en-US/brand.dtd
DEFAULT_STRINGSPATH = locales/en-US/android_strings.dtd
LOCALIZED_BRANDPATH = $(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd
LOCALIZED_STRINGSPATH = $(DEPTH)/dist/bin/chrome/android-res/res/values-$(AB_CD)/android_strings.dtd
ifdef MOZ_CRASHREPORTER
PROCESSEDJAVAFILES += CrashReporter.java
MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/crash_reporter.png
RES_LAYOUT += res/layout/crash_reporter.xml
endif
MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/address_bar_bg.xml \
mobile/android/base/resources/drawable/address_bar_url_default.xml \
mobile/android/base/resources/drawable/address_bar_url_pressed.xml \
mobile/android/base/resources/drawable/awesomebar_tab_focus.xml \
mobile/android/base/resources/drawable/awesomebar_tab_focus_selected.xml \
mobile/android/base/resources/drawable/awesomebar_tab_indicator.xml \
mobile/android/base/resources/drawable/awesomebar_tab_press.xml \
mobile/android/base/resources/drawable/awesomebar_tab_press_selected.xml \
mobile/android/base/resources/drawable/awesomebar_tab_selected.xml \
mobile/android/base/resources/drawable/awesomebar_tab_unselected.xml \
mobile/android/base/resources/drawable/desktop_notification.png \
mobile/android/base/resources/drawable/favicon.png \
mobile/android/base/resources/drawable/progress_spinner.xml \
mobile/android/base/resources/drawable/progress_spinner_1.png \
mobile/android/base/resources/drawable/progress_spinner_2.png \
mobile/android/base/resources/drawable/progress_spinner_3.png \
mobile/android/base/resources/drawable/progress_spinner_4.png \
mobile/android/base/resources/drawable/progress_spinner_5.png \
mobile/android/base/resources/drawable/progress_spinner_6.png \
mobile/android/base/resources/drawable/progress_spinner_7.png \
mobile/android/base/resources/drawable/progress_spinner_8.png \
mobile/android/base/resources/drawable/progress_spinner_9.png \
mobile/android/base/resources/drawable/progress_spinner_10.png \
mobile/android/base/resources/drawable/progress_spinner_11.png \
mobile/android/base/resources/drawable/progress_spinner_12.png \
mobile/android/base/resources/drawable/progress_spinner_13.png \
mobile/android/base/resources/drawable/progress_spinner_14.png \
mobile/android/base/resources/drawable/progress_spinner_15.png \
mobile/android/base/resources/drawable/progress_spinner_16.png \
mobile/android/base/resources/drawable/progress_spinner_17.png \
mobile/android/base/resources/drawable/progress_spinner_18.png \
mobile/android/base/resources/drawable/start.png \
mobile/android/base/resources/drawable/site_security_level.xml \
mobile/android/base/resources/drawable/tab_new.png \
mobile/android/base/resources/drawable/tab_close.png \
mobile/android/base/resources/drawable/tabs_button.xml \
mobile/android/base/resources/drawable/tabs_level.xml \
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)
MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' '; fi)
include $(topsrcdir)/config/rules.mk
ifneq ($(AB_CD),en-US)
LOCALIZED_STRINGS_XML = res/values-$(AB_rCD)/strings.xml
endif
# Override the Java settings with some specific android settings
include $(topsrcdir)/config/android-common.mk
# Note that we're going to set up a dependency directly between embed_android.dex and the java files
# Instead of on the .class files, since more than one .class file might be produced per .java file
classes.dex: $(JAVAFILES) $(PROCESSEDJAVAFILES) R.java
$(NSINSTALL) -D classes
$(JAVAC) $(JAVAC_FLAGS) -Xlint:unchecked -Xlint:deprecation -d classes $(addprefix $(srcdir)/,$(JAVAFILES)) $(PROCESSEDJAVAFILES) R.java
$(DX) --dex --output=$@ classes
AndroidManifest.xml $(PROCESSEDJAVAFILES) package-name.txt: % : %.in
$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
$(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
res/drawable/icon.png: $(MOZ_APP_ICON)
$(NSINSTALL) -D res/drawable
cp $(ICON_PATH) $@
res/drawable-hdpi/icon.png: $(MOZ_APP_ICON)
$(NSINSTALL) -D res/drawable-hdpi
cp $(ICON_PATH_HDPI) $@
RES_DRAWABLE = $(addprefix res/drawable/,$(notdir $(MOZ_ANDROID_DRAWABLES)))
$(RES_DRAWABLE): $(addprefix $(topsrcdir)/,$(MOZ_ANDROID_DRAWABLES))
$(NSINSTALL) -D res/drawable
$(NSINSTALL) $^ res/drawable/
$(RES_LAYOUT): $(subst res/,$(srcdir)/resources/,$(RES_LAYOUT))
$(NSINSTALL) -D res/layout
$(NSINSTALL) $^ res/layout
$(RES_LAYOUT_V11): $(subst res/,$(srcdir)/resources/,$(RES_LAYOUT_V11))
$(NSINSTALL) -D res/layout-v11
$(NSINSTALL) $(srcdir)/resources/layout-v11/* res/layout-v11/
$(RES_VALUES): $(subst res/,$(srcdir)/resources/,$(RES_VALUES))
$(NSINSTALL) -D res/values
$(NSINSTALL) $^ res/values
$(RES_VALUES_V11): $(subst res/,$(srcdir)/resources/,$(RES_VALUES_V11))
$(NSINSTALL) -D res/values-v11
$(NSINSTALL) $(srcdir)/resources/values-v11/* res/values-v11
$(RES_XML): $(subst res/,$(srcdir)/resources/,$(RES_XML))
$(NSINSTALL) -D res/xml
$(NSINSTALL) $(srcdir)/resources/xml/* res/xml/
$(RES_ANIM): $(subst res/,$(srcdir)/resources/,$(RES_ANIM))
$(NSINSTALL) -D res/anim
$(NSINSTALL) $(srcdir)/resources/anim/* res/anim/
$(RES_DRAWABLE_MDPI_V8): $(subst res/,$(srcdir)/resources/,$(RES_DRAWABLE_MDPI_V8))
$(NSINSTALL) -D res/drawable-mdpi-v8
$(NSINSTALL) $(srcdir)/resources/drawable-mdpi-v8/* res/drawable-mdpi-v8/
$(RES_DRAWABLE_HDPI_V8): $(subst res/,$(srcdir)/resources/,$(RES_DRAWABLE_HDPI_V8))
$(NSINSTALL) -D res/drawable-hdpi-v8
$(NSINSTALL) $(srcdir)/resources/drawable-hdpi-v8/* res/drawable-hdpi-v8/
$(RES_DRAWABLE_MDPI_V9): $(subst res/,$(srcdir)/resources/,$(RES_DRAWABLE_MDPI_V9))
$(NSINSTALL) -D res/drawable-mdpi-v9
$(NSINSTALL) $(srcdir)/resources/drawable-mdpi-v9/* res/drawable-mdpi-v9/
$(RES_DRAWABLE_HDPI_V9): $(subst res/,$(srcdir)/resources/,$(RES_DRAWABLE_HDPI_V9))
$(NSINSTALL) -D res/drawable-hdpi-v9
$(NSINSTALL) $(srcdir)/resources/drawable-hdpi-v9/* res/drawable-hdpi-v9/
$(RES_DRAWABLE_MDPI_V11): $(subst res/,$(srcdir)/resources/,$(RES_DRAWABLE_MDPI_V11))
$(NSINSTALL) -D res/drawable-mdpi-v11
$(NSINSTALL) $(srcdir)/resources/drawable-mdpi-v11/* res/drawable-mdpi-v11/
$(RES_DRAWABLE_HDPI_V11): $(subst res/,$(srcdir)/resources/,$(RES_DRAWABLE_HDPI_V11))
$(NSINSTALL) -D res/drawable-hdpi-v11
$(NSINSTALL) $(srcdir)/resources/drawable-hdpi-v11/* res/drawable-hdpi-v11/
$(RES_DRAWABLE_XHDPI_V11): $(subst res/,$(srcdir)/resources/,$(RES_DRAWABLE_XHDPI_V11))
$(NSINSTALL) -D res/drawable-xhdpi-v11
$(NSINSTALL) $(srcdir)/resources/drawable-xhdpi-v11/* res/drawable-xhdpi-v11/
$(RES_COLOR): $(subst res/,$(srcdir)/resources/,$(RES_COLOR))
$(NSINSTALL) -D res/color
$(NSINSTALL) $^ res/color
R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_LAYOUT_V11) $(RES_DRAWABLE) $(RES_VALUES) $(RES_VALUES_V11) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_MDPI_V8) $(RES_DRAWABLE_HDPI_V8) $(RES_DRAWABLE_MDPI_V9) $(RES_DRAWABLE_HDPI_V9) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_COLOR) res/drawable/icon.png res/drawable-hdpi/icon.png res/values/strings.xml AndroidManifest.xml
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko
gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_LAYOUT) $(RES_LAYOUT_V11) $(RES_DRAWABLE) $(RES_VALUES) $(RES_VALUES_V11) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_MDPI_V8) $(RES_DRAWABLE_HDPI_V8) $(RES_DRAWABLE_MDPI_V9) $(RES_DRAWABLE_HDPI_V9) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_COLOR) res/values/strings.xml FORCE
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@
res/values/strings.xml: $(DEFAULT_BRANDPATH) $(DEFAULT_STRINGSPATH) $(srcdir)/strings.xml.in
mkdir -p res/values
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(DEFAULT_BRANDPATH)" \
-DSTRINGSPATH="$(DEFAULT_STRINGSPATH)" \
$(srcdir)/strings.xml.in \
> $@
res/values-$(AB_rCD)/strings.xml: $(LOCALIZED_BRANDPATH) $(LOCALIZED_STRINGSPATH) $(srcdir)/strings.xml.in
mkdir -p res/values-$(AB_rCD)
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(call core_abspath,$(LOCALIZED_BRANDPATH))" \
-DSTRINGSPATH="$(call core_abspath,$(LOCALIZED_STRINGSPATH))" \
$(srcdir)/strings.xml.in \
> $@
chrome:: $(LOCALIZED_STRINGS_XML)
libs:: classes.dex package-name.txt
$(INSTALL) classes.dex $(FINAL_TARGET)
$(INSTALL) package-name.txt $(FINAL_TARGET)

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

@ -50,6 +50,8 @@ import android.net.Uri;
public class NotificationHandler
extends BroadcastReceiver
{
private static final String LOGTAG = "GeckoNotificationHandler";
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null)
@ -68,14 +70,14 @@ public class NotificationHandler
alertCookie = "";
}
Log.i("GeckoAppJava", "NotificationHandler.handleIntent\n" +
Log.i(LOGTAG, "NotificationHandler.handleIntent\n" +
"- action = '" + action + "'\n" +
"- alertName = '" + alertName + "'\n" +
"- alertCookie = '" + alertCookie + "'");
int notificationID = alertName.hashCode();
Log.i("GeckoAppJava", "Handle notification ID " + notificationID);
Log.i(LOGTAG, "Handle notification ID " + notificationID);
if (App.mAppContext != null) {
// This should call the observer, if any
@ -93,11 +95,11 @@ public class NotificationHandler
appIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appIntent.putExtra("args", "-alert " + alertName + (alertCookie.length() > 0 ? "#" + alertCookie : ""));
try {
Log.i("GeckoAppJava", "startActivity with intent: Action='" + appIntent.getAction() + "'" +
Log.i(LOGTAG, "startActivity with intent: Action='" + appIntent.getAction() + "'" +
", args='" + appIntent.getStringExtra("args") + "'" );
context.startActivity(appIntent);
} catch (ActivityNotFoundException e) {
Log.e("GeckoAppJava", "NotificationHandler Exception: ", e);
Log.e(LOGTAG, "NotificationHandler Exception: ", e);
}
}
}

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

@ -0,0 +1,461 @@
/* -*- 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):
* Wes Johnston <wjohnston@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.io.*;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import android.util.Log;
import java.lang.String;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
import android.widget.CheckBox;
import android.widget.CheckedTextView;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnMultiChoiceClickListener;
import org.json.JSONArray;
import org.json.JSONObject;
import android.text.method.PasswordTransformationMethod;
import android.graphics.Color;
import android.text.InputType;
import android.app.AlertDialog;
public class PromptService implements OnClickListener, OnCancelListener, OnItemClickListener {
private static final String LOGTAG = "GeckoPromptService";
private PromptInput[] mInputs;
private AlertDialog mDialog = null;
private class PromptButton {
public String label = "";
PromptButton(JSONObject aJSONButton) {
try {
label = aJSONButton.getString("label");
} catch(Exception ex) { }
}
}
private class PromptInput {
private String label = "";
private String type = "";
private String hint = "";
private JSONObject mJSONInput = null;
private View view = null;
public PromptInput(JSONObject aJSONInput) {
mJSONInput = aJSONInput;
try {
label = aJSONInput.getString("label");
} catch(Exception ex) { }
try {
type = aJSONInput.getString("type");
} catch(Exception ex) { }
try {
hint = aJSONInput.getString("hint");
} catch(Exception ex) { }
}
public View getView() {
if (type.equals("checkbox")) {
CheckBox checkbox = new CheckBox(GeckoApp.mAppContext);
checkbox.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
checkbox.setText(label);
try {
Boolean value = mJSONInput.getBoolean("checked");
checkbox.setChecked(value);
} catch(Exception ex) { }
view = (View)checkbox;
} else if (type.equals("textbox") || this.type.equals("password")) {
EditText input = new EditText(GeckoApp.mAppContext);
int inputtype = InputType.TYPE_CLASS_TEXT;
if (type.equals("password")) {
inputtype |= InputType.TYPE_TEXT_VARIATION_PASSWORD | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
}
input.setInputType(inputtype);
try {
String value = mJSONInput.getString("value");
input.setText(value);
} catch(Exception ex) { }
if (!hint.equals("")) {
input.setHint(hint);
}
view = (View)input;
} else if (type.equals("menulist")) {
Spinner spinner = new Spinner(GeckoApp.mAppContext);
try {
String[] listitems = getStringArray(mJSONInput, "values");
if (listitems.length > 0) {
ArrayAdapter<String> adapter = new ArrayAdapter<String>(GeckoApp.mAppContext, android.R.layout.simple_dropdown_item_1line, listitems);
spinner.setAdapter(adapter);
}
} catch(Exception ex) { }
view = (View)spinner;
}
return view;
}
public String getName() {
return type;
}
public String getValue() {
if (this.type.equals("checkbox")) {
CheckBox checkbox = (CheckBox)view;
return checkbox.isChecked() ? "true" : "false";
} else if (type.equals("textbox") || type.equals("password")) {
EditText edit = (EditText)view;
return edit.getText().toString();
} else if (type.equals("menulist")) {
Spinner spinner = (Spinner)view;
return Integer.toString(spinner.getSelectedItemPosition());
}
return "";
}
}
public void Show(String aTitle, String aText, PromptButton[] aButtons, PromptListItem[] aMenuList, boolean aMultipleSelection) {
AlertDialog.Builder builder = new AlertDialog.Builder(GeckoApp.mAppContext);
if (!aTitle.equals("")) {
builder.setTitle(aTitle);
}
if (!aText.equals("")) {
builder.setMessage(aText);
}
int length = mInputs.length;
if (aMenuList.length > 0) {
int resourceId = android.R.layout.select_dialog_item;
if (mSelected != null && mSelected.length > 0) {
if (aMultipleSelection) {
resourceId = android.R.layout.select_dialog_multichoice;
} else {
resourceId = android.R.layout.select_dialog_singlechoice;
}
}
PromptListAdapter adapter = new PromptListAdapter(GeckoApp.mAppContext, resourceId, aMenuList);
if (mSelected != null && mSelected.length > 0) {
if (aMultipleSelection) {
LayoutInflater inflater = GeckoApp.mAppContext.getLayoutInflater();
adapter.listView = (ListView) inflater.inflate(R.layout.select_dialog_list, null);
adapter.listView.setOnItemClickListener(this);
builder.setInverseBackgroundForced(true);
adapter.listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
adapter.listView.setAdapter(adapter);
builder.setView(adapter.listView);
} else {
int selectedIndex = -1;
for (int i = 0; i < mSelected.length; i++) {
if (mSelected[i]) {
selectedIndex = i;
break;
}
}
mSelected = null;
builder.setSingleChoiceItems(adapter, selectedIndex, this);
}
} else {
builder.setAdapter(adapter, this);
mSelected = null;
}
} else if (length == 1) {
builder.setView(mInputs[0].getView());
} else if (length > 1) {
LinearLayout linearLayout = new LinearLayout(GeckoApp.mAppContext);
linearLayout.setOrientation(LinearLayout.VERTICAL);
for (int i = 0; i < length; i++) {
View content = mInputs[i].getView();
linearLayout.addView(content);
}
builder.setView((View)linearLayout);
}
length = aButtons.length;
if (length > 0) {
builder.setPositiveButton(aButtons[0].label, this);
}
if (length > 1) {
builder.setNeutralButton(aButtons[1].label, this);
}
if (length > 2) {
builder.setNegativeButton(aButtons[2].label, this);
}
mDialog = builder.create();
mDialog.setOnCancelListener(this);
mDialog.show();
}
public void onClick(DialogInterface aDialog, int aWhich) {
JSONObject ret = new JSONObject();
try {
int button = -1;
ListView list = mDialog.getListView();
if (list != null || mSelected != null) {
button = aWhich;
if (mSelected != null) {
JSONArray selected = new JSONArray();
for (int i = 0; i < mSelected.length; i++) {
selected.put(mSelected[i]);
}
ret.put("button", selected);
} else {
ret.put("button", button);
}
} else {
switch(aWhich) {
case DialogInterface.BUTTON_POSITIVE : button = 0; break;
case DialogInterface.BUTTON_NEUTRAL : button = 1; break;
case DialogInterface.BUTTON_NEGATIVE : button = 2; break;
}
ret.put("button", button);
}
if (mInputs != null) {
for (int i = 0; i < mInputs.length; i++) {
ret.put(mInputs[i].getName(), mInputs[i].getValue());
}
}
} catch(Exception ex) {
Log.i(LOGTAG, "Error building return: " + ex);
}
if (mDialog != null) {
mDialog.dismiss();
}
finishDialog(ret.toString());
}
private boolean[] mSelected = null;
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mSelected[position] = !mSelected[position];
}
public void onCancel(DialogInterface aDialog) {
JSONObject ret = new JSONObject();
try {
ret.put("button", -1);
} catch(Exception ex) { }
finishDialog(ret.toString());
}
public void finishDialog(String aReturn) {
mInputs = null;
mDialog = null;
mSelected = null;
try {
GeckoAppShell.sPromptQueue.put(aReturn);
} catch(Exception ex) { }
}
public void processMessage(JSONObject geckoObject) {
String title = "";
try {
title = geckoObject.getString("title");
} catch(Exception ex) { }
String text = "";
try {
text = geckoObject.getString("text");
} catch(Exception ex) { }
JSONArray buttons = new JSONArray();
try {
buttons = geckoObject.getJSONArray("buttons");
} catch(Exception ex) { }
int length = buttons.length();
PromptButton[] promptbuttons = new PromptButton[length];
for (int i = 0; i < length; i++) {
try {
promptbuttons[i] = new PromptButton(buttons.getJSONObject(i));
} catch(Exception ex) { }
}
JSONArray inputs = new JSONArray();
try {
inputs = geckoObject.getJSONArray("inputs");
} catch(Exception ex) { }
length = inputs.length();
mInputs = new PromptInput[length];
for (int i = 0; i < length; i++) {
try {
mInputs[i] = new PromptInput(inputs.getJSONObject(i));
} catch(Exception ex) { }
}
PromptListItem[] menuitems = getListItemArray(geckoObject, "listitems");
mSelected = getBooleanArray(geckoObject, "selected");
boolean multiple = false;
try {
multiple = geckoObject.getBoolean("multiple");
} catch(Exception ex) { }
this.Show(title, text, promptbuttons, menuitems, multiple);
}
private String[] getStringArray(JSONObject aObject, String aName) {
JSONArray items = new JSONArray();
try {
items = aObject.getJSONArray(aName);
} catch(Exception ex) { }
int length = items.length();
String[] list = new String[length];
for (int i = 0; i < length; i++) {
try {
list[i] = items.getString(i);
} catch(Exception ex) { }
}
return list;
}
private boolean[] getBooleanArray(JSONObject aObject, String aName) {
JSONArray items = new JSONArray();
try {
items = aObject.getJSONArray(aName);
} catch(Exception ex) { return null; }
int length = items.length();
boolean[] list = new boolean[length];
for (int i = 0; i < length; i++) {
try {
list[i] = items.getBoolean(i);
} catch(Exception ex) { }
}
return list;
}
private PromptListItem[] getListItemArray(JSONObject aObject, String aName) {
JSONArray items = new JSONArray();
try {
items = aObject.getJSONArray(aName);
} catch(Exception ex) { }
int length = items.length();
PromptListItem[] list = new PromptListItem[length];
for (int i = 0; i < length; i++) {
try {
list[i] = new PromptListItem(items.getJSONObject(i));
} catch(Exception ex) { }
}
return list;
}
private class PromptListItem {
public String label = "";
public boolean isGroup = false;
public boolean inGroup = false;
public boolean disabled = false;
public int id = 0;
PromptListItem(JSONObject aObject) {
try { label = aObject.getString("label"); } catch(Exception ex) { }
try { isGroup = aObject.getBoolean("isGroup"); } catch(Exception ex) { }
try { inGroup = aObject.getBoolean("inGroup"); } catch(Exception ex) { }
try { disabled = aObject.getBoolean("disabled"); } catch(Exception ex) { }
try { id = aObject.getInt("id"); } catch(Exception ex) { }
}
}
public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
public ListView listView = null;
private PromptListItem[] mList;
private int mResourceId = -1;
PromptListAdapter(Context context, int textViewResourceId, PromptListItem[] objects) {
super(context, textViewResourceId, objects);
mList = objects;
mResourceId = textViewResourceId;
}
public int getCount() {
return mList.length;
}
public PromptListItem getItem(int position) {
return mList[position];
}
public long getItemId(int position) {
return mList[position].id;
}
public View getView(int position, View convertView, ViewGroup parent) {
PromptListItem item = getItem(position);
int resourceId = mResourceId;
if (item.isGroup) {
resourceId = R.layout.list_item_header;
}
LayoutInflater inflater = GeckoApp.mAppContext.getLayoutInflater();
View row = inflater.inflate(resourceId, null);
if (!item.isGroup){
try {
CheckedTextView ct = (CheckedTextView)row.findViewById(android.R.id.text1);
if (ct != null){
ct.setEnabled(!item.disabled);
ct.setClickable(item.disabled);
// Apparently just using ct.setChecked(true) doesn't work, so this
// is stolen from the android source code as a way to set the checked
// state of these items
if (mSelected[position] && listView != null) {
listView.setItemChecked(position, true);
}
}
} catch (Exception ex) { }
}
TextView t1 = (TextView) row.findViewById(android.R.id.text1);
if (t1 != null) {
t1.setText(item.label);
}
return row;
}
}
}

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

@ -46,10 +46,11 @@ import java.io.*;
import org.mozilla.gecko.GeckoAppShell;
public class Restarter extends Activity {
private static final String LOGTAG = "GeckoRestarter";
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i("Restarter", "trying to restart @MOZ_APP_NAME@");
Log.i(LOGTAG, "trying to restart @MOZ_APP_NAME@");
try {
int countdown = 40;
while (GeckoAppShell.checkForGeckoProcs() && --countdown > 0) {
@ -71,7 +72,7 @@ public class Restarter extends Activity {
}
}
} catch (Exception e) {
Log.i("Restarter", e.toString());
Log.i(LOGTAG, e.toString());
}
try {
String action = "android.intent.action.MAIN";
@ -82,10 +83,10 @@ public class Restarter extends Activity {
if (b != null)
intent.putExtras(b);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.i("GeckoAppJava", intent.toString());
Log.i(LOGTAG, intent.toString());
startActivity(intent);
} catch (Exception e) {
Log.i("Restarter", e.toString());
Log.i(LOGTAG, e.toString());
}
// Give the new process time to start before we die
GeckoAppShell.waitForAnotherGeckoProc();

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

@ -0,0 +1,18 @@
package org.mozilla.gecko;
import android.graphics.Canvas;
import java.nio.Buffer;
public class SurfaceLockInfo {
public int dirtyTop;
public int dirtyLeft;
public int dirtyRight;
public int dirtyBottom;
public int bpr;
public int format;
public int width;
public int height;
public Buffer buffer;
public Canvas canvas;
}

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

@ -0,0 +1,475 @@
/* -*- 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) 2009-2010
* 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 android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.Bitmap;
import android.provider.Browser;
import android.util.DisplayMetrics;
import android.util.Log;
import android.graphics.Bitmap;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Tab {
public static enum AgentMode { MOBILE, DESKTOP };
private static final String LOGTAG = "GeckoTab";
static int sMinDim = 0;
private int mId;
private String mUrl;
private String mTitle;
private Drawable mFavicon;
private String mFaviconUrl;
private String mSecurityMode;
private Drawable mThumbnail;
private List<HistoryEntry> mHistory;
private int mHistoryIndex;
private boolean mLoading;
private boolean mBookmark;
private HashMap<String, DoorHanger> mDoorHangers;
private long mFaviconLoadId;
private AgentMode mAgentMode = AgentMode.MOBILE;
static class HistoryEntry {
public final String mUri; // must never be null
public String mTitle; // must never be null
public HistoryEntry(String uri, String title) {
mUri = uri;
mTitle = title;
}
}
public Tab() {
this(-1, "");
}
public Tab(int id, String url) {
mId = id;
mUrl = url;
mTitle = "";
mFavicon = null;
mFaviconUrl = null;
mSecurityMode = "unknown";
mThumbnail = null;
mHistory = new ArrayList<HistoryEntry>();
mHistoryIndex = -1;
mBookmark = false;
mDoorHangers = new HashMap<String, DoorHanger>();
mFaviconLoadId = 0;
}
public int getId() {
return mId;
}
public String getURL() {
return mUrl;
}
public String getTitle() {
return mTitle;
}
public String getDisplayTitle() {
if (mTitle != null && mTitle.length() > 0) {
return mTitle;
}
return mUrl;
}
public Drawable getFavicon() {
return mFavicon;
}
public Drawable getThumbnail() {
return mThumbnail;
}
public void updateThumbnail(final Bitmap b) {
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
if (sMinDim == 0) {
DisplayMetrics metrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
sMinDim = Math.min(metrics.widthPixels, metrics.heightPixels);
}
Bitmap bitmap = Bitmap.createBitmap(b, 0, 0, sMinDim, sMinDim);
mThumbnail = new BitmapDrawable(bitmap);
saveThumbnailToDB(bitmap);
}
});
}
public String getFaviconURL() {
return mFaviconUrl;
}
public String getSecurityMode() {
return mSecurityMode;
}
public boolean isLoading() {
return mLoading;
}
public boolean isBookmark() {
return mBookmark;
}
public void updateURL(String url) {
if (url != null && url.length() > 0) {
mUrl = url;
Log.i(LOGTAG, "Updated url: " + url + " for tab with id: " + mId);
updateBookmark();
}
}
public void updateTitle(String title) {
mTitle = (title == null ? "" : title);
Log.i(LOGTAG, "Updated title: " + mTitle + " for tab with id: " + mId);
final HistoryEntry he = getLastHistoryEntry();
if (he != null) {
he.mTitle = mTitle;
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
GlobalHistory.getInstance().update(he.mUri, he.mTitle);
}
});
} else {
Log.e(LOGTAG, "Requested title update on empty history stack");
}
}
public void setLoading(boolean loading) {
mLoading = loading;
}
private void setBookmark(boolean bookmark) {
mBookmark = bookmark;
}
public void setFaviconLoadId(long faviconLoadId) {
mFaviconLoadId = faviconLoadId;
}
public long getFaviconLoadId() {
return mFaviconLoadId;
}
public HistoryEntry getLastHistoryEntry() {
if (mHistory.isEmpty())
return null;
return mHistory.get(mHistoryIndex);
}
public void updateFavicon(Drawable favicon) {
mFavicon = favicon;
Log.i(LOGTAG, "Updated favicon for tab with id: " + mId);
}
public void updateFaviconURL(String faviconUrl) {
mFaviconUrl = faviconUrl;
Log.i(LOGTAG, "Updated favicon URL for tab with id: " + mId);
}
public void updateSecurityMode(String mode) {
mSecurityMode = mode;
}
private void updateBookmark() {
new CheckBookmarkTask().execute();
}
public void addBookmark() {
new AddBookmarkTask().execute();
}
public void removeBookmark() {
new RemoveBookmarkTask().execute();
}
public boolean doReload() {
if (mHistory.isEmpty())
return false;
GeckoEvent e = new GeckoEvent("Session:Reload", "");
GeckoAppShell.sendEventToGecko(e);
return true;
}
public boolean doBack() {
if (mHistoryIndex < 1) {
return false;
}
GeckoEvent e = new GeckoEvent("Session:Back", "");
GeckoAppShell.sendEventToGecko(e);
return true;
}
public boolean doStop() {
GeckoEvent e = new GeckoEvent("Session:Stop", "");
GeckoAppShell.sendEventToGecko(e);
return true;
}
public boolean canDoForward() {
return (mHistoryIndex + 1 < mHistory.size());
}
public boolean doForward() {
if (mHistoryIndex + 1 >= mHistory.size()) {
return false;
}
GeckoEvent e = new GeckoEvent("Session:Forward", "");
GeckoAppShell.sendEventToGecko(e);
return true;
}
public void addDoorHanger(String value, DoorHanger dh) {
mDoorHangers.put(value, dh);
}
public void removeDoorHanger(String value) {
mDoorHangers.remove(value);
}
public void removeAllDoorHangers() {
mDoorHangers = new HashMap<String, DoorHanger>();
}
public void removeTransientDoorHangers() {
for (String value : mDoorHangers.keySet()) {
DoorHanger dh = mDoorHangers.get(value);
if (dh.shouldRemove())
mDoorHangers.remove(value);
}
}
public DoorHanger getDoorHanger(String value) {
if (mDoorHangers == null)
return null;
if (mDoorHangers.containsKey(value))
return mDoorHangers.get(value);
return null;
}
public HashMap<String, DoorHanger> getDoorHangers() {
return mDoorHangers;
}
void handleSessionHistoryMessage(String event, JSONObject message) throws JSONException {
if (event.equals("New")) {
final String uri = message.getString("uri");
mHistoryIndex++;
while (mHistory.size() > mHistoryIndex) {
mHistory.remove(mHistoryIndex);
}
HistoryEntry he = new HistoryEntry(uri, "");
mHistory.add(he);
GeckoAppShell.getHandler().post(new Runnable() {
public void run() {
GlobalHistory.getInstance().add(uri);
}
});
} else if (event.equals("Back")) {
if (mHistoryIndex - 1 < 0) {
Log.e(LOGTAG, "Received unexpected back notification");
return;
}
mHistoryIndex--;
} else if (event.equals("Forward")) {
if (mHistoryIndex + 1 >= mHistory.size()) {
Log.e(LOGTAG, "Received unexpected forward notification");
return;
}
mHistoryIndex++;
} else if (event.equals("Goto")) {
int index = message.getInt("index");
if (index < 0 || index >= mHistory.size()) {
Log.e(LOGTAG, "Received unexpected history-goto notification");
return;
}
mHistoryIndex = index;
} else if (event.equals("Purge")) {
mHistory.clear();
mHistoryIndex = -1;
}
}
private class CheckBookmarkTask extends GeckoAsyncTask<Void, Void, Boolean> {
@Override
protected Boolean doInBackground(Void... unused) {
ContentResolver resolver = Tabs.getInstance().getContentResolver();
Cursor cursor = resolver.query(Browser.BOOKMARKS_URI,
null,
Browser.BookmarkColumns.URL + " = ? and " + Browser.BookmarkColumns.BOOKMARK + " = ?",
new String[] { getURL(), "1" },
Browser.BookmarkColumns.URL);
int count = cursor.getCount();
cursor.close();
if (count == 1)
return true;
else
return false;
}
@Override
protected void onPostExecute(Boolean isBookmark) {
setBookmark(isBookmark.booleanValue());
}
}
private class AddBookmarkTask extends GeckoAsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... unused) {
ContentResolver resolver = Tabs.getInstance().getContentResolver();
Cursor cursor = resolver.query(Browser.BOOKMARKS_URI,
null,
Browser.BookmarkColumns.URL + " = ?",
new String[] { getURL() },
Browser.BookmarkColumns.URL);
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.BOOKMARK, "1");
values.put(Browser.BookmarkColumns.TITLE, getTitle());
if (cursor.getCount() == 1) {
//entry exists, update the bookmark flag
resolver.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { getURL() });
} else {
//add a new entry
values.put(Browser.BookmarkColumns.URL, mUrl);
resolver.insert(Browser.BOOKMARKS_URI,
values);
}
cursor.close();
return null;
}
@Override
protected void onPostExecute(Void unused) {
setBookmark(true);
}
}
private void saveThumbnailToDB(Bitmap bitmap) {
try {
ContentResolver resolver = Tabs.getInstance().getContentResolver();
Cursor cursor = resolver.query(Browser.BOOKMARKS_URI,
null,
Browser.BookmarkColumns.URL + " = ?",
new String[] { getURL() },
Browser.BookmarkColumns.URL);
ContentValues values = new ContentValues();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
values.put("thumbnail", bos.toByteArray());
if (cursor.getCount() == 1) {
//entry exists, update the bookmark flag
resolver.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { getURL() });
} else {
//add a new entry
values.put(Browser.BookmarkColumns.URL, mUrl);
resolver.insert(Browser.BOOKMARKS_URI,
values);
}
cursor.close();
} catch (Exception e) {
// ignore
}
}
private class RemoveBookmarkTask extends GeckoAsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... unused) {
ContentResolver resolver = Tabs.getInstance().getContentResolver();
ContentValues values = new ContentValues();
values.put(Browser.BookmarkColumns.BOOKMARK, "0");
resolver.update(Browser.BOOKMARKS_URI,
values,
Browser.BookmarkColumns.URL + " = ?",
new String[] { getURL() });
return null;
}
@Override
protected void onPostExecute(Void unused) {
setBookmark(false);
}
}
public void setAgentMode(AgentMode agentMode) {
mAgentMode = agentMode;
}
public AgentMode getAgentMode() {
return mAgentMode;
}
}

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

@ -0,0 +1,176 @@
/* -*- 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) 2009-2010
* 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.*;
import android.content.ContentResolver;
import android.graphics.drawable.*;
import android.util.Log;
import org.json.JSONObject;
public class Tabs implements GeckoEventListener {
private static final String LOGTAG = "GeckoTabs";
private static int selectedTab = -1;
private HashMap<Integer, Tab> tabs;
private ArrayList<Tab> order;
private ContentResolver resolver;
private Tabs() {
tabs = new HashMap<Integer, Tab>();
order = new ArrayList<Tab>();
GeckoAppShell.registerGeckoEventListener("SessionHistory:New", this);
GeckoAppShell.registerGeckoEventListener("SessionHistory:Back", this);
GeckoAppShell.registerGeckoEventListener("SessionHistory:Forward", this);
GeckoAppShell.registerGeckoEventListener("SessionHistory:Goto", this);
GeckoAppShell.registerGeckoEventListener("SessionHistory:Purge", this);
}
public int getCount() {
return tabs.size();
}
public Tab addTab(int id, String url) {
if (tabs.containsKey(id))
return tabs.get(id);
Tab tab = new Tab(id, url);
tabs.put(id, tab);
order.add(tab);
Log.i(LOGTAG, "Added a tab with id: " + id + ", url: " + url);
return tab;
}
public void removeTab(int id) {
if (tabs.containsKey(id)) {
order.remove(getTab(id));
tabs.remove(id);
Log.i(LOGTAG, "Removed a tab with id: " + id);
}
}
public Tab selectTab(int id) {
if (!tabs.containsKey(id))
return null;
selectedTab = id;
return tabs.get(id);
}
public int getIndexOf(Tab tab) {
return order.lastIndexOf(tab);
}
public Tab getTabAt(int index) {
if (index < order.size())
return order.get(index);
else
return null;
}
public Tab getSelectedTab() {
return tabs.get(selectedTab);
}
public int getSelectedTabId() {
return selectedTab;
}
public boolean isSelectedTab(Tab tab) {
return (tab.getId() == selectedTab);
}
public Tab getTab(int id) {
if (getCount() == 0)
return null;
if (!tabs.containsKey(id))
return null;
return tabs.get(id);
}
public HashMap<Integer, Tab> getTabs() {
if (getCount() == 0)
return null;
return tabs;
}
public ArrayList<Tab> getTabsInOrder() {
if (getCount() == 0)
return null;
return order;
}
public void setContentResolver(ContentResolver resolver) {
this.resolver = resolver;
}
public ContentResolver getContentResolver() {
return resolver;
}
//Making Tabs a singleton class
private static class TabsInstanceHolder {
private static final Tabs INSTANCE = new Tabs();
}
public static Tabs getInstance() {
return Tabs.TabsInstanceHolder.INSTANCE;
}
// GeckoEventListener implementation
public void handleMessage(String event, JSONObject message) {
try {
if (event.startsWith("SessionHistory:")) {
Tab tab = getTab(message.getInt("tabID"));
if (tab != null) {
event = event.substring("SessionHistory:".length());
tab.handleSessionHistoryMessage(event, message);
}
}
} catch (Exception e) {
Log.i(LOGTAG, "handleMessage throws " + e + " for message: " + event);
}
}
}

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

@ -0,0 +1,274 @@
/* -*- 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.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.graphics.Typeface;
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;
private boolean mWaitingForClose;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.tabs_tray);
mWaitingForClose = false;
mList = (ListView) findViewById(R.id.list);
LinearLayout addTab = (LinearLayout) findViewById(R.id.add_tab);
addTab.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
GeckoApp.mAppContext.addTab();
finishActivity();
}
});
// Adding a native divider for the add-tab
LinearLayout lastDivider = new LinearLayout(this);
lastDivider.setOrientation(LinearLayout.HORIZONTAL);
lastDivider.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, mList.getDividerHeight()));
lastDivider.setBackgroundDrawable(mList.getDivider());
addTab.addView(lastDivider, 0);
LinearLayout container = (LinearLayout) findViewById(R.id.container);
container.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
finishActivity();
}
});
GeckoApp.registerOnTabsChangedListener(this);
onTabsChanged(null);
}
@Override
public void onDestroy() {
super.onDestroy();
GeckoApp.unregisterOnTabsChangedListener(this);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
// This function is called after the initial list is populated
// Scrolling to the selected tab can happen here
if (hasFocus) {
int position = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab());
if (position != -1)
mList.smoothScrollToPosition(position);
}
}
public void onTabsChanged(Tab tab) {
if (Tabs.getInstance().getCount() == 1)
finishActivity();
if (mTabsAdapter == null) {
mTabsAdapter = new TabsAdapter(this, Tabs.getInstance().getTabsInOrder());
mList.setAdapter(mTabsAdapter);
return;
}
int position = mTabsAdapter.getPositionForTab(tab);
if (position == -1)
return;
if (Tabs.getInstance().getIndexOf(tab) == -1) {
mWaitingForClose = false;
mTabsAdapter = new TabsAdapter(this, Tabs.getInstance().getTabsInOrder());
mList.setAdapter(mTabsAdapter);
} else {
View view = mList.getChildAt(position - mList.getFirstVisiblePosition());
mTabsAdapter.assignValues(view, tab);
}
}
void finishActivity() {
finish();
overridePendingTransition(0, R.anim.shrink_fade_out);
}
// Adapter to bind tabs into a list
private class TabsAdapter extends BaseAdapter {
public TabsAdapter(Context context, ArrayList<Tab> tabs) {
mContext = context;
mInflater = LayoutInflater.from(mContext);
mTabs = new ArrayList<Tab>();
if (tabs == null)
return;
for (int i = 0; i < tabs.size(); i++) {
mTabs.add(tabs.get(i));
}
}
@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;
}
public int getPositionForTab(Tab tab) {
if (mTabs == null || tab == null)
return -1;
return mTabs.indexOf(tab);
}
public void assignValues(View view, Tab tab) {
if (view == null || tab == null)
return;
ImageView favicon = (ImageView) view.findViewById(R.id.favicon);
Drawable faviconImage = tab.getFavicon();
if (faviconImage != null)
favicon.setImageDrawable(faviconImage);
else
favicon.setImageResource(R.drawable.favicon);
TextView title = (TextView) view.findViewById(R.id.title);
title.setText(tab.getDisplayTitle());
if (Tabs.getInstance().isSelectedTab(tab))
title.setTypeface(title.getTypeface(), Typeface.BOLD);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
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()));
finishActivity();
}
});
assignValues(convertView, tab);
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) {
if (mWaitingForClose)
return;
mWaitingForClose = true;
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", String.valueOf(id)));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", v.getTag().toString()));
} else {
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", v.getTag().toString()));
GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", String.valueOf(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;
}
}

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

@ -0,0 +1,73 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.CairoUtils;
import android.graphics.Bitmap;
import java.nio.ByteBuffer;
/** A Cairo image that simply saves a buffer of pixel data. */
public class BufferedCairoImage extends CairoImage {
private ByteBuffer mBuffer;
private int mWidth, mHeight, mFormat;
/** Creates a buffered Cairo image from a byte buffer. */
public BufferedCairoImage(ByteBuffer inBuffer, int inWidth, int inHeight, int inFormat) {
mBuffer = inBuffer; mWidth = inWidth; mHeight = inHeight; mFormat = inFormat;
}
/** Creates a buffered Cairo image from an Android bitmap. */
public BufferedCairoImage(Bitmap bitmap) {
mFormat = CairoUtils.bitmapConfigToCairoFormat(bitmap.getConfig());
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
}
@Override
public ByteBuffer getBuffer() { return mBuffer; }
@Override
public int getWidth() { return mWidth; }
@Override
public int getHeight() { return mHeight; }
@Override
public int getFormat() { return mFormat; }
}

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

@ -0,0 +1,67 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import javax.microedition.khronos.opengles.GL10;
/** Information needed to render Cairo bitmaps using OpenGL ES. */
public class CairoGLInfo {
public final int internalFormat;
public final int format;
public final int type;
public CairoGLInfo(int cairoFormat) {
switch (cairoFormat) {
case CairoImage.FORMAT_ARGB32:
internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE;
break;
case CairoImage.FORMAT_RGB24:
internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE;
break;
case CairoImage.FORMAT_RGB16_565:
internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5;
break;
case CairoImage.FORMAT_A8:
case CairoImage.FORMAT_A1:
throw new RuntimeException("Cairo FORMAT_A1 and FORMAT_A8 unsupported");
default:
throw new RuntimeException("Unknown Cairo format");
}
}
}

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

@ -0,0 +1,59 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import java.nio.ByteBuffer;
/*
* A bitmap with pixel data in one of the formats that Cairo understands.
*/
public abstract class CairoImage {
public abstract ByteBuffer getBuffer();
public abstract int getWidth();
public abstract int getHeight();
public abstract int getFormat();
public static final int FORMAT_INVALID = -1;
public static final int FORMAT_ARGB32 = 0;
public static final int FORMAT_RGB24 = 1;
public static final int FORMAT_A8 = 2;
public static final int FORMAT_A1 = 3;
public static final int FORMAT_RGB16_565 = 4;
}

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

@ -0,0 +1,85 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.CairoImage;
import android.graphics.Bitmap;
import javax.microedition.khronos.opengles.GL10;
/**
* Utility methods useful when displaying Cairo bitmaps using OpenGL ES.
*/
public class CairoUtils {
private CairoUtils() { /* Don't call me. */ }
public static int bitsPerPixelForCairoFormat(int cairoFormat) {
switch (cairoFormat) {
case CairoImage.FORMAT_A1: return 1;
case CairoImage.FORMAT_A8: return 8;
case CairoImage.FORMAT_RGB16_565: return 16;
case CairoImage.FORMAT_RGB24: return 24;
case CairoImage.FORMAT_ARGB32: return 32;
default:
throw new RuntimeException("Unknown Cairo format");
}
}
public static int bitmapConfigToCairoFormat(Bitmap.Config config) {
if (config == null)
return CairoImage.FORMAT_ARGB32; /* Droid Pro fix. */
switch (config) {
case ALPHA_8: return CairoImage.FORMAT_A8;
case ARGB_4444: throw new RuntimeException("ARGB_444 unsupported");
case ARGB_8888: return CairoImage.FORMAT_ARGB32;
case RGB_565: return CairoImage.FORMAT_RGB16_565;
default: throw new RuntimeException("Unknown Skia bitmap config");
}
}
public static Bitmap.Config cairoFormatTobitmapConfig(int format) {
switch (format) {
case CairoImage.FORMAT_A8: return Bitmap.Config.ALPHA_8;
case CairoImage.FORMAT_ARGB32: return Bitmap.Config.ARGB_8888;
case CairoImage.FORMAT_RGB16_565: return Bitmap.Config.RGB_565;
default:
throw new RuntimeException("Unknown CairoImage format");
}
}
}

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

@ -0,0 +1,73 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.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.gfx;
import org.mozilla.gecko.FloatUtils;
import org.json.JSONException;
import org.json.JSONObject;
public class FloatSize {
public final float width, height;
public FloatSize(FloatSize size) { width = size.width; height = size.height; }
public FloatSize(IntSize size) { width = size.width; height = size.height; }
public FloatSize(float aWidth, float aHeight) { width = aWidth; height = aHeight; }
public FloatSize(JSONObject json) {
try {
width = (float)json.getDouble("width");
height = (float)json.getDouble("height");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() { return "(" + width + "," + height + ")"; }
public boolean fuzzyEquals(FloatSize size) {
return (FloatUtils.fuzzyEquals(size.width, width) &&
FloatUtils.fuzzyEquals(size.height, height));
}
public FloatSize scale(float factor) {
return new FloatSize(width * factor, height * factor);
}
}

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

@ -0,0 +1,279 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.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.gfx;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerRenderer;
import org.mozilla.gecko.gfx.PointUtils;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.FloatUtils;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoEventListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.DisplayMetrics;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.nio.ByteBuffer;
/**
* Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our
* compositor.
*
* TODO: Throttle down Gecko's priority when we pan and zoom.
*/
public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventListener {
private static final String LOGTAG = "GeckoSoftwareLayerClient";
private Context mContext;
private int mWidth, mHeight, mFormat;
private IntSize mScreenSize, mViewportSize;
private ByteBuffer mBuffer;
private final SingleTileLayer mTileLayer;
/* The viewport rect that Gecko is currently displaying. */
private ViewportMetrics mGeckoViewport;
private CairoImage mCairoImage;
private static final long MIN_VIEWPORT_CHANGE_DELAY = 350L;
private long mLastViewportChangeTime;
private boolean mPendingViewportAdjust;
public GeckoSoftwareLayerClient(Context context) {
mContext = context;
mWidth = LayerController.TILE_WIDTH;
mHeight = LayerController.TILE_HEIGHT;
mFormat = CairoImage.FORMAT_RGB16_565;
mScreenSize = new IntSize(1, 1);
mBuffer = ByteBuffer.allocateDirect(mWidth * mHeight * 2);
mCairoImage = new CairoImage() {
@Override
public ByteBuffer getBuffer() { return mBuffer; }
@Override
public int getWidth() { return mWidth; }
@Override
public int getHeight() { return mHeight; }
@Override
public int getFormat() { return mFormat; }
};
mTileLayer = new SingleTileLayer(mCairoImage);
}
/** Attaches the root layer to the layer controller so that Gecko appears. */
@Override
public void setLayerController(LayerController layerController) {
super.setLayerController(layerController);
layerController.setRoot(mTileLayer);
if (mGeckoViewport != null)
layerController.setViewportMetrics(mGeckoViewport);
geometryChanged();
GeckoAppShell.registerGeckoEventListener("Viewport:Update", this);
}
public void beginDrawing() {
beginTransaction(mTileLayer);
}
private void updateViewport(String viewportDescription, final boolean onlyUpdatePageSize) {
try {
JSONObject viewportObject = new JSONObject(viewportDescription);
mGeckoViewport = new ViewportMetrics(viewportObject);
mTileLayer.setOrigin(PointUtils.round(mGeckoViewport.getDisplayportOrigin()));
mTileLayer.setResolution(mGeckoViewport.getZoomFactor());
// Make sure LayerController metrics changes only happen in the
// UI thread.
final LayerController controller = getLayerController();
if (controller != null) {
controller.post(new Runnable() {
@Override
public void run() {
if (onlyUpdatePageSize) {
// Don't adjust page size when zooming unless zoom levels are
// approximately equal.
if (FloatUtils.fuzzyEquals(controller.getZoomFactor(), mGeckoViewport.getZoomFactor()))
controller.setPageSize(mGeckoViewport.getPageSize());
} else {
controller.setViewportMetrics(mGeckoViewport);
}
}
});
}
} catch (JSONException e) {
Log.e(LOGTAG, "Bad viewport description: " + viewportDescription);
throw new RuntimeException(e);
}
}
/*
* TODO: Would be cleaner if this took an android.graphics.Rect instead, but that would require
* a little more JNI magic.
*/
public void endDrawing(int x, int y, int width, int height, String metadata) {
try {
updateViewport(metadata, true);
Rect rect = new Rect(x, y, x + width, y + height);
mTileLayer.invalidate(rect);
} finally {
endTransaction(mTileLayer);
}
}
public ViewportMetrics getGeckoViewportMetrics() {
// Return a copy, as we modify this inside the Gecko thread
if (mGeckoViewport != null)
return new ViewportMetrics(mGeckoViewport);
return null;
}
public Bitmap getBitmap() {
Bitmap b = Bitmap.createBitmap(mWidth, mHeight,
CairoUtils.cairoFormatTobitmapConfig(mFormat));
b.copyPixelsFromBuffer(mBuffer.asIntBuffer());
return b;
}
/** Returns the back buffer. This function is for Gecko to use. */
public ByteBuffer lockBuffer() {
return mBuffer;
}
/**
* Gecko calls this function to signal that it is done with the back buffer. After this call,
* it is forbidden for Gecko to touch the buffer.
*/
public void unlockBuffer() {
/* no-op */
}
@Override
public void geometryChanged() {
/* Let Gecko know if the screensize has changed */
DisplayMetrics metrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics);
if (metrics.widthPixels != mScreenSize.width ||
metrics.heightPixels != mScreenSize.height) {
mScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
Log.i(LOGTAG, "Screen-size changed to " + mScreenSize);
GeckoEvent event = new GeckoEvent(GeckoEvent.SIZE_CHANGED,
LayerController.TILE_WIDTH, LayerController.TILE_HEIGHT,
metrics.widthPixels, metrics.heightPixels);
GeckoAppShell.sendEventToGecko(event);
}
render();
}
@Override
public void render() {
adjustViewportWithThrottling();
}
private void adjustViewportWithThrottling() {
if (!getLayerController().getRedrawHint())
return;
if (mPendingViewportAdjust)
return;
long timeDelta = System.currentTimeMillis() - mLastViewportChangeTime;
if (timeDelta < MIN_VIEWPORT_CHANGE_DELAY) {
getLayerController().getView().postDelayed(
new Runnable() {
public void run() {
mPendingViewportAdjust = false;
adjustViewport();
}
}, MIN_VIEWPORT_CHANGE_DELAY - timeDelta);
mPendingViewportAdjust = true;
return;
}
adjustViewport();
}
private void adjustViewport() {
Log.i(LOGTAG, "Adjusting viewport");
ViewportMetrics viewportMetrics =
new ViewportMetrics(getLayerController().getViewportMetrics());
PointF viewportOffset = viewportMetrics.getOptimumViewportOffset();
viewportMetrics.setViewportOffset(viewportOffset);
viewportMetrics.setViewport(viewportMetrics.getClampedViewport());
GeckoEvent event = new GeckoEvent("Viewport:Change", viewportMetrics.toJSON());
GeckoAppShell.sendEventToGecko(event);
mLastViewportChangeTime = System.currentTimeMillis();
}
public void handleMessage(String event, JSONObject message) {
if ("Viewport:Update".equals(event)) {
beginTransaction(mTileLayer);
try {
updateViewport(message.getString("viewport"), false);
} catch (JSONException e) {
Log.e(LOGTAG, "Unable to update viewport", e);
} finally {
endTransaction(mTileLayer);
}
}
}
}

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

@ -0,0 +1,15 @@
package org.mozilla.gecko.gfx;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.KeyEvent;
public interface InputConnectionHandler
{
InputConnection onCreateInputConnection(EditorInfo outAttrs);
boolean onKeyPreIme(int keyCode, KeyEvent event);
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyLongPress(int keyCode, KeyEvent event);
boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
}

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

@ -0,0 +1,77 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.FloatSize;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.Math;
public class IntSize {
public final int width, height;
public IntSize(IntSize size) { width = size.width; height = size.height; }
public IntSize(int inWidth, int inHeight) { width = inWidth; height = inHeight; }
public IntSize(FloatSize size) {
width = Math.round(size.width);
height = Math.round(size.height);
}
public IntSize(JSONObject json) {
try {
width = json.getInt("width");
height = json.getInt("height");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public boolean equals(IntSize size) {
return ((size.width == width) && (size.height == height));
}
@Override
public String toString() { return "(" + width + "," + height + ")"; }
public IntSize scale(float factor) {
return new IntSize((int)Math.round(width * factor),
(int)Math.round(height * factor));
}
}

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

@ -0,0 +1,182 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.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.gfx;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.RectF;
import android.util.Log;
import java.util.concurrent.locks.ReentrantLock;
import javax.microedition.khronos.opengles.GL10;
public abstract class Layer {
private final ReentrantLock mTransactionLock;
private boolean mInTransaction;
private Point mOrigin;
private Point mNewOrigin;
private float mResolution;
private float mNewResolution;
private LayerView mView;
public Layer() {
mTransactionLock = new ReentrantLock();
mOrigin = new Point(0, 0);
mResolution = 1.0f;
}
/** Updates the layer. */
public final void update(GL10 gl) {
if (mTransactionLock.isHeldByCurrentThread()) {
throw new RuntimeException("draw() called while transaction lock held by this " +
"thread?!");
}
if (mTransactionLock.tryLock()) {
try {
performUpdates(gl);
} finally {
mTransactionLock.unlock();
}
}
}
/** Subclasses override this function to draw the layer. */
public abstract void draw(RenderContext context);
/** Given the intrinsic size of the layer, returns the pixel boundaries of the layer rect. */
protected RectF getBounds(RenderContext context, FloatSize size) {
float scaleFactor = context.zoomFactor / mResolution;
float x = mOrigin.x * scaleFactor, y = mOrigin.y * scaleFactor;
float width = size.width * scaleFactor, height = size.height * scaleFactor;
return new RectF(x, y, x + width, y + height);
}
/**
* Call this before modifying the layer. Note that, for TileLayers, "modifying the layer"
* includes altering the underlying CairoImage in any way. Thus you must call this function
* before modifying the byte buffer associated with this layer.
*
* This function may block, so you should never call this on the main UI thread.
*/
public void beginTransaction(LayerView aView) {
if (mTransactionLock.isHeldByCurrentThread())
throw new RuntimeException("Nested transactions are not supported");
mTransactionLock.lock();
mView = aView;
mInTransaction = true;
mNewResolution = mResolution;
}
public void beginTransaction() {
beginTransaction(null);
}
/** Call this when you're done modifying the layer. */
public void endTransaction() {
if (!mInTransaction)
throw new RuntimeException("endTransaction() called outside a transaction");
mInTransaction = false;
mTransactionLock.unlock();
if (mView != null)
mView.requestRender();
}
/** Returns true if the layer is currently in a transaction and false otherwise. */
protected boolean inTransaction() {
return mInTransaction;
}
/** Returns the current layer origin. */
public Point getOrigin() {
return mOrigin;
}
/** Sets the origin. Only valid inside a transaction. */
public void setOrigin(Point newOrigin) {
if (!mInTransaction)
throw new RuntimeException("setOrigin() is only valid inside a transaction");
mNewOrigin = newOrigin;
}
/** Returns the current layer's resolution. */
public float getResolution() {
return mResolution;
}
/**
* Sets the layer resolution. This value is used to determine how many pixels per
* device pixel this layer was rendered at. This will be reflected by scaling by
* the reciprocal of the resolution in the layer's transform() function.
* Only valid inside a transaction. */
public void setResolution(float newResolution) {
if (!mInTransaction)
throw new RuntimeException("setResolution() is only valid inside a transaction");
mNewResolution = newResolution;
}
/**
* Subclasses may override this method to perform custom layer updates. This will be called
* with the transaction lock held. Subclass implementations of this method must call the
* superclass implementation.
*/
protected void performUpdates(GL10 gl) {
if (mNewOrigin != null) {
mOrigin = mNewOrigin;
mNewOrigin = null;
}
if (mNewResolution != 0.0f) {
mResolution = mNewResolution;
mNewResolution = 0.0f;
}
}
public static class RenderContext {
public final RectF viewport;
public final FloatSize pageSize;
public final float zoomFactor;
public RenderContext(RectF aViewport, FloatSize aPageSize, float aZoomFactor) {
viewport = aViewport;
pageSize = aPageSize;
zoomFactor = aZoomFactor;
}
}
}

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

@ -0,0 +1,75 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
/**
* A layer client provides tiles and manages other information used by the layer controller.
*/
public abstract class LayerClient {
private LayerController mLayerController;
public abstract void geometryChanged();
protected abstract void render();
public LayerController getLayerController() { return mLayerController; }
public void setLayerController(LayerController layerController) {
mLayerController = layerController;
}
/**
* A utility function for calling TileLayer.beginTransaction with the
* appropriate LayerView.
*/
public void beginTransaction(TileLayer aTileLayer) {
if (mLayerController != null) {
LayerView view = mLayerController.getView();
if (view != null) {
aTileLayer.beginTransaction(view);
return;
}
}
aTileLayer.beginTransaction();
}
// Included for symmetry.
public void endTransaction(TileLayer aTileLayer) {
aTileLayer.endTransaction();
}
}

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

@ -0,0 +1,311 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.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.gfx;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.Layer;
import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.ui.PanZoomController;
import org.mozilla.gecko.GeckoApp;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.MotionEvent;
import android.view.GestureDetector;
import android.view.ScaleGestureDetector;
import android.view.View.OnTouchListener;
import java.lang.Math;
import java.util.ArrayList;
/**
* The layer controller manages a tile that represents the visible page. It does panning and
* zooming natively by delegating to a panning/zooming controller. Touch events can be dispatched
* to a higher-level view.
*/
public class LayerController {
private Layer mRootLayer; /* The root layer. */
private LayerView mView; /* The main rendering view. */
private Context mContext; /* The current context. */
private ViewportMetrics mViewportMetrics; /* The current viewport metrics. */
private PanZoomController mPanZoomController;
/*
* The panning and zooming controller, which interprets pan and zoom gestures for us and
* updates our visible rect appropriately.
*/
private OnTouchListener mOnTouchListener; /* The touch listener. */
private LayerClient mLayerClient; /* The layer client. */
private boolean mForceRedraw;
/* NB: These must be powers of two due to the OpenGL ES 1.x restriction on NPOT textures. */
public static final int TILE_WIDTH = 1024;
public static final int TILE_HEIGHT = 2048;
/* If the visible rect is within the danger zone (measured in pixels from each edge of a tile),
* we start aggressively redrawing to minimize checkerboarding. */
private static final int DANGER_ZONE_X = 75;
private static final int DANGER_ZONE_Y = 150;
public LayerController(Context context) {
mContext = context;
mForceRedraw = true;
mViewportMetrics = new ViewportMetrics();
mPanZoomController = new PanZoomController(this);
mView = new LayerView(context, this);
}
public void setRoot(Layer layer) { mRootLayer = layer; }
public void setLayerClient(LayerClient layerClient) {
mLayerClient = layerClient;
layerClient.setLayerController(this);
}
public void setForceRedraw() {
mForceRedraw = true;
}
public LayerClient getLayerClient() { return mLayerClient; }
public Layer getRoot() { return mRootLayer; }
public LayerView getView() { return mView; }
public Context getContext() { return mContext; }
public ViewportMetrics getViewportMetrics() { return mViewportMetrics; }
public RectF getViewport() {
return mViewportMetrics.getViewport();
}
public FloatSize getViewportSize() {
return mViewportMetrics.getSize();
}
public FloatSize getPageSize() {
return mViewportMetrics.getPageSize();
}
public PointF getOrigin() {
return mViewportMetrics.getOrigin();
}
public float getZoomFactor() {
return mViewportMetrics.getZoomFactor();
}
public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); }
public Bitmap getShadowPattern() { return getDrawable("shadow"); }
public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; }
public ScaleGestureDetector.OnScaleGestureListener getScaleGestureListener() { return mPanZoomController; }
private Bitmap getDrawable(String name) {
Resources resources = mContext.getResources();
int resourceID = resources.getIdentifier(name, "drawable", mContext.getPackageName());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options);
}
/**
* The view calls this to indicate that the viewport changed size.
*
* TODO: Refactor this to use an interface. Expose that interface only to the view and not
* to the layer client. That way, the layer client won't be tempted to call this, which might
* result in an infinite loop.
*/
public void setViewportSize(FloatSize size) {
mViewportMetrics.setSize(size);
setForceRedraw();
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged(false);
mView.requestRender();
}
public void scrollTo(PointF point) {
mViewportMetrics.setOrigin(point);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged(false);
GeckoApp.mAppContext.repositionPluginViews(false);
mView.requestRender();
}
public void scrollBy(PointF point) {
PointF origin = mViewportMetrics.getOrigin();
origin.offset(point.x, point.y);
mViewportMetrics.setOrigin(origin);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged(false);
GeckoApp.mAppContext.repositionPluginViews(false);
mView.requestRender();
}
public void setViewport(RectF viewport) {
mViewportMetrics.setViewport(viewport);
notifyLayerClientOfGeometryChange();
mPanZoomController.geometryChanged(false);
GeckoApp.mAppContext.repositionPluginViews(false);
mView.requestRender();
}
public void setPageSize(FloatSize size) {
if (mViewportMetrics.getPageSize().fuzzyEquals(size))
return;
mViewportMetrics.setPageSize(size);
// Page size is owned by the LayerClient, so no need to notify it of
// this change.
mPanZoomController.geometryChanged(false);
mView.requestRender();
}
public void setViewportMetrics(ViewportMetrics viewport) {
mViewportMetrics = new ViewportMetrics(viewport);
// We assume this was called by the LayerClient (as it includes page
// size), so no need to notify it of this change.
mPanZoomController.geometryChanged(true);
GeckoApp.mAppContext.repositionPluginViews(false);
mView.requestRender();
}
public void scaleTo(float zoomFactor, PointF focus) {
mViewportMetrics.scaleTo(zoomFactor, focus);
// We assume the zoom level will only be modified by the
// PanZoomController, so no need to notify it of this change.
notifyLayerClientOfGeometryChange();
GeckoApp.mAppContext.repositionPluginViews(false);
mView.requestRender();
}
public boolean post(Runnable action) { return mView.post(action); }
public void setOnTouchListener(OnTouchListener onTouchListener) {
mOnTouchListener = onTouchListener;
}
/**
* The view as well as the controller itself use this method to notify the layer client that
* the geometry changed.
*/
public void notifyLayerClientOfGeometryChange() {
if (mLayerClient != null)
mLayerClient.geometryChanged();
}
/**
* Returns true if this controller is fine with performing a redraw operation or false if it
* would prefer that the action didn't take place.
*/
public boolean getRedrawHint() {
if (mForceRedraw) {
mForceRedraw = false;
return true;
}
return aboutToCheckerboard() && mPanZoomController.getRedrawHint();
}
private RectF getTileRect() {
float x = mRootLayer.getOrigin().x, y = mRootLayer.getOrigin().y;
return new RectF(x, y, x + TILE_WIDTH, y + TILE_HEIGHT);
}
// Returns true if a checkerboard is about to be visible.
private boolean aboutToCheckerboard() {
// Increase the size of the viewport (and clamp to page boundaries), and
// intersect it with the tile's displayport to determine whether we're
// close to checkerboarding.
FloatSize pageSize = getPageSize();
RectF adjustedViewport = RectUtils.expand(getViewport(), DANGER_ZONE_X, DANGER_ZONE_Y);
if (adjustedViewport.top < 0) adjustedViewport.top = 0;
if (adjustedViewport.left < 0) adjustedViewport.left = 0;
if (adjustedViewport.right > pageSize.width) adjustedViewport.right = pageSize.width;
if (adjustedViewport.bottom > pageSize.height) adjustedViewport.bottom = pageSize.height;
return !getTileRect().contains(adjustedViewport);
}
/**
* Converts a point from layer view coordinates to layer coordinates. In other words, given a
* point measured in pixels from the top left corner of the layer view, returns the point in
* pixels measured from the top left corner of the root layer, in the coordinate system of the
* layer itself. This method is used by the viewport controller as part of the process of
* translating touch events to Gecko's coordinate system.
*/
public PointF convertViewPointToLayerPoint(PointF viewPoint) {
if (mRootLayer == null)
return null;
// Undo the transforms.
PointF origin = mViewportMetrics.getOrigin();
PointF newPoint = new PointF(origin.x, origin.y);
newPoint.offset(viewPoint.x, viewPoint.y);
Point rootOrigin = mRootLayer.getOrigin();
newPoint.offset(-rootOrigin.x, -rootOrigin.y);
return newPoint;
}
/*
* Gesture detection. This is handled only at a high level in this class; we dispatch to the
* pan/zoom controller to do the dirty work.
*/
public boolean onTouchEvent(MotionEvent event) {
if (mPanZoomController.onTouchEvent(event))
return true;
if (mOnTouchListener != null)
return mOnTouchListener.onTouch(mView, event);
return false;
}
}

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

@ -0,0 +1,258 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@mozilla.com>
* Chris Lord <chrislord.net@gmail.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.gfx;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.Layer.RenderContext;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.NinePatchTileLayer;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.gfx.TextureReaper;
import org.mozilla.gecko.gfx.TextLayer;
import org.mozilla.gecko.gfx.TileLayer;
import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.opengl.GLSurfaceView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import java.nio.ByteBuffer;
/**
* The layer renderer implements the rendering logic for a layer view.
*/
public class LayerRenderer implements GLSurfaceView.Renderer {
private static final float BACKGROUND_COLOR_R = 0.81f;
private static final float BACKGROUND_COLOR_G = 0.81f;
private static final float BACKGROUND_COLOR_B = 0.81f;
private final LayerView mView;
private final SingleTileLayer mCheckerboardLayer;
private final NinePatchTileLayer mShadowLayer;
private final TextLayer mFPSLayer;
private final ScrollbarLayer mHorizScrollLayer;
private final ScrollbarLayer mVertScrollLayer;
// FPS display
private long mFrameCountTimestamp;
private long mFrameTime;
private int mFrameCount; // number of frames since last timestamp
public LayerRenderer(LayerView view) {
mView = view;
LayerController controller = view.getController();
CairoImage checkerboardImage = new BufferedCairoImage(controller.getCheckerboardPattern());
mCheckerboardLayer = new SingleTileLayer(true, checkerboardImage);
CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern());
mShadowLayer = new NinePatchTileLayer(shadowImage);
mFPSLayer = TextLayer.create(new IntSize(64, 32), "-- FPS");
mHorizScrollLayer = ScrollbarLayer.create(false);
mVertScrollLayer = ScrollbarLayer.create(true);
mFrameCountTimestamp = System.currentTimeMillis();
mFrameCount = 0;
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
gl.glClearDepthf(1.0f); /* FIXME: Is this needed? */
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glShadeModel(GL10.GL_SMOOTH); /* FIXME: Is this needed? */
gl.glDisable(GL10.GL_DITHER);
gl.glEnable(GL10.GL_TEXTURE_2D);
}
/**
* Called whenever a new frame is about to be drawn.
*
* FIXME: This is racy. Layers and page sizes can be modified by the pan/zoom controller while
* this is going on.
*/
public void onDrawFrame(GL10 gl) {
checkFPS();
TextureReaper.get().reap(gl);
LayerController controller = mView.getController();
Layer rootLayer = controller.getRoot();
RenderContext screenContext = createScreenContext(), pageContext = createPageContext();
/* Update layers. */
if (rootLayer != null) rootLayer.update(gl);
mShadowLayer.update(gl);
mCheckerboardLayer.update(gl);
mFPSLayer.update(gl);
mVertScrollLayer.update(gl);
mHorizScrollLayer.update(gl);
/* Draw the background. */
gl.glClearColor(BACKGROUND_COLOR_R, BACKGROUND_COLOR_G, BACKGROUND_COLOR_B, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
/* Draw the drop shadow, if we need to. */
Rect pageRect = getPageRect();
RectF untransformedPageRect = new RectF(0.0f, 0.0f, pageRect.width(), pageRect.height());
if (!untransformedPageRect.contains(controller.getViewport()))
mShadowLayer.draw(pageContext);
/* Draw the checkerboard. */
Rect scissorRect = transformToScissorRect(pageRect);
gl.glEnable(GL10.GL_SCISSOR_TEST);
gl.glScissor(scissorRect.left, scissorRect.top,
scissorRect.width(), scissorRect.height());
mCheckerboardLayer.draw(screenContext);
/* Draw the layer the client added to us. */
if (rootLayer != null)
rootLayer.draw(pageContext);
gl.glDisable(GL10.GL_SCISSOR_TEST);
/* Draw the vertical scrollbar. */
IntSize screenSize = new IntSize(controller.getViewportSize());
if (pageRect.height() > screenSize.height)
mVertScrollLayer.draw(pageContext);
/* Draw the horizontal scrollbar. */
if (pageRect.width() > screenSize.width)
mHorizScrollLayer.draw(pageContext);
/* Draw the FPS. */
try {
gl.glEnable(GL10.GL_BLEND);
mFPSLayer.draw(screenContext);
} finally {
gl.glDisable(GL10.GL_BLEND);
}
}
private RenderContext createScreenContext() {
LayerController layerController = mView.getController();
IntSize viewportSize = new IntSize(layerController.getViewportSize());
RectF viewport = new RectF(0.0f, 0.0f, viewportSize.width, viewportSize.height);
FloatSize pageSize = new FloatSize(layerController.getPageSize());
return new RenderContext(viewport, pageSize, 1.0f);
}
private RenderContext createPageContext() {
LayerController layerController = mView.getController();
Rect viewport = new Rect();
layerController.getViewport().round(viewport);
FloatSize pageSize = new FloatSize(layerController.getPageSize());
float zoomFactor = layerController.getZoomFactor();
return new RenderContext(new RectF(viewport), pageSize, zoomFactor);
}
private Rect getPageRect() {
LayerController controller = mView.getController();
Point origin = PointUtils.round(controller.getOrigin());
IntSize pageSize = new IntSize(controller.getPageSize());
origin.negate();
return new Rect(origin.x, origin.y,
origin.x + pageSize.width, origin.y + pageSize.height);
}
private Rect transformToScissorRect(Rect rect) {
LayerController controller = mView.getController();
IntSize screenSize = new IntSize(controller.getViewportSize());
int left = Math.max(0, rect.left);
int top = Math.max(0, rect.top);
int right = Math.min(screenSize.width, rect.right);
int bottom = Math.min(screenSize.height, rect.bottom);
return new Rect(left, screenSize.height - bottom, right,
(screenSize.height - bottom) + (bottom - top));
}
public void onSurfaceChanged(GL10 gl, final int width, final int height) {
gl.glViewport(0, 0, width, height);
// updating the state in the view/controller/client should be
// done on the main UI thread, not the GL renderer thread
mView.post(new Runnable() {
public void run() {
mView.setViewportSize(new IntSize(width, height));
}
});
/* TODO: Throw away tile images? */
}
private void checkFPS() {
mFrameTime += mView.getRenderTime();
mFrameCount ++;
if (System.currentTimeMillis() >= mFrameCountTimestamp + 1000) {
mFrameCountTimestamp = System.currentTimeMillis();
// Extrapolate FPS based on time taken by frames drawn.
// XXX This doesn't take into account the vblank, so the FPS
// can show higher than it actually is.
mFrameCount = (int)(mFrameCount * 1000000000L / mFrameTime);
mFPSLayer.beginTransaction();
try {
mFPSLayer.setText(mFrameCount + " FPS");
} finally {
mFPSLayer.endTransaction();
}
mFrameCount = 0;
mFrameTime = 0;
}
}
}

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

@ -0,0 +1,170 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.InputConnectionHandler;
import org.mozilla.gecko.gfx.LayerController;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.ScaleGestureDetector;
/**
* A view rendered by the layer compositor.
*
* This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a
* mediator between the LayerRenderer and the LayerController.
*/
public class LayerView extends GLSurfaceView {
private Context mContext;
private LayerController mController;
private InputConnectionHandler mInputConnectionHandler;
private LayerRenderer mRenderer;
private GestureDetector mGestureDetector;
private ScaleGestureDetector mScaleGestureDetector;
private long mRenderTime;
private boolean mRenderTimeReset;
public LayerView(Context context, LayerController controller) {
super(context);
mContext = context;
mController = controller;
mRenderer = new LayerRenderer(this);
setRenderer(mRenderer);
setRenderMode(RENDERMODE_WHEN_DIRTY);
mGestureDetector = new GestureDetector(context, controller.getGestureListener());
mScaleGestureDetector = new ScaleGestureDetector(context, controller.getScaleGestureListener());
mInputConnectionHandler = null;
setFocusable(true);
setFocusableInTouchMode(true);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mGestureDetector.onTouchEvent(event))
return true;
mScaleGestureDetector.onTouchEvent(event);
if (mScaleGestureDetector.isInProgress())
return true;
return mController.onTouchEvent(event);
}
public LayerController getController() { return mController; }
/** The LayerRenderer calls this to indicate that the window has changed size. */
public void setViewportSize(IntSize size) {
mController.setViewportSize(new FloatSize(size));
}
public void setInputConnectionHandler(InputConnectionHandler handler) {
mInputConnectionHandler = handler;
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
if (mInputConnectionHandler != null)
return mInputConnectionHandler.onCreateInputConnection(outAttrs);
return null;
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (mInputConnectionHandler != null)
return mInputConnectionHandler.onKeyPreIme(keyCode, event);
return false;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (mInputConnectionHandler != null)
return mInputConnectionHandler.onKeyDown(keyCode, event);
return false;
}
@Override
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
if (mInputConnectionHandler != null)
return mInputConnectionHandler.onKeyLongPress(keyCode, event);
return false;
}
@Override
public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
if (mInputConnectionHandler != null)
return mInputConnectionHandler.onKeyMultiple(keyCode, repeatCount, event);
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (mInputConnectionHandler != null)
return mInputConnectionHandler.onKeyUp(keyCode, event);
return false;
}
@Override
public void requestRender() {
super.requestRender();
synchronized(this) {
if (!mRenderTimeReset) {
mRenderTimeReset = true;
mRenderTime = System.nanoTime();
}
}
}
/**
* Returns the time elapsed between the first call of requestRender() after
* the last call of getRenderTime(), in nanoseconds.
*/
public long getRenderTime() {
synchronized(this) {
mRenderTimeReset = false;
return System.nanoTime() - mRenderTime;
}
}
}

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

@ -0,0 +1,124 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.FloatSize;
import android.graphics.PointF;
import android.graphics.RectF;
import android.opengl.GLES11;
import android.opengl.GLES11Ext;
import android.util.Log;
import javax.microedition.khronos.opengles.GL10;
import java.nio.FloatBuffer;
/**
* Encapsulates the logic needed to draw a nine-patch bitmap using OpenGL ES.
*
* For more information on nine-patch bitmaps, see the following document:
* http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
*/
public class NinePatchTileLayer extends TileLayer {
private static final int PATCH_SIZE = 16;
private static final int TEXTURE_SIZE = 48;
public NinePatchTileLayer(CairoImage image) {
super(false, image);
}
@Override
public void draw(RenderContext context) {
if (!initialized())
return;
GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
GLES11.glEnable(GL10.GL_BLEND);
try {
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
drawPatches(context);
} finally {
GLES11.glDisable(GL10.GL_BLEND);
}
}
private void drawPatches(RenderContext context) {
/*
* We divide the nine-patch bitmap up as follows:
*
* +---+---+---+
* | 0 | 1 | 2 |
* +---+---+---+
* | 3 | | 4 |
* +---+---+---+
* | 5 | 6 | 7 |
* +---+---+---+
*/
FloatSize size = context.pageSize;
float width = size.width, height = size.height;
drawPatch(context, 0, 0, /* 0 */
0.0f, 0.0f, PATCH_SIZE, PATCH_SIZE);
drawPatch(context, PATCH_SIZE, 0, /* 1 */
PATCH_SIZE, 0.0f, width, PATCH_SIZE);
drawPatch(context, PATCH_SIZE * 2, 0, /* 2 */
PATCH_SIZE + width, 0.0f, PATCH_SIZE, PATCH_SIZE);
drawPatch(context, 0, PATCH_SIZE, /* 3 */
0.0f, PATCH_SIZE, PATCH_SIZE, height);
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE, /* 4 */
PATCH_SIZE + width, PATCH_SIZE, PATCH_SIZE, height);
drawPatch(context, 0, PATCH_SIZE * 2, /* 5 */
0.0f, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
drawPatch(context, PATCH_SIZE, PATCH_SIZE * 2, /* 6 */
PATCH_SIZE, PATCH_SIZE + height, width, PATCH_SIZE);
drawPatch(context, PATCH_SIZE * 2, PATCH_SIZE * 2, /* 7 */
PATCH_SIZE + width, PATCH_SIZE + height, PATCH_SIZE, PATCH_SIZE);
}
private void drawPatch(RenderContext context, int textureX, int textureY, float tileX,
float tileY, float tileWidth, float tileHeight) {
int[] cropRect = { textureX, textureY + PATCH_SIZE, PATCH_SIZE, -PATCH_SIZE };
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect,
0);
RectF viewport = context.viewport;
float viewportHeight = viewport.height();
float drawX = tileX - viewport.left - PATCH_SIZE;
float drawY = viewportHeight - (tileY + tileHeight - viewport.top - PATCH_SIZE);
GLES11Ext.glDrawTexfOES(drawX, drawY, 0.0f, tileWidth, tileHeight);
}
}

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

@ -0,0 +1,164 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoUtils;
import org.mozilla.gecko.gfx.FloatSize;
import org.mozilla.gecko.gfx.LayerClient;
import org.mozilla.gecko.gfx.PointUtils;
import org.mozilla.gecko.gfx.SingleTileLayer;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Environment;
import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
/**
* A stand-in for Gecko that renders cached content of the previous page. We use this until Gecko
* is up, then we hand off control to it.
*/
public class PlaceholderLayerClient extends LayerClient {
private static final String LOGTAG = "PlaceholderLayerClient";
private Context mContext;
private ViewportMetrics mViewport;
private boolean mViewportUnknown;
private int mWidth, mHeight, mFormat;
private ByteBuffer mBuffer;
private PlaceholderLayerClient(Context context) {
mContext = context;
String viewport = GeckoApp.mAppContext.getLastViewport();
mViewportUnknown = true;
if (viewport != null) {
try {
JSONObject viewportObject = new JSONObject(viewport);
mViewport = new ViewportMetrics(viewportObject);
mViewportUnknown = false;
} catch (JSONException e) {
Log.e(LOGTAG, "Error parsing saved viewport!");
mViewport = new ViewportMetrics();
}
} else {
mViewport = new ViewportMetrics();
}
loadScreenshot();
}
public static PlaceholderLayerClient createInstance(Context context) {
return new PlaceholderLayerClient(context);
}
public void destroy() {
if (mBuffer != null) {
GeckoAppShell.freeDirectBuffer(mBuffer);
mBuffer = null;
}
}
boolean loadScreenshot() {
if (GeckoApp.mAppContext.mLastScreen == null)
return false;
Bitmap bitmap = BitmapFactory.decodeStream(new ByteArrayInputStream(GeckoApp.mAppContext.mLastScreen));
if (bitmap == null)
return false;
Bitmap.Config config = bitmap.getConfig();
mWidth = bitmap.getWidth();
mHeight = bitmap.getHeight();
mFormat = CairoUtils.bitmapConfigToCairoFormat(config);
int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
mBuffer = GeckoAppShell.allocateDirectBuffer(mWidth * mHeight * bpp);
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
if (mViewportUnknown) {
mViewport.setPageSize(new FloatSize(mWidth, mHeight));
if (getLayerController() != null)
getLayerController().setPageSize(mViewport.getPageSize());
}
return true;
}
void showScreenshot() {
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
SingleTileLayer tileLayer = new SingleTileLayer(image);
beginTransaction(tileLayer);
tileLayer.setOrigin(PointUtils.round(mViewport.getDisplayportOrigin()));
endTransaction(tileLayer);
getLayerController().setRoot(tileLayer);
}
@Override
public void geometryChanged() { /* no-op */ }
@Override
public void render() { /* no-op */ }
@Override
public void setLayerController(LayerController layerController) {
super.setLayerController(layerController);
if (mViewportUnknown)
mViewport.setViewport(layerController.getViewport());
layerController.setViewportMetrics(mViewport);
BufferedCairoImage image = new BufferedCairoImage(mBuffer, mWidth, mHeight, mFormat);
SingleTileLayer tileLayer = new SingleTileLayer(image);
beginTransaction(tileLayer);
tileLayer.setOrigin(PointUtils.round(mViewport.getDisplayportOrigin()));
endTransaction(tileLayer);
layerController.setRoot(tileLayer);
}
}

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

@ -0,0 +1,61 @@
/* -*- 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):
* Kartikaya Gupta <kgupta@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.gfx;
import android.graphics.Point;
import android.graphics.PointF;
import java.lang.Math;
public final class PointUtils {
public static PointF add(PointF one, PointF two) {
return new PointF(one.x + two.x, one.y + two.y);
}
public static PointF subtract(PointF one, PointF two) {
return new PointF(one.x - two.x, one.y - two.y);
}
public static PointF scale(PointF point, float factor) {
return new PointF(point.x * factor, point.y * factor);
}
public static Point round(PointF point) {
return new Point(Math.round(point.x), Math.round(point.y));
}
}

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

@ -0,0 +1,109 @@
/* -*- 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):
* Kartikaya Gupta <kgupta@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.gfx;
import android.graphics.Rect;
import android.graphics.RectF;
import org.json.JSONException;
import org.json.JSONObject;
public final class RectUtils {
public static Rect create(JSONObject json) {
try {
int x = json.getInt("x");
int y = json.getInt("y");
int width = json.getInt("width");
int height = json.getInt("height");
return new Rect(x, y, x + width, y + height);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public static Rect contract(Rect rect, int lessWidth, int lessHeight) {
float halfLessWidth = (float)lessWidth / 2.0f;
float halfLessHeight = (float)lessHeight / 2.0f;
return new Rect((int)Math.round((float)rect.left + halfLessWidth),
(int)Math.round((float)rect.top + halfLessHeight),
(int)Math.round((float)rect.right - halfLessWidth),
(int)Math.round((float)rect.bottom - halfLessHeight));
}
public static RectF contract(RectF rect, float lessWidth, float lessHeight) {
float halfLessWidth = lessWidth / 2;
float halfLessHeight = lessHeight / 2;
return new RectF(rect.left + halfLessWidth,
rect.top + halfLessHeight,
rect.right - halfLessWidth,
rect.bottom - halfLessHeight);
}
public static RectF expand(RectF rect, float moreWidth, float moreHeight) {
float halfMoreWidth = moreWidth / 2;
float halfMoreHeight = moreHeight / 2;
return new RectF(rect.left - halfMoreWidth,
rect.top - halfMoreHeight,
rect.right + halfMoreWidth,
rect.bottom + halfMoreHeight);
}
public static RectF intersect(RectF one, RectF two) {
float left = Math.max(one.left, two.left);
float top = Math.max(one.top, two.top);
float right = Math.min(one.right, two.right);
float bottom = Math.min(one.bottom, two.bottom);
return new RectF(left, top, Math.max(right, left), Math.max(bottom, top));
}
public static RectF scale(RectF rect, float scale) {
float x = rect.left * scale;
float y = rect.top * scale;
return new RectF(x, y,
x + (rect.width() * scale),
y + (rect.height() * scale));
}
public static Rect round(RectF rect) {
return new Rect(Math.round(rect.left), Math.round(rect.top),
Math.round(rect.right), Math.round(rect.bottom));
}
public static IntSize getSize(Rect rect) {
return new IntSize(rect.width(), rect.height());
}
}

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

@ -0,0 +1,101 @@
/* -*- 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):
* Kartikaya Gupta <kgupta@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.gfx;
import android.graphics.PointF;
import android.graphics.RectF;
import android.opengl.GLES11;
import android.opengl.GLES11Ext;
import android.util.Log;
import java.nio.ByteBuffer;
import javax.microedition.khronos.opengles.GL10;
/**
* Draws a small rect. This is scaled to become a scrollbar.
*/
public class ScrollbarLayer extends TileLayer {
private static final int BAR_SIZE = 8; // has to be power of 2
private final boolean mVertical;
private ScrollbarLayer(CairoImage image, boolean vertical) {
super(false, image);
mVertical = vertical;
}
public static ScrollbarLayer create(boolean vertical) {
ByteBuffer buffer = ByteBuffer.allocateDirect(4);
buffer.put(3, (byte)127); // Set A to 0.5; leave R/G/B at 0.
CairoImage image = new BufferedCairoImage(buffer, 1, 1, CairoImage.FORMAT_ARGB32);
return new ScrollbarLayer(image, vertical);
}
@Override
public void draw(RenderContext context) {
if (!initialized())
return;
try {
GLES11.glEnable(GL10.GL_BLEND);
RectF rect = mVertical ? getVerticalRect(context) : getHorizontalRect(context);
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
float y = context.viewport.height() - rect.bottom;
GLES11Ext.glDrawTexfOES(rect.left, y, 0.0f, rect.width(), rect.height());
} finally {
GLES11.glDisable(GL10.GL_BLEND);
}
}
private RectF getVerticalRect(RenderContext context) {
RectF viewport = context.viewport;
FloatSize pageSize = context.pageSize;
float barStart = viewport.height() * viewport.top / pageSize.height;
float barEnd = viewport.height() * viewport.bottom / pageSize.height;
return new RectF(viewport.width() - BAR_SIZE, barStart, viewport.width(), barEnd);
}
private RectF getHorizontalRect(RenderContext context) {
RectF viewport = context.viewport;
FloatSize pageSize = context.pageSize;
float barStart = viewport.width() * viewport.left / pageSize.width;
float barEnd = viewport.width() * viewport.right / pageSize.width;
return new RectF(barStart, viewport.height() - BAR_SIZE, barEnd, viewport.height());
}
}

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

@ -0,0 +1,101 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.CairoUtils;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.LayerController;
import org.mozilla.gecko.gfx.TileLayer;
import android.graphics.PointF;
import android.graphics.RectF;
import android.opengl.GLES11;
import android.opengl.GLES11Ext;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
/**
* Encapsulates the logic needed to draw a single textured tile.
*
* TODO: Repeating textures really should be their own type of layer.
*/
public class SingleTileLayer extends TileLayer {
public SingleTileLayer(CairoImage image) { this(false, image); }
public SingleTileLayer(boolean repeat, CairoImage image) {
super(repeat, image);
}
@Override
public void draw(RenderContext context) {
// mTextureIDs may be null here during startup if Layer.java's draw method
// failed to acquire the transaction lock and call performUpdates.
if (!initialized())
return;
GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID());
RectF bounds;
int[] cropRect;
IntSize size = getSize();
RectF viewport = context.viewport;
if (repeats()) {
bounds = new RectF(0.0f, 0.0f, viewport.width(), viewport.height());
int width = (int)Math.round(viewport.width());
int height = (int)Math.round(-viewport.height());
cropRect = new int[] { 0, size.height, width, height };
} else {
bounds = getBounds(context, new FloatSize(size));
cropRect = new int[] { 0, size.height, size.width, -size.height };
}
GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect,
0);
float height = bounds.height();
float left = bounds.left - viewport.left;
float top = viewport.height() - (bounds.top + height - viewport.top);
GLES11Ext.glDrawTexfOES(left, top, 0.0f, bounds.width(), height);
}
}

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

@ -0,0 +1,104 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.IntSize;
import org.mozilla.gecko.gfx.SingleTileLayer;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
/**
* Draws text on a layer. This is used for the frame rate meter.
*/
public class TextLayer extends SingleTileLayer {
private final ByteBuffer mBuffer;
private final IntSize mSize;
/*
* This awkward pattern is necessary due to Java's restrictions on when one can call superclass
* constructors.
*/
private TextLayer(ByteBuffer buffer, BufferedCairoImage image, IntSize size, String text) {
super(false, image);
mBuffer = buffer;
mSize = size;
renderText(text);
}
public static TextLayer create(IntSize size, String text) {
ByteBuffer buffer = ByteBuffer.allocateDirect(size.width * size.height * 4);
BufferedCairoImage image = new BufferedCairoImage(buffer, size.width, size.height,
CairoImage.FORMAT_ARGB32);
return new TextLayer(buffer, image, size, text);
}
public void setText(String text) {
renderText(text);
invalidate();
}
private void renderText(String text) {
Bitmap bitmap = Bitmap.createBitmap(mSize.width, mSize.height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint textPaint = new Paint();
textPaint.setAntiAlias(true);
textPaint.setColor(Color.WHITE);
textPaint.setFakeBoldText(true);
textPaint.setTextSize(18.0f);
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
float width = textPaint.measureText(text) + 18.0f;
Paint backgroundPaint = new Paint();
backgroundPaint.setColor(Color.argb(127, 0, 0, 0));
canvas.drawRect(0.0f, 0.0f, width, 18.0f + 6.0f, backgroundPaint);
canvas.drawText(text, 6.0f, 18.0f, textPaint);
bitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
}
}

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

@ -0,0 +1,71 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import javax.microedition.khronos.opengles.GL10;
import java.util.ArrayList;
/** Manages a list of dead tiles, so we don't leak resources. */
public class TextureReaper {
private static TextureReaper sSharedInstance;
private ArrayList<Integer> mDeadTextureIDs;
private TextureReaper() { mDeadTextureIDs = new ArrayList<Integer>(); }
public static TextureReaper get() {
if (sSharedInstance == null)
sSharedInstance = new TextureReaper();
return sSharedInstance;
}
public void add(int[] textureIDs) {
for (int textureID : textureIDs)
mDeadTextureIDs.add(textureID);
}
public void reap(GL10 gl) {
int[] deadTextureIDs = new int[mDeadTextureIDs.size()];
for (int i = 0; i < deadTextureIDs.length; i++)
deadTextureIDs[i] = mDeadTextureIDs.get(i);
mDeadTextureIDs.clear();
gl.glDeleteTextures(deadTextureIDs.length, deadTextureIDs, 0);
}
}

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

@ -0,0 +1,182 @@
/* -*- 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) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Patrick Walton <pcwalton@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.gfx;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11Ext;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
/**
* Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL
* ES.
*/
public abstract class TileLayer extends Layer {
private static final String LOGTAG = "GeckoTileLayer";
private final ArrayList<Rect> mDirtyRects;
private final CairoImage mImage;
private final boolean mRepeat;
private final IntSize mSize;
private int[] mTextureIDs;
public TileLayer(boolean repeat, CairoImage image) {
mRepeat = repeat;
mImage = image;
mSize = new IntSize(image.getWidth(), image.getHeight());
mDirtyRects = new ArrayList<Rect>();
/*
* Assert that the image has a power-of-two size. OpenGL ES < 2.0 doesn't support NPOT
* textures and OpenGL ES doesn't seem to let us efficiently slice up a NPOT bitmap.
*/
int width = mImage.getWidth(), height = mImage.getHeight();
if ((width & (width - 1)) != 0 || (height & (height - 1)) != 0) {
throw new RuntimeException("TileLayer: NPOT images are unsupported (dimensions are " +
width + "x" + height + ")");
}
}
public IntSize getSize() { return mSize; }
protected boolean repeats() { return mRepeat; }
protected int getTextureID() { return mTextureIDs[0]; }
protected boolean initialized() { return mImage != null && mTextureIDs != null; }
@Override
protected void finalize() throws Throwable {
if (mTextureIDs != null)
TextureReaper.get().add(mTextureIDs);
}
/**
* Invalidates the given rect so that it will be uploaded again. Only valid inside a
* transaction.
*/
public void invalidate(Rect rect) {
if (!inTransaction())
throw new RuntimeException("invalidate() is only valid inside a transaction");
mDirtyRects.add(rect);
}
public void invalidate() {
invalidate(new Rect(0, 0, mSize.width, mSize.height));
}
@Override
protected void performUpdates(GL10 gl) {
super.performUpdates(gl);
if (mTextureIDs == null) {
uploadFullTexture(gl);
} else {
for (Rect dirtyRect : mDirtyRects)
uploadDirtyRect(gl, dirtyRect);
}
mDirtyRects.clear();
}
private void uploadFullTexture(GL10 gl) {
mTextureIDs = new int[1];
gl.glGenTextures(mTextureIDs.length, mTextureIDs, 0);
int cairoFormat = mImage.getFormat();
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
bindAndSetGLParameters(gl);
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height,
0, glInfo.format, glInfo.type, mImage.getBuffer());
}
private void uploadDirtyRect(GL10 gl, Rect dirtyRect) {
if (mTextureIDs == null)
throw new RuntimeException("uploadDirtyRect() called with null texture ID!");
int width = mSize.width;
int cairoFormat = mImage.getFormat();
CairoGLInfo glInfo = new CairoGLInfo(cairoFormat);
bindAndSetGLParameters(gl);
/*
* Upload the changed rect. We have to widen to the full width of the texture
* because we can't count on the device having support for GL_EXT_unpack_subimage,
* and going line-by-line is too slow.
*/
Buffer viewBuffer = mImage.getBuffer().slice();
int bpp = CairoUtils.bitsPerPixelForCairoFormat(cairoFormat) / 8;
int position = dirtyRect.top * width * bpp;
if (position > viewBuffer.limit()) {
Log.e(LOGTAG, "### Position outside tile! " + dirtyRect.top);
return;
}
viewBuffer.position(position);
gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, width, dirtyRect.height(),
glInfo.format, glInfo.type, viewBuffer);
}
private void bindAndSetGLParameters(GL10 gl) {
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureIDs[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
int repeatMode = mRepeat ? GL10.GL_REPEAT : GL10.GL_CLAMP_TO_EDGE;
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, repeatMode);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, repeatMode);
}
protected static FloatBuffer createBuffer(float[] values) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(values.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
floatBuffer.put(values);
floatBuffer.position(0);
return floatBuffer;
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше