Bug 847839 - Part 2: Remove obsolete files for Android XUL Fennec. r=mfinkle

This commit is contained in:
Chris Peterson 2013-03-07 17:55:49 +00:00
parent 41b11690cb
commit e52b304c31
39 changed files with 2 additions and 6740 deletions

View File

@ -10,7 +10,6 @@ namespace dom {
// Make sure that any change here is also made in
// * mobile/android/base/GeckoScreenOrientationListener.java
// * embedding/android/GeckoScreenOrientationListener.java
typedef uint32_t ScreenOrientation;
static const ScreenOrientation eScreenOrientation_None = 0;

View File

@ -18,7 +18,7 @@ interface nsIMobileMessageCallback : nsISupports
/**
* All SMS related errors that could apply to SmsRequest objects.
* Make sure to keep this list in sync with the list in:
* embedding/android/GeckoSmsManager.java
* mobile/android/GeckoSmsManager.java
*/
const unsigned short SUCCESS_NO_ERROR = 0;
const unsigned short NO_SIGNAL_ERROR = 1;

View File

@ -14,7 +14,7 @@ namespace mobilemessage {
// For MmsMessageData.state and SmsMessageData.deliveryState
// Please keep the following files in sync with enum below:
// embedding/android/GeckoSmsManager.java
// mobile/android/base/GeckoSmsManager.java
enum DeliveryState {
eDeliveryState_Sent = 0,
eDeliveryState_Received,

View File

@ -1,113 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.*;
import android.net.Uri;
import android.util.Log;
import android.widget.RemoteViews;
import java.net.*;
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;
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);
}
public boolean isProgressStyle() {
return mProgressStyle;
}
public void show() {
mNotificationManager.notify(mId, this);
}
public void setCustomIcon(Uri aIconUri) {
if (aIconUri == null || aIconUri.getScheme() == null)
return;
// Custom view
int layout = R.layout.notification_icon_text;
RemoteViews view = new RemoteViews(GeckoApp.mAppContext.getPackageName(), layout);
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);
if (mText.length() > 0) {
view.setTextViewText(R.id.notificationText, mText);
}
contentView = view;
mNotificationManager.notify(mId, this);
} catch(Exception ex) {
Log.e("GeckoAlert", "failed to create bitmap", ex);
}
}
public void updateProgress(String aAlertText, long aProgress, long aProgressMax) {
if (!mProgressStyle) {
// Custom view
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);
contentView = view;
flags |= FLAG_ONGOING_EVENT | FLAG_ONLY_ALERT_ONCE;
mProgressStyle = true;
}
String text;
double percent = 0;
if (aProgressMax > 0)
percent = ((double)aProgress / (double)aProgressMax);
if (aAlertText.length() > 0)
text = aAlertText;
else
text = NumberFormat.getPercentInstance().format(percent);
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);
// Update the notification
mNotificationManager.notify(mId, this);
mPrevPercent = percent;
mPrevAlertText = text;
}
}

View File

@ -1,149 +0,0 @@
#filter substitution
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="@ANDROID_PACKAGE_NAME@"
android:installLocation="auto"
android:versionCode="@ANDROID_VERSION_CODE@"
android:versionName="@MOZ_APP_VERSION@"
#ifdef MOZ_ANDROID_SHARED_ID
android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
#endif
>
<uses-sdk android:minSdkVersion="5"
android:targetSdkVersion="11"/>
<compatible-screens>
<!-- all xlarge size screens -->
<screen android:screenSize="xlarge" android:screenDensity="ldpi" />
<screen android:screenSize="xlarge" android:screenDensity="mdpi" />
<screen android:screenSize="xlarge" android:screenDensity="hdpi" />
<screen android:screenSize="xlarge" android:screenDensity="xhdpi" />
</compatible-screens>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.VIBRATE"/>
#ifdef MOZ_WEBSMS_BACKEND
<!-- WebSMS -->
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
#endif
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
<uses-feature android:name="android.hardware.touchscreen"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<application android:label="@MOZ_APP_DISPLAYNAME@"
android:icon="@drawable/icon"
#if MOZILLA_OFFICIAL
android:debuggable="false">
#else
android:debuggable="true">
#endif
<activity android:name="App"
android:label="@MOZ_APP_DISPLAYNAME@"
android:configChanges="keyboard|keyboardHidden|mcc|mnc"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:launchMode="singleTask"
android:theme="@style/GreyTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Default browser intents -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="about" />
<data android:scheme="javascript" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:mimeType="text/html"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="application/xhtml+xml"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.WEB_SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="" />
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
<!-- For debugging -->
<intent-filter>
<action android:name="org.mozilla.gecko.DEBUG" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name="NotificationHandler">
<intent-filter>
<action android:name="org.mozilla.gecko.ACTION_ALERT_CLICK" />
<action android:name="org.mozilla.gecko.ACTION_ALERT_CLEAR" />
</intent-filter>
</receiver>
<activity android:name="Restarter"
android:process="@ANDROID_PACKAGE_NAME@Restarter"
android:theme="@style/GreyTheme"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="org.mozilla.gecko.restart"/>
</intent-filter>
</activity>
#if MOZ_CRASHREPORTER
<activity android:name="CrashReporter"
android:label="@string/crash_reporter_title"
android:icon="@drawable/crash_reporter"
android:excludeFromRecents="true">
<intent-filter>
<action android:name="org.mozilla.gecko.reportCrash" />
</intent-filter>
</activity>
#endif
<activity android:name="LauncherShortcuts"
android:label="@string/launcher_shortcuts_title"
android:theme="@android:style/Theme.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.VideoPlayer"
android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation"
android:theme="@android:style/Theme.NoTitleBar" >
<intent-filter>
<action android:name="org.mozilla.gecko.PLAY_VIDEO" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@ -1,19 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#filter substitution
package @ANDROID_PACKAGE_NAME@;
import org.mozilla.gecko.GeckoApp;
public class App extends GeckoApp {
public String getPackageName() {
return "@ANDROID_PACKAGE_NAME@";
}
public String getContentProcessName() {
return "@MOZ_CHILD_PROCESS_NAME@";
}
};

View File

@ -1,311 +0,0 @@
/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#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_SUFFIX = "/mozilla/Crash Reports/";
private static final String PENDING_SUFFIX = CRASH_REPORT_SUFFIX + "pending";
private static final String SUBMITTED_SUFFIX = CRASH_REPORT_SUFFIX + "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() {
try {
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
} catch (Exception e) {
Log.e(LOGTAG, "exception while closing progress dialog: ", e);
}
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(getFilesDir(), PENDING_SUFFIX);
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);
}
}, "CrashReporter Thread").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();
return;
}
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() == HttpURLConnection.HTTP_OK) {
File submittedDir = new File(getFilesDir(),
SUBMITTED_SUFFIX);
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");
}
}

View File

