merge birch to mozilla-central
--HG-- rename : embedding/android/GeckoBatteryManager.java => mobile/android/base/GeckoBatteryManager.java
|
@ -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>
|
|
@ -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
|
После Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 1.6 KiB |
После Ширина: | Высота: | Размер: 1.4 KiB |
После Ширина: | Высота: | Размер: 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%";
|
После Ширина: | Высота: | Размер: 1.9 KiB |
После Ширина: | Высота: | Размер: 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%¤tAppVersion=%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>
|
Двоичные данные
mobile/android/app/profile/extensions/feedback@mobile.mozilla.org/skin/beta-hdpi.png
Normal file
После Ширина: | Высота: | Размер: 2.6 KiB |
Двоичные данные
mobile/android/app/profile/extensions/feedback@mobile.mozilla.org/skin/dino-32.png
Normal file
После Ширина: | Высота: | Размер: 1.5 KiB |
Двоичные данные
mobile/android/app/profile/extensions/feedback@mobile.mozilla.org/skin/happy-32.png
Normal file
После Ширина: | Высота: | Размер: 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");
|
||||
}
|
Двоичные данные
mobile/android/app/profile/extensions/feedback@mobile.mozilla.org/skin/sad-32.png
Normal file
После Ширина: | Высота: | Размер: 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;
|
||||
}
|
||||
}
|
||||
|