@ -1,858 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
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;
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);
if (SmsManager.getInstance() != null) {
SmsManager.getInstance().start();
}
GeckoNetworkManager.getInstance().init();
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);
GeckoNetworkManager.getInstance().stop();
GeckoScreenOrientationListener.getInstance().stop();
}
@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);
GeckoNetworkManager.getInstance().start();
GeckoScreenOrientationListener.getInstance().start();
}
@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));
if (SmsManager.getInstance() != null) {
SmsManager.getInstance().stop();
if (isFinishing())
SmsManager.getInstance().shutdown();
}
GeckoNetworkManager.getInstance().stop();
GeckoScreenOrientationListener.getInstance().stop();
super.onDestroy();
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();
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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,150 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import java.lang.Math;
import java.util.Date;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;
public class GeckoBatteryManager
extends BroadcastReceiver
{
// Those constants should be keep in sync with the ones in:
// dom/battery/Constants.h
private final static double kDefaultLevel = 1.0;
private final static boolean kDefaultCharging = true;
private final static double kDefaultRemainingTime = -1.0;
private final static double kUnknownRemainingTime = -1.0;
private static Date sLastLevelChange = new Date(0);
private static boolean sNotificationsEnabled = false;
private static double sLevel = kDefaultLevel;
private static boolean sCharging = kDefaultCharging;
private static double sRemainingTime = kDefaultRemainingTime;;
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
Log.e("GeckoBatteryManager", "Got an unexpected intent!");
return;
}
boolean previousCharging = isCharging();
double previousLevel = getLevel();
if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false)) {
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
if (plugged == -1) {
sCharging = kDefaultCharging;
Log.e("GeckoBatteryManager", "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.
sCharging = plugged != 0;
}
if (sCharging != previousCharging) {
sRemainingTime = kUnknownRemainingTime;
// The new remaining time is going to take some time to show up but
// it's the best way to show a not too wrong value.
sLastLevelChange = new Date(0);
}
// We need two doubles because sLevel is a double.
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!");
sLevel = kDefaultLevel;
} else {
sLevel = current / max;
}
if (sLevel == 1.0 && sCharging) {
sRemainingTime = 0.0;
} else if (sLevel != previousLevel) {
// Estimate remaining time.
if (sLastLevelChange.getTime() != 0) {
Date currentTime = new Date();
long dt = (currentTime.getTime() - sLastLevelChange.getTime()) / 1000;
double dLevel = sLevel - previousLevel;
if (sCharging) {
if (dLevel < 0) {
Log.w("GeckoBatteryManager", "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!");
sRemainingTime = kUnknownRemainingTime;
} else {
sRemainingTime = Math.round(dt / -dLevel * sLevel);
}
}
sLastLevelChange = currentTime;
} else {
// That's the first time we got an update, we can't do anything.
sLastLevelChange = new Date();
}
}
} else {
sLevel = kDefaultLevel;
sCharging = kDefaultCharging;
sRemainingTime = kDefaultRemainingTime;
}
/*
* We want to inform listeners if the following conditions are fulfilled:
* - we have at least one observer;
* - the charging state or the level has changed.
*
* Note: no need to check for a remaining time change given that it's only
* updated if there is a level change or a charging change.
*
* The idea is to prevent doing all the way to the DOM code in the child
* process to finally not send an event.
*/
if (sNotificationsEnabled &&
(previousCharging != isCharging() || previousLevel != getLevel())) {
GeckoAppShell.notifyBatteryChange(getLevel(), isCharging(), getRemainingTime());
}
}
public static boolean isCharging() {
return sCharging;
}
public static double getLevel() {
return sLevel;
}
public static double getRemainingTime() {
return sRemainingTime;
}
public static void enableNotifications() {
sNotificationsEnabled = true;
}
public static void disableNotifications() {
sNotificationsEnabled = false;
}
public static double[] getCurrentInformation() {
return new double[] { getLevel(), isCharging() ? 1.0 : 0.0, getRemainingTime() };
}
}

View File

@ -1,30 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.content.*;
import android.net.*;
public class GeckoConnectivityReceiver
extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
String status;
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
if (info == null)
status = "unknown";
else if (!info.isConnected())
status = "down";
else
status = "up";
if (GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
GeckoAppShell.onChangeNetworkLinkStatus(status);
}
}

View File

@ -1,400 +0,0 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.os.*;
import android.app.*;
import android.view.*;
import android.content.*;
import android.graphics.*;
import android.widget.*;
import android.hardware.*;
import android.location.*;
import android.util.FloatMath;
import android.util.DisplayMetrics;
import java.nio.ByteBuffer;
import android.util.Log;
/* We're not allowed to hold on to most events given to us
* so we save the parts of the events we want to use in GeckoEvent.
* Fields have different meanings depending on the event type.
*/
public class GeckoEvent {
// these constants should match the ones in mobile/android/base/GeckoEvent.java
// as much as possible
public static final int INVALID = -1;
public static final int NATIVE_POKE = 0;
public static final int KEY_EVENT = 1;
public static final int MOTION_EVENT = 2;
public static final int SENSOR_EVENT = 3;
public static final int LOCATION_EVENT = 5;
public static final int IME_EVENT = 6;
public static final int DRAW = 7;
public static final int SIZE_CHANGED = 8;
public static final int ACTIVITY_STOPPING = 9;
public static final int ACTIVITY_PAUSING = 10;
public static final int ACTIVITY_SHUTDOWN = 11;
public static final int LOAD_URI = 12;
public static final int SURFACE_CREATED = 13;
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 VIEWPORT = 20;
public static final int VISITED = 21;
public static final int NETWORK_CHANGED = 22;
public static final int SCREENORIENTATION_CHANGED = 27;
/**
* These DOM_KEY_LOCATION constants mirror the DOM KeyboardEvent's constants.
* @see https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent#Key_location_constants
*/
private static final int DOM_KEY_LOCATION_STANDARD = 0;
private static final int DOM_KEY_LOCATION_LEFT = 1;
private static final int DOM_KEY_LOCATION_RIGHT = 2;
private static final int DOM_KEY_LOCATION_NUMPAD = 3;
private static final int DOM_KEY_LOCATION_MOBILE = 4;
private static final int DOM_KEY_LOCATION_JOYSTICK = 5;
public static final int IME_COMPOSITION_END = 0;
public static final int IME_COMPOSITION_BEGIN = 1;
public static final int IME_SET_TEXT = 2;
public static final int IME_GET_TEXT = 3;
public static final int IME_DELETE_TEXT = 4;
public static final int IME_SET_SELECTION = 5;
public static final int IME_GET_SELECTION = 6;
public static final int IME_ADD_RANGE = 7;
public static final int IME_RANGE_CARETPOSITION = 1;
public static final int IME_RANGE_RAWINPUT = 2;
public static final int IME_RANGE_SELECTEDRAWTEXT = 3;
public static final int IME_RANGE_CONVERTEDTEXT = 4;
public static final int IME_RANGE_SELECTEDCONVERTEDTEXT = 5;
public static final int IME_RANGE_UNDERLINE = 1;
public static final int IME_RANGE_FORECOLOR = 2;
public static final int IME_RANGE_BACKCOLOR = 4;
public int mType;
public int mAction;
public long mTime;
public Point[] mPoints;
public int[] mPointIndicies;
public int mPointerIndex;
public float[] mOrientations;
public float[] mPressures;
public Point[] mPointRadii;
public Rect mRect;
public double mX, mY, mZ;
public int mMetaState, mFlags;
public int mKeyCode, mUnicodeChar;
public int mRepeatCount;
public int mOffset, mCount;
public String mCharacters, mCharactersExtra;
public int mRangeType, mRangeStyles;
public int mRangeForeColor, mRangeBackColor;
public Location mLocation;
public Address mAddress;
public int mDomKeyLocation;
public double mBandwidth;
public boolean mCanBeMetered;
public short mScreenOrientation;
public int mNativeWindow;
public ByteBuffer mBuffer;
public GeckoEvent() {
mType = NATIVE_POKE;
}
public GeckoEvent(int evType) {
mType = evType;
}
public GeckoEvent(KeyEvent k) {
mType = KEY_EVENT;
mAction = k.getAction();
mTime = k.getEventTime();
mMetaState = k.getMetaState();
mFlags = k.getFlags();
mKeyCode = k.getKeyCode();
mUnicodeChar = k.getUnicodeChar();
mRepeatCount = k.getRepeatCount();
mCharacters = k.getCharacters();
mDomKeyLocation = isJoystickButton(mKeyCode) ? DOM_KEY_LOCATION_JOYSTICK : DOM_KEY_LOCATION_MOBILE;
}
/**
* This method tests if a key is one of the described in:
* https://bugzilla.mozilla.org/show_bug.cgi?id=756504#c0
* @param keyCode int with the key code (Android key constant from KeyEvent)
* @return true if the key is one of the listed above, false otherwise.
*/
private static boolean isJoystickButton(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_CENTER:
case KeyEvent.KEYCODE_DPAD_LEFT:
case KeyEvent.KEYCODE_DPAD_RIGHT:
case KeyEvent.KEYCODE_DPAD_DOWN:
case KeyEvent.KEYCODE_DPAD_UP:
return true;
default:
if (Build.VERSION.SDK_INT >= 12) {
return KeyEvent.isGamepadButton(keyCode);
}
return GeckoEvent.isGamepadButton(keyCode);
}
}
/**
* This method is a replacement for the the KeyEvent.isGamepadButton method to be
* compatible with Build.VERSION.SDK_INT < 12. This is an implementantion of the
* same method isGamepadButton available after SDK 12.
* @param keyCode int with the key code (Android key constant from KeyEvent).
* @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}.
*/
private static boolean isGamepadButton(int keyCode) {
switch (keyCode) {
case KeyEvent.KEYCODE_BUTTON_A:
case KeyEvent.KEYCODE_BUTTON_B:
case KeyEvent.KEYCODE_BUTTON_C:
case KeyEvent.KEYCODE_BUTTON_X:
case KeyEvent.KEYCODE_BUTTON_Y:
case KeyEvent.KEYCODE_BUTTON_Z:
case KeyEvent.KEYCODE_BUTTON_L1:
case KeyEvent.KEYCODE_BUTTON_R1:
case KeyEvent.KEYCODE_BUTTON_L2:
case KeyEvent.KEYCODE_BUTTON_R2:
case KeyEvent.KEYCODE_BUTTON_THUMBL:
case KeyEvent.KEYCODE_BUTTON_THUMBR:
case KeyEvent.KEYCODE_BUTTON_START:
case KeyEvent.KEYCODE_BUTTON_SELECT:
case KeyEvent.KEYCODE_BUTTON_MODE:
case KeyEvent.KEYCODE_BUTTON_1:
case KeyEvent.KEYCODE_BUTTON_2:
case KeyEvent.KEYCODE_BUTTON_3:
case KeyEvent.KEYCODE_BUTTON_4:
case KeyEvent.KEYCODE_BUTTON_5:
case KeyEvent.KEYCODE_BUTTON_6:
case KeyEvent.KEYCODE_BUTTON_7:
case KeyEvent.KEYCODE_BUTTON_8:
case KeyEvent.KEYCODE_BUTTON_9:
case KeyEvent.KEYCODE_BUTTON_10:
case KeyEvent.KEYCODE_BUTTON_11:
case KeyEvent.KEYCODE_BUTTON_12:
case KeyEvent.KEYCODE_BUTTON_13:
case KeyEvent.KEYCODE_BUTTON_14:
case KeyEvent.KEYCODE_BUTTON_15:
case KeyEvent.KEYCODE_BUTTON_16:
return true;
default:
return false;
}
}
public GeckoEvent(MotionEvent m) {
mType = MOTION_EVENT;
mAction = m.getAction();
mTime = m.getEventTime();
mMetaState = m.getMetaState();
switch (mAction & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: {
mCount = m.getPointerCount();
mPoints = new Point[mCount];
mPointIndicies = new int[mCount];
mOrientations = new float[mCount];
mPressures = new float[mCount];
mPointRadii = new Point[mCount];
mPointerIndex = (mAction & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
for (int i = 0; i < mCount; i++) {
addMotionPoint(i, i, m);
}
break;
}
default: {
mCount = 0;
mPointerIndex = -1;
mPoints = new Point[mCount];
mPointIndicies = new int[mCount];
mOrientations = new float[mCount];
mPressures = new float[mCount];
mPointRadii = new Point[mCount];
}
}
}
public void addMotionPoint(int index, int eventIndex, MotionEvent event) {
PointF geckoPoint = new PointF(event.getX(eventIndex), event.getY(eventIndex));
mPoints[index] = new Point((int)Math.round(geckoPoint.x), (int)Math.round(geckoPoint.y));
mPointIndicies[index] = event.getPointerId(eventIndex);
// getToolMajor, getToolMinor and getOrientation are API Level 9 features
if (Build.VERSION.SDK_INT >= 9) {
double radians = event.getOrientation(eventIndex);
mOrientations[index] = (float) Math.toDegrees(radians);
// w3c touchevents spec does not allow orientations == 90
// this shifts it to -90, which will be shifted to zero below
if (mOrientations[index] == 90)
mOrientations[index] = -90;
// w3c touchevent radius are given by an orientation between 0 and 90
// the radius is found by removing the orientation and measuring the x and y
// radius of the resulting ellipse
// for android orientations >= 0 and < 90, the major axis should correspond to
// just reporting the y radius as the major one, and x as minor
// however, for a radius < 0, we have to shift the orientation by adding 90, and
// reverse which radius is major and minor
if (mOrientations[index] < 0) {
mOrientations[index] += 90;
mPointRadii[index] = new Point((int)event.getToolMajor(eventIndex)/2,
(int)event.getToolMinor(eventIndex)/2);
} else {
mPointRadii[index] = new Point((int)event.getToolMinor(eventIndex)/2,
(int)event.getToolMajor(eventIndex)/2);
}
} else {
float size = event.getSize(eventIndex);
DisplayMetrics displaymetrics = new DisplayMetrics();
GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
size = size*Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels);
mPointRadii[index] = new Point((int)size,(int)size);
mOrientations[index] = 0;
}
mPressures[index] = event.getPressure(eventIndex);
}
public GeckoEvent(SensorEvent s) {
int sensor_type = s.sensor.getType();
switch(sensor_type) {
case Sensor.TYPE_ACCELEROMETER:
mType = SENSOR_EVENT;
mFlags = 1; // hal::SENSOR_ACCELERATION
mX = s.values[0];
mY = s.values[1];
mZ = s.values[2];
break;
case Sensor.TYPE_ORIENTATION:
mType = SENSOR_EVENT;
mFlags = 0; // hal::SENSOR_ORIENTATION
mX = s.values[0];
mY = s.values[1];
mZ = s.values[2];
Log.i("GeckoEvent", "SensorEvent type = " + s.sensor.getType() + " " + s.sensor.getName() + " " + mX + " " + mY + " " + mZ );
break;
case Sensor.TYPE_PROXIMITY:
mType = SENSOR_EVENT;
mFlags = 2; // hal:SENSOR_PROXIMITY
mX = s.values[0];
mY = 0;
mZ = s.sensor.getMaximumRange();
Log.i("GeckoEvent", "SensorEvent type = " + s.sensor.getType() +
" " + s.sensor.getName() + " " + mX);
break;
}
}
public GeckoEvent(Location l) {
mType = LOCATION_EVENT;
mLocation = l;
}
public GeckoEvent(int imeAction, int offset, int count) {
mType = IME_EVENT;
mAction = imeAction;
mOffset = offset;
mCount = count;
}
private void InitIMERange(int action, int offset, int count,
int rangeType, int rangeStyles,
int rangeForeColor, int rangeBackColor) {
mType = IME_EVENT;
mAction = action;
mOffset = offset;
mCount = count;
mRangeType = rangeType;
mRangeStyles = rangeStyles;
mRangeForeColor = rangeForeColor;
mRangeBackColor = rangeBackColor;
return;
}
public GeckoEvent(int offset, int count,
int rangeType, int rangeStyles,
int rangeForeColor, int rangeBackColor, String text) {
InitIMERange(IME_SET_TEXT, offset, count, rangeType, rangeStyles,
rangeForeColor, rangeBackColor);
mCharacters = text;
}
public GeckoEvent(int offset, int count,
int rangeType, int rangeStyles,
int rangeForeColor, int rangeBackColor) {
InitIMERange(IME_ADD_RANGE, offset, count, rangeType, rangeStyles,
rangeForeColor, rangeBackColor);
}
public GeckoEvent(int etype, Rect dirty) {
if (etype != DRAW) {
mType = INVALID;
return;
}
mType = etype;
mRect = dirty;
}
public GeckoEvent(int etype, int w, int h, int screenw, int screenh) {
if (etype != SIZE_CHANGED) {
mType = INVALID;
return;
}
mType = etype;
mPoints = new Point[3];
mPoints[0] = new Point(w, h);
mPoints[1] = new Point(screenw, screenh);
mPoints[2] = new Point(0, 0);
}
public GeckoEvent(String subject, String data) {
mType = BROADCAST;
mCharacters = subject;
mCharactersExtra = data;
}
public GeckoEvent(String uri) {
mType = LOAD_URI;
mCharacters = uri;
}
public GeckoEvent(double bandwidth, boolean canBeMetered) {
mType = NETWORK_CHANGED;
mBandwidth = bandwidth;
mCanBeMetered = canBeMetered;
}
public GeckoEvent(short aScreenOrientation) {
mType = SCREENORIENTATION_CHANGED;
mScreenOrientation = aScreenOrientation;
}
}

View File

@ -1,701 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import android.os.*;
import android.app.*;
import android.text.*;
import android.text.style.*;
import android.view.*;
import android.view.inputmethod.*;
import android.content.*;
import android.R;
import android.util.*;
public class GeckoInputConnection
extends BaseInputConnection
implements TextWatcher
{
private class ChangeNotification {
public String mText;
public int mStart;
public int mEnd;
public int mNewEnd;
ChangeNotification(String text, int start, int oldEnd, int newEnd) {
mText = text;
mStart = start;
mEnd = oldEnd;
mNewEnd = newEnd;
}
ChangeNotification(int start, int end) {
mText = null;
mStart = start;
mEnd = end;
mNewEnd = 0;
}
}
public GeckoInputConnection (View targetView) {
super(targetView, true);
mQueryResult = new SynchronousQueue<String>();
}
@Override
public boolean beginBatchEdit() {
//Log.d("GeckoAppJava", "IME: beginBatchEdit");
mBatchMode = true;
return true;
}
@Override
public boolean commitCompletion(CompletionInfo text) {
//Log.d("GeckoAppJava", "IME: commitCompletion");
return commitText(text.getText(), 1);
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
//Log.d("GeckoAppJava", "IME: commitText");
setComposingText(text, newCursorPosition);
finishComposingText();
return true;
}
@Override
public boolean deleteSurroundingText(int leftLength, int rightLength) {
//Log.d("GeckoAppJava", "IME: deleteSurroundingText");
if (leftLength == 0 && rightLength == 0)
return true;
/* deleteSurroundingText is supposed to ignore the composing text,
so we cancel any pending composition, delete the text, and then
restart the composition */
if (mComposing) {
// Cancel current composition
GeckoAppShell.sendEventToGecko(
new GeckoEvent(0, 0, 0, 0, 0, 0, null));
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_COMPOSITION_END, 0, 0));
}
// Select text to be deleted
int delStart, delLen;
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
try {
mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: deleteSurroundingText interrupted", e);
return false;
}
delStart = mSelectionStart > leftLength ?
mSelectionStart - leftLength : 0;
delLen = mSelectionStart + rightLength - delStart;
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION, delStart, delLen));
// Restore composition / delete text
if (mComposing) {
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_COMPOSITION_BEGIN, 0, 0));
if (mComposingText.length() > 0) {
/* IME_SET_TEXT doesn't work well with empty strings */
GeckoAppShell.sendEventToGecko(
new GeckoEvent(0, mComposingText.length(),
GeckoEvent.IME_RANGE_RAWINPUT,
GeckoEvent.IME_RANGE_UNDERLINE, 0, 0,
mComposingText.toString()));
}
} else {
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0));
}
return true;
}
@Override
public boolean endBatchEdit() {
//Log.d("GeckoAppJava", "IME: endBatchEdit");
mBatchMode = false;
if (!mBatchChanges.isEmpty()) {
InputMethodManager imm = (InputMethodManager)
GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
for (ChangeNotification n : mBatchChanges) {
if (n.mText != null)
notifyTextChange(imm, n.mText, n.mStart, n.mEnd, n.mNewEnd);
else
notifySelectionChange(imm, n.mStart, n.mEnd);
}
}
mBatchChanges.clear();
}
return true;
}
@Override
public boolean finishComposingText() {
//Log.d("GeckoAppJava", "IME: finishComposingText");
if (mComposing) {
// Set style to none
GeckoAppShell.sendEventToGecko(
new GeckoEvent(0, mComposingText.length(),
GeckoEvent.IME_RANGE_RAWINPUT, 0, 0, 0,
mComposingText));
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_COMPOSITION_END, 0, 0));
mComposing = false;
mComposingText = "";
if (!mBatchMode) {
// Make sure caret stays at the same position
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION,
mCompositionStart + mCompositionSelStart, 0));
}
}
return true;
}
@Override
public int getCursorCapsMode(int reqModes) {
//Log.d("GeckoAppJava", "IME: getCursorCapsMode");
return 0;
}
@Override
public Editable getEditable() {
Log.w("GeckoAppJava", "IME: getEditable called from " +
Thread.currentThread().getStackTrace()[0].toString());
return null;
}
@Override
public boolean performContextMenuAction(int id) {
//Log.d("GeckoAppJava", "IME: performContextMenuAction");
// First we need to ask Gecko to tell us the full contents of the
// text field we're about to operate on.
String text;
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_TEXT, 0, Integer.MAX_VALUE));
try {
text = mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e);
return false;
}
switch (id) {
case R.id.selectAll:
setSelection(0, text.length());
break;
case R.id.cut:
// Fill the clipboard
GeckoAppShell.setClipboardText(text);
// If GET_TEXT returned an empty selection, we'll select everything
if (mSelectionLength <= 0)
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION, 0, text.length()));
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0));
break;
case R.id.paste:
commitText(GeckoAppShell.getClipboardText(), 1);
break;
case R.id.copy:
// If there is no selection set, we must be doing "Copy All",
// otherwise, we need to get the selection from Gecko
if (mSelectionLength > 0) {
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
try {
text = mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: performContextMenuAction interrupted", e);
return false;
}
}
GeckoAppShell.setClipboardText(text);
break;
}
return true;
}
@Override
public ExtractedText getExtractedText(ExtractedTextRequest req, int flags) {
if (req == null)
return null;
// Bail out here if gecko isn't running, otherwise we deadlock
// below when waiting for the reply to IME_GET_SELECTION.
if (!GeckoApp.checkLaunchState(GeckoApp.LaunchState.GeckoRunning))
return null;
//Log.d("GeckoAppJava", "IME: getExtractedText");
ExtractedText extract = new ExtractedText();
extract.flags = 0;
extract.partialStartOffset = -1;
extract.partialEndOffset = -1;
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_SELECTION, 0, 0));
try {
mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e);
return null;
}
extract.selectionStart = mSelectionStart;
extract.selectionEnd = mSelectionStart + mSelectionLength;
// bug 617298 - IME_GET_TEXT sometimes gives the wrong result due to
// a stale cache. Use a set of three workarounds:
// 1. Sleep for 20 milliseconds and hope the child updates us with the new text.
// Very evil and, consequentially, most effective.
try {
Thread.sleep(20);
} catch (InterruptedException e) {}
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_TEXT, 0, Integer.MAX_VALUE));
try {
extract.startOffset = 0;
extract.text = mQueryResult.take();
// 2. Make a guess about what the text actually is
if (mComposing && extract.selectionEnd > extract.text.length())
extract.text = extract.text.subSequence(0, Math.min(extract.text.length(), mCompositionStart)) + mComposingText;
// 3. If all else fails, make sure our selection indexes make sense
extract.selectionStart = Math.min(extract.selectionStart, extract.text.length());
extract.selectionEnd = Math.min(extract.selectionEnd, extract.text.length());
if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0)
mUpdateRequest = req;
return extract;
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: getExtractedText interrupted", e);
return null;
}
}
@Override
public CharSequence getTextAfterCursor(int length, int flags) {
//Log.d("GeckoAppJava", "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);
return null;
}
/* Compatible with both positive and negative length
(no need for separate code for getTextBeforeCursor) */
int textStart = mSelectionStart;
int textLength = length;
if (length < 0) {
textStart += length;
textLength = -length;
if (textStart < 0) {
textStart = 0;
textLength = mSelectionStart;
}
}
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_TEXT, textStart, textLength));
try {
return mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: getTextBefore/AfterCursor: Interrupted!", e);
return null;
}
}
@Override
public CharSequence getTextBeforeCursor(int length, int flags) {
//Log.d("GeckoAppJava", "IME: getTextBeforeCursor");
return getTextAfterCursor(-length, flags);
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
//Log.d("GeckoAppJava", "IME: setComposingText");
// Set new composing text
mComposingText = text != null ? text.toString() : "";
if (!mComposing) {
if (mComposingText.length() == 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
// compositing.
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));
mComposing = true;
mCompositionStart = mSelectionLength >= 0 ?
mSelectionStart : mSelectionStart + mSelectionLength;
}
// Set new selection
// New selection should be within the composition
mCompositionSelStart = newCursorPosition > 0 ? mComposingText.length() : 0;
mCompositionSelLen = 0;
// Handle composition text styles
if (text != null && text instanceof Spanned) {
Spanned span = (Spanned) text;
int spanStart = 0, spanEnd = 0;
boolean pastSelStart = false, pastSelEnd = false;
do {
int rangeType = GeckoEvent.IME_RANGE_CONVERTEDTEXT;
int rangeStyles = 0, rangeForeColor = 0, rangeBackColor = 0;
// Find next offset where there is a style transition
spanEnd = span.nextSpanTransition(spanStart + 1, text.length(),
CharacterStyle.class);
// We need to count the selection as a transition
if (mCompositionSelLen >= 0) {
if (!pastSelStart && spanEnd >= mCompositionSelStart) {
spanEnd = mCompositionSelStart;
pastSelStart = true;
} else if (!pastSelEnd && spanEnd >=
mCompositionSelStart + mCompositionSelLen) {
spanEnd = mCompositionSelStart + mCompositionSelLen;
pastSelEnd = true;
rangeType = GeckoEvent.IME_RANGE_SELECTEDRAWTEXT;
}
} else {
if (!pastSelEnd && spanEnd >=
mCompositionSelStart + mCompositionSelLen) {
spanEnd = mCompositionSelStart + mCompositionSelLen;
pastSelEnd = true;
} else if (!pastSelStart &&
spanEnd >= mCompositionSelStart) {
spanEnd = mCompositionSelStart;
pastSelStart = true;
rangeType = GeckoEvent.IME_RANGE_SELECTEDRAWTEXT;
}
}
// Empty range, continue
if (spanEnd <= spanStart)
continue;
// Get and iterate through list of span objects within range
CharacterStyle styles[] = span.getSpans(
spanStart, spanEnd, CharacterStyle.class);
for (CharacterStyle style : styles) {
if (style instanceof UnderlineSpan) {
// Text should be underlined
rangeStyles |= GeckoEvent.IME_RANGE_UNDERLINE;
} else if (style instanceof ForegroundColorSpan) {
// Text should be of a different foreground color
rangeStyles |= GeckoEvent.IME_RANGE_FORECOLOR;
rangeForeColor =
((ForegroundColorSpan)style).getForegroundColor();
} else if (style instanceof BackgroundColorSpan) {
// Text should be of a different background color
rangeStyles |= GeckoEvent.IME_RANGE_BACKCOLOR;
rangeBackColor =
((BackgroundColorSpan)style).getBackgroundColor();
}
}
// Add range to array, the actual styles are
// applied when IME_SET_TEXT is sent
GeckoAppShell.sendEventToGecko(
new GeckoEvent(spanStart, spanEnd - spanStart,
rangeType, rangeStyles,
rangeForeColor, rangeBackColor));
spanStart = spanEnd;
} while (spanStart < text.length());
} else {
GeckoAppShell.sendEventToGecko(
new GeckoEvent(0, text == null ? 0 : text.length(),
GeckoEvent.IME_RANGE_RAWINPUT,
GeckoEvent.IME_RANGE_UNDERLINE, 0, 0));
}
// Change composition (treating selection end as where the caret is)
GeckoAppShell.sendEventToGecko(
new GeckoEvent(mCompositionSelStart + mCompositionSelLen, 0,
GeckoEvent.IME_RANGE_CARETPOSITION, 0, 0, 0,
mComposingText));
return true;
}
@Override
public boolean setComposingRegion(int start, int end) {
//Log.d("GeckoAppJava", "IME: setComposingRegion(start=" + start + ", end=" + end + ")");
if (start < 0 || end < start)
return true;
CharSequence text = null;
if (start == mCompositionStart && end - start == mComposingText.length()) {
// Use mComposingText to avoid extra call to Gecko
text = mComposingText;
}
finishComposingText();
if (text == null && start < end) {
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_GET_TEXT, start, end - start));
try {
text = mQueryResult.take();
} catch (InterruptedException e) {
Log.e("GeckoAppJava", "IME: setComposingRegion interrupted", e);
return false;
}
}
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start, end - start));
// Call setComposingText with the same text to start composition and let Gecko know about new composing region
setComposingText(text, 1);
return true;
}
@Override
public boolean setSelection(int start, int end) {
//Log.d("GeckoAppJava", "IME: setSelection");
if (mComposing) {
/* Translate to fake selection positions */
start -= mCompositionStart;
end -= mCompositionStart;
if (start < 0)
start = 0;
else if (start > mComposingText.length())
start = mComposingText.length();
if (end < 0)
end = 0;
else if (end > mComposingText.length())
end = mComposingText.length();
mCompositionSelStart = start;
mCompositionSelLen = end - start;
} else {
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION,
start, end - start));
}
return true;
}
public boolean onKeyDel() {
// Some IMEs don't update us on deletions
// In that case we are not updated when a composition
// is destroyed, and Bad Things happen
if (!mComposing)
return false;
if (mComposingText.length() > 0) {
mComposingText = mComposingText.substring(0,
mComposingText.length() - 1);
if (mComposingText.length() > 0)
return false;
}
commitText(null, 1);
return true;
}
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));
if (mBatchMode) {
mBatchChanges.add(new ChangeNotification(text, start, oldEnd, newEnd));
return;
}
mNumPendingChanges = Math.max(mNumPendingChanges - 1, 0);
// 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);
if (mUpdateRequest == null)
return;
mUpdateExtract.flags = 0;
// We update from (0, oldEnd) to (0, newEnd) because some Android IMEs
// assume that updates start at zero, according to jchen.
mUpdateExtract.partialStartOffset = 0;
mUpdateExtract.partialEndOffset = oldEnd;
// Faster to not query for selection
mUpdateExtract.selectionStart = newEnd;
mUpdateExtract.selectionEnd = newEnd;
mUpdateExtract.text = text.substring(0, newEnd);
mUpdateExtract.startOffset = 0;
imm.updateExtractedText(GeckoApp.surfaceView,
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));
if (mBatchMode) {
mBatchChanges.add(new ChangeNotification(start, end));
return;
}
if (mComposing)
imm.updateSelection(GeckoApp.surfaceView,
mCompositionStart + mCompositionSelStart,
mCompositionStart + mCompositionSelStart + mCompositionSelLen,
mCompositionStart,
mCompositionStart + mComposingText.length());
else
imm.updateSelection(GeckoApp.surfaceView, 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,
Math.min(start, maxLen),
Math.min(end, maxLen));
}
public void reset() {
mComposing = false;
mComposingText = "";
mUpdateRequest = null;
mNumPendingChanges = 0;
mBatchMode = false;
mBatchChanges.clear();
}
// 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));
mNumPendingChanges++;
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start, before));
if (count == 0) {
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_DELETE_TEXT, 0, 0));
} else {
// Start and stop composition to force UI updates.
finishComposingText();
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_COMPOSITION_BEGIN, 0, 0));
GeckoAppShell.sendEventToGecko(
new GeckoEvent(0, count,
GeckoEvent.IME_RANGE_RAWINPUT, 0, 0, 0,
s.subSequence(start, start + count).toString()));
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_COMPOSITION_END, 0, 0));
GeckoAppShell.sendEventToGecko(
new GeckoEvent(GeckoEvent.IME_SET_SELECTION, start + count, 0));
}
// Block this thread until all pending events are processed
GeckoAppShell.geckoEventSync();
}
public void afterTextChanged(Editable s)
{
}
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
// Is a composition active?
boolean mComposing;
// Composition text when a composition is active
String mComposingText = "";
// Start index of the composition within the text body
int mCompositionStart;
/* During a composition, we should not alter the real selection,
therefore we keep our own offsets to emulate selection */
// Start of fake selection, relative to start of composition
int mCompositionSelStart;
// Length of fake selection
int mCompositionSelLen;
// Number of in flight changes
int mNumPendingChanges;
boolean mBatchMode;
private CopyOnWriteArrayList<ChangeNotification> mBatchChanges =
new CopyOnWriteArrayList<ChangeNotification>();
ExtractedTextRequest mUpdateRequest;
final ExtractedText mUpdateExtract = new ExtractedText();
int mSelectionStart, mSelectionLength;
SynchronousQueue<String> mQueryResult;
}

View File

@ -1,303 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import java.lang.Math;
import android.util.Log;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.telephony.TelephonyManager;
/*
* A part of the work of GeckoNetworkManager is to give an estimation of the
* download speed of the current connection. For known to be fast connection, we
* simply use a predefined value (we don't care about being precise). For mobile
* connections, we sort them in groups (generations) and estimate the average
* real life download speed of that specific generation. This value comes from
* researches (eg. Wikipedia articles) or is simply an arbitrary estimation.
* Precision isn't important, we mostly need an order of magnitude.
*
* Each group is composed with networks represented by the constant from
* Android's ConnectivityManager and the description comming from the same
* class.
*
* 2G (15 bk/s):
* int NETWORK_TYPE_IDEN Current network is iDen
* int NETWORK_TYPE_CDMA Current network is CDMA: Either IS95A or IS95B
*
* 2.5G (60 kb/s)
* int NETWORK_TYPE_GPRS Current network is GPRS
* int NETWORK_TYPE_1xRTT Current network is 1xRTT
*
* 2.75G (200 kb/s)
* int NETWORK_TYPE_EDGE Current network is EDGE
*
* 3G (300 kb/s)
* int NETWORK_TYPE_UMTS Current network is UMTS
* int NETWORK_TYPE_EVDO_0 Current network is EVDO revision 0
*
* 3.5G (7 Mb/s)
* int NETWORK_TYPE_HSPA Current network is HSPA
* int NETWORK_TYPE_HSDPA Current network is HSDPA
* int NETWORK_TYPE_HSUPA Current network is HSUPA
* int NETWORK_TYPE_EVDO_A Current network is EVDO revision A
* int NETWORK_TYPE_EVDO_B Current network is EVDO revision B
* int NETWORK_TYPE_EHRPD Current network is eHRPD
*
* 3.75G (20 Mb/s)
* int NETWORK_TYPE_HSPAP Current network is HSPA+
*
* 3.9G (50 Mb/s)
* int NETWORK_TYPE_LTE Current network is LTE
*/
public class GeckoNetworkManager
extends BroadcastReceiver
{
static private final GeckoNetworkManager sInstance = new GeckoNetworkManager();
static private final double kDefaultBandwidth = -1.0;
static private final boolean kDefaultCanBeMetered = false;
static private final double kMaxBandwidth = 20.0;
static private final double kNetworkSpeedEthernet = 20.0; // 20 Mb/s
static private final double kNetworkSpeedWifi = 20.0; // 20 Mb/s
static private final double kNetworkSpeedWiMax = 40.0; // 40 Mb/s
static private final double kNetworkSpeed_2_G = 15.0 / 1024.0; // 15 kb/s
static private final double kNetworkSpeed_2_5_G = 60.0 / 1024.0; // 60 kb/s
static private final double kNetworkSpeed_2_75_G = 200.0 / 1024.0; // 200 kb/s
static private final double kNetworkSpeed_3_G = 300.0 / 1024.0; // 300 kb/s
static private final double kNetworkSpeed_3_5_G = 7.0; // 7 Mb/s
static private final double kNetworkSpeed_3_75_G = 20.0; // 20 Mb/s
static private final double kNetworkSpeed_3_9_G = 50.0; // 50 Mb/s
private enum NetworkType {
NETWORK_NONE,
NETWORK_ETHERNET,
NETWORK_WIFI,
NETWORK_WIMAX,
NETWORK_2_G, // 2G
NETWORK_2_5_G, // 2.5G
NETWORK_2_75_G, // 2.75G
NETWORK_3_G, // 3G
NETWORK_3_5_G, // 3.5G
NETWORK_3_75_G, // 3.75G
NETWORK_3_9_G, // 3.9G
NETWORK_UNKNOWN
}
private NetworkType mNetworkType = NetworkType.NETWORK_NONE;
private IntentFilter mNetworkFilter = new IntentFilter();
// Whether the manager should be listening to Network Information changes.
private boolean mShouldBeListening = false;
// Whether the manager should notify Gecko that a change in Network
// Information happened.
private boolean mShouldNotify = false;
public static GeckoNetworkManager getInstance() {
return sInstance;
}
@Override
public void onReceive(Context aContext, Intent aIntent) {
updateNetworkType();
}
public void init() {
mNetworkFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mNetworkType = getNetworkType();
}
public void start() {
mShouldBeListening = true;
updateNetworkType();
if (mShouldNotify) {
startListening();
}
}
private void startListening() {
GeckoApp.mAppContext.registerReceiver(sInstance, mNetworkFilter);
}
public void stop() {
mShouldBeListening = false;
if (mShouldNotify) {
stopListening();
}
}
private void stopListening() {
GeckoApp.mAppContext.unregisterReceiver(sInstance);
}
private void updateNetworkType() {
NetworkType previousNetworkType = mNetworkType;
mNetworkType = getNetworkType();
if (mNetworkType == previousNetworkType || !mShouldNotify) {
return;
}
GeckoAppShell.sendEventToGecko(new GeckoEvent(getNetworkSpeed(mNetworkType),
isNetworkUsuallyMetered(mNetworkType)));
}
public double[] getCurrentInformation() {
return new double[] { getNetworkSpeed(mNetworkType),
isNetworkUsuallyMetered(mNetworkType) ? 1.0 : 0.0 };
}
public void enableNotifications() {
// We set mShouldNotify *after* calling updateNetworkType() to make sure we
// don't notify an eventual change in mNetworkType.
updateNetworkType();
mShouldNotify = true;
if (mShouldBeListening) {
startListening();
}
}
public void disableNotifications() {
mShouldNotify = false;
if (mShouldBeListening) {
stopListening();
}
}
private static NetworkType getNetworkType() {
ConnectivityManager cm =
(ConnectivityManager)GeckoApp.mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) {
Log.w("GeckoNetworkManager", "Could not access Connectivity service");
return NetworkType.NETWORK_NONE;
}
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null) {
return NetworkType.NETWORK_NONE;
}
switch (ni.getType()) {
case ConnectivityManager.TYPE_ETHERNET:
return NetworkType.NETWORK_ETHERNET;
case ConnectivityManager.TYPE_WIFI:
return NetworkType.NETWORK_WIFI;
case ConnectivityManager.TYPE_WIMAX:
return NetworkType.NETWORK_WIMAX;
case ConnectivityManager.TYPE_MOBILE:
break; // We will handle sub-types after the switch.
default:
Log.w("GeckoNetworkManager", "Ignoring the current network type.");
return NetworkType.NETWORK_UNKNOWN;
}
TelephonyManager tm =
(TelephonyManager)GeckoApp.mAppContext.getSystemService(Context.TELEPHONY_SERVICE);
if (tm == null) {
Log.w("GeckoNetworkManager", "Could not access Telephony service");
return NetworkType.NETWORK_UNKNOWN;
}
switch (tm.getNetworkType()) {
case TelephonyManager.NETWORK_TYPE_IDEN:
case TelephonyManager.NETWORK_TYPE_CDMA:
return NetworkType.NETWORK_2_G;
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_1xRTT:
return NetworkType.NETWORK_2_5_G;
case TelephonyManager.NETWORK_TYPE_EDGE:
return NetworkType.NETWORK_2_75_G;
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_EVDO_0:
return NetworkType.NETWORK_3_G;
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
case TelephonyManager.NETWORK_TYPE_EHRPD:
return NetworkType.NETWORK_3_5_G;
case TelephonyManager.NETWORK_TYPE_HSPAP:
return NetworkType.NETWORK_3_75_G;
case TelephonyManager.NETWORK_TYPE_LTE:
return NetworkType.NETWORK_3_9_G;
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
default:
Log.w("GeckoNetworkManager", "Connected to an unknown mobile network!");
return NetworkType.NETWORK_UNKNOWN;
}
}
private static double getNetworkSpeed(NetworkType aType) {
switch (aType) {
case NETWORK_NONE:
return 0.0;
case NETWORK_ETHERNET:
return kNetworkSpeedEthernet;
case NETWORK_WIFI:
return kNetworkSpeedWifi;
case NETWORK_WIMAX:
return kNetworkSpeedWiMax;
case NETWORK_2_G:
return kNetworkSpeed_2_G;
case NETWORK_2_5_G:
return kNetworkSpeed_2_5_G;
case NETWORK_2_75_G:
return kNetworkSpeed_2_75_G;
case NETWORK_3_G:
return kNetworkSpeed_3_G;
case NETWORK_3_5_G:
return kNetworkSpeed_3_5_G;
case NETWORK_3_75_G:
return kNetworkSpeed_3_75_G;
case NETWORK_3_9_G:
return kNetworkSpeed_3_9_G;
case NETWORK_UNKNOWN:
default:
return kDefaultBandwidth;
}
}
private static boolean isNetworkUsuallyMetered(NetworkType aType) {
switch (aType) {
case NETWORK_NONE:
case NETWORK_UNKNOWN:
case NETWORK_ETHERNET:
case NETWORK_WIFI:
case NETWORK_WIMAX:
return false;
case NETWORK_2_G:
case NETWORK_2_5_G:
case NETWORK_2_75_G:
case NETWORK_3_G:
case NETWORK_3_5_G:
case NETWORK_3_75_G:
case NETWORK_3_9_G:
return true;
default:
Log.e("GeckoNetworkManager", "Got an unexpected network type!");
return false;
}
}
}

View File

@ -1,161 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.content.Context;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.content.pm.ActivityInfo;
public class GeckoScreenOrientationListener
{
private static final String LOGTAG = "GeckoScreenOrientationListener";
static class OrientationEventListenerImpl extends OrientationEventListener {
public OrientationEventListenerImpl(Context c) {
super(c);
}
@Override
public void onOrientationChanged(int aOrientation) {
GeckoScreenOrientationListener.getInstance().updateScreenOrientation();
}
}
static private GeckoScreenOrientationListener sInstance = null;
// Make sure that any change in dom/base/ScreenOrientation.h happens here too.
static public final short eScreenOrientation_None = 0;
static public final short eScreenOrientation_PortraitPrimary = 1; // PR_BIT(0)
static public final short eScreenOrientation_PortraitSecondary = 2; // PR_BIT(1)
static public final short eScreenOrientation_LandscapePrimary = 4; // PR_BIT(2)
static public final short eScreenOrientation_LandscapeSecondary = 8; // PR_BIT(3)
private short mOrientation;
private OrientationEventListenerImpl mListener = null;
// Whether the listener should be listening to changes.
private boolean mShouldBeListening = false;
// Whether the listener should notify Gecko that a change happened.
private boolean mShouldNotify = false;
private GeckoScreenOrientationListener() {
mListener = new OrientationEventListenerImpl(GeckoApp.mAppContext);
}
public static GeckoScreenOrientationListener getInstance() {
if (sInstance == null) {
sInstance = new GeckoScreenOrientationListener();
}
return sInstance;
}
public void start() {
mShouldBeListening = true;
updateScreenOrientation();
if (mShouldNotify) {
startListening();
}
}
public void stop() {
mShouldBeListening = false;
if (mShouldNotify) {
stopListening();
}
}
public void enableNotifications() {
updateScreenOrientation();
mShouldNotify = true;
if (mShouldBeListening) {
startListening();
}
}
public void disableNotifications() {
mShouldNotify = false;
if (mShouldBeListening) {
stopListening();
}
}
private void startListening() {
mListener.enable();
}
private void stopListening() {
mListener.disable();
}
// NOTE: this is public so OrientationEventListenerImpl can access it.
// Unfortunately, Java doesn't know about friendship.
public void updateScreenOrientation() {
int rotation = GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getRotation();
short previousOrientation = mOrientation;
if (rotation == Surface.ROTATION_0) {
mOrientation = eScreenOrientation_PortraitPrimary;
} else if (rotation == Surface.ROTATION_180) {
mOrientation = eScreenOrientation_PortraitSecondary;
} else if (rotation == Surface.ROTATION_270) {
mOrientation = eScreenOrientation_LandscapeSecondary;
} else if (rotation == Surface.ROTATION_90) {
mOrientation = eScreenOrientation_LandscapePrimary;
} else {
Log.e(LOGTAG, "Unexpected value received! (" + rotation + ")");
return;
}
if (mShouldNotify && mOrientation != previousOrientation) {
GeckoAppShell.sendEventToGecko(new GeckoEvent(mOrientation));
}
}
public short getScreenOrientation() {
return mOrientation;
}
public void lockScreenOrientation(int aOrientation) {
int orientation = 0;
switch (aOrientation) {
case eScreenOrientation_PortraitPrimary:
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
case eScreenOrientation_PortraitSecondary:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
break;
case eScreenOrientation_PortraitPrimary | eScreenOrientation_PortraitSecondary:
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
break;
case eScreenOrientation_LandscapePrimary:
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
break;
case eScreenOrientation_LandscapeSecondary:
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
break;
case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary:
orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
break;
default:
Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")");
}
GeckoApp.mAppContext.setRequestedOrientation(orientation);
updateScreenOrientation();
}
public void unlockScreenOrientation() {
GeckoApp.mAppContext.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
updateScreenOrientation();
}
}

View File

@ -1,777 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
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 = "";
mIMEModeHint = "";
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;
else if (mIMEModeHint.equalsIgnoreCase("numeric"))
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER |
InputType.TYPE_NUMBER_FLAG_SIGNED |
InputType.TYPE_NUMBER_FLAG_DECIMAL;
else if (mIMEModeHint.equalsIgnoreCase("digit"))
outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
else if (mIMEModeHint.equalsIgnoreCase("uppercase"))
outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
else if (mIMEModeHint.equalsIgnoreCase("lowercase"))
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
else if (mIMEModeHint.equalsIgnoreCase("titlecase"))
outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
else if (mIMEModeHint.equalsIgnoreCase("autocapitalized"))
outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
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));
}
// geolocation
public void onLocationChanged(Location location)
{
GeckoAppShell.sendEventToGecko(new GeckoEvent(location));
}
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 mIMEModeHint;
String mIMEActionHint;
boolean mIMELandscapeFS;
// Software rendering
Bitmap mSoftwareBitmap;
ByteBuffer mSoftwareBuffer;
Bitmap mSoftwareBufferCopy;
final SynchronousQueue<Object> mSyncDraws = new SynchronousQueue<Object>();
}

View File

@ -1,227 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#filter substitution
package @ANDROID_PACKAGE_NAME@;
import java.io.*;
import java.util.*;
import org.json.*;
import org.mozilla.gecko.*;
import android.os.*;
import android.content.*;
import android.app.*;
import android.text.*;
import android.util.*;
import android.widget.*;
import android.database.sqlite.*;
import android.database.*;
import android.view.*;
import android.net.Uri;
import android.graphics.*;
public class LauncherShortcuts extends Activity {
private ArrayList <HashMap<String, String>> mWebappsList;
private File mWebappsFolder;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.launch_app_list);
final Intent intent = getIntent();
final String action = intent.getAction();
if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
// Doing it as a background task, as it involves file access
new FetchWebApps().execute();
}
}
public void onListItemClick(int id) {
HashMap<String, String> map = mWebappsList.get(id);
String uri = map.get("uri").toString();
String title = map.get("title").toString();
String appKey = map.get("appKey").toString();
String favicon = map.get("favicon").toString();
File manifestFile = new File(mWebappsFolder, appKey + "/manifest.json");
// Parse the contents into a string
String manifestJson = new String();
try {
BufferedReader in = new BufferedReader(new FileReader(manifestFile));
String line = new String();
while ((line = in.readLine()) != null) {
manifestJson += line;
}
} catch (IOException e) { }
try {
JSONObject manifest = (JSONObject) new JSONTokener(manifestJson).nextValue();
uri += manifest.getString("launch_path");
} catch (JSONException e) { }
Intent shortcutintent = new Intent("org.mozilla.gecko.WEBAPP");
shortcutintent.setClass(this, App.class);
shortcutintent.putExtra("args", "--webapp=" + uri);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutintent);
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int size;
switch (dm.densityDpi) {
case DisplayMetrics.DENSITY_MEDIUM:
size = 48;
break;
case DisplayMetrics.DENSITY_HIGH:
size = 72;
break;
default:
size = 72;
}
Bitmap bitmap = BitmapFactory.decodeFile(favicon);
if (bitmap != null) {
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, scaledBitmap);
}
// Now, return the result to the launcher
setResult(RESULT_OK, intent);
finish();
}
private class FetchWebApps extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... unused) {
mWebappsList = null;
Context context = getApplicationContext();
File home = new File(context.getFilesDir(), "mozilla");
if (!home.exists())
home = new File(context.getExternalFilesDir(null).getPath(), "mozilla");
if (!home.exists())
return null;
File profile = null;
String[] files = home.list();
for (String file : files) {
if (file.endsWith(".default")) {
profile = new File(home, file);
break;
}
}
if (profile == null)
return null;
// Save the folder path to be used during click event
mWebappsFolder = new File(profile, "webapps");
if (!mWebappsFolder.exists())
return null;
File webapps = new File(mWebappsFolder, "webapps.json");
if (!webapps.exists())
return null;
// Parse the contents into a string
String webappsJson = new String();
try {
BufferedReader in = new BufferedReader(new FileReader(webapps));
String line = new String();
while ((line = in.readLine()) != null) {
webappsJson += line;
}
} catch (IOException e) { }
if (webappsJson.length() == 0)
return null;
mWebappsList = new ArrayList<HashMap<String, String>>();
try {
JSONObject webApps = (JSONObject) new JSONTokener(webappsJson).nextValue();
Iterator<Object> appKeys = webApps.keys();
HashMap<String, String> map;
while (appKeys.hasNext()) {
String appKey = appKeys.next().toString();
JSONObject app = webApps.getJSONObject(appKey);
map = new HashMap<String, String>();
map.put("appKey", appKey);
map.put("favicon", mWebappsFolder.getPath() + "/" + appKey + "/icon.png");
map.put("title", app.getString("title"));
map.put("uri", app.getString("appURI"));
mWebappsList.add(map);
}
} catch (JSONException e) {}
return null;
}
@Override
protected void onPostExecute(Void unused) {
if (mWebappsList != null) {
AlertDialog.Builder builder;
if (android.os.Build.VERSION.SDK_INT >= 11) {
builder = new AlertDialog.Builder(LauncherShortcuts.this, AlertDialog.THEME_HOLO_LIGHT);
} else {
builder = new AlertDialog.Builder(LauncherShortcuts.this);
}
builder.setTitle(R.string.launcher_shortcuts_title);
builder.setAdapter(new SimpleAdapter(
LauncherShortcuts.this,
mWebappsList,
R.layout.launch_app_listitem,
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);
finish();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
finish();
}
});
builder.create().show();
} else {
Toast.makeText(LauncherShortcuts.this, R.string.launcher_shortcuts_empty, Toast.LENGTH_LONG).show();
finish();
}
}
}
}

View File

@ -1,170 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/ipc/app/defs.mk
JAVAFILES = \
GeckoApp.java \
GeckoAppShell.java \
GeckoConnectivityReceiver.java \
GeckoEvent.java \
GeckoSurfaceView.java \
GeckoInputConnection.java \
AlertNotification.java \
SurfaceInfo.java \
GeckoBatteryManager.java \
VideoPlayer.java \
GeckoNetworkManager.java \
GeckoScreenOrientationListener.java \
$(NULL)
ifdef MOZ_WEBSMS_BACKEND
JAVAFILES += GeckoSmsManager.java
endif
PROCESSEDJAVAFILES = \
App.java \
Restarter.java \
NotificationHandler.java \
LauncherShortcuts.java \
SmsManager.java \
$(NULL)
ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
MIN_CPU_VERSION=7
else
MIN_CPU_VERSION=5
endif
ifeq (,$(ANDROID_VERSION_CODE))
# increment the version code by 1 so xul fennec will win any compatability ties
ANDROID_VERSION_CODE=$(shell echo `$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10` + 1 | bc)
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)
# 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 \
res/layout/videoplayer.xml \
$(NULL)
RES_VALUES = res/values/colors.xml res/values/themes.xml
JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
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)
RESOURCES=$(RES_LAYOUT) $(RES_VALUES)
RES_DIRS= \
res/layout \
res/values \
$(NULL)
include $(topsrcdir)/config/rules.mk
# 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
$(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_DIRS):
rm -rf $@
$(NSINSTALL) -D $@
RES_DRAWABLE = $(addprefix res/drawable/,$(notdir $(MOZ_ANDROID_DRAWABLES)))
$(RES_DRAWABLE): $(addprefix $(topsrcdir)/,$(MOZ_ANDROID_DRAWABLES))
$(NSINSTALL) -D res/drawable
$(NSINSTALL) $^ res/drawable/
$(RESOURCES): $(RES_DIRS) $(subst res/,$(srcdir)/resources/,$(RESOURCES))
@echo "creating $@"
$(NSINSTALL) $(subst res/,$(srcdir)/resources/,$@) $(dir $@)
R.java: $(MOZ_APP_ICON) $(RES_LAYOUT) $(RES_DRAWABLE) $(RES_VALUES) res/drawable/icon.png res/drawable-hdpi/icon.png AndroidManifest.xml chrome
$(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 $@
libs:: classes.dex
$(INSTALL) classes.dex $(FINAL_TARGET)

View File

@ -1,72 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#filter substitution
package @ANDROID_PACKAGE_NAME@;
import android.app.NotificationManager;
import android.content.Intent;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.net.Uri;
public class NotificationHandler
extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null)
handleIntent(context, intent);
}
protected void handleIntent(Context context, Intent notificationIntent) {
String action = notificationIntent.getAction();
String alertName = "";
String alertCookie = "";
Uri data = notificationIntent.getData();
if (data != null) {
alertName = data.getSchemeSpecificPart();
alertCookie = data.getFragment();
if (alertCookie == null)
alertCookie = "";
}
Log.i("GeckoAppJava", "NotificationHandler.handleIntent\n" +
"- action = '" + action + "'\n" +
"- alertName = '" + alertName + "'\n" +
"- alertCookie = '" + alertCookie + "'");
int notificationID = alertName.hashCode();
Log.i("GeckoAppJava", "Handle notification ID " + notificationID);
if (App.mAppContext != null) {
// This should call the observer, if any
App.mAppContext.handleNotification(action, alertName, alertCookie);
} else {
// The app is not running, just cancel this notification
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(notificationID);
}
if (App.ACTION_ALERT_CLICK.equals(action)) {
// Start or bring to front the main activity
Intent appIntent = new Intent(Intent.ACTION_MAIN);
appIntent.setClassName(context, "@ANDROID_PACKAGE_NAME@.App");
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() + "'" +
", args='" + appIntent.getStringExtra("args") + "'" );
context.startActivity(appIntent);
} catch (ActivityNotFoundException e) {
Log.e("GeckoAppJava", "NotificationHandler Exception: ", e);
}
}
}
}

View File

@ -1,62 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#filter substitution
package @ANDROID_PACKAGE_NAME@;
import android.app.*;
import android.content.*;
import android.util.*;
import android.os.*;
import java.io.*;
import org.mozilla.gecko.GeckoAppShell;
public class Restarter extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i("Restarter", "trying to restart @MOZ_APP_NAME@");
try {
int countdown = 40;
while (GeckoAppShell.checkForGeckoProcs() && --countdown > 0) {
// Wait for the old process to die before we continue
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException ie) {}
}
if (countdown <= 0) {
// if the countdown expired, something is hung
GeckoAppShell.killAnyZombies();
countdown = 10;
// wait for the kill to take effect
while (GeckoAppShell.checkForGeckoProcs() && --countdown > 0) {
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException ie) {}
}
}
} catch (Exception e) {
Log.i("Restarter", e.toString());
}
try {
String action = "android.intent.action.MAIN";
Intent intent = new Intent(action);
intent.setClassName("@ANDROID_PACKAGE_NAME@",
"@ANDROID_PACKAGE_NAME@.App");
Bundle b = getIntent().getExtras();
if (b != null)
intent.putExtras(b);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.i("GeckoAppJava", intent.toString());
startActivity(intent);
} catch (Exception e) {
Log.i("Restarter", e.toString());
}
// Give the new process time to start before we die
GeckoAppShell.waitForAnotherGeckoProc();
System.exit(0);
}
};

View File

@ -1,38 +0,0 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
#ifdef MOZ_WEBSMS_BACKEND
import org.mozilla.gecko.GeckoSmsManager;
#endif
class SmsManager
{
static private ISmsManager sInstance = null;
static public ISmsManager getInstance() {
#ifdef MOZ_WEBSMS_BACKEND
if (sInstance == null) {
sInstance = new GeckoSmsManager();
}
#endif
return sInstance;
}
}
interface ISmsManager
{
public void start();
public void stop();
public void shutdown();
public void send(String aNumber, String aMessage, int aRequestId);
public void getMessage(int aMessageId, int aRequestId);
public void deleteMessage(int aMessageId, int aRequestId);
public void createMessageList(long aStartDate, long aEndDate, String[] aNumbers, int aNumbersCount, int aDeliveryState, boolean aReverse, int aRequestId);
public void getNextMessageInList(int aListId, int aRequestId);
public void clearMessageList(int aListId);
}

View File

@ -1,11 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
public class SurfaceInfo {
public int format;
public int width;
public int height;
}

View File

@ -1,42 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
# special case some locale codes, he and id
# http://code.google.com/p/android/issues/detail?id=3639
AB_rCD = $(if $(filter he, $(AB_CD)),iw,$(if $(filter id, $(AB_CD)),in,$(subst -,-r,$(AB_CD))))
STRINGSPATH = $(call core_abspath,$(call MERGE_FILE,android_strings.dtd))
ifeq (,$(XPI_NAME))
BRANDPATH = $(call core_abspath,$(DEPTH)/dist/bin/chrome/$(AB_CD)/locale/branding/brand.dtd)
else
BRANDPATH = $(call core_abspath,$(DIST)/xpi-stage/$(XPI_NAME)/chrome/$(AB_CD)/locale/branding/brand.dtd)
endif
DEFINES += -DAB_CD=$(AB_CD)
libs realchrome:: ../res/values/strings.xml ;
chrome-%:: AB_CD=$*
chrome-%::
@$(MAKE) ../res/values-$(AB_rCD)/strings.xml AB_CD=$*
%/strings.xml: FORCE
$(NSINSTALL) -D $*
# we don't have branding yet, but we need it. Call it explicitly
@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales realchrome
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(BRANDPATH)" \
-DSTRINGSPATH="$(STRINGSPATH)" \
$(srcdir)/../strings.xml.in \
> $@
include $(topsrcdir)/config/rules.mk

View File

@ -1,25 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!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">

View File

@ -1,9 +0,0 @@
; This Source Code Form is subject to the terms of the Mozilla Public
; License, v. 2.0. If a copy of the MPL was not distributed with this
; file, You can obtain one at http://mozilla.org/MPL/2.0/.
[general]
depth = ../../..
[compare]
dirs = embedding/android

View File

@ -1,5 +0,0 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

View File

@ -1,6 +0,0 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += ['locales']

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,52 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<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>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<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>

View File

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<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>

View File

@ -1,38 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<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>

View File

@ -1,57 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<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>

View File

@ -1,50 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<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>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources>
<color name="splash_background">#000000</color>
<color name="splash_msgfont">#ffffff</color>
<color name="splash_urlfont">#000000</color>
<color name="splash_content">#ffffff</color>
</resources>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<resources>
<style name="GreyTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">@color/splash_background</item>
</style>
</resources>

View File

@ -1,31 +0,0 @@
#filter substitution
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!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>

View File

@ -17,4 +17,3 @@ dirs = mobile mobile/android mobile/android/base
[includes]
toolkit = toolkit/locales/l10n.ini
services_sync = services/sync/locales/l10n.ini
embedding_android = embedding/android/locales/l10n.ini