summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-04-23 15:32:23 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-04-23 15:32:23 -0400
commitabe80cc31d5a40ebed743085011fbcda0c1a9a10 (patch)
treefb3762f06b84745b182af281abb107b95a9fcf01 /mobile/android/geckoview/src
parent63295d0087eb58a6eb34cad324c4c53d1b220491 (diff)
downloadUXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar
UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar.gz
UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar.lz
UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.tar.xz
UXP-abe80cc31d5a40ebed743085011fbcda0c1a9a10.zip
Issue #1053 - Drop support Android and remove Fennec - Part 1a: Remove mobile/android
Diffstat (limited to 'mobile/android/geckoview/src')
-rw-r--r--mobile/android/geckoview/src/main/AndroidManifest.xml39
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/AlarmReceiver.java42
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/AndroidGamepadManager.java425
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java169
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/ContextGetter.java15
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java474
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java503
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java410
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java2235
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java202
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java1589
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableClient.java33
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableListener.java43
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java27
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java1060
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoNetworkManager.java491
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java1002
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfileDirectories.java230
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java423
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSharedPrefs.java318
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java677
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java736
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewChrome.java81
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewContent.java56
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewFragment.java52
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputConnectionListener.java25
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputMethods.java76
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/NSSBridge.java55
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/NotificationListener.java17
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/PrefsHelper.java308
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/SysInfo.java237
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/TouchEventInterceptor.java14
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/JNITarget.java14
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/ReflectionTarget.java18
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/RobocopTarget.java15
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WebRTCJNITarget.java14
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WrapForJNI.java51
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BitmapUtils.java290
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImage.java94
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImageGLInfo.java35
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java605
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FloatSize.java54
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FullScreenState.java12
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java694
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java282
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/IntSize.java89
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java275
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java711
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java300
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/Overscroll.java21
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/OverscrollEdgeEffect.java162
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java38
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java15
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java73
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PointUtils.java51
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java29
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RectUtils.java126
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RenderTask.java80
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/StackScroller.java695
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/SurfaceTextureListener.java38
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java28
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/ByteBufferInputStream.java64
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/DirectBufferAllocator.java52
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java549
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/JNIObject.java11
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeReference.java13
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeZip.java84
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java134
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionBlock.java133
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/Permissions.java210
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionsHelper.java32
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/ByteBufferInputStream.java38
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/MatrixBlobCursor.java366
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridge.java387
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridgeException.java18
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandler.java11
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandlerMap.java24
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityUtils.java72
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BundleEventListener.java25
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java117
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContextUtils.java51
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/DateUtil.java55
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/EventCallback.java29
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FileUtils.java259
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FloatUtils.java43
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GamepadUtils.java140
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java76
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoEventListener.java14
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoJarReader.java261
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoRequest.java94
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareCodecCapabilityUtils.java169
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java117
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INIParser.java176
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INISection.java123
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IOUtils.java129
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/InputOptionsUtils.java45
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java109
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/JSONUtils.java69
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/MenuUtils.java33
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeEventListener.java23
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSContainer.java37
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSObject.java533
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NetworkUtils.java177
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NonEvictingLruCache.java44
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/PrefUtils.java70
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java155
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/RawResource.java52
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java293
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java247
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UIAsyncTask.java121
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UUIDUtil.java19
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WeakReferenceHandler.java27
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WindowUtils.java59
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffix.java121
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffixPatterns.java117
-rw-r--r--mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java147
-rw-r--r--mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java267
-rw-r--r--mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java192
118 files changed, 0 insertions, 24006 deletions
diff --git a/mobile/android/geckoview/src/main/AndroidManifest.xml b/mobile/android/geckoview/src/main/AndroidManifest.xml
deleted file mode 100644
index 4e2aaf447..000000000
--- a/mobile/android/geckoview/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="org.mozilla.geckoview">
-
- <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
- <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.RECEIVE_BOOT_COMPLETED"/>
- <!-- READ_EXTERNAL_STORAGE was added in API 16, and is only enforced in API
- 19+. We declare it so that the bouncer APK and the main APK have the
- same set of permissions. -->
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
- <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
-
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
- <uses-permission android:name="android.permission.VIBRATE"/>
-
- <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"/>
-
- <!--#ifdef MOZ_WEBRTC-->
- <!--<uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
- <!--<uses-feature android:name="android.hardware.audio.low_latency" android:required="false"/>-->
- <!--<uses-feature android:name="android.hardware.camera.any" android:required="false"/>-->
- <!--<uses-feature android:name="android.hardware.microphone" android:required="false"/>-->
- <!--#endif-->
-
- <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"/>
-
- <!-- App requires OpenGL ES 2.0 -->
- <uses-feature android:glEsVersion="0x00020000" android:required="true" />
-
-</manifest>
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AlarmReceiver.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AlarmReceiver.java
deleted file mode 100644
index a098113fa..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AlarmReceiver.java
+++ /dev/null
@@ -1,42 +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 org.mozilla.gecko.annotation.WrapForJNI;
-
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.content.BroadcastReceiver;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.util.Log;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-public class AlarmReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- PowerManager powerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- final WakeLock wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, "GeckoAlarm");
- wakeLock.acquire();
-
- AlarmReceiver.notifyAlarmFired();
- TimerTask releaseLockTask = new TimerTask() {
- @Override
- public void run() {
- wakeLock.release();
- }
- };
- Timer timer = new Timer();
- // 5 seconds ought to be enough for anybody
- timer.schedule(releaseLockTask, 5 * 1000);
- }
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- private static native void notifyAlarmFired();
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AndroidGamepadManager.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AndroidGamepadManager.java
deleted file mode 100644
index 54e1b0931..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/AndroidGamepadManager.java
+++ /dev/null
@@ -1,425 +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.util.ArrayList;
-import java.util.List;
-import java.util.Timer;
-import java.util.TimerTask;
-
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.util.GamepadUtils;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.content.Context;
-import android.hardware.input.InputManager;
-import android.util.SparseArray;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-
-public class AndroidGamepadManager {
- // This is completely arbitrary.
- private static final float TRIGGER_PRESSED_THRESHOLD = 0.25f;
- private static final long POLL_TIMER_PERIOD = 1000; // milliseconds
-
- private static enum Axis {
- X(MotionEvent.AXIS_X),
- Y(MotionEvent.AXIS_Y),
- Z(MotionEvent.AXIS_Z),
- RZ(MotionEvent.AXIS_RZ);
-
- public final int axis;
-
- private Axis(int axis) {
- this.axis = axis;
- }
- }
-
- // A list of gamepad button mappings. Axes are determined at
- // runtime, as they vary by Android version.
- private static enum Trigger {
- Left(6),
- Right(7);
-
- public final int button;
-
- private Trigger(int button) {
- this.button = button;
- }
- }
-
- private static final int FIRST_DPAD_BUTTON = 12;
- // A list of axis number, gamepad button mappings for negative, positive.
- // Button mappings are added to FIRST_DPAD_BUTTON.
- private static enum DpadAxis {
- UpDown(MotionEvent.AXIS_HAT_Y, 0, 1),
- LeftRight(MotionEvent.AXIS_HAT_X, 2, 3);
-
- public final int axis;
- public final int negativeButton;
- public final int positiveButton;
-
- private DpadAxis(int axis, int negativeButton, int positiveButton) {
- this.axis = axis;
- this.negativeButton = negativeButton;
- this.positiveButton = positiveButton;
- }
- }
-
- private static enum Button {
- A(KeyEvent.KEYCODE_BUTTON_A),
- B(KeyEvent.KEYCODE_BUTTON_B),
- X(KeyEvent.KEYCODE_BUTTON_X),
- Y(KeyEvent.KEYCODE_BUTTON_Y),
- L1(KeyEvent.KEYCODE_BUTTON_L1),
- R1(KeyEvent.KEYCODE_BUTTON_R1),
- L2(KeyEvent.KEYCODE_BUTTON_L2),
- R2(KeyEvent.KEYCODE_BUTTON_R2),
- SELECT(KeyEvent.KEYCODE_BUTTON_SELECT),
- START(KeyEvent.KEYCODE_BUTTON_START),
- THUMBL(KeyEvent.KEYCODE_BUTTON_THUMBL),
- THUMBR(KeyEvent.KEYCODE_BUTTON_THUMBR),
- DPAD_UP(KeyEvent.KEYCODE_DPAD_UP),
- DPAD_DOWN(KeyEvent.KEYCODE_DPAD_DOWN),
- DPAD_LEFT(KeyEvent.KEYCODE_DPAD_LEFT),
- DPAD_RIGHT(KeyEvent.KEYCODE_DPAD_RIGHT);
-
- public final int button;
-
- private Button(int button) {
- this.button = button;
- }
- }
-
- private static class Gamepad {
- // ID from GamepadService
- public int id;
- // Retain axis state so we can determine changes.
- public float axes[];
- public boolean dpad[];
- public int triggerAxes[];
- public float triggers[];
-
- public Gamepad(int serviceId, int deviceId) {
- id = serviceId;
- axes = new float[Axis.values().length];
- dpad = new boolean[4];
- triggers = new float[2];
-
- InputDevice device = InputDevice.getDevice(deviceId);
- if (device != null) {
- // LTRIGGER/RTRIGGER don't seem to be exposed on older
- // versions of Android.
- if (device.getMotionRange(MotionEvent.AXIS_LTRIGGER) != null && device.getMotionRange(MotionEvent.AXIS_RTRIGGER) != null) {
- triggerAxes = new int[]{MotionEvent.AXIS_LTRIGGER,
- MotionEvent.AXIS_RTRIGGER};
- } else if (device.getMotionRange(MotionEvent.AXIS_BRAKE) != null && device.getMotionRange(MotionEvent.AXIS_GAS) != null) {
- triggerAxes = new int[]{MotionEvent.AXIS_BRAKE,
- MotionEvent.AXIS_GAS};
- } else {
- triggerAxes = null;
- }
- }
- }
- }
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- private static native void onGamepadChange(int id, boolean added);
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- private static native void onButtonChange(int id, int button, boolean pressed, float value);
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- private static native void onAxisChange(int id, boolean[] valid, float[] values);
-
- private static boolean sStarted;
- private static final SparseArray<Gamepad> sGamepads = new SparseArray<>();
- private static final SparseArray<List<KeyEvent>> sPendingGamepads = new SparseArray<>();
- private static InputManager.InputDeviceListener sListener;
- private static Timer sPollTimer;
-
- private AndroidGamepadManager() {
- }
-
- @WrapForJNI
- private static void start() {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- doStart();
- }
- });
- }
-
- /* package */ static void doStart() {
- ThreadUtils.assertOnUiThread();
- if (!sStarted) {
- scanForGamepads();
- addDeviceListener();
- sStarted = true;
- }
- }
-
- @WrapForJNI
- private static void stop() {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- doStop();
- }
- });
- }
-
- /* package */ static void doStop() {
- ThreadUtils.assertOnUiThread();
- if (sStarted) {
- removeDeviceListener();
- sPendingGamepads.clear();
- sGamepads.clear();
- sStarted = false;
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void onGamepadAdded(final int device_id, final int service_id) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- handleGamepadAdded(device_id, service_id);
- }
- });
- }
-
- /* package */ static void handleGamepadAdded(int deviceId, int serviceId) {
- ThreadUtils.assertOnUiThread();
- if (!sStarted) {
- return;
- }
-
- final List<KeyEvent> pending = sPendingGamepads.get(deviceId);
- if (pending == null) {
- removeGamepad(deviceId);
- return;
- }
-
- sPendingGamepads.remove(deviceId);
- sGamepads.put(deviceId, new Gamepad(serviceId, deviceId));
- // Handle queued KeyEvents
- for (KeyEvent ev : pending) {
- handleKeyEvent(ev);
- }
- }
-
- private static float deadZone(MotionEvent ev, int axis) {
- if (GamepadUtils.isValueInDeadZone(ev, axis)) {
- return 0.0f;
- }
- return ev.getAxisValue(axis);
- }
-
- private static void mapDpadAxis(Gamepad gamepad,
- boolean pressed,
- float value,
- int which) {
- if (pressed != gamepad.dpad[which]) {
- gamepad.dpad[which] = pressed;
- onButtonChange(gamepad.id, FIRST_DPAD_BUTTON + which, pressed, Math.abs(value));
- }
- }
-
- public static boolean handleMotionEvent(MotionEvent ev) {
- ThreadUtils.assertOnUiThread();
- if (!sStarted) {
- return false;
- }
-
- final Gamepad gamepad = sGamepads.get(ev.getDeviceId());
- if (gamepad == null) {
- // Not a device we care about.
- return false;
- }
-
- // First check the analog stick axes
- boolean[] valid = new boolean[Axis.values().length];
- float[] axes = new float[Axis.values().length];
- boolean anyValidAxes = false;
- for (Axis axis : Axis.values()) {
- float value = deadZone(ev, axis.axis);
- int i = axis.ordinal();
- if (value != gamepad.axes[i]) {
- axes[i] = value;
- gamepad.axes[i] = value;
- valid[i] = true;
- anyValidAxes = true;
- }
- }
- if (anyValidAxes) {
- // Send an axismove event.
- onAxisChange(gamepad.id, valid, axes);
- }
-
- // Map triggers to buttons.
- if (gamepad.triggerAxes != null) {
- for (Trigger trigger : Trigger.values()) {
- int i = trigger.ordinal();
- int axis = gamepad.triggerAxes[i];
- float value = deadZone(ev, axis);
- if (value != gamepad.triggers[i]) {
- gamepad.triggers[i] = value;
- boolean pressed = value > TRIGGER_PRESSED_THRESHOLD;
- onButtonChange(gamepad.id, trigger.button, pressed, value);
- }
- }
- }
- // Map d-pad to buttons.
- for (DpadAxis dpadaxis : DpadAxis.values()) {
- float value = deadZone(ev, dpadaxis.axis);
- mapDpadAxis(gamepad, value < 0.0f, value, dpadaxis.negativeButton);
- mapDpadAxis(gamepad, value > 0.0f, value, dpadaxis.positiveButton);
- }
- return true;
- }
-
- public static boolean handleKeyEvent(KeyEvent ev) {
- ThreadUtils.assertOnUiThread();
- if (!sStarted) {
- return false;
- }
-
- int deviceId = ev.getDeviceId();
- final List<KeyEvent> pendingGamepad = sPendingGamepads.get(deviceId);
- if (pendingGamepad != null) {
- // Queue up key events for pending devices.
- pendingGamepad.add(ev);
- return true;
- }
-
- if (sGamepads.get(deviceId) == null) {
- InputDevice device = ev.getDevice();
- if (device != null &&
- (device.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
- // This is a gamepad we haven't seen yet.
- addGamepad(device);
- sPendingGamepads.get(deviceId).add(ev);
- return true;
- }
- // Not a device we care about.
- return false;
- }
-
- int key = -1;
- for (Button button : Button.values()) {
- if (button.button == ev.getKeyCode()) {
- key = button.ordinal();
- break;
- }
- }
- if (key == -1) {
- // Not a key we know how to handle.
- return false;
- }
- if (ev.getRepeatCount() > 0) {
- // We would handle this key, but we're not interested in
- // repeats. Eat it.
- return true;
- }
-
- Gamepad gamepad = sGamepads.get(deviceId);
- boolean pressed = ev.getAction() == KeyEvent.ACTION_DOWN;
- onButtonChange(gamepad.id, key, pressed, pressed ? 1.0f : 0.0f);
- return true;
- }
-
- private static void scanForGamepads() {
- int[] deviceIds = InputDevice.getDeviceIds();
- if (deviceIds == null) {
- return;
- }
- for (int i = 0; i < deviceIds.length; i++) {
- InputDevice device = InputDevice.getDevice(deviceIds[i]);
- if (device == null) {
- continue;
- }
- if ((device.getSources() & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD) {
- continue;
- }
- addGamepad(device);
- }
- }
-
- private static void addGamepad(InputDevice device) {
- sPendingGamepads.put(device.getId(), new ArrayList<KeyEvent>());
- onGamepadChange(device.getId(), true);
- }
-
- private static void removeGamepad(int deviceId) {
- Gamepad gamepad = sGamepads.get(deviceId);
- onGamepadChange(gamepad.id, false);
- sGamepads.remove(deviceId);
- }
-
- private static void addDeviceListener() {
- if (Versions.preJB) {
- // Poll known gamepads to see if they've disappeared.
- sPollTimer = new Timer();
- sPollTimer.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- for (int i = 0; i < sGamepads.size(); ++i) {
- final int deviceId = sGamepads.keyAt(i);
- if (InputDevice.getDevice(deviceId) == null) {
- removeGamepad(deviceId);
- }
- }
- }
- }, POLL_TIMER_PERIOD, POLL_TIMER_PERIOD);
- return;
- }
- sListener = new InputManager.InputDeviceListener() {
- @Override
- public void onInputDeviceAdded(int deviceId) {
- InputDevice device = InputDevice.getDevice(deviceId);
- if (device == null) {
- return;
- }
- if ((device.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
- addGamepad(device);
- }
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- if (sPendingGamepads.get(deviceId) != null) {
- // Got removed before Gecko's ack reached us.
- // gamepadAdded will deal with it.
- sPendingGamepads.remove(deviceId);
- return;
- }
- if (sGamepads.get(deviceId) != null) {
- removeGamepad(deviceId);
- }
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- }
- };
- ((InputManager)GeckoAppShell.getContext().getSystemService(Context.INPUT_SERVICE)).registerInputDeviceListener(sListener, ThreadUtils.getUiHandler());
- }
-
- private static void removeDeviceListener() {
- if (Versions.preJB) {
- if (sPollTimer != null) {
- sPollTimer.cancel();
- sPollTimer = null;
- }
- return;
- }
- ((InputManager)GeckoAppShell.getContext().getSystemService(Context.INPUT_SERVICE)).unregisterInputDeviceListener(sListener);
- sListener = null;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
deleted file mode 100644
index c4f64fd3d..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java
+++ /dev/null
@@ -1,169 +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 org.mozilla.gecko.util.ActivityUtils;
-import org.mozilla.gecko.util.HardwareUtils;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.RectF;
-import android.hardware.SensorEventListener;
-import android.location.LocationListener;
-import android.view.View;
-import android.widget.AbsoluteLayout;
-
-public class BaseGeckoInterface implements GeckoAppShell.GeckoInterface {
- // Bug 908744: Implement GeckoEventListener
- // Bug 908752: Implement SensorEventListener
- // Bug 908755: Implement LocationListener
- // Bug 908756: Implement Tabs.OnTabsChangedListener
- // Bug 908760: Implement GeckoEventResponder
-
- private final Context mContext;
- private GeckoProfile mProfile;
- private final EventDispatcher eventDispatcher;
-
- public BaseGeckoInterface(Context context) {
- mContext = context;
- eventDispatcher = new EventDispatcher();
- }
-
- @Override
- public EventDispatcher getAppEventDispatcher() {
- return eventDispatcher;
- }
-
- @Override
- public GeckoProfile getProfile() {
- // Fall back to default profile if we didn't load a specific one
- if (mProfile == null) {
- mProfile = GeckoProfile.get(mContext);
- }
- return mProfile;
- }
-
- @Override
- public Activity getActivity() {
- return (Activity)mContext;
- }
-
- @Override
- public String getDefaultUAString() {
- return HardwareUtils.isTablet() ? AppConstants.USER_AGENT_FENNEC_TABLET :
- AppConstants.USER_AGENT_FENNEC_MOBILE;
- }
-
- // Bug 908775: Implement this
- @Override
- public void doRestart() {}
-
- @Override
- public void setFullScreen(final boolean fullscreen) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- ActivityUtils.setFullScreen(getActivity(), fullscreen);
- }
- });
- }
-
- // Bug 908779: Implement this
- @Override
- public void addPluginView(final View view) {}
-
- // Bug 908781: Implement this
- @Override
- public void removePluginView(final View view) {}
-
- @Override
- public void enableOrientationListener() {}
-
- @Override
- public void disableOrientationListener() {}
-
- // Bug 908786: Implement this
- @Override
- public void addAppStateListener(GeckoAppShell.AppStateListener listener) {}
-
- // Bug 908787: Implement this
- @Override
- public void removeAppStateListener(GeckoAppShell.AppStateListener listener) {}
-
- // Bug 908789: Implement this
- @Override
- public void notifyWakeLockChanged(String topic, String state) {}
-
- @Override
- public boolean areTabsShown() {
- return false;
- }
-
- // Bug 908791: Implement this
- @Override
- public AbsoluteLayout getPluginContainer() {
- return null;
- }
-
- @Override
- public void notifyCheckUpdateResult(String result) {
- GeckoAppShell.notifyObservers("Update:CheckResult", result);
- }
-
- // Bug 908792: Implement this
- @Override
- public void invalidateOptionsMenu() {}
-
- @Override
- public void createShortcut(String title, String URI) {
- // By default, do nothing.
- }
-
- @Override
- public void checkUriVisited(String uri) {
- // By default, no URIs are considered visited.
- }
-
- @Override
- public void markUriVisited(final String uri) {
- // By default, no URIs are marked as visited.
- }
-
- @Override
- public void setUriTitle(final String uri, final String title) {
- // By default, no titles are associated with URIs.
- }
-
- @Override
- public void setAccessibilityEnabled(boolean enabled) {
- // By default, take no action when accessibility is toggled on or off.
- }
-
- @Override
- public boolean openUriExternal(String targetURI, String mimeType, String packageName, String className, String action, String title) {
- // By default, never open external URIs.
- return false;
- }
-
- @Override
- public String[] getHandlersForMimeType(String mimeType, String action) {
- // By default, offer no handlers for any MIME type.
- return new String[] {};
- }
-
- @Override
- public String[] getHandlersForURL(String url, String action) {
- // By default, offer no handlers for any URL.
- return new String[] {};
- }
-
- @Override
- public String getDefaultChromeURI() {
- // By default, use the GeckoView-specific chrome URI.
- return "chrome://browser/content/geckoview.xul";
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/ContextGetter.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/ContextGetter.java
deleted file mode 100644
index 315854633..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/ContextGetter.java
+++ /dev/null
@@ -1,15 +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.content.Context;
-import android.content.SharedPreferences;
-
-public interface ContextGetter {
- Context getContext();
- SharedPreferences getSharedPreferences();
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
deleted file mode 100644
index 0c8eeff9e..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/CrashHandler.java
+++ /dev/null
@@ -1,474 +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.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.Arrays;
-import java.util.UUID;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.util.Log;
-
-public class CrashHandler implements Thread.UncaughtExceptionHandler {
-
- private static final String LOGTAG = "GeckoCrashHandler";
- private static final Thread MAIN_THREAD = Thread.currentThread();
- private static final String DEFAULT_SERVER_URL =
- "https://crash-reports.mozilla.com/submit?id=%1$s&version=%2$s&buildid=%3$s";
-
- // Context for getting device information
- protected final Context appContext;
- // Thread that this handler applies to, or null for a global handler
- protected final Thread handlerThread;
- protected final Thread.UncaughtExceptionHandler systemUncaughtHandler;
-
- protected boolean crashing;
- protected boolean unregistered;
-
- /**
- * Get the root exception from the 'cause' chain of an exception.
- *
- * @param exc An exception
- * @return The root exception
- */
- public static Throwable getRootException(Throwable exc) {
- for (Throwable cause = exc; cause != null; cause = cause.getCause()) {
- exc = cause;
- }
- return exc;
- }
-
- /**
- * Get the standard stack trace string of an exception.
- *
- * @param exc An exception
- * @return The exception stack trace.
- */
- public static String getExceptionStackTrace(final Throwable exc) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- exc.printStackTrace(pw);
- pw.flush();
- return sw.toString();
- }
-
- /**
- * Terminate the current process.
- */
- public static void terminateProcess() {
- Process.killProcess(Process.myPid());
- }
-
- /**
- * Create and register a CrashHandler for all threads and thread groups.
- */
- public CrashHandler() {
- this((Context) null);
- }
-
- /**
- * Create and register a CrashHandler for all threads and thread groups.
- *
- * @param appContext A Context for retrieving application information.
- */
- public CrashHandler(final Context appContext) {
- this.appContext = appContext;
- this.handlerThread = null;
- this.systemUncaughtHandler = Thread.getDefaultUncaughtExceptionHandler();
- Thread.setDefaultUncaughtExceptionHandler(this);
- }
-
- /**
- * Create and register a CrashHandler for a particular thread.
- *
- * @param thread A thread to register the CrashHandler
- */
- public CrashHandler(final Thread thread) {
- this(thread, null);
- }
-
- /**
- * Create and register a CrashHandler for a particular thread.
- *
- * @param thread A thread to register the CrashHandler
- * @param appContext A Context for retrieving application information.
- */
- public CrashHandler(final Thread thread, final Context appContext) {
- this.appContext = appContext;
- this.handlerThread = thread;
- this.systemUncaughtHandler = thread.getUncaughtExceptionHandler();
- thread.setUncaughtExceptionHandler(this);
- }
-
- /**
- * Unregister this CrashHandler for exception handling.
- */
- public void unregister() {
- unregistered = true;
-
- // Restore the previous handler if we are still the topmost handler.
- // If not, we are part of a chain of handlers, and we cannot just restore the previous
- // handler, because that would replace whatever handler that's above us in the chain.
-
- if (handlerThread != null) {
- if (handlerThread.getUncaughtExceptionHandler() == this) {
- handlerThread.setUncaughtExceptionHandler(systemUncaughtHandler);
- }
- } else {
- if (Thread.getDefaultUncaughtExceptionHandler() == this) {
- Thread.setDefaultUncaughtExceptionHandler(systemUncaughtHandler);
- }
- }
- }
-
- /**
- * Record an exception stack in logs.
- *
- * @param thread The exception thread
- * @param exc An exception
- */
- public static void logException(final Thread thread, final Throwable exc) {
- try {
- Log.e(LOGTAG, ">>> REPORTING UNCAUGHT EXCEPTION FROM THREAD "
- + thread.getId() + " (\"" + thread.getName() + "\")", exc);
-
- if (MAIN_THREAD != thread) {
- Log.e(LOGTAG, "Main thread (" + MAIN_THREAD.getId() + ") stack:");
- for (StackTraceElement ste : MAIN_THREAD.getStackTrace()) {
- Log.e(LOGTAG, " " + ste.toString());
- }
- }
- } catch (final Throwable e) {
- // If something throws here, we want to continue to report the exception,
- // so we catch all exceptions and ignore them.
- }
- }
-
- private static long getCrashTime() {
- return System.currentTimeMillis() / 1000;
- }
-
- private static long getStartupTime() {
- // Process start time is also the proc file modified time.
- final long uptimeMins = (new File("/proc/self/cmdline")).lastModified();
- if (uptimeMins == 0L) {
- return getCrashTime();
- }
- return uptimeMins / 1000;
- }
-
- private static String getJavaPackageName() {
- return CrashHandler.class.getPackage().getName();
- }
-
- private static String getProcessName() {
- try {
- final FileReader reader = new FileReader("/proc/self/cmdline");
- final char[] buffer = new char[64];
- try {
- if (reader.read(buffer) > 0) {
- // cmdline is delimited by '\0', and we want the first token.
- final int nul = Arrays.asList(buffer).indexOf('\0');
- return (new String(buffer, 0, nul < 0 ? buffer.length : nul)).trim();
- }
- } finally {
- reader.close();
- }
- } catch (final IOException e) {
- }
-
- return null;
- }
-
- protected String getAppPackageName() {
- final Context context = getAppContext();
-
- if (context != null) {
- return context.getPackageName();
- }
-
- // Package name is also the process name in most cases.
- String processName = getProcessName();
- if (processName != null) {
- return processName;
- }
-
- // Fallback to using CrashHandler's package name.
- return getJavaPackageName();
- }
-
- protected Context getAppContext() {
- return appContext;
- }
-
- /**
- * Get the crash "extras" to be reported.
- *
- * @param thread The exception thread
- * @param exc An exception
- * @return "Extras" in the from of a Bundle
- */
- protected Bundle getCrashExtras(final Thread thread, final Throwable exc) {
- final Context context = getAppContext();
- final Bundle extras = new Bundle();
- final String pkgName = getAppPackageName();
- final String processName = getProcessName();
-
- extras.putString("ProductName", pkgName);
- extras.putLong("CrashTime", getCrashTime());
- extras.putLong("StartupTime", getStartupTime());
- extras.putString("AndroidProcessName", getProcessName());
-
- if (context != null) {
- final PackageManager pkgMgr = context.getPackageManager();
- try {
- final PackageInfo pkgInfo = pkgMgr.getPackageInfo(pkgName, 0);
- extras.putString("Version", pkgInfo.versionName);
- extras.putInt("BuildID", pkgInfo.versionCode);
- extras.putLong("InstallTime", pkgInfo.lastUpdateTime / 1000);
- } catch (final PackageManager.NameNotFoundException e) {
- Log.i(LOGTAG, "Error getting package info", e);
- }
- }
-
- extras.putString("JavaStackTrace", getExceptionStackTrace(exc));
- return extras;
- }
-
- /**
- * Get the crash minidump content to be reported.
- *
- * @param thread The exception thread
- * @param exc An exception
- * @return Minidump content
- */
- protected byte[] getCrashDump(final Thread thread, final Throwable exc) {
- return new byte[0]; // No minidump.
- }
-
- protected static String normalizeUrlString(final String str) {
- if (str == null) {
- return "";
- }
- return Uri.encode(str);
- }
-
- /**
- * Get the server URL to send the crash report to.
- *
- * @param extras The crash extras Bundle
- */
- protected String getServerUrl(final Bundle extras) {
- return String.format(DEFAULT_SERVER_URL,
- normalizeUrlString(extras.getString("ProductID")),
- normalizeUrlString(extras.getString("Version")),
- normalizeUrlString(extras.getString("BuildID")));
- }
-
- /**
- * Launch the crash reporter activity that sends the crash report to the server.
- *
- * @param dumpFile Path for the minidump file
- * @param extraFile Path for the crash extra file
- * @return Whether the crash reporter was successfully launched
- */
- protected boolean launchCrashReporter(final String dumpFile, final String extraFile) {
- try {
- final Context context = getAppContext();
- final String javaPkg = getJavaPackageName();
- final String pkg = getAppPackageName();
- final String component = javaPkg + ".CrashReporter";
- final String action = javaPkg + ".reportCrash";
- final ProcessBuilder pb;
-
- if (context != null) {
- final Intent intent = new Intent(action);
- intent.setComponent(new ComponentName(pkg, component));
- intent.putExtra("minidumpPath", dumpFile);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
- return true;
- }
-
- // Avoid AppConstants dependency for SDK version constants,
- // because CrashHandler could be used outside of Fennec code.
- if (Build.VERSION.SDK_INT < 17) {
- pb = new ProcessBuilder(
- "/system/bin/am", "start",
- "-a", action,
- "-n", pkg + '/' + component,
- "--es", "minidumpPath", dumpFile);
- } else {
- pb = new ProcessBuilder(
- "/system/bin/am", "start",
- "--user", /* USER_CURRENT_OR_SELF */ "-3",
- "-a", action,
- "-n", pkg + '/' + component,
- "--es", "minidumpPath", dumpFile);
- }
-
- pb.start().waitFor();
-
- } catch (final IOException e) {
- Log.e(LOGTAG, "Error launching crash reporter", e);
- return false;
-
- } catch (final InterruptedException e) {
- Log.i(LOGTAG, "Interrupted while waiting to launch crash reporter", e);
- // Fall-through
- }
- return true;
- }
-
- /**
- * Report an exception to Socorro.
- *
- * @param thread The exception thread
- * @param exc An exception
- * @return Whether the exception was successfully reported
- */
- protected boolean reportException(final Thread thread, final Throwable exc) {
- final Context context = getAppContext();
- final String id = UUID.randomUUID().toString();
-
- // Use the cache directory under the app directory to store crash files.
- final File dir;
- if (context != null) {
- dir = context.getCacheDir();
- } else {
- dir = new File("/data/data/" + getAppPackageName() + "/cache");
- }
-
- dir.mkdirs();
- if (!dir.exists()) {
- return false;
- }
-
- final File dmpFile = new File(dir, id + ".dmp");
- final File extraFile = new File(dir, id + ".extra");
-
- try {
- // Write out minidump file as binary.
-
- final byte[] minidump = getCrashDump(thread, exc);
- final FileOutputStream dmpStream = new FileOutputStream(dmpFile);
- try {
- dmpStream.write(minidump);
- } finally {
- dmpStream.close();
- }
-
- } catch (final IOException e) {
- Log.e(LOGTAG, "Error writing minidump file", e);
- return false;
- }
-
- try {
- // Write out crash extra file as text.
-
- final Bundle extras = getCrashExtras(thread, exc);
- final String url = getServerUrl(extras);
- extras.putString("ServerURL", url);
-
- final BufferedWriter extraWriter = new BufferedWriter(new FileWriter(extraFile));
- try {
- for (String key : extras.keySet()) {
- // Each extra line is in the format, key=value, with newlines escaped.
- extraWriter.write(key);
- extraWriter.write('=');
- extraWriter.write(String.valueOf(extras.get(key)).replace("\n", "\\n"));
- extraWriter.write('\n');
- }
- } finally {
- extraWriter.close();
- }
-
- } catch (final IOException e) {
- Log.e(LOGTAG, "Error writing extra file", e);
- return false;
- }
-
- return launchCrashReporter(dmpFile.getAbsolutePath(), extraFile.getAbsolutePath());
- }
-
- /**
- * Implements the default behavior for handling uncaught exceptions.
- *
- * @param thread The exception thread
- * @param exc An uncaught exception
- */
- @Override
- public void uncaughtException(Thread thread, Throwable exc) {
- if (this.crashing) {
- // Prevent possible infinite recusions.
- return;
- }
-
- if (thread == null) {
- // Gecko may pass in null for thread to denote the current thread.
- thread = Thread.currentThread();
- }
-
- try {
- if (!this.unregistered) {
- // Only process crash ourselves if we have not been unregistered.
-
- this.crashing = true;
- exc = getRootException(exc);
- logException(thread, exc);
-
- if (reportException(thread, exc)) {
- // Reporting succeeded; we can terminate our process now.
- return;
- }
- }
-
- if (systemUncaughtHandler != null) {
- // Follow the chain of uncaught handlers.
- systemUncaughtHandler.uncaughtException(thread, exc);
- }
- } finally {
- terminateProcess();
- }
- }
-
- public static CrashHandler createDefaultCrashHandler(final Context context) {
- return new CrashHandler(context) {
- @Override
- protected Bundle getCrashExtras(final Thread thread, final Throwable exc) {
- final Bundle extras = super.getCrashExtras(thread, exc);
-
- extras.putString("ProductName", AppConstants.MOZ_APP_BASENAME);
- extras.putString("ProductID", AppConstants.MOZ_APP_ID);
- extras.putString("Version", AppConstants.MOZ_APP_VERSION);
- extras.putString("BuildID", AppConstants.MOZ_APP_BUILDID);
- extras.putString("Vendor", AppConstants.MOZ_APP_VENDOR);
- extras.putString("ReleaseChannel", AppConstants.MOZ_UPDATE_CHANNEL);
- return extras;
- }
-
- @Override
- public boolean reportException(final Thread thread, final Throwable exc) {
- return false;
- }
- };
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
deleted file mode 100644
index 6c4e67b43..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/EventDispatcher.java
+++ /dev/null
@@ -1,503 +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 org.mozilla.gecko.annotation.ReflectionTarget;
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.util.BundleEventListener;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.GeckoEventListener;
-import org.mozilla.gecko.util.NativeEventListener;
-import org.mozilla.gecko.util.NativeJSContainer;
-import org.mozilla.gecko.util.NativeJSObject;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-@RobocopTarget
-public final class EventDispatcher {
- private static final String LOGTAG = "GeckoEventDispatcher";
- /* package */ static final String GUID = "__guid__";
- private static final String STATUS_ERROR = "error";
- private static final String STATUS_SUCCESS = "success";
-
- private static final EventDispatcher INSTANCE = new EventDispatcher();
-
- /**
- * The capacity of a HashMap is rounded up to the next power-of-2. Every time the size
- * of the map goes beyond 75% of the capacity, the map is rehashed. Therefore, to
- * empirically determine the initial capacity that avoids rehashing, we need to
- * determine the initial size, divide it by 75%, and round up to the next power-of-2.
- */
- private static final int DEFAULT_GECKO_NATIVE_EVENTS_COUNT = 0; // Default for HashMap
- private static final int DEFAULT_GECKO_JSON_EVENTS_COUNT = 256; // Empirically measured
- private static final int DEFAULT_UI_EVENTS_COUNT = 0; // Default for HashMap
- private static final int DEFAULT_BACKGROUND_EVENTS_COUNT = 0; // Default for HashMap
-
- private final Map<String, List<NativeEventListener>> mGeckoThreadNativeListeners =
- new HashMap<String, List<NativeEventListener>>(DEFAULT_GECKO_NATIVE_EVENTS_COUNT);
- private final Map<String, List<GeckoEventListener>> mGeckoThreadJSONListeners =
- new HashMap<String, List<GeckoEventListener>>(DEFAULT_GECKO_JSON_EVENTS_COUNT);
- private final Map<String, List<BundleEventListener>> mUiThreadListeners =
- new HashMap<String, List<BundleEventListener>>(DEFAULT_UI_EVENTS_COUNT);
- private final Map<String, List<BundleEventListener>> mBackgroundThreadListeners =
- new HashMap<String, List<BundleEventListener>>(DEFAULT_BACKGROUND_EVENTS_COUNT);
-
- @ReflectionTarget
- public static EventDispatcher getInstance() {
- return INSTANCE;
- }
-
- public EventDispatcher() {
- }
-
- private <T> void registerListener(final Class<?> listType,
- final Map<String, List<T>> listenersMap,
- final T listener,
- final String[] events) {
- try {
- synchronized (listenersMap) {
- for (final String event : events) {
- List<T> listeners = listenersMap.get(event);
- if (listeners == null) {
- // Java doesn't let us put Class<? extends List<T>> as the type for listType.
- @SuppressWarnings("unchecked")
- final Class<? extends List<T>> type = (Class) listType;
- listeners = type.newInstance();
- listenersMap.put(event, listeners);
- }
- if (!AppConstants.RELEASE_OR_BETA && listeners.contains(listener)) {
- throw new IllegalStateException("Already registered " + event);
- }
- listeners.add(listener);
- }
- }
- } catch (final IllegalAccessException | InstantiationException e) {
- throw new IllegalArgumentException("Invalid new list type", e);
- }
- }
-
- private void checkNotRegisteredElsewhere(final Map<String, ?> allowedMap,
- final String[] events) {
- if (AppConstants.RELEASE_OR_BETA) {
- // for performance reasons, we only check for
- // already-registered listeners in non-release builds.
- return;
- }
- for (final Map<String, ?> listenersMap : Arrays.asList(mGeckoThreadNativeListeners,
- mGeckoThreadJSONListeners,
- mUiThreadListeners,
- mBackgroundThreadListeners)) {
- if (listenersMap == allowedMap) {
- continue;
- }
- synchronized (listenersMap) {
- for (final String event : events) {
- if (listenersMap.get(event) != null) {
- throw new IllegalStateException(
- "Already registered " + event + " under a different type");
- }
- }
- }
- }
- }
-
- private <T> void unregisterListener(final Map<String, List<T>> listenersMap,
- final T listener,
- final String[] events) {
- synchronized (listenersMap) {
- for (final String event : events) {
- List<T> listeners = listenersMap.get(event);
- if ((listeners == null ||
- !listeners.remove(listener)) && !AppConstants.RELEASE_OR_BETA) {
- throw new IllegalArgumentException(event + " was not registered");
- }
- }
- }
- }
-
- public void registerGeckoThreadListener(final NativeEventListener listener,
- final String... events) {
- checkNotRegisteredElsewhere(mGeckoThreadNativeListeners, events);
-
- // For listeners running on the Gecko thread, we want to notify the listeners
- // outside of our synchronized block, because the listeners may take an
- // indeterminate amount of time to run. Therefore, to ensure concurrency when
- // iterating the list outside of the synchronized block, we use a
- // CopyOnWriteArrayList.
- registerListener(CopyOnWriteArrayList.class,
- mGeckoThreadNativeListeners, listener, events);
- }
-
- @Deprecated // Use NativeEventListener instead
- public void registerGeckoThreadListener(final GeckoEventListener listener,
- final String... events) {
- checkNotRegisteredElsewhere(mGeckoThreadJSONListeners, events);
-
- registerListener(CopyOnWriteArrayList.class,
- mGeckoThreadJSONListeners, listener, events);
- }
-
- public void registerUiThreadListener(final BundleEventListener listener,
- final String... events) {
- checkNotRegisteredElsewhere(mUiThreadListeners, events);
-
- registerListener(ArrayList.class,
- mUiThreadListeners, listener, events);
- }
-
- @ReflectionTarget
- public void registerBackgroundThreadListener(final BundleEventListener listener,
- final String... events) {
- checkNotRegisteredElsewhere(mBackgroundThreadListeners, events);
-
- registerListener(ArrayList.class,
- mBackgroundThreadListeners, listener, events);
- }
-
- public void unregisterGeckoThreadListener(final NativeEventListener listener,
- final String... events) {
- unregisterListener(mGeckoThreadNativeListeners, listener, events);
- }
-
- @Deprecated // Use NativeEventListener instead
- public void unregisterGeckoThreadListener(final GeckoEventListener listener,
- final String... events) {
- unregisterListener(mGeckoThreadJSONListeners, listener, events);
- }
-
- public void unregisterUiThreadListener(final BundleEventListener listener,
- final String... events) {
- unregisterListener(mUiThreadListeners, listener, events);
- }
-
- public void unregisterBackgroundThreadListener(final BundleEventListener listener,
- final String... events) {
- unregisterListener(mBackgroundThreadListeners, listener, events);
- }
-
- private List<NativeEventListener> getNativeListeners(final String type) {
- final List<NativeEventListener> listeners;
- synchronized (mGeckoThreadNativeListeners) {
- listeners = mGeckoThreadNativeListeners.get(type);
- }
- return listeners;
- }
-
- private List<GeckoEventListener> getGeckoListeners(final String type) {
- final List<GeckoEventListener> listeners;
- synchronized (mGeckoThreadJSONListeners) {
- listeners = mGeckoThreadJSONListeners.get(type);
- }
- return listeners;
- }
-
- public boolean dispatchEvent(final NativeJSContainer message) {
- // First try native listeners.
- final String type = message.optString("type", null);
- if (type == null) {
- Log.e(LOGTAG, "JSON message must have a type property");
- return true; // It may seem odd to return true here, but it's necessary to preserve the correct behavior.
- }
-
- final List<NativeEventListener> listeners = getNativeListeners(type);
-
- final String guid = message.optString(GUID, null);
- EventCallback callback = null;
- if (guid != null) {
- callback = new GeckoEventCallback(guid, type);
- }
-
- if (listeners != null) {
- if (listeners.isEmpty()) {
- Log.w(LOGTAG, "No listeners for " + type);
-
- // There were native listeners, and they're gone. Return a failure rather than
- // looking for JSON listeners. This is an optimization, as we can safely assume
- // that an event which previously had native listeners will never have JSON
- // listeners.
- return false;
- }
- try {
- for (final NativeEventListener listener : listeners) {
- listener.handleMessage(type, message, callback);
- }
- } catch (final NativeJSObject.InvalidPropertyException e) {
- Log.e(LOGTAG, "Exception occurred while handling " + type, e);
- }
- // If we found native listeners, we assume we don't have any other types of listeners
- // and return early. This assumption is checked when registering listeners.
- return true;
- }
-
- // Check for thread event listeners before checking for JSON event listeners,
- // because checking for thread listeners is very fast and doesn't require us to
- // serialize into JSON and construct a JSONObject.
- if (dispatchToThreads(type, message, /* bundle */ null, callback)) {
- // If we found thread listeners, we assume we don't have any other types of listeners
- // and return early. This assumption is checked when registering listeners.
- return true;
- }
-
- try {
- // If we didn't find native listeners, try JSON listeners.
- return dispatchEvent(new JSONObject(message.toString()), callback);
- } catch (final JSONException e) {
- Log.e(LOGTAG, "Cannot parse JSON", e);
- } catch (final UnsupportedOperationException e) {
- Log.e(LOGTAG, "Cannot convert message to JSON", e);
- }
-
- return true;
- }
-
- /**
- * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners).
- *
- * @param message Bundle message with "type" value specifying the event type.
- */
- public void dispatch(final Bundle message) {
- dispatch(message, /* callback */ null);
- }
-
- /**
- * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners).
- *
- * @param message Bundle message with "type" value specifying the event type.
- * @param callback Optional object for callbacks from events.
- */
- public void dispatch(final Bundle message, final EventCallback callback) {
- if (message == null) {
- throw new IllegalArgumentException("Null message");
- }
-
- final String type = message.getCharSequence("type").toString();
- if (type == null) {
- Log.e(LOGTAG, "Bundle message must have a type property");
- return;
- }
- dispatchToThreads(type, /* js */ null, message, /* callback */ callback);
- }
-
- /**
- * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners).
- *
- * @param type Event type
- * @param message Bundle message
- */
- public void dispatch(final String type, final Bundle message) {
- dispatch(type, message, /* callback */ null);
- }
-
- /**
- * Dispatch event to any registered Bundle listeners (non-Gecko thread listeners).
- *
- * @param type Event type
- * @param message Bundle message
- * @param callback Optional object for callbacks from events.
- */
- public void dispatch(final String type, final Bundle message, final EventCallback callback) {
- dispatchToThreads(type, /* js */ null, message, /* callback */ callback);
- }
-
- private boolean dispatchToThreads(final String type,
- final NativeJSObject jsMessage,
- final Bundle bundleMessage,
- final EventCallback callback) {
- if (dispatchToThread(type, jsMessage, bundleMessage, callback,
- mUiThreadListeners, ThreadUtils.getUiHandler())) {
- return true;
- }
-
- if (dispatchToThread(type, jsMessage, bundleMessage, callback,
- mBackgroundThreadListeners, ThreadUtils.getBackgroundHandler())) {
- return true;
- }
-
- if (jsMessage == null) {
- Log.w(LOGTAG, "No listeners for " + type + " in dispatchToThreads");
- }
-
- if (!AppConstants.RELEASE_OR_BETA && jsMessage == null) {
- // We're dispatching a Bundle message. Because Gecko thread listeners are not
- // supported for Bundle messages, do a sanity check to make sure we don't have
- // matching Gecko thread listeners.
- boolean hasGeckoListener = false;
- synchronized (mGeckoThreadNativeListeners) {
- hasGeckoListener |= mGeckoThreadNativeListeners.containsKey(type);
- }
- synchronized (mGeckoThreadJSONListeners) {
- hasGeckoListener |= mGeckoThreadJSONListeners.containsKey(type);
- }
- if (hasGeckoListener) {
- throw new IllegalStateException(
- "Dispatching Bundle message to Gecko listener " + type);
- }
- }
-
- return false;
- }
-
- private boolean dispatchToThread(final String type,
- final NativeJSObject jsMessage,
- final Bundle bundleMessage,
- final EventCallback callback,
- final Map<String, List<BundleEventListener>> listenersMap,
- final Handler thread) {
- // We need to hold the lock throughout dispatching, to ensure the listeners list
- // is consistent, while we iterate over it. We don't have to worry about listeners
- // running for a long time while we have the lock, because the listeners will run
- // on a separate thread.
- synchronized (listenersMap) {
- final List<BundleEventListener> listeners = listenersMap.get(type);
- if (listeners == null) {
- return false;
- }
-
- if (listeners.isEmpty()) {
- Log.w(LOGTAG, "No listeners for " + type + " in dispatchToThread");
-
- // There were native listeners, and they're gone.
- return false;
- }
-
- final Bundle messageAsBundle;
- try {
- messageAsBundle = jsMessage != null ? jsMessage.toBundle() : bundleMessage;
- } catch (final NativeJSObject.InvalidPropertyException e) {
- Log.e(LOGTAG, "Exception occurred while handling " + type, e);
- return true;
- }
-
- // Event listeners will call | callback.sendError | if applicable.
- for (final BundleEventListener listener : listeners) {
- thread.post(new Runnable() {
- @Override
- public void run() {
- listener.handleMessage(type, messageAsBundle, callback);
- }
- });
- }
- return true;
- }
- }
-
- public boolean dispatchEvent(final JSONObject message, final EventCallback callback) {
- // {
- // "type": "value",
- // "event_specific": "value",
- // ...
- try {
- final String type = message.getString("type");
-
- final List<GeckoEventListener> listeners = getGeckoListeners(type);
-
- if (listeners == null || listeners.isEmpty()) {
- Log.w(LOGTAG, "No listeners for " + type + " in dispatchEvent");
-
- return false;
- }
-
- for (final GeckoEventListener listener : listeners) {
- listener.handleMessage(type, message);
- }
- } catch (final JSONException e) {
- Log.e(LOGTAG, "handleGeckoMessage throws " + e, e);
- }
-
- return true;
- }
-
- @RobocopTarget
- @Deprecated
- public static void sendResponse(JSONObject message, Object response) {
- sendResponseHelper(STATUS_SUCCESS, message, response);
- }
-
- @Deprecated
- public static void sendError(JSONObject message, Object response) {
- sendResponseHelper(STATUS_ERROR, message, response);
- }
-
- @Deprecated
- private static void sendResponseHelper(String status, JSONObject message, Object response) {
- try {
- final String topic = message.getString("type") + ":Response";
- final JSONObject wrapper = new JSONObject();
- wrapper.put(GUID, message.getString(GUID));
- wrapper.put("status", status);
- wrapper.put("response", response);
-
- if (ThreadUtils.isOnGeckoThread()) {
- GeckoAppShell.syncNotifyObservers(topic, wrapper.toString());
- } else {
- GeckoAppShell.notifyObservers(topic, wrapper.toString(),
- GeckoThread.State.PROFILE_READY);
- }
- } catch (final JSONException e) {
- Log.e(LOGTAG, "Unable to send response", e);
- }
- }
-
- /* package */ static class GeckoEventCallback implements EventCallback {
- private final String guid;
- private final String type;
- private boolean sent;
-
- public GeckoEventCallback(final String guid, final String type) {
- this.guid = guid;
- this.type = type;
- }
-
- @Override
- public void sendSuccess(final Object response) {
- sendResponse(STATUS_SUCCESS, response);
- }
-
- @Override
- public void sendError(final Object response) {
- sendResponse(STATUS_ERROR, response);
- }
-
- private void sendResponse(final String status, final Object response) {
- if (sent) {
- throw new IllegalStateException("Callback has already been executed for type=" +
- type + ", guid=" + guid);
- }
-
- sent = true;
-
- try {
- final String topic = type + ":Response";
- final JSONObject wrapper = new JSONObject();
- wrapper.put(GUID, guid);
- wrapper.put("status", status);
- wrapper.put("response", response);
-
- if (ThreadUtils.isOnGeckoThread()) {
- GeckoAppShell.syncNotifyObservers(topic, wrapper.toString());
- } else {
- GeckoAppShell.notifyObservers(topic, wrapper.toString(),
- GeckoThread.State.PROFILE_READY);
- }
- } catch (final JSONException e) {
- Log.e(LOGTAG, "Unable to send response for: " + type, e);
- }
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java
deleted file mode 100644
index 8d4c0fb2a..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAccessibility.java
+++ /dev/null
@@ -1,410 +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 org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.UIAsyncTask;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
-
-import com.googlecode.eyesfree.braille.selfbraille.SelfBrailleClient;
-import com.googlecode.eyesfree.braille.selfbraille.WriteData;
-
-public class GeckoAccessibility {
- private static final String LOGTAG = "GeckoAccessibility";
- private static final int VIRTUAL_ENTRY_POINT_BEFORE = 1;
- private static final int VIRTUAL_CURSOR_POSITION = 2;
- private static final int VIRTUAL_ENTRY_POINT_AFTER = 3;
-
- private static boolean sEnabled;
- // Used to store the JSON message and populate the event later in the code path.
- private static JSONObject sHoverEnter;
- private static AccessibilityNodeInfo sVirtualCursorNode;
- private static int sCurrentNode;
-
- // This is the number Brailleback uses to start indexing routing keys.
- private static final int BRAILLE_CLICK_BASE_INDEX = -275000000;
- private static SelfBrailleClient sSelfBrailleClient;
-
- public static void updateAccessibilitySettings (final Context context) {
- new UIAsyncTask.WithoutParams<Void>(ThreadUtils.getBackgroundHandler()) {
- @Override
- public Void doInBackground() {
- JSONObject ret = new JSONObject();
- sEnabled = false;
- AccessibilityManager accessibilityManager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- sEnabled = accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled();
- if (Versions.feature16Plus && sEnabled && sSelfBrailleClient == null) {
- sSelfBrailleClient = new SelfBrailleClient(context, false);
- }
-
- try {
- ret.put("enabled", sEnabled);
- } catch (Exception ex) {
- Log.e(LOGTAG, "Error building JSON arguments for Accessibility:Settings:", ex);
- }
-
- GeckoAppShell.notifyObservers("Accessibility:Settings", ret.toString());
- return null;
- }
-
- @Override
- public void onPostExecute(Void args) {
- final GeckoAppShell.GeckoInterface geckoInterface = GeckoAppShell.getGeckoInterface();
- if (geckoInterface == null) {
- return;
- }
- geckoInterface.setAccessibilityEnabled(sEnabled);
- }
- }.execute();
- }
-
- private static void populateEventFromJSON (AccessibilityEvent event, JSONObject message) {
- final JSONArray textArray = message.optJSONArray("text");
- if (textArray != null) {
- for (int i = 0; i < textArray.length(); i++)
- event.getText().add(textArray.optString(i));
- }
-
- event.setContentDescription(message.optString("description"));
- event.setEnabled(message.optBoolean("enabled", true));
- event.setChecked(message.optBoolean("checked"));
- event.setPassword(message.optBoolean("password"));
- event.setAddedCount(message.optInt("addedCount", -1));
- event.setRemovedCount(message.optInt("removedCount", -1));
- event.setFromIndex(message.optInt("fromIndex", -1));
- event.setItemCount(message.optInt("itemCount", -1));
- event.setCurrentItemIndex(message.optInt("currentItemIndex", -1));
- event.setBeforeText(message.optString("beforeText"));
- event.setToIndex(message.optInt("toIndex", -1));
- event.setScrollable(message.optBoolean("scrollable"));
- event.setScrollX(message.optInt("scrollX", -1));
- event.setScrollY(message.optInt("scrollY", -1));
- event.setMaxScrollX(message.optInt("maxScrollX", -1));
- event.setMaxScrollY(message.optInt("maxScrollY", -1));
- }
-
- private static void sendDirectAccessibilityEvent(int eventType, JSONObject message) {
- final Context context = GeckoAppShell.getApplicationContext();
- final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType);
- accEvent.setClassName(GeckoAccessibility.class.getName());
- accEvent.setPackageName(context.getPackageName());
- populateEventFromJSON(accEvent, message);
- AccessibilityManager accessibilityManager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- try {
- accessibilityManager.sendAccessibilityEvent(accEvent);
- } catch (IllegalStateException e) {
- // Accessibility is off.
- }
- }
-
- public static boolean isEnabled() {
- return sEnabled;
- }
-
- public static void sendAccessibilityEvent (final JSONObject message) {
- if (!sEnabled)
- return;
-
- final int eventType = message.optInt("eventType", -1);
- if (eventType < 0) {
- Log.e(LOGTAG, "No accessibility event type provided");
- return;
- }
-
- sendAccessibilityEvent(message, eventType);
- }
-
- public static void sendAccessibilityEvent (final JSONObject message, final int eventType) {
- if (!sEnabled)
- return;
-
- final String exitView = message.optString("exitView");
- if (exitView.equals("moveNext")) {
- sCurrentNode = VIRTUAL_ENTRY_POINT_AFTER;
- } else if (exitView.equals("movePrevious")) {
- sCurrentNode = VIRTUAL_ENTRY_POINT_BEFORE;
- } else {
- sCurrentNode = VIRTUAL_CURSOR_POSITION;
- }
-
- if (Versions.preJB) {
- // Before Jelly Bean we send events directly from here while spoofing the source by setting
- // the package and class name manually.
- ThreadUtils.postToBackgroundThread(new Runnable() {
- @Override
- public void run() {
- sendDirectAccessibilityEvent(eventType, message);
- }
- });
- } else {
- // In Jelly Bean we populate an AccessibilityNodeInfo with the minimal amount of data to have
- // it work with TalkBack.
- final View view = GeckoAppShell.getLayerView();
- if (view == null)
- return;
-
- if (sVirtualCursorNode == null)
- sVirtualCursorNode = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION);
- sVirtualCursorNode.setEnabled(message.optBoolean("enabled", true));
- sVirtualCursorNode.setClickable(message.optBoolean("clickable"));
- sVirtualCursorNode.setCheckable(message.optBoolean("checkable"));
- sVirtualCursorNode.setChecked(message.optBoolean("checked"));
- sVirtualCursorNode.setPassword(message.optBoolean("password"));
-
- final JSONArray textArray = message.optJSONArray("text");
- StringBuilder sb = new StringBuilder();
- if (textArray != null && textArray.length() > 0) {
- sb.append(textArray.optString(0));
- for (int i = 1; i < textArray.length(); i++) {
- sb.append(" ").append(textArray.optString(i));
- }
- sVirtualCursorNode.setText(sb.toString());
- }
- sVirtualCursorNode.setContentDescription(message.optString("description"));
-
- JSONObject bounds = message.optJSONObject("bounds");
- if (bounds != null) {
- Rect relativeBounds = new Rect(bounds.optInt("left"), bounds.optInt("top"),
- bounds.optInt("right"), bounds.optInt("bottom"));
- sVirtualCursorNode.setBoundsInParent(relativeBounds);
- int[] locationOnScreen = new int[2];
- view.getLocationOnScreen(locationOnScreen);
- Rect screenBounds = new Rect(relativeBounds);
- screenBounds.offset(locationOnScreen[0], locationOnScreen[1]);
- sVirtualCursorNode.setBoundsInScreen(screenBounds);
- }
-
- final JSONObject braille = message.optJSONObject("brailleOutput");
- if (braille != null) {
- sendBrailleText(view, braille.optString("text"),
- braille.optInt("selectionStart"), braille.optInt("selectionEnd"));
- }
-
- if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) {
- sHoverEnter = message;
- }
-
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
- event.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
- event.setClassName(GeckoAccessibility.class.getName());
- if (eventType == AccessibilityEvent.TYPE_ANNOUNCEMENT ||
- eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
- event.setSource(view, View.NO_ID);
- } else {
- event.setSource(view, VIRTUAL_CURSOR_POSITION);
- }
- populateEventFromJSON(event, message);
- ((ViewParent) view).requestSendAccessibilityEvent(view, event);
- }
- });
-
- }
- }
-
- private static void sendBrailleText(final View view, final String text, final int selectionStart, final int selectionEnd) {
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION);
- WriteData data = WriteData.forInfo(info);
- data.setText(text);
- // Set either the focus blink or the current caret position/selection
- data.setSelectionStart(selectionStart);
- data.setSelectionEnd(selectionEnd);
- sSelfBrailleClient.write(data);
- }
-
- public static void setDelegate(View view) {
- // Only use this delegate in Jelly Bean.
- if (Versions.feature16Plus) {
- view.setAccessibilityDelegate(new GeckoAccessibilityDelegate());
- view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
- }
-
- public static void setAccessibilityManagerListeners(final Context context) {
- AccessibilityManager accessibilityManager =
- (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
-
- accessibilityManager.addAccessibilityStateChangeListener(new AccessibilityManager.AccessibilityStateChangeListener() {
- @Override
- public void onAccessibilityStateChanged(boolean enabled) {
- updateAccessibilitySettings(context);
- }
- });
-
- if (Versions.feature19Plus) {
- accessibilityManager.addTouchExplorationStateChangeListener(new AccessibilityManager.TouchExplorationStateChangeListener() {
- @Override
- public void onTouchExplorationStateChanged(boolean enabled) {
- updateAccessibilitySettings(context);
- }
- });
- }
- }
-
- public static void onLayerViewFocusChanged(boolean gainFocus) {
- if (sEnabled)
- GeckoAppShell.notifyObservers("Accessibility:Focus", gainFocus ? "true" : "false");
- }
-
- public static class GeckoAccessibilityDelegate extends View.AccessibilityDelegate {
- AccessibilityNodeProvider mAccessibilityNodeProvider;
-
- @Override
- public AccessibilityNodeProvider getAccessibilityNodeProvider(final View host) {
- if (mAccessibilityNodeProvider == null)
- // The accessibility node structure for web content consists of 3 LayerView child nodes:
- // 1. VIRTUAL_ENTRY_POINT_BEFORE: Represents the entry point before the LayerView.
- // 2. VIRTUAL_CURSOR_POSITION: Represents the current position of the virtual cursor.
- // 3. VIRTUAL_ENTRY_POINT_AFTER: Represents the entry point after the LayerView.
- mAccessibilityNodeProvider = new AccessibilityNodeProvider() {
- @Override
- public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualDescendantId) {
- AccessibilityNodeInfo info = (virtualDescendantId == VIRTUAL_CURSOR_POSITION && sVirtualCursorNode != null) ?
- AccessibilityNodeInfo.obtain(sVirtualCursorNode) :
- AccessibilityNodeInfo.obtain(host, virtualDescendantId);
-
- switch (virtualDescendantId) {
- case View.NO_ID:
- // This is the parent LayerView node, populate it with children.
- onInitializeAccessibilityNodeInfo(host, info);
- info.addChild(host, VIRTUAL_ENTRY_POINT_BEFORE);
- info.addChild(host, VIRTUAL_CURSOR_POSITION);
- info.addChild(host, VIRTUAL_ENTRY_POINT_AFTER);
- break;
- default:
- info.setParent(host);
- info.setSource(host, virtualDescendantId);
- info.setVisibleToUser(host.isShown());
- info.setPackageName(GeckoAppShell.getApplicationContext().getPackageName());
- info.setClassName(host.getClass().getName());
- info.setEnabled(true);
- info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
- info.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
- info.addAction(AccessibilityNodeInfo.ACTION_CLICK);
- info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
- info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
- info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
- info.addAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
- info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
- info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER |
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD |
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE |
- AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
- break;
- }
- return info;
- }
-
- @Override
- public boolean performAction (int virtualViewId, int action, Bundle arguments) {
- if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) {
- // The accessibility focus is permanently on the middle node, VIRTUAL_CURSOR_POSITION.
- // When we enter the view forward or backward we just ask Gecko to get focus, keeping the current position.
- if (virtualViewId == VIRTUAL_CURSOR_POSITION && sHoverEnter != null) {
- GeckoAccessibility.sendAccessibilityEvent(sHoverEnter, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
- } else {
- GeckoAppShell.notifyObservers("Accessibility:Focus", "true");
- }
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_CLICK && virtualViewId == VIRTUAL_CURSOR_POSITION) {
- GeckoAppShell.notifyObservers("Accessibility:ActivateObject", null);
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_LONG_CLICK && virtualViewId == VIRTUAL_CURSOR_POSITION) {
- GeckoAppShell.notifyObservers("Accessibility:LongPress", null);
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && virtualViewId == VIRTUAL_CURSOR_POSITION) {
- GeckoAppShell.notifyObservers("Accessibility:ScrollForward", null);
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD && virtualViewId == VIRTUAL_CURSOR_POSITION) {
- GeckoAppShell.notifyObservers("Accessibility:ScrollBackward", null);
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT && virtualViewId == VIRTUAL_CURSOR_POSITION) {
- String traversalRule = "";
- if (arguments != null) {
- traversalRule = arguments.getString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
- }
- GeckoAppShell.notifyObservers("Accessibility:NextObject", traversalRule);
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT && virtualViewId == VIRTUAL_CURSOR_POSITION) {
- String traversalRule = "";
- if (arguments != null) {
- traversalRule = arguments.getString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING);
- }
- GeckoAppShell.notifyObservers("Accessibility:PreviousObject", traversalRule);
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY &&
- virtualViewId == VIRTUAL_CURSOR_POSITION) {
- // XXX: Self brailling gives this action with a bogus argument instead of an actual click action;
- // the argument value is the BRAILLE_CLICK_BASE_INDEX - the index of the routing key that was hit.
- // Other negative values are used by ChromeVox, but we don't support them.
- // FAKE_GRANULARITY_READ_CURRENT = -1
- // FAKE_GRANULARITY_READ_TITLE = -2
- // FAKE_GRANULARITY_STOP_SPEECH = -3
- // FAKE_GRANULARITY_CHANGE_SHIFTER = -4
- int granularity = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
- if (granularity <= BRAILLE_CLICK_BASE_INDEX) {
- int keyIndex = BRAILLE_CLICK_BASE_INDEX - granularity;
- JSONObject activationData = new JSONObject();
- try {
- activationData.put("keyIndex", keyIndex);
- } catch (JSONException e) {
- return true;
- }
- GeckoAppShell.notifyObservers("Accessibility:ActivateObject", activationData.toString());
- } else if (granularity > 0) {
- JSONObject movementData = new JSONObject();
- try {
- movementData.put("direction", "Next");
- movementData.put("granularity", granularity);
- } catch (JSONException e) {
- return true;
- }
- GeckoAppShell.notifyObservers("Accessibility:MoveByGranularity", movementData.toString());
- }
- return true;
- } else if (action == AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY &&
- virtualViewId == VIRTUAL_CURSOR_POSITION) {
- JSONObject movementData = new JSONObject();
- int granularity = arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
- try {
- movementData.put("direction", "Previous");
- movementData.put("granularity", granularity);
- } catch (JSONException e) {
- return true;
- }
- if (granularity > 0) {
- GeckoAppShell.notifyObservers("Accessibility:MoveByGranularity", movementData.toString());
- }
- return true;
- }
- return host.performAccessibilityAction(action, arguments);
- }
- };
-
- return mAccessibilityNodeProvider;
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
deleted file mode 100644
index 152981649..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java
+++ /dev/null
@@ -1,2235 +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.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PipedInputStream;
-import java.io.PipedOutputStream;
-import java.net.MalformedURLException;
-import java.net.Proxy;
-import java.net.URLConnection;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-import java.util.concurrent.ConcurrentHashMap;
-
-import android.annotation.SuppressLint;
-import org.mozilla.gecko.annotation.JNITarget;
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.gfx.BitmapUtils;
-import org.mozilla.gecko.gfx.LayerView;
-import org.mozilla.gecko.gfx.PanZoomController;
-import org.mozilla.gecko.permissions.Permissions;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.GeckoRequest;
-import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
-import org.mozilla.gecko.util.HardwareUtils;
-import org.mozilla.gecko.util.NativeEventListener;
-import org.mozilla.gecko.util.NativeJSContainer;
-import org.mozilla.gecko.util.NativeJSObject;
-import org.mozilla.gecko.util.ProxySelector;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.Signature;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.ImageFormat;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.SurfaceTexture;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.hardware.Camera;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.location.Criteria;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationManager;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.os.Vibrator;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.Display;
-import android.view.HapticFeedbackConstants;
-import android.view.Surface;
-import android.view.SurfaceView;
-import android.view.TextureView;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-import android.webkit.MimeTypeMap;
-import android.widget.AbsoluteLayout;
-
-public class GeckoAppShell
-{
- private static final String LOGTAG = "GeckoAppShell";
-
- // We have static members only.
- private GeckoAppShell() { }
-
- private static final CrashHandler CRASH_HANDLER = new CrashHandler() {
- @Override
- protected String getAppPackageName() {
- return AppConstants.ANDROID_PACKAGE_NAME;
- }
-
- @Override
- protected Context getAppContext() {
- return sContextGetter != null ? getApplicationContext() : null;
- }
-
- @Override
- protected Bundle getCrashExtras(final Thread thread, final Throwable exc) {
- final Bundle extras = super.getCrashExtras(thread, exc);
-
- extras.putString("ProductName", AppConstants.MOZ_APP_BASENAME);
- extras.putString("ProductID", AppConstants.MOZ_APP_ID);
- extras.putString("Version", AppConstants.MOZ_APP_VERSION);
- extras.putString("BuildID", AppConstants.MOZ_APP_BUILDID);
- extras.putString("Vendor", AppConstants.MOZ_APP_VENDOR);
- extras.putString("ReleaseChannel", AppConstants.MOZ_UPDATE_CHANNEL);
- return extras;
- }
-
- @Override
- public void uncaughtException(final Thread thread, final Throwable exc) {
- if (GeckoThread.isState(GeckoThread.State.EXITING) ||
- GeckoThread.isState(GeckoThread.State.EXITED)) {
- // We've called System.exit. All exceptions after this point are Android
- // berating us for being nasty to it.
- return;
- }
-
- super.uncaughtException(thread, exc);
- }
-
- @Override
- public boolean reportException(final Thread thread, final Throwable exc) {
- try {
- if (exc instanceof OutOfMemoryError) {
- SharedPreferences prefs = getSharedPreferences();
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(PREFS_OOM_EXCEPTION, true);
-
- // Synchronously write to disk so we know it's done before we
- // shutdown
- editor.commit();
- }
-
- reportJavaCrash(exc, getExceptionStackTrace(exc));
-
- } catch (final Throwable e) {
- }
-
- // reportJavaCrash should have caused us to hard crash. If we're still here,
- // it probably means Gecko is not loaded, and we should do something else.
- return false;
- }
- };
-
- public static CrashHandler ensureCrashHandling() {
- // Crash handling is automatically enabled when GeckoAppShell is loaded.
- return CRASH_HANDLER;
- }
-
- private static volatile boolean locationHighAccuracyEnabled;
-
- // See also HardwareUtils.LOW_MEMORY_THRESHOLD_MB.
- private static final int HIGH_MEMORY_DEVICE_THRESHOLD_MB = 768;
-
- static private int sDensityDpi;
- static private int sScreenDepth;
-
- /* Is the value in sVibrationEndTime valid? */
- private static boolean sVibrationMaybePlaying;
-
- /* Time (in System.nanoTime() units) when the currently-playing vibration
- * is scheduled to end. This value is valid only when
- * sVibrationMaybePlaying is true. */
- private static long sVibrationEndTime;
-
- private static Sensor gAccelerometerSensor;
- private static Sensor gLinearAccelerometerSensor;
- private static Sensor gGyroscopeSensor;
- private static Sensor gOrientationSensor;
- private static Sensor gProximitySensor;
- private static Sensor gLightSensor;
- private static Sensor gRotationVectorSensor;
- private static Sensor gGameRotationVectorSensor;
-
- private static final String GECKOREQUEST_RESPONSE_KEY = "response";
- private static final String GECKOREQUEST_ERROR_KEY = "error";
-
- /*
- * Keep in sync with constants found here:
- * http://dxr.mozilla.org/mozilla-central/source/uriloader/base/nsIWebProgressListener.idl
- */
- static public final int WPL_STATE_START = 0x00000001;
- static public final int WPL_STATE_STOP = 0x00000010;
- static public final int WPL_STATE_IS_DOCUMENT = 0x00020000;
- static public final int WPL_STATE_IS_NETWORK = 0x00040000;
-
- /* Keep in sync with constants found here:
- http://dxr.mozilla.org/mozilla-central/source/netwerk/base/nsINetworkLinkService.idl
- */
- static public final int LINK_TYPE_UNKNOWN = 0;
- static public final int LINK_TYPE_ETHERNET = 1;
- static public final int LINK_TYPE_USB = 2;
- static public final int LINK_TYPE_WIFI = 3;
- static public final int LINK_TYPE_WIMAX = 4;
- static public final int LINK_TYPE_2G = 5;
- static public final int LINK_TYPE_3G = 6;
- static public final int LINK_TYPE_4G = 7;
-
- public static final String PREFS_OOM_EXCEPTION = "OOMException";
-
- /* The Android-side API: API methods that Android calls */
-
- // helper methods
- @WrapForJNI
- /* package */ static native void reportJavaCrash(Throwable exc, String stackTrace);
-
- @WrapForJNI(dispatchTo = "gecko")
- public static native void notifyUriVisited(String uri);
-
- private static LayerView sLayerView;
- private static Rect sScreenSize;
-
- public static void setLayerView(LayerView lv) {
- if (sLayerView == lv) {
- return;
- }
- sLayerView = lv;
- }
-
- @RobocopTarget
- public static LayerView getLayerView() {
- return sLayerView;
- }
-
- /**
- * Sends an asynchronous request to Gecko.
- *
- * The response data will be passed to {@link GeckoRequest#onResponse(NativeJSObject)} if the
- * request succeeds; otherwise, {@link GeckoRequest#onError()} will fire.
- *
- * It can be called from any thread. The GeckoRequest callbacks will be executed on the Gecko thread.
- *
- * @param request The request to dispatch. Cannot be null.
- */
- @RobocopTarget
- public static void sendRequestToGecko(final GeckoRequest request) {
- final String responseMessage = "Gecko:Request" + request.getId();
-
- EventDispatcher.getInstance().registerGeckoThreadListener(new NativeEventListener() {
- @Override
- public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
- EventDispatcher.getInstance().unregisterGeckoThreadListener(this, event);
- if (!message.has(GECKOREQUEST_RESPONSE_KEY)) {
- request.onError(message.getObject(GECKOREQUEST_ERROR_KEY));
- return;
- }
- request.onResponse(message.getObject(GECKOREQUEST_RESPONSE_KEY));
- }
- }, responseMessage);
-
- notifyObservers(request.getName(), request.getData());
- }
-
- // Synchronously notify a Gecko observer; must be called from Gecko thread.
- @WrapForJNI(calledFrom = "gecko")
- public static native void syncNotifyObservers(String topic, String data);
-
- @WrapForJNI(stubName = "NotifyObservers", dispatchTo = "gecko")
- private static native void nativeNotifyObservers(String topic, String data);
-
- @RobocopTarget
- public static void notifyObservers(final String topic, final String data) {
- notifyObservers(topic, data, GeckoThread.State.RUNNING);
- }
-
- public static void notifyObservers(final String topic, final String data, final GeckoThread.State state) {
- if (GeckoThread.isStateAtLeast(state)) {
- nativeNotifyObservers(topic, data);
- } else {
- GeckoThread.queueNativeCallUntil(
- state, GeckoAppShell.class, "nativeNotifyObservers",
- String.class, topic, String.class, data);
- }
- }
-
- /*
- * The Gecko-side API: API methods that Gecko calls
- */
-
- @WrapForJNI(exceptionMode = "ignore")
- private static String getExceptionStackTrace(Throwable e) {
- return CrashHandler.getExceptionStackTrace(CrashHandler.getRootException(e));
- }
-
- @WrapForJNI(exceptionMode = "ignore")
- private static void handleUncaughtException(Throwable e) {
- CRASH_HANDLER.uncaughtException(null, e);
- }
-
- @WrapForJNI
- public static void openWindowForNotification() {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
-
- getApplicationContext().startActivity(intent);
- }
-
- private static float getLocationAccuracy(Location location) {
- float radius = location.getAccuracy();
- return (location.hasAccuracy() && radius > 0) ? radius : 1001;
- }
-
- @SuppressLint("MissingPermission") // Permissions are explicitly checked for in enableLocation()
- private static Location getLastKnownLocation(LocationManager lm) {
- Location lastKnownLocation = null;
- List<String> providers = lm.getAllProviders();
-
- for (String provider : providers) {
- Location location = lm.getLastKnownLocation(provider);
- if (location == null) {
- continue;
- }
-
- if (lastKnownLocation == null) {
- lastKnownLocation = location;
- continue;
- }
-
- long timeDiff = location.getTime() - lastKnownLocation.getTime();
- if (timeDiff > 0 ||
- (timeDiff == 0 &&
- getLocationAccuracy(location) < getLocationAccuracy(lastKnownLocation))) {
- lastKnownLocation = location;
- }
- }
-
- return lastKnownLocation;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- @SuppressLint("MissingPermission") // Permissions are explicitly checked for within this method
- private static void enableLocation(final boolean enable) {
- final Runnable requestLocation = new Runnable() {
- @Override
- public void run() {
- LocationManager lm = getLocationManager(getApplicationContext());
- if (lm == null) {
- return;
- }
-
- if (!enable) {
- lm.removeUpdates(getLocationListener());
- return;
- }
-
- Location lastKnownLocation = getLastKnownLocation(lm);
- if (lastKnownLocation != null) {
- getLocationListener().onLocationChanged(lastKnownLocation);
- }
-
- Criteria criteria = new Criteria();
- criteria.setSpeedRequired(false);
- criteria.setBearingRequired(false);
- criteria.setAltitudeRequired(false);
- if (locationHighAccuracyEnabled) {
- criteria.setAccuracy(Criteria.ACCURACY_FINE);
- criteria.setCostAllowed(true);
- criteria.setPowerRequirement(Criteria.POWER_HIGH);
- } else {
- criteria.setAccuracy(Criteria.ACCURACY_COARSE);
- criteria.setCostAllowed(false);
- criteria.setPowerRequirement(Criteria.POWER_LOW);
- }
-
- String provider = lm.getBestProvider(criteria, true);
- if (provider == null)
- return;
-
- Looper l = Looper.getMainLooper();
- lm.requestLocationUpdates(provider, 100, 0.5f, getLocationListener(), l);
- }
- };
-
- Permissions
- .from((Activity) getContext())
- .withPermissions(Manifest.permission.ACCESS_FINE_LOCATION)
- .onUIThread()
- .doNotPromptIf(!enable)
- .run(requestLocation);
- }
-
- private static LocationManager getLocationManager(Context context) {
- try {
- return (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- } catch (NoSuchFieldError e) {
- // Some Tegras throw exceptions about missing the CONTROL_LOCATION_UPDATES permission,
- // which allows enabling/disabling location update notifications from the cell radio.
- // CONTROL_LOCATION_UPDATES is not for use by normal applications, but we might be
- // hitting this problem if the Tegras are confused about missing cell radios.
- Log.e(LOGTAG, "LOCATION_SERVICE not found?!", e);
- return null;
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void enableLocationHighAccuracy(final boolean enable) {
- locationHighAccuracyEnabled = enable;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean setAlarm(int aSeconds, int aNanoSeconds) {
- AlarmManager am = (AlarmManager)
- getApplicationContext().getSystemService(Context.ALARM_SERVICE);
-
- Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
- PendingIntent pi = PendingIntent.getBroadcast(
- getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- // AlarmManager only supports millisecond precision
- long time = ((long) aSeconds * 1000) + ((long) aNanoSeconds / 1_000_000L);
- am.setExact(AlarmManager.RTC_WAKEUP, time, pi);
-
- return true;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void disableAlarm() {
- AlarmManager am = (AlarmManager)
- getApplicationContext().getSystemService(Context.ALARM_SERVICE);
-
- Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
- PendingIntent pi = PendingIntent.getBroadcast(
- getApplicationContext(), 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- am.cancel(pi);
- }
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- /* package */ static native void onSensorChanged(int hal_type, float x, float y, float z,
- float w, int accuracy, long time);
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- /* package */ static native void onLocationChanged(double latitude, double longitude,
- double altitude, float accuracy,
- float bearing, float speed, long time);
-
- private static class DefaultListeners
- implements SensorEventListener, LocationListener, NotificationListener {
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- private static int HalSensorAccuracyFor(int androidAccuracy) {
- switch (androidAccuracy) {
- case SensorManager.SENSOR_STATUS_UNRELIABLE:
- return GeckoHalDefines.SENSOR_ACCURACY_UNRELIABLE;
- case SensorManager.SENSOR_STATUS_ACCURACY_LOW:
- return GeckoHalDefines.SENSOR_ACCURACY_LOW;
- case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM:
- return GeckoHalDefines.SENSOR_ACCURACY_MED;
- case SensorManager.SENSOR_STATUS_ACCURACY_HIGH:
- return GeckoHalDefines.SENSOR_ACCURACY_HIGH;
- }
- return GeckoHalDefines.SENSOR_ACCURACY_UNKNOWN;
- }
-
- @Override
- public void onSensorChanged(SensorEvent s) {
- int sensor_type = s.sensor.getType();
- int hal_type = 0;
- float x = 0.0f, y = 0.0f, z = 0.0f, w = 0.0f;
- final int accuracy = HalSensorAccuracyFor(s.accuracy);
- // SensorEvent timestamp is in nanoseconds, Gecko expects microseconds.
- final long time = s.timestamp / 1000;
-
- switch (sensor_type) {
- case Sensor.TYPE_ACCELEROMETER:
- case Sensor.TYPE_LINEAR_ACCELERATION:
- case Sensor.TYPE_ORIENTATION:
- if (sensor_type == Sensor.TYPE_ACCELEROMETER) {
- hal_type = GeckoHalDefines.SENSOR_ACCELERATION;
- } else if (sensor_type == Sensor.TYPE_LINEAR_ACCELERATION) {
- hal_type = GeckoHalDefines.SENSOR_LINEAR_ACCELERATION;
- } else {
- hal_type = GeckoHalDefines.SENSOR_ORIENTATION;
- }
- x = s.values[0];
- y = s.values[1];
- z = s.values[2];
- break;
-
- case Sensor.TYPE_GYROSCOPE:
- hal_type = GeckoHalDefines.SENSOR_GYROSCOPE;
- x = (float) Math.toDegrees(s.values[0]);
- y = (float) Math.toDegrees(s.values[1]);
- z = (float) Math.toDegrees(s.values[2]);
- break;
-
- case Sensor.TYPE_PROXIMITY:
- hal_type = GeckoHalDefines.SENSOR_PROXIMITY;
- x = s.values[0];
- z = s.sensor.getMaximumRange();
- break;
-
- case Sensor.TYPE_LIGHT:
- hal_type = GeckoHalDefines.SENSOR_LIGHT;
- x = s.values[0];
- break;
-
- case Sensor.TYPE_ROTATION_VECTOR:
- case Sensor.TYPE_GAME_ROTATION_VECTOR: // API >= 18
- hal_type = (sensor_type == Sensor.TYPE_ROTATION_VECTOR ?
- GeckoHalDefines.SENSOR_ROTATION_VECTOR :
- GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR);
- x = s.values[0];
- y = s.values[1];
- z = s.values[2];
- if (s.values.length >= 4) {
- w = s.values[3];
- } else {
- // s.values[3] was optional in API <= 18, so we need to compute it
- // The values form a unit quaternion, so we can compute the angle of
- // rotation purely based on the given 3 values.
- w = 1.0f - s.values[0] * s.values[0] -
- s.values[1] * s.values[1] - s.values[2] * s.values[2];
- w = (w > 0.0f) ? (float) Math.sqrt(w) : 0.0f;
- }
- break;
- }
-
- GeckoAppShell.onSensorChanged(hal_type, x, y, z, w, accuracy, time);
- }
-
- // Geolocation.
- @Override
- public void onLocationChanged(Location location) {
- // No logging here: user-identifying information.
- GeckoAppShell.onLocationChanged(location.getLatitude(), location.getLongitude(),
- location.getAltitude(), location.getAccuracy(),
- location.getBearing(), location.getSpeed(),
- location.getTime());
- }
-
- @Override
- public void onProviderDisabled(String provider)
- {
- }
-
- @Override
- public void onProviderEnabled(String provider)
- {
- }
-
- @Override
- public void onStatusChanged(String provider, int status, Bundle extras)
- {
- }
-
- @Override // NotificationListener
- public void showNotification(String name, String cookie, String host,
- String title, String text, String imageUrl) {
- // Default is to not show the notification, and immediate send close message.
- GeckoAppShell.onNotificationClose(name, cookie);
- }
-
- @Override // NotificationListener
- public void showPersistentNotification(String name, String cookie, String host,
- String title, String text, String imageUrl,
- String data) {
- // Default is to not show the notification, and immediate send close message.
- GeckoAppShell.onNotificationClose(name, cookie);
- }
-
- @Override // NotificationListener
- public void closeNotification(String name) {
- // Do nothing.
- }
- }
-
- private static final DefaultListeners DEFAULT_LISTENERS = new DefaultListeners();
- private static SensorEventListener sSensorListener = DEFAULT_LISTENERS;
- private static LocationListener sLocationListener = DEFAULT_LISTENERS;
- private static NotificationListener sNotificationListener = DEFAULT_LISTENERS;
-
- public static SensorEventListener getSensorListener() {
- return sSensorListener;
- }
-
- public static void setSensorListener(final SensorEventListener listener) {
- sSensorListener = (listener != null) ? listener : DEFAULT_LISTENERS;
- }
-
- public static LocationListener getLocationListener() {
- return sLocationListener;
- }
-
- public static void setLocationListener(final LocationListener listener) {
- sLocationListener = (listener != null) ? listener : DEFAULT_LISTENERS;
- }
-
- public static NotificationListener getNotificationListener() {
- return sNotificationListener;
- }
-
- public static void setNotificationListener(final NotificationListener listener) {
- sNotificationListener = (listener != null) ? listener : DEFAULT_LISTENERS;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void enableSensor(int aSensortype) {
- GeckoInterface gi = getGeckoInterface();
- if (gi == null) {
- return;
- }
- SensorManager sm = (SensorManager)
- getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
-
- switch (aSensortype) {
- case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR:
- if (gGameRotationVectorSensor == null) {
- gGameRotationVectorSensor = sm.getDefaultSensor(15);
- // sm.getDefaultSensor(
- // Sensor.TYPE_GAME_ROTATION_VECTOR); // API >= 18
- }
- if (gGameRotationVectorSensor != null) {
- sm.registerListener(getSensorListener(),
- gGameRotationVectorSensor,
- SensorManager.SENSOR_DELAY_FASTEST);
- }
- if (gGameRotationVectorSensor != null) {
- break;
- }
- // Fallthrough
-
- case GeckoHalDefines.SENSOR_ROTATION_VECTOR:
- if (gRotationVectorSensor == null) {
- gRotationVectorSensor = sm.getDefaultSensor(
- Sensor.TYPE_ROTATION_VECTOR);
- }
- if (gRotationVectorSensor != null) {
- sm.registerListener(getSensorListener(),
- gRotationVectorSensor,
- SensorManager.SENSOR_DELAY_FASTEST);
- }
- if (gRotationVectorSensor != null) {
- break;
- }
- // Fallthrough
-
- case GeckoHalDefines.SENSOR_ORIENTATION:
- if (gOrientationSensor == null) {
- gOrientationSensor = sm.getDefaultSensor(
- Sensor.TYPE_ORIENTATION);
- }
- if (gOrientationSensor != null) {
- sm.registerListener(getSensorListener(),
- gOrientationSensor,
- SensorManager.SENSOR_DELAY_FASTEST);
- }
- break;
-
- case GeckoHalDefines.SENSOR_ACCELERATION:
- if (gAccelerometerSensor == null) {
- gAccelerometerSensor = sm.getDefaultSensor(
- Sensor.TYPE_ACCELEROMETER);
- }
- if (gAccelerometerSensor != null) {
- sm.registerListener(getSensorListener(),
- gAccelerometerSensor,
- SensorManager.SENSOR_DELAY_FASTEST);
- }
- break;
-
- case GeckoHalDefines.SENSOR_PROXIMITY:
- if (gProximitySensor == null) {
- gProximitySensor = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
- }
- if (gProximitySensor != null) {
- sm.registerListener(getSensorListener(),
- gProximitySensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- }
- break;
-
- case GeckoHalDefines.SENSOR_LIGHT:
- if (gLightSensor == null) {
- gLightSensor = sm.getDefaultSensor(Sensor.TYPE_LIGHT);
- }
- if (gLightSensor != null) {
- sm.registerListener(getSensorListener(),
- gLightSensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- }
- break;
-
- case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION:
- if (gLinearAccelerometerSensor == null) {
- gLinearAccelerometerSensor = sm.getDefaultSensor(
- Sensor.TYPE_LINEAR_ACCELERATION);
- }
- if (gLinearAccelerometerSensor != null) {
- sm.registerListener(getSensorListener(),
- gLinearAccelerometerSensor,
- SensorManager.SENSOR_DELAY_FASTEST);
- }
- break;
-
- case GeckoHalDefines.SENSOR_GYROSCOPE:
- if (gGyroscopeSensor == null) {
- gGyroscopeSensor = sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
- }
- if (gGyroscopeSensor != null) {
- sm.registerListener(getSensorListener(),
- gGyroscopeSensor,
- SensorManager.SENSOR_DELAY_FASTEST);
- }
- break;
-
- default:
- Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " +
- aSensortype);
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void disableSensor(int aSensortype) {
- GeckoInterface gi = getGeckoInterface();
- if (gi == null)
- return;
-
- SensorManager sm = (SensorManager)
- getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
-
- switch (aSensortype) {
- case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR:
- if (gGameRotationVectorSensor != null) {
- sm.unregisterListener(getSensorListener(), gGameRotationVectorSensor);
- break;
- }
- // Fallthrough
-
- case GeckoHalDefines.SENSOR_ROTATION_VECTOR:
- if (gRotationVectorSensor != null) {
- sm.unregisterListener(getSensorListener(), gRotationVectorSensor);
- break;
- }
- // Fallthrough
-
- case GeckoHalDefines.SENSOR_ORIENTATION:
- if (gOrientationSensor != null) {
- sm.unregisterListener(getSensorListener(), gOrientationSensor);
- }
- break;
-
- case GeckoHalDefines.SENSOR_ACCELERATION:
- if (gAccelerometerSensor != null) {
- sm.unregisterListener(getSensorListener(), gAccelerometerSensor);
- }
- break;
-
- case GeckoHalDefines.SENSOR_PROXIMITY:
- if (gProximitySensor != null) {
- sm.unregisterListener(getSensorListener(), gProximitySensor);
- }
- break;
-
- case GeckoHalDefines.SENSOR_LIGHT:
- if (gLightSensor != null) {
- sm.unregisterListener(getSensorListener(), gLightSensor);
- }
- break;
-
- case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION:
- if (gLinearAccelerometerSensor != null) {
- sm.unregisterListener(getSensorListener(), gLinearAccelerometerSensor);
- }
- break;
-
- case GeckoHalDefines.SENSOR_GYROSCOPE:
- if (gGyroscopeSensor != null) {
- sm.unregisterListener(getSensorListener(), gGyroscopeSensor);
- }
- break;
- default:
- Log.w(LOGTAG, "Error! Can't disable unknown SENSOR type " + aSensortype);
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void moveTaskToBack() {
- if (getGeckoInterface() != null)
- getGeckoInterface().getActivity().moveTaskToBack(true);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public static void scheduleRestart() {
- getGeckoInterface().doRestart();
- }
-
- // Creates a homescreen shortcut for a web page.
- // This is the entry point from nsIShellService.
- @WrapForJNI(calledFrom = "gecko")
- public static void createShortcut(final String aTitle, final String aURI) {
- final GeckoInterface geckoInterface = getGeckoInterface();
- if (geckoInterface == null) {
- return;
- }
- geckoInterface.createShortcut(aTitle, aURI);
- }
-
- @JNITarget
- static public int getPreferredIconSize() {
- ActivityManager am = (ActivityManager)
- getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
- return am.getLauncherLargeIconSize();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static String[] getHandlersForMimeType(String aMimeType, String aAction) {
- final GeckoInterface geckoInterface = getGeckoInterface();
- if (geckoInterface == null) {
- return new String[] {};
- }
- return geckoInterface.getHandlersForMimeType(aMimeType, aAction);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static String[] getHandlersForURL(String aURL, String aAction) {
- final GeckoInterface geckoInterface = getGeckoInterface();
- if (geckoInterface == null) {
- return new String[] {};
- }
- return geckoInterface.getHandlersForURL(aURL, aAction);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean getHWEncoderCapability() {
- return HardwareCodecCapabilityUtils.getHWEncoderCapability();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean getHWDecoderCapability() {
- return HardwareCodecCapabilityUtils.getHWDecoderCapability();
- }
-
- static List<ResolveInfo> queryIntentActivities(Intent intent) {
- final PackageManager pm = getApplicationContext().getPackageManager();
-
- // Exclude any non-exported activities: we can't open them even if we want to!
- // Bug 1031569 has some details.
- final ArrayList<ResolveInfo> list = new ArrayList<>();
- for (ResolveInfo ri: pm.queryIntentActivities(intent, 0)) {
- if (ri.activityInfo.exported) {
- list.add(ri);
- }
- }
-
- return list;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public static String getExtensionFromMimeType(String aMimeType) {
- return MimeTypeMap.getSingleton().getExtensionFromMimeType(aMimeType);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public static String getMimeTypeFromExtensions(String aFileExt) {
- StringTokenizer st = new StringTokenizer(aFileExt, ".,; ");
- String type = null;
- String subType = null;
- while (st.hasMoreElements()) {
- String ext = st.nextToken();
- String mt = getMimeTypeFromExtension(ext);
- if (mt == null)
- continue;
- int slash = mt.indexOf('/');
- String tmpType = mt.substring(0, slash);
- if (!tmpType.equalsIgnoreCase(type))
- type = type == null ? tmpType : "*";
- String tmpSubType = mt.substring(slash + 1);
- if (!tmpSubType.equalsIgnoreCase(subType))
- subType = subType == null ? tmpSubType : "*";
- }
- if (type == null)
- type = "*";
- if (subType == null)
- subType = "*";
- return type + "/" + subType;
- }
-
- static boolean isUriSafeForScheme(Uri aUri) {
- // Bug 794034 - We don't want to pass MWI or USSD codes to the
- // dialer, and ensure the Uri class doesn't parse a URI
- // containing a fragment ('#')
- final String scheme = aUri.getScheme();
- if ("tel".equals(scheme) || "sms".equals(scheme)) {
- final String number = aUri.getSchemeSpecificPart();
- if (number.contains("#") || number.contains("*") || aUri.getFragment() != null) {
- return false;
- }
- }
- return true;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean openUriExternal(String targetURI,
- String mimeType,
- String packageName,
- String className,
- String action,
- String title) {
- final GeckoInterface geckoInterface = getGeckoInterface();
- if (geckoInterface == null) {
- return false;
- }
- return geckoInterface.openUriExternal(targetURI, mimeType, packageName, className, action, title);
- }
-
- @WrapForJNI(dispatchTo = "gecko")
- private static native void notifyAlertListener(String name, String topic, String cookie);
-
- /**
- * Called by the NotificationListener to notify Gecko that a notification has been
- * shown.
- */
- public static void onNotificationShow(final String name, final String cookie) {
- if (GeckoThread.isRunning()) {
- notifyAlertListener(name, "alertshow", cookie);
- }
- }
-
- /**
- * Called by the NotificationListener to notify Gecko that a previously shown
- * notification has been closed.
- */
- public static void onNotificationClose(final String name, final String cookie) {
- if (GeckoThread.isRunning()) {
- notifyAlertListener(name, "alertfinished", cookie);
- }
- }
-
- /**
- * Called by the NotificationListener to notify Gecko that a previously shown
- * notification has been clicked on.
- */
- public static void onNotificationClick(final String name, final String cookie) {
- if (GeckoThread.isRunning()) {
- notifyAlertListener(name, "alertclickcallback", cookie);
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void showNotification(String name, String cookie, String title,
- String text, String host, String imageUrl,
- String persistentData) {
- if (persistentData == null) {
- getNotificationListener().showNotification(name, cookie, title, text, host, imageUrl);
- return;
- }
-
- getNotificationListener().showPersistentNotification(
- name, cookie, title, text, host, imageUrl, persistentData);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void closeNotification(String name) {
- getNotificationListener().closeNotification(name);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public static int getDpi() {
- if (sDensityDpi == 0) {
- sDensityDpi = getApplicationContext().getResources().getDisplayMetrics().densityDpi;
- }
-
- return sDensityDpi;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static float getDensity() {
- return getApplicationContext().getResources().getDisplayMetrics().density;
- }
-
- private static boolean isHighMemoryDevice() {
- return HardwareUtils.getMemSize() > HIGH_MEMORY_DEVICE_THRESHOLD_MB;
- }
-
- /**
- * Returns the colour depth of the default screen. This will either be
- * 24 or 16.
- */
- @WrapForJNI(calledFrom = "gecko")
- public static synchronized int getScreenDepth() {
- if (sScreenDepth == 0) {
- sScreenDepth = 16;
- PixelFormat info = new PixelFormat();
- final WindowManager wm = (WindowManager)
- getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
- PixelFormat.getPixelFormatInfo(wm.getDefaultDisplay().getPixelFormat(), info);
- if (info.bitsPerPixel >= 24 && isHighMemoryDevice()) {
- sScreenDepth = 24;
- }
- }
-
- return sScreenDepth;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static synchronized void setScreenDepthOverride(int aScreenDepth) {
- if (sScreenDepth != 0) {
- Log.e(LOGTAG, "Tried to override screen depth after it's already been set");
- return;
- }
-
- sScreenDepth = aScreenDepth;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void setFullScreen(boolean fullscreen) {
- if (getGeckoInterface() != null)
- getGeckoInterface().setFullScreen(fullscreen);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void performHapticFeedback(boolean aIsLongPress) {
- // Don't perform haptic feedback if a vibration is currently playing,
- // because the haptic feedback will nuke the vibration.
- if (!sVibrationMaybePlaying || System.nanoTime() >= sVibrationEndTime) {
- LayerView layerView = getLayerView();
- layerView.performHapticFeedback(aIsLongPress ?
- HapticFeedbackConstants.LONG_PRESS :
- HapticFeedbackConstants.VIRTUAL_KEY);
- }
- }
-
- private static Vibrator vibrator() {
- return (Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
- }
-
- // Helper method to convert integer array to long array.
- private static long[] convertIntToLongArray(int[] input) {
- long[] output = new long[input.length];
- for (int i = 0; i < input.length; i++) {
- output[i] = input[i];
- }
- return output;
- }
-
- // Vibrate only if haptic feedback is enabled.
- public static void vibrateOnHapticFeedbackEnabled(int[] milliseconds) {
- if (Settings.System.getInt(getApplicationContext().getContentResolver(),
- Settings.System.HAPTIC_FEEDBACK_ENABLED, 0) > 0) {
- vibrate(convertIntToLongArray(milliseconds), -1);
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void vibrate(long milliseconds) {
- sVibrationEndTime = System.nanoTime() + milliseconds * 1000000;
- sVibrationMaybePlaying = true;
- vibrator().vibrate(milliseconds);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void vibrate(long[] pattern, int repeat) {
- // If pattern.length is even, the last element in the pattern is a
- // meaningless delay, so don't include it in vibrationDuration.
- long vibrationDuration = 0;
- int iterLen = pattern.length - (pattern.length % 2 == 0 ? 1 : 0);
- for (int i = 0; i < iterLen; i++) {
- vibrationDuration += pattern[i];
- }
-
- sVibrationEndTime = System.nanoTime() + vibrationDuration * 1000000;
- sVibrationMaybePlaying = true;
- vibrator().vibrate(pattern, repeat);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void cancelVibrate() {
- sVibrationMaybePlaying = false;
- sVibrationEndTime = 0;
- vibrator().cancel();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void setKeepScreenOn(final boolean on) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- // TODO
- }
- });
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean isNetworkLinkUp() {
- ConnectivityManager cm = (ConnectivityManager)
- getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- try {
- NetworkInfo info = cm.getActiveNetworkInfo();
- if (info == null || !info.isConnected())
- return false;
- } catch (SecurityException se) {
- return false;
- }
- return true;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean isNetworkLinkKnown() {
- ConnectivityManager cm = (ConnectivityManager)
- getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- try {
- if (cm.getActiveNetworkInfo() == null)
- return false;
- } catch (SecurityException se) {
- return false;
- }
- return true;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static int getNetworkLinkType() {
- ConnectivityManager cm = (ConnectivityManager)
- getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo info = cm.getActiveNetworkInfo();
- if (info == null) {
- return LINK_TYPE_UNKNOWN;
- }
-
- switch (info.getType()) {
- case ConnectivityManager.TYPE_ETHERNET:
- return LINK_TYPE_ETHERNET;
- case ConnectivityManager.TYPE_WIFI:
- return LINK_TYPE_WIFI;
- case ConnectivityManager.TYPE_WIMAX:
- return LINK_TYPE_WIMAX;
- case ConnectivityManager.TYPE_MOBILE:
- break; // We will handle sub-types after the switch.
- default:
- Log.w(LOGTAG, "Ignoring the current network type.");
- return LINK_TYPE_UNKNOWN;
- }
-
- TelephonyManager tm = (TelephonyManager)
- getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
- if (tm == null) {
- Log.e(LOGTAG, "Telephony service does not exist");
- return LINK_TYPE_UNKNOWN;
- }
-
- switch (tm.getNetworkType()) {
- case TelephonyManager.NETWORK_TYPE_IDEN:
- case TelephonyManager.NETWORK_TYPE_CDMA:
- case TelephonyManager.NETWORK_TYPE_GPRS:
- return LINK_TYPE_2G;
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- case TelephonyManager.NETWORK_TYPE_EDGE:
- return LINK_TYPE_2G; // 2.5G
- case TelephonyManager.NETWORK_TYPE_UMTS:
- case TelephonyManager.NETWORK_TYPE_EVDO_0:
- return LINK_TYPE_3G;
- 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 LINK_TYPE_3G; // 3.5G
- case TelephonyManager.NETWORK_TYPE_HSPAP:
- return LINK_TYPE_3G; // 3.75G
- case TelephonyManager.NETWORK_TYPE_LTE:
- return LINK_TYPE_4G; // 3.9G
- case TelephonyManager.NETWORK_TYPE_UNKNOWN:
- default:
- Log.w(LOGTAG, "Connected to an unknown mobile network!");
- return LINK_TYPE_UNKNOWN;
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static int[] getSystemColors() {
- // attrsAppearance[] must correspond to AndroidSystemColors structure in android/AndroidBridge.h
- final int[] attrsAppearance = {
- android.R.attr.textColor,
- android.R.attr.textColorPrimary,
- android.R.attr.textColorPrimaryInverse,
- android.R.attr.textColorSecondary,
- android.R.attr.textColorSecondaryInverse,
- android.R.attr.textColorTertiary,
- android.R.attr.textColorTertiaryInverse,
- android.R.attr.textColorHighlight,
- android.R.attr.colorForeground,
- android.R.attr.colorBackground,
- android.R.attr.panelColorForeground,
- android.R.attr.panelColorBackground
- };
-
- int[] result = new int[attrsAppearance.length];
-
- final ContextThemeWrapper contextThemeWrapper =
- new ContextThemeWrapper(getApplicationContext(), android.R.style.TextAppearance);
-
- final TypedArray appearance = contextThemeWrapper.getTheme().obtainStyledAttributes(attrsAppearance);
-
- if (appearance != null) {
- for (int i = 0; i < appearance.getIndexCount(); i++) {
- int idx = appearance.getIndex(i);
- int color = appearance.getColor(idx, 0);
- result[idx] = color;
- }
- appearance.recycle();
- }
-
- return result;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public static void killAnyZombies() {
- GeckoProcessesVisitor visitor = new GeckoProcessesVisitor() {
- @Override
- public boolean callback(int pid) {
- if (pid != android.os.Process.myPid())
- android.os.Process.killProcess(pid);
- return true;
- }
- };
-
- EnumerateGeckoProcesses(visitor);
- }
-
- interface GeckoProcessesVisitor {
- boolean callback(int pid);
- }
-
- private static void EnumerateGeckoProcesses(GeckoProcessesVisitor visiter) {
- int pidColumn = -1;
- int userColumn = -1;
-
- try {
- // run ps and parse its output
- java.lang.Process ps = Runtime.getRuntime().exec("ps");
- BufferedReader in = new BufferedReader(new InputStreamReader(ps.getInputStream()),
- 2048);
-
- String headerOutput = in.readLine();
-
- // figure out the column offsets. We only care about the pid and user fields
- StringTokenizer st = new StringTokenizer(headerOutput);
-
- int tokenSoFar = 0;
- while (st.hasMoreTokens()) {
- String next = st.nextToken();
- if (next.equalsIgnoreCase("PID"))
- pidColumn = tokenSoFar;
- else if (next.equalsIgnoreCase("USER"))
- userColumn = tokenSoFar;
- tokenSoFar++;
- }
-
- // alright, the rest are process entries.
- String psOutput = null;
- while ((psOutput = in.readLine()) != null) {
- String[] split = psOutput.split("\\s+");
- if (split.length <= pidColumn || split.length <= userColumn)
- continue;
- int uid = android.os.Process.getUidForName(split[userColumn]);
- if (uid == android.os.Process.myUid() &&
- !split[split.length - 1].equalsIgnoreCase("ps")) {
- int pid = Integer.parseInt(split[pidColumn]);
- boolean keepGoing = visiter.callback(pid);
- if (keepGoing == false)
- break;
- }
- }
- in.close();
- }
- catch (Exception e) {
- Log.w(LOGTAG, "Failed to enumerate Gecko processes.", e);
- }
- }
-
- public static String getAppNameByPID(int pid) {
- BufferedReader cmdlineReader = null;
- String path = "/proc/" + pid + "/cmdline";
- try {
- File cmdlineFile = new File(path);
- if (!cmdlineFile.exists())
- return "";
- cmdlineReader = new BufferedReader(new FileReader(cmdlineFile));
- return cmdlineReader.readLine().trim();
- } catch (Exception ex) {
- return "";
- } finally {
- if (null != cmdlineReader) {
- try {
- cmdlineReader.close();
- } catch (Exception e) { }
- }
- }
- }
-
- public static void listOfOpenFiles() {
- int pidColumn = -1;
- int nameColumn = -1;
-
- try {
- String filter = GeckoProfile.get(getApplicationContext()).getDir().toString();
- Log.d(LOGTAG, "[OPENFILE] Filter: " + filter);
-
- // run lsof and parse its output
- java.lang.Process lsof = Runtime.getRuntime().exec("lsof");
- BufferedReader in = new BufferedReader(new InputStreamReader(lsof.getInputStream()), 2048);
-
- String headerOutput = in.readLine();
- StringTokenizer st = new StringTokenizer(headerOutput);
- int token = 0;
- while (st.hasMoreTokens()) {
- String next = st.nextToken();
- if (next.equalsIgnoreCase("PID"))
- pidColumn = token;
- else if (next.equalsIgnoreCase("NAME"))
- nameColumn = token;
- token++;
- }
-
- // alright, the rest are open file entries.
- Map<Integer, String> pidNameMap = new TreeMap<Integer, String>();
- String output = null;
- while ((output = in.readLine()) != null) {
- String[] split = output.split("\\s+");
- if (split.length <= pidColumn || split.length <= nameColumn)
- continue;
- final Integer pid = Integer.valueOf(split[pidColumn]);
- String name = pidNameMap.get(pid);
- if (name == null) {
- name = getAppNameByPID(pid.intValue());
- pidNameMap.put(pid, name);
- }
- String file = split[nameColumn];
- if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(file) && file.startsWith(filter))
- Log.d(LOGTAG, "[OPENFILE] " + name + "(" + split[pidColumn] + ") : " + file);
- }
- in.close();
- } catch (Exception e) { }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static byte[] getIconForExtension(String aExt, int iconSize) {
- try {
- if (iconSize <= 0)
- iconSize = 16;
-
- if (aExt != null && aExt.length() > 1 && aExt.charAt(0) == '.')
- aExt = aExt.substring(1);
-
- PackageManager pm = getApplicationContext().getPackageManager();
- Drawable icon = getDrawableForExtension(pm, aExt);
- if (icon == null) {
- // Use a generic icon
- icon = pm.getDefaultActivityIcon();
- }
-
- Bitmap bitmap = ((BitmapDrawable)icon).getBitmap();
- if (bitmap.getWidth() != iconSize || bitmap.getHeight() != iconSize)
- bitmap = Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, true);
-
- ByteBuffer buf = ByteBuffer.allocate(iconSize * iconSize * 4);
- bitmap.copyPixelsToBuffer(buf);
-
- return buf.array();
- }
- catch (Exception e) {
- Log.w(LOGTAG, "getIconForExtension failed.", e);
- return null;
- }
- }
-
- public static String getMimeTypeFromExtension(String ext) {
- final MimeTypeMap mtm = MimeTypeMap.getSingleton();
- return mtm.getMimeTypeFromExtension(ext);
- }
-
- private static Drawable getDrawableForExtension(PackageManager pm, String aExt) {
- Intent intent = new Intent(Intent.ACTION_VIEW);
- final String mimeType = getMimeTypeFromExtension(aExt);
- if (mimeType != null && mimeType.length() > 0)
- intent.setType(mimeType);
- else
- return null;
-
- List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
- if (list.size() == 0)
- return null;
-
- ResolveInfo resolveInfo = list.get(0);
-
- if (resolveInfo == null)
- return null;
-
- ActivityInfo activityInfo = resolveInfo.activityInfo;
-
- return activityInfo.loadIcon(pm);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean getShowPasswordSetting() {
- try {
- int showPassword =
- Settings.System.getInt(getApplicationContext().getContentResolver(),
- Settings.System.TEXT_SHOW_PASSWORD, 1);
- return (showPassword > 0);
- }
- catch (Exception e) {
- return true;
- }
- }
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- public static native void onFullScreenPluginHidden(View view);
-
- @WrapForJNI(calledFrom = "gecko")
- private static void addFullScreenPluginView(View view) {
- if (getGeckoInterface() != null)
- getGeckoInterface().addPluginView(view);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void removeFullScreenPluginView(View view) {
- if (getGeckoInterface() != null)
- getGeckoInterface().removePluginView(view);
- }
-
- /**
- * A plugin that wish to be loaded in the WebView must provide this permission
- * in their AndroidManifest.xml.
- */
- public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
- public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
-
- 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 static final ArrayList<PackageInfo> mPackageInfoCache = new ArrayList<>();
-
- // Returns null if plugins are blocked on the device.
- static String[] getPluginDirectories() {
-
- // Block on Pixel C.
- if ((new File("/system/lib/hw/power.dragon.so")).exists()) {
- Log.w(LOGTAG, "Blocking plugins because of Pixel C device (bug 1255122)");
- return null;
- }
- // An awful hack to detect Tegra devices. Easiest way to do it without spinning up a EGL context.
- boolean isTegra = (new File("/system/lib/hw/gralloc.tegra.so")).exists() ||
- (new File("/system/lib/hw/gralloc.tegra3.so")).exists() ||
- (new File("/sys/class/nvidia-gpu")).exists();
- if (isTegra) {
- // disable on KitKat (bug 957694)
- if (Versions.feature19Plus) {
- Log.w(LOGTAG, "Blocking plugins because of Tegra (bug 957694)");
- return null;
- }
-
- // disable Flash on Tegra ICS with CM9 and other custom firmware (bug 736421)
- final File vfile = new File("/proc/version");
- try {
- if (vfile.canRead()) {
- final BufferedReader reader = new BufferedReader(new FileReader(vfile));
- try {
- final String version = reader.readLine();
- if (version.indexOf("CM9") != -1 ||
- version.indexOf("cyanogen") != -1 ||
- version.indexOf("Nova") != -1) {
- Log.w(LOGTAG, "Blocking plugins because of Tegra 2 + unofficial ICS bug (bug 736421)");
- return null;
- }
- } finally {
- reader.close();
- }
- }
- } catch (IOException ex) {
- // Do nothing.
- }
- }
-
- ArrayList<String> directories = new ArrayList<String>();
- PackageManager pm = getApplicationContext().getPackageManager();
- List<ResolveInfo> plugins = pm.queryIntentServices(new Intent(PLUGIN_ACTION),
- 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, "Ignoring bad plugin.");
- continue;
- }
-
- // Blacklist HTC's flash lite.
- // See bug #704516 - We're not quite sure what Flash Lite does,
- // but loading it causes Flash to give errors and fail to draw.
- if (serviceInfo.packageName.equals("com.htc.flashliteplugin")) {
- Log.w(LOGTAG, "Skipping HTC's flash lite plugin");
- continue;
- }
-
-
- // 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, "Not 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, "Not 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, "Not 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, "Not 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 defined type.");
- 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()]);
- }
-
- static String getPluginPackage(String pluginLib) {
-
- if (pluginLib == null || pluginLib.length() == 0) {
- return null;
- }
-
- synchronized (mPackageInfoCache) {
- for (PackageInfo pkgInfo : mPackageInfoCache) {
- if (pluginLib.contains(pkgInfo.packageName)) {
- return pkgInfo.packageName;
- }
- }
- }
-
- return null;
- }
-
- static Class<?> getPluginClass(String packageName, String className)
- throws NameNotFoundException, ClassNotFoundException {
- Context pluginContext = getApplicationContext().createPackageContext(packageName,
- Context.CONTEXT_INCLUDE_CODE |
- Context.CONTEXT_IGNORE_SECURITY);
- ClassLoader pluginCL = pluginContext.getClassLoader();
- return pluginCL.loadClass(className);
- }
-
- @WrapForJNI
- private static Class<?> loadPluginClass(String className, String libName) {
- if (getGeckoInterface() == null)
- return null;
- try {
- final String packageName = getPluginPackage(libName);
- final int contextFlags = Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY;
- final Context pluginContext = getApplicationContext().createPackageContext(
- packageName, contextFlags);
- return pluginContext.getClassLoader().loadClass(className);
- } catch (java.lang.ClassNotFoundException cnfe) {
- Log.w(LOGTAG, "Couldn't find plugin class " + className, cnfe);
- return null;
- } catch (android.content.pm.PackageManager.NameNotFoundException nnfe) {
- Log.w(LOGTAG, "Couldn't find package.", nnfe);
- return null;
- }
- }
-
- private static Context sApplicationContext;
- private static ContextGetter sContextGetter;
-
- @Deprecated
- @WrapForJNI
- public static Context getContext() {
- return sContextGetter.getContext();
- }
-
- public static void setContextGetter(ContextGetter cg) {
- sContextGetter = cg;
- }
-
- @WrapForJNI
- public static Context getApplicationContext() {
- return sApplicationContext;
- }
-
- public static void setApplicationContext(final Context context) {
- sApplicationContext = context;
- }
-
- public static SharedPreferences getSharedPreferences() {
- if (sContextGetter == null) {
- throw new IllegalStateException("No ContextGetter; cannot fetch prefs.");
- }
- return sContextGetter.getSharedPreferences();
- }
-
- public interface AppStateListener {
- public void onPause();
- public void onResume();
- public void onOrientationChanged();
- }
-
- public interface GeckoInterface {
- public EventDispatcher getAppEventDispatcher();
- public GeckoProfile getProfile();
- public Activity getActivity();
- public String getDefaultUAString();
- public void doRestart();
- public void setFullScreen(boolean fullscreen);
- public void addPluginView(View view);
- public void removePluginView(final View view);
- public void enableOrientationListener();
- public void disableOrientationListener();
- public void addAppStateListener(AppStateListener listener);
- public void removeAppStateListener(AppStateListener listener);
- public void notifyWakeLockChanged(String topic, String state);
- public boolean areTabsShown();
- public AbsoluteLayout getPluginContainer();
- public void notifyCheckUpdateResult(String result);
- public void invalidateOptionsMenu();
-
- /**
- * Create a shortcut -- generally a home-screen icon -- linking the given title to the given URI.
- * <p>
- * This method is always invoked on the Gecko thread.
- *
- * @param title of URI to link to.
- * @param URI to link to.
- */
- public void createShortcut(String title, String URI);
-
- /**
- * Check if the given URI is visited.
- * <p/>
- * If it has been visited, call {@link GeckoAppShell#notifyUriVisited(String)}. (If it
- * has not been visited, do nothing.)
- * <p/>
- * This method is always invoked on the Gecko thread.
- *
- * @param uri to check.
- */
- public void checkUriVisited(String uri);
-
- /**
- * Mark the given URI as visited in Gecko.
- * <p/>
- * Implementors may maintain some local store of visited URIs in order to be able to
- * answer {@link #checkUriVisited(String)} requests affirmatively.
- * <p/>
- * This method is always invoked on the Gecko thread.
- *
- * @param uri to mark.
- */
- public void markUriVisited(final String uri);
-
- /**
- * Set the title of the given URI, as determined by Gecko.
- * <p/>
- * This method is always invoked on the Gecko thread.
- *
- * @param uri given.
- * @param title to associate with the given URI.
- */
- public void setUriTitle(final String uri, final String title);
-
- public void setAccessibilityEnabled(boolean enabled);
-
- public boolean openUriExternal(String targetURI, String mimeType, String packageName, String className, String action, String title);
-
- public String[] getHandlersForMimeType(String mimeType, String action);
- public String[] getHandlersForURL(String url, String action);
-
- /**
- * URI of the underlying chrome window to be opened, or null to use the default GeckoView
- * XUL container <tt>chrome://browser/content/geckoview.xul</tt>. See
- * <a href="https://developer.mozilla.org/en/docs/toolkit.defaultChromeURI">https://developer.mozilla.org/en/docs/toolkit.defaultChromeURI</a>
- *
- * @return URI or null.
- */
- String getDefaultChromeURI();
- };
-
- private static GeckoInterface sGeckoInterface;
-
- public static GeckoInterface getGeckoInterface() {
- return sGeckoInterface;
- }
-
- public static void setGeckoInterface(GeckoInterface aGeckoInterface) {
- sGeckoInterface = aGeckoInterface;
- }
-
- /* package */ static Camera sCamera;
-
- private static final int kPreferredFPS = 25;
- private static byte[] sCameraBuffer;
-
- private static class CameraCallback implements Camera.PreviewCallback {
- @WrapForJNI(calledFrom = "gecko")
- private static native void onFrameData(int camera, byte[] data);
-
- private final int mCamera;
-
- public CameraCallback(int camera) {
- mCamera = camera;
- }
-
- @Override
- public void onPreviewFrame(byte[] data, Camera camera) {
- onFrameData(mCamera, data);
-
- if (sCamera != null) {
- sCamera.addCallbackBuffer(sCameraBuffer);
- }
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- if (getGeckoInterface() != null)
- getGeckoInterface().enableOrientationListener();
- } catch (Exception e) { }
- }
- });
-
- // [0] = 0|1 (failure/success)
- // [1] = width
- // [2] = height
- // [3] = fps
- int[] result = new int[4];
- result[0] = 0;
-
- if (Camera.getNumberOfCameras() == 0) {
- return result;
- }
-
- try {
- sCamera = Camera.open(aCamera);
-
- Camera.Parameters params = sCamera.getParameters();
- params.setPreviewFormat(ImageFormat.NV21);
-
- // use the preview fps closest to 25 fps.
- int fpsDelta = 1000;
- try {
- Iterator<Integer> it = params.getSupportedPreviewFrameRates().iterator();
- while (it.hasNext()) {
- int nFps = it.next();
- if (Math.abs(nFps - kPreferredFPS) < fpsDelta) {
- fpsDelta = Math.abs(nFps - kPreferredFPS);
- params.setPreviewFrameRate(nFps);
- }
- }
- } catch (Exception e) {
- params.setPreviewFrameRate(kPreferredFPS);
- }
-
- // set up the closest preview size available
- Iterator<Camera.Size> sit = params.getSupportedPreviewSizes().iterator();
- int sizeDelta = 10000000;
- int bufferSize = 0;
- while (sit.hasNext()) {
- Camera.Size size = sit.next();
- if (Math.abs(size.width * size.height - aWidth * aHeight) < sizeDelta) {
- sizeDelta = Math.abs(size.width * size.height - aWidth * aHeight);
- params.setPreviewSize(size.width, size.height);
- bufferSize = size.width * size.height;
- }
- }
-
- sCamera.setParameters(params);
- sCameraBuffer = new byte[(bufferSize * 12) / 8];
- sCamera.addCallbackBuffer(sCameraBuffer);
- sCamera.setPreviewCallbackWithBuffer(new CameraCallback(aCamera));
- sCamera.startPreview();
- params = sCamera.getParameters();
- result[0] = 1;
- result[1] = params.getPreviewSize().width;
- result[2] = params.getPreviewSize().height;
- result[3] = params.getPreviewFrameRate();
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "initCamera RuntimeException.", e);
- result[0] = result[1] = result[2] = result[3] = 0;
- }
- return result;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static synchronized void closeCamera() {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- if (getGeckoInterface() != null)
- getGeckoInterface().disableOrientationListener();
- } catch (Exception e) { }
- }
- });
- if (sCamera != null) {
- sCamera.stopPreview();
- sCamera.release();
- sCamera = null;
- sCameraBuffer = null;
- }
- }
-
- /*
- * Battery API related methods.
- */
- @WrapForJNI(calledFrom = "gecko")
- private static void enableBatteryNotifications() {
- GeckoBatteryManager.enableNotifications();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void handleGeckoMessage(final NativeJSContainer message) {
- boolean success = EventDispatcher.getInstance().dispatchEvent(message);
- if (getGeckoInterface() != null && getGeckoInterface().getAppEventDispatcher() != null) {
- success |= getGeckoInterface().getAppEventDispatcher().dispatchEvent(message);
- }
-
- if (!success) {
- final String type = message.optString("type", null);
- final String guid = message.optString(EventDispatcher.GUID, null);
- if (type != null && guid != null) {
- (new EventDispatcher.GeckoEventCallback(guid, type)).sendError("No listeners for request");
- }
- }
- message.disposeNative();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void disableBatteryNotifications() {
- GeckoBatteryManager.disableNotifications();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static double[] getCurrentBatteryInformation() {
- return GeckoBatteryManager.getCurrentInformation();
- }
-
- @WrapForJNI(stubName = "CheckURIVisited", calledFrom = "gecko")
- private static void checkUriVisited(String uri) {
- final GeckoInterface geckoInterface = getGeckoInterface();
- if (geckoInterface == null) {
- return;
- }
- geckoInterface.checkUriVisited(uri);
- }
-
- @WrapForJNI(stubName = "MarkURIVisited", calledFrom = "gecko")
- private static void markUriVisited(final String uri) {
- final GeckoInterface geckoInterface = getGeckoInterface();
- if (geckoInterface == null) {
- return;
- }
- geckoInterface.markUriVisited(uri);
- }
-
- @WrapForJNI(stubName = "SetURITitle", calledFrom = "gecko")
- private static void setUriTitle(final String uri, final String title) {
- final GeckoInterface geckoInterface = getGeckoInterface();
- if (geckoInterface == null) {
- return;
- }
- geckoInterface.setUriTitle(uri, title);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void hideProgressDialog() {
- // unused stub
- }
-
- /* Called by JNI from AndroidBridge, and by reflection from tests/BaseTest.java.in */
- @WrapForJNI(calledFrom = "gecko")
- @RobocopTarget
- public static boolean isTablet() {
- return HardwareUtils.isTablet();
- }
-
- private static boolean sImeWasEnabledOnLastResize = false;
- public static void viewSizeChanged() {
- GeckoView v = (GeckoView) getLayerView();
- if (v == null) {
- return;
- }
- boolean imeIsEnabled = v.isIMEEnabled();
- if (imeIsEnabled && !sImeWasEnabledOnLastResize) {
- // The IME just came up after not being up, so let's scroll
- // to the focused input.
- notifyObservers("ScrollTo:FocusedInput", "");
- }
- sImeWasEnabledOnLastResize = imeIsEnabled;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static double[] getCurrentNetworkInformation() {
- return GeckoNetworkManager.getInstance().getCurrentInformation();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void enableNetworkNotifications() {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- GeckoNetworkManager.getInstance().enableNotifications();
- }
- });
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void disableNetworkNotifications() {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- GeckoNetworkManager.getInstance().disableNotifications();
- }
- });
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static short getScreenOrientation() {
- return GeckoScreenOrientation.getInstance().getScreenOrientation().value;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static int getScreenAngle() {
- return GeckoScreenOrientation.getInstance().getAngle();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void enableScreenOrientationNotifications() {
- GeckoScreenOrientation.getInstance().enableNotifications();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void disableScreenOrientationNotifications() {
- GeckoScreenOrientation.getInstance().disableNotifications();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void lockScreenOrientation(int aOrientation) {
- GeckoScreenOrientation.getInstance().lock(aOrientation);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void unlockScreenOrientation() {
- GeckoScreenOrientation.getInstance().unlock();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void notifyWakeLockChanged(String topic, String state) {
- if (getGeckoInterface() != null)
- getGeckoInterface().notifyWakeLockChanged(topic, state);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean unlockProfile() {
- // Try to kill any zombie Fennec's that might be running
- GeckoAppShell.killAnyZombies();
-
- // Then force unlock this profile
- if (getGeckoInterface() != null) {
- GeckoProfile profile = getGeckoInterface().getProfile();
- File lock = profile.getFile(".parentlock");
- return lock.exists() && lock.delete();
- }
- return false;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static String getProxyForURI(String spec, String scheme, String host, int port) {
- final ProxySelector ps = new ProxySelector();
-
- Proxy proxy = ps.select(scheme, host);
- if (Proxy.NO_PROXY.equals(proxy)) {
- return "DIRECT";
- }
-
- switch (proxy.type()) {
- case HTTP:
- return "PROXY " + proxy.address().toString();
- case SOCKS:
- return "SOCKS " + proxy.address().toString();
- }
-
- return "DIRECT";
- }
-
- @WrapForJNI
- private static InputStream createInputStream(URLConnection connection) throws IOException {
- return connection.getInputStream();
- }
-
- private static class BitmapConnection extends URLConnection {
- private Bitmap bitmap;
-
- BitmapConnection(Bitmap b) throws MalformedURLException, IOException {
- super(null);
- bitmap = b;
- }
-
- @Override
- public void connect() {}
-
- @Override
- public InputStream getInputStream() throws IOException {
- return new BitmapInputStream();
- }
-
- @Override
- public String getContentType() {
- return "image/png";
- }
-
- private final class BitmapInputStream extends PipedInputStream {
- private boolean mHaveConnected = false;
-
- @Override
- public synchronized int read(byte[] buffer, int byteOffset, int byteCount)
- throws IOException {
- if (mHaveConnected) {
- return super.read(buffer, byteOffset, byteCount);
- }
-
- final PipedOutputStream output = new PipedOutputStream();
- connect(output);
- ThreadUtils.postToBackgroundThread(
- new Runnable() {
- @Override
- public void run() {
- try {
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, output);
- output.close();
- } catch (IOException ioe) { }
- }
- });
- mHaveConnected = true;
- return super.read(buffer, byteOffset, byteCount);
- }
- }
- }
-
- @WrapForJNI
- private static URLConnection getConnection(String url) {
- try {
- String spec;
- if (url.startsWith("android://")) {
- spec = url.substring(10);
- } else {
- spec = url.substring(8);
- }
-
- // Check if we are loading a package icon.
- try {
- if (spec.startsWith("icon/")) {
- String[] splits = spec.split("/");
- if (splits.length != 2) {
- return null;
- }
- final String pkg = splits[1];
- final PackageManager pm = getApplicationContext().getPackageManager();
- final Drawable d = pm.getApplicationIcon(pkg);
- final Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(d);
- return new BitmapConnection(bitmap);
- }
- } catch (Exception ex) {
- Log.e(LOGTAG, "error", ex);
- }
-
- // if the colon got stripped, put it back
- int colon = spec.indexOf(':');
- if (colon == -1 || colon > spec.indexOf('/')) {
- spec = spec.replaceFirst("/", ":/");
- }
- } catch (Exception ex) {
- return null;
- }
- return null;
- }
-
- @WrapForJNI
- private static String connectionGetMimeType(URLConnection connection) {
- return connection.getContentType();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static int getMaxTouchPoints() {
- PackageManager pm = getApplicationContext().getPackageManager();
- if (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND)) {
- // at least, 5+ fingers.
- return 5;
- } else if (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT)) {
- // at least, 2+ fingers.
- return 2;
- } else if (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)) {
- // 2 fingers
- return 2;
- } else if (pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
- // 1 finger
- return 1;
- }
- return 0;
- }
-
- public static synchronized void resetScreenSize() {
- sScreenSize = null;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static synchronized Rect getScreenSize() {
- if (sScreenSize == null) {
- final WindowManager wm = (WindowManager)
- getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
- final Display disp = wm.getDefaultDisplay();
- sScreenSize = new Rect(0, 0, disp.getWidth(), disp.getHeight());
- }
- return sScreenSize;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java
deleted file mode 100644
index 1a41c390a..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java
+++ /dev/null
@@ -1,202 +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.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.BatteryManager;
-import android.os.Build;
-import android.os.SystemClock;
-import android.util.Log;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-
-public class GeckoBatteryManager extends BroadcastReceiver {
- private static final String LOGTAG = "GeckoBatteryManager";
-
- // Those constants should be keep in sync with the ones in:
- // dom/battery/Constants.h
- private final static double kDefaultLevel = 1.0;
- private final static boolean kDefaultCharging = true;
- private final static double kDefaultRemainingTime = 0.0;
- private final static double kUnknownRemainingTime = -1.0;
-
- private static long sLastLevelChange;
- private static boolean sNotificationsEnabled;
- private static double sLevel = kDefaultLevel;
- private static boolean sCharging = kDefaultCharging;
- private static double sRemainingTime = kDefaultRemainingTime;
-
- private static final GeckoBatteryManager sInstance = new GeckoBatteryManager();
-
- private final IntentFilter mFilter;
- private Context mApplicationContext;
- private boolean mIsEnabled;
-
- public static GeckoBatteryManager getInstance() {
- return sInstance;
- }
-
- private GeckoBatteryManager() {
- mFilter = new IntentFilter();
- mFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
- }
-
- public synchronized void start(final Context context) {
- if (mIsEnabled) {
- Log.w(LOGTAG, "Already started!");
- return;
- }
-
- mApplicationContext = context.getApplicationContext();
- // registerReceiver will return null if registering fails.
- if (mApplicationContext.registerReceiver(this, mFilter) == null) {
- Log.e(LOGTAG, "Registering receiver failed");
- } else {
- mIsEnabled = true;
- }
- }
-
- public synchronized void stop() {
- if (!mIsEnabled) {
- Log.w(LOGTAG, "Already stopped!");
- return;
- }
-
- mApplicationContext.unregisterReceiver(this);
- mApplicationContext = null;
- mIsEnabled = false;
- }
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- private static native void onBatteryChange(double level, boolean charging,
- double remainingTime);
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
- Log.e(LOGTAG, "Got an unexpected intent!");
- return;
- }
-
- boolean previousCharging = isCharging();
- double previousLevel = getLevel();
-
- // NOTE: it might not be common (in 2012) but technically, Android can run
- // on a device that has no battery so we want to make sure it's not the case
- // before bothering checking for battery state.
- // However, the Galaxy Nexus phone advertises itself as battery-less which
- // force us to special-case the logic.
- // See the Google bug: https://code.google.com/p/android/issues/detail?id=22035
- if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false) ||
- Build.MODEL.equals("Galaxy Nexus")) {
- int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
- if (plugged == -1) {
- sCharging = kDefaultCharging;
- Log.e(LOGTAG, "Failed to get the plugged status!");
- } else {
- // Likely, if plugged > 0, it's likely plugged and charging but the doc
- // isn't clear about that.
- 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 = 0;
- }
-
- // We need two doubles because sLevel is a double.
- double current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
- double max = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
- if (current == -1 || max == -1) {
- Log.e(LOGTAG, "Failed to get battery level!");
- sLevel = kDefaultLevel;
- } else {
- sLevel = current / max;
- }
-
- if (sLevel == 1.0 && sCharging) {
- sRemainingTime = kDefaultRemainingTime;
- } else if (sLevel != previousLevel) {
- // Estimate remaining time.
- if (sLastLevelChange != 0) {
- // Use elapsedRealtime() because we want to track time across device sleeps.
- long currentTime = SystemClock.elapsedRealtime();
- long dt = (currentTime - sLastLevelChange) / 1000;
- double dLevel = sLevel - previousLevel;
-
- if (sCharging) {
- if (dLevel < 0) {
- sRemainingTime = kUnknownRemainingTime;
- } else {
- sRemainingTime = Math.round(dt / dLevel * (1.0 - sLevel));
- }
- } else {
- if (dLevel > 0) {
- Log.w(LOGTAG, "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 = SystemClock.elapsedRealtime();
- }
- }
- } 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())) {
- onBatteryChange(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() };
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
deleted file mode 100644
index 695cff443..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
+++ /dev/null
@@ -1,1589 +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.reflect.Array;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import org.json.JSONObject;
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.gfx.LayerView;
-import org.mozilla.gecko.mozglue.JNIObject;
-import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
-
-import android.graphics.RectF;
-import android.os.Handler;
-import android.os.Looper;
-import android.text.Editable;
-import android.text.InputFilter;
-import android.text.NoCopySpan;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.style.CharacterStyle;
-import android.util.Log;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-
-/*
- GeckoEditable implements only some functions of Editable
- The field mText contains the actual underlying
- SpannableStringBuilder/Editable that contains our text.
-*/
-final class GeckoEditable extends JNIObject
- implements InvocationHandler, Editable, GeckoEditableClient {
-
- private static final boolean DEBUG = false;
- private static final String LOGTAG = "GeckoEditable";
-
- // Filters to implement Editable's filtering functionality
- private InputFilter[] mFilters;
-
- private final AsyncText mText;
- private final Editable mProxy;
- private final ConcurrentLinkedQueue<Action> mActions;
- private KeyCharacterMap mKeyMap;
-
- // mIcRunHandler is the Handler that currently runs Gecko-to-IC Runnables
- // mIcPostHandler is the Handler to post Gecko-to-IC Runnables to
- // The two can be different when switching from one handler to another
- private Handler mIcRunHandler;
- private Handler mIcPostHandler;
-
- /* package */ GeckoEditableListener mListener;
- /* package */ GeckoView mView;
-
- /* package */ boolean mInBatchMode; // Used by IC thread
- /* package */ boolean mNeedSync; // Used by IC thread
- // Gecko side needs an updated composition from Java;
- private boolean mNeedUpdateComposition; // Used by IC thread
- private boolean mSuppressKeyUp; // Used by IC thread
-
- private boolean mGeckoFocused; // Used by Gecko thread
- private boolean mIgnoreSelectionChange; // Used by Gecko thread
-
- private static final int IME_RANGE_CARETPOSITION = 1;
- private static final int IME_RANGE_RAWINPUT = 2;
- private static final int IME_RANGE_SELECTEDRAWTEXT = 3;
- private static final int IME_RANGE_CONVERTEDTEXT = 4;
- private static final int IME_RANGE_SELECTEDCONVERTEDTEXT = 5;
-
- private static final int IME_RANGE_LINE_NONE = 0;
- private static final int IME_RANGE_LINE_DOTTED = 1;
- private static final int IME_RANGE_LINE_DASHED = 2;
- private static final int IME_RANGE_LINE_SOLID = 3;
- private static final int IME_RANGE_LINE_DOUBLE = 4;
- private static final int IME_RANGE_LINE_WAVY = 5;
-
- private static final int IME_RANGE_UNDERLINE = 1;
- private static final int IME_RANGE_FORECOLOR = 2;
- private static final int IME_RANGE_BACKCOLOR = 4;
- private static final int IME_RANGE_LINECOLOR = 8;
-
- @WrapForJNI(dispatchTo = "proxy")
- private native void onKeyEvent(int action, int keyCode, int scanCode, int metaState,
- long time, int unicodeChar, int baseUnicodeChar,
- int domPrintableKeyValue, int repeatCount, int flags,
- boolean isSynthesizedImeKey, KeyEvent event);
-
- private void onKeyEvent(KeyEvent event, int action, int savedMetaState,
- boolean isSynthesizedImeKey) {
- // Use a separate action argument so we can override the key's original action,
- // e.g. change ACTION_MULTIPLE to ACTION_DOWN. That way we don't have to allocate
- // a new key event just to change its action field.
- //
- // Normally we expect event.getMetaState() to reflect the current meta-state; however,
- // some software-generated key events may not have event.getMetaState() set, e.g. key
- // events from Swype. Therefore, it's necessary to combine the key's meta-states
- // with the meta-states that we keep separately in KeyListener
- final int metaState = event.getMetaState() | savedMetaState;
- final int unmodifiedMetaState = metaState &
- ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK | KeyEvent.META_META_MASK);
- final int unicodeChar = event.getUnicodeChar(metaState);
- final int domPrintableKeyValue =
- unicodeChar >= ' ' ? unicodeChar :
- unmodifiedMetaState != metaState ? event.getUnicodeChar(unmodifiedMetaState) :
- 0;
- onKeyEvent(action, event.getKeyCode(), event.getScanCode(),
- metaState, event.getEventTime(), unicodeChar,
- // e.g. for Ctrl+A, Android returns 0 for unicodeChar,
- // but Gecko expects 'a', so we return that in baseUnicodeChar.
- event.getUnicodeChar(0), domPrintableKeyValue, event.getRepeatCount(),
- event.getFlags(), isSynthesizedImeKey, event);
- }
-
- @WrapForJNI(dispatchTo = "proxy")
- private native void onImeSynchronize();
-
- @WrapForJNI(dispatchTo = "proxy")
- private native void onImeReplaceText(int start, int end, String text);
-
- @WrapForJNI(dispatchTo = "proxy")
- private native void onImeAddCompositionRange(int start, int end, int rangeType,
- int rangeStyles, int rangeLineStyle,
- boolean rangeBoldLine, int rangeForeColor,
- int rangeBackColor, int rangeLineColor);
-
- @WrapForJNI(dispatchTo = "proxy")
- private native void onImeUpdateComposition(int start, int end);
-
- @WrapForJNI(dispatchTo = "proxy")
- private native void onImeRequestCursorUpdates(int requestMode);
-
- /**
- * Class that encapsulates asynchronous text editing. There are two copies of the
- * text, a current copy and a shadow copy. Both can be modified independently through
- * the current*** and shadow*** methods, respectively. The current copy can only be
- * modified on the Gecko side and reflects the authoritative version of the text. The
- * shadow copy can only be modified on the IC side and reflects what we think the
- * current text is. Periodically, the shadow copy can be synced to the current copy
- * through syncShadowText, so the shadow copy once again refers to the same text as
- * the current copy.
- */
- private final class AsyncText {
- // The current text is the update-to-date version of the text, and is only updated
- // on the Gecko side.
- private final SpannableStringBuilder mCurrentText = new SpannableStringBuilder();
- // Track changes on the current side for syncing purposes.
- // Start of the changed range in current text since last sync.
- private int mCurrentStart = Integer.MAX_VALUE;
- // End of the changed range (before the change) in current text since last sync.
- private int mCurrentOldEnd;
- // End of the changed range (after the change) in current text since last sync.
- private int mCurrentNewEnd;
- // Track selection changes separately.
- private boolean mCurrentSelectionChanged;
-
- // The shadow text is what we think the current text is on the Java side, and is
- // periodically synced with the current text.
- private final SpannableStringBuilder mShadowText = new SpannableStringBuilder();
- // Track changes on the shadow side for syncing purposes.
- // Start of the changed range in shadow text since last sync.
- private int mShadowStart = Integer.MAX_VALUE;
- // End of the changed range (before the change) in shadow text since last sync.
- private int mShadowOldEnd;
- // End of the changed range (after the change) in shadow text since last sync.
- private int mShadowNewEnd;
-
- private void addCurrentChangeLocked(final int start, final int oldEnd, final int newEnd) {
- // Merge the new change into any existing change.
- mCurrentStart = Math.min(mCurrentStart, start);
- mCurrentOldEnd += Math.max(0, oldEnd - mCurrentNewEnd);
- mCurrentNewEnd = newEnd + Math.max(0, mCurrentNewEnd - oldEnd);
- }
-
- public synchronized void currentReplace(final int start, final int end,
- final CharSequence newText) {
- if (DEBUG) {
- ThreadUtils.assertOnGeckoThread();
- }
- mCurrentText.replace(start, end, newText);
- addCurrentChangeLocked(start, end, start + newText.length());
- }
-
- public synchronized void currentSetSelection(final int start, final int end) {
- if (DEBUG) {
- ThreadUtils.assertOnGeckoThread();
- }
- Selection.setSelection(mCurrentText, start, end);
- mCurrentSelectionChanged = true;
- }
-
- public synchronized void currentSetSpan(final Object obj, final int start,
- final int end, final int flags) {
- if (DEBUG) {
- ThreadUtils.assertOnGeckoThread();
- }
- mCurrentText.setSpan(obj, start, end, flags);
- addCurrentChangeLocked(start, end, end);
- }
-
- public synchronized void currentRemoveSpan(final Object obj) {
- if (DEBUG) {
- ThreadUtils.assertOnGeckoThread();
- }
- if (obj == null) {
- mCurrentText.clearSpans();
- addCurrentChangeLocked(0, mCurrentText.length(), mCurrentText.length());
- return;
- }
- final int start = mCurrentText.getSpanStart(obj);
- final int end = mCurrentText.getSpanEnd(obj);
- if (start < 0 || end < 0) {
- return;
- }
- mCurrentText.removeSpan(obj);
- addCurrentChangeLocked(start, end, end);
- }
-
- // Return Spanned instead of Editable because the returned object is supposed to
- // be read-only. Editing should be done through one of the current*** methods.
- public Spanned getCurrentText() {
- if (DEBUG) {
- ThreadUtils.assertOnGeckoThread();
- }
- return mCurrentText;
- }
-
- private void addShadowChange(final int start, final int oldEnd, final int newEnd) {
- // Merge the new change into any existing change.
- mShadowStart = Math.min(mShadowStart, start);
- mShadowOldEnd += Math.max(0, oldEnd - mShadowNewEnd);
- mShadowNewEnd = newEnd + Math.max(0, mShadowNewEnd - oldEnd);
- }
-
- public void shadowReplace(final int start, final int end,
- final CharSequence newText)
- {
- if (DEBUG) {
- assertOnIcThread();
- }
- mShadowText.replace(start, end, newText);
- addShadowChange(start, end, start + newText.length());
- }
-
- public void shadowSetSpan(final Object obj, final int start,
- final int end, final int flags) {
- if (DEBUG) {
- assertOnIcThread();
- }
- mShadowText.setSpan(obj, start, end, flags);
- addShadowChange(start, end, end);
- }
-
- public void shadowRemoveSpan(final Object obj) {
- if (DEBUG) {
- assertOnIcThread();
- }
- if (obj == null) {
- mShadowText.clearSpans();
- addShadowChange(0, mShadowText.length(), mShadowText.length());
- return;
- }
- final int start = mShadowText.getSpanStart(obj);
- final int end = mShadowText.getSpanEnd(obj);
- if (start < 0 || end < 0) {
- return;
- }
- mShadowText.removeSpan(obj);
- addShadowChange(start, end, end);
- }
-
- // Return Spanned instead of Editable because the returned object is supposed to
- // be read-only. Editing should be done through one of the shadow*** methods.
- public Spanned getShadowText() {
- if (DEBUG) {
- assertOnIcThread();
- }
- return mShadowText;
- }
-
- public synchronized void syncShadowText(final GeckoEditableListener listener) {
- if (DEBUG) {
- assertOnIcThread();
- }
-
- if (mCurrentStart > mCurrentOldEnd && mShadowStart > mShadowOldEnd) {
- // Still check selection changes.
- if (!mCurrentSelectionChanged) {
- return;
- }
- final int start = Selection.getSelectionStart(mCurrentText);
- final int end = Selection.getSelectionEnd(mCurrentText);
- Selection.setSelection(mShadowText, start, end);
- mCurrentSelectionChanged = false;
-
- if (listener != null) {
- listener.onSelectionChange();
- }
- return;
- }
-
- // Copy the portion of the current text that has changed over to the shadow
- // text, with consideration for any concurrent changes in the shadow text.
- final int start = Math.min(mShadowStart, mCurrentStart);
- final int shadowEnd = mShadowNewEnd + Math.max(0, mCurrentOldEnd - mShadowOldEnd);
- final int currentEnd = mCurrentNewEnd + Math.max(0, mShadowOldEnd - mCurrentOldEnd);
-
- // Perform replacement in two steps (delete and insert) so that old spans are
- // properly deleted before identical new spans are inserted. Otherwise the new
- // spans won't be inserted due to the text already having the old spans.
- mShadowText.delete(start, shadowEnd);
- mShadowText.insert(start, mCurrentText, start, currentEnd);
-
- // SpannableStringBuilder has some internal logic to fix up selections, but we
- // don't want that, so we always fix up the selection a second time.
- final int selStart = Selection.getSelectionStart(mCurrentText);
- final int selEnd = Selection.getSelectionEnd(mCurrentText);
- Selection.setSelection(mShadowText, selStart, selEnd);
-
- if (DEBUG && !mShadowText.equals(mCurrentText)) {
- // Sanity check.
- throw new IllegalStateException("Failed to sync: " +
- mShadowStart + '-' + mShadowOldEnd + '-' + mShadowNewEnd + '/' +
- mCurrentStart + '-' + mCurrentOldEnd + '-' + mCurrentNewEnd);
- }
-
- if (listener != null) {
- // Call onTextChange after selection fix-up but before we call
- // onSelectionChange.
- listener.onTextChange();
-
- if (mCurrentSelectionChanged || (mCurrentOldEnd != mCurrentNewEnd &&
- (selStart >= mCurrentStart || selEnd >= mCurrentStart))) {
- listener.onSelectionChange();
- }
- }
-
- // These values ensure the first change is properly added.
- mCurrentStart = mShadowStart = Integer.MAX_VALUE;
- mCurrentOldEnd = mShadowOldEnd = 0;
- mCurrentNewEnd = mShadowNewEnd = 0;
- mCurrentSelectionChanged = false;
- }
- }
-
- /* An action that alters the Editable
-
- Each action corresponds to a Gecko event. While the Gecko event is being sent to the Gecko
- thread, the action stays on top of mActions queue. After the Gecko event is processed and
- replied, the action is removed from the queue
- */
- private static final class Action {
- // For input events (keypress, etc.); use with onImeSynchronize
- static final int TYPE_EVENT = 0;
- // For Editable.replace() call; use with onImeReplaceText
- static final int TYPE_REPLACE_TEXT = 1;
- // For Editable.setSpan() call; use with onImeSynchronize
- static final int TYPE_SET_SPAN = 2;
- // For Editable.removeSpan() call; use with onImeSynchronize
- static final int TYPE_REMOVE_SPAN = 3;
- // For switching handler; use with onImeSynchronize
- static final int TYPE_SET_HANDLER = 4;
-
- final int mType;
- int mStart;
- int mEnd;
- CharSequence mSequence;
- Object mSpanObject;
- int mSpanFlags;
- Handler mHandler;
-
- Action(int type) {
- mType = type;
- }
-
- static Action newReplaceText(CharSequence text, int start, int end) {
- if (start < 0 || start > end) {
- Log.e(LOGTAG, "invalid replace text offsets: " + start + " to " + end);
- throw new IllegalArgumentException("invalid replace text offsets");
- }
-
- final Action action = new Action(TYPE_REPLACE_TEXT);
- action.mSequence = text;
- action.mStart = start;
- action.mEnd = end;
- return action;
- }
-
- static Action newSetSpan(Object object, int start, int end, int flags) {
- if (start < 0 || start > end) {
- Log.e(LOGTAG, "invalid span offsets: " + start + " to " + end);
- throw new IllegalArgumentException("invalid span offsets");
- }
- final Action action = new Action(TYPE_SET_SPAN);
- action.mSpanObject = object;
- action.mStart = start;
- action.mEnd = end;
- action.mSpanFlags = flags;
- return action;
- }
-
- static Action newRemoveSpan(Object object) {
- final Action action = new Action(TYPE_REMOVE_SPAN);
- action.mSpanObject = object;
- return action;
- }
-
- static Action newSetHandler(Handler handler) {
- final Action action = new Action(TYPE_SET_HANDLER);
- action.mHandler = handler;
- return action;
- }
- }
-
- private void icOfferAction(final Action action) {
- if (DEBUG) {
- assertOnIcThread();
- Log.d(LOGTAG, "offer: Action(" +
- getConstantName(Action.class, "TYPE_", action.mType) + ")");
- }
-
- if (mListener == null) {
- // We haven't initialized or we've been destroyed.
- return;
- }
-
- mActions.offer(action);
-
- switch (action.mType) {
- case Action.TYPE_EVENT:
- case Action.TYPE_SET_HANDLER:
- onImeSynchronize();
- break;
-
- case Action.TYPE_SET_SPAN:
- mText.shadowSetSpan(action.mSpanObject, action.mStart,
- action.mEnd, action.mSpanFlags);
- action.mSequence = TextUtils.substring(
- mText.getShadowText(), action.mStart, action.mEnd);
-
- mNeedUpdateComposition |= (action.mSpanFlags & Spanned.SPAN_INTERMEDIATE) == 0 &&
- ((action.mSpanFlags & Spanned.SPAN_COMPOSING) != 0 ||
- action.mSpanObject == Selection.SELECTION_START ||
- action.mSpanObject == Selection.SELECTION_END);
-
- onImeSynchronize();
- break;
-
- case Action.TYPE_REMOVE_SPAN:
- final int flags = mText.getShadowText().getSpanFlags(action.mSpanObject);
- mText.shadowRemoveSpan(action.mSpanObject);
-
- mNeedUpdateComposition |= (flags & Spanned.SPAN_INTERMEDIATE) == 0 &&
- (flags & Spanned.SPAN_COMPOSING) != 0;
-
- onImeSynchronize();
- break;
-
- case Action.TYPE_REPLACE_TEXT:
- // Always sync text after a replace action, so that if the Gecko
- // text is not changed, we will revert the shadow text to before.
- mNeedSync = true;
-
- // Because we get composition styling here essentially for free,
- // we don't need to check if we're in batch mode.
- if (!icMaybeSendComposition(
- action.mSequence, /* useEntireText */ true, /* notifyGecko */ false)) {
- // Since we don't have a composition, we can try sending key events.
- sendCharKeyEvents(action);
- }
- mText.shadowReplace(action.mStart, action.mEnd, action.mSequence);
- onImeReplaceText(action.mStart, action.mEnd, action.mSequence.toString());
- break;
-
- default:
- throw new IllegalStateException("Action not processed");
- }
- }
-
- private KeyEvent [] synthesizeKeyEvents(CharSequence cs) {
- try {
- if (mKeyMap == null) {
- mKeyMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
- }
- } catch (Exception e) {
- // KeyCharacterMap.UnavailableException is not found on Gingerbread;
- // besides, it seems like HC and ICS will throw something other than
- // KeyCharacterMap.UnavailableException; so use a generic Exception here
- return null;
- }
- KeyEvent [] keyEvents = mKeyMap.getEvents(cs.toString().toCharArray());
- if (keyEvents == null || keyEvents.length == 0) {
- return null;
- }
- return keyEvents;
- }
-
- private void sendCharKeyEvents(Action action) {
- if (action.mSequence.length() != 1 ||
- (action.mSequence instanceof Spannable &&
- ((Spannable)action.mSequence).nextSpanTransition(
- -1, Integer.MAX_VALUE, null) < Integer.MAX_VALUE)) {
- // Spans are not preserved when we use key events,
- // so we need the sequence to not have any spans
- return;
- }
- KeyEvent [] keyEvents = synthesizeKeyEvents(action.mSequence);
- if (keyEvents == null) {
- return;
- }
- for (KeyEvent event : keyEvents) {
- if (KeyEvent.isModifierKey(event.getKeyCode())) {
- continue;
- }
- if (event.getAction() == KeyEvent.ACTION_UP && mSuppressKeyUp) {
- continue;
- }
- if (DEBUG) {
- Log.d(LOGTAG, "sending: " + event);
- }
- onKeyEvent(event, event.getAction(),
- /* metaState */ 0, /* isSynthesizedImeKey */ true);
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- GeckoEditable(final GeckoView v) {
- if (DEBUG) {
- // Called by nsWindow.
- ThreadUtils.assertOnGeckoThread();
- }
-
- mText = new AsyncText();
- mActions = new ConcurrentLinkedQueue<Action>();
-
- final Class<?>[] PROXY_INTERFACES = { Editable.class };
- mProxy = (Editable)Proxy.newProxyInstance(
- Editable.class.getClassLoader(),
- PROXY_INTERFACES, this);
-
- mIcRunHandler = mIcPostHandler = ThreadUtils.getUiHandler();
-
- onViewChange(v);
- }
-
- @WrapForJNI(dispatchTo = "proxy") @Override
- protected native void disposeNative();
-
- @WrapForJNI(calledFrom = "gecko")
- private void onViewChange(final GeckoView v) {
- if (DEBUG) {
- // Called by nsWindow.
- ThreadUtils.assertOnGeckoThread();
- Log.d(LOGTAG, "onViewChange(" + v + ")");
- }
-
- final GeckoEditableListener newListener =
- v != null ? GeckoInputConnection.create(v, this) : null;
-
- final Runnable setListenerRunnable = new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(LOGTAG, "onViewChange (set listener)");
- }
-
- mListener = newListener;
-
- if (newListener == null) {
- // We're being destroyed. By this point, we should have cleared all
- // pending Runnables on the IC thread, so it's safe to call
- // disposeNative here.
- GeckoEditable.this.disposeNative();
- }
- }
- };
-
- // Post to UI thread first to make sure any code that is using the old input
- // connection has finished running, before we switch to a new input connection or
- // before we clear the input connection on destruction.
- final Handler icHandler = mIcPostHandler;
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(LOGTAG, "onViewChange (set IC)");
- }
-
- if (mView != null) {
- // Detach the previous view.
- mView.setInputConnectionListener(null);
- }
- if (v != null) {
- // And attach the new view.
- v.setInputConnectionListener((InputConnectionListener) newListener);
- }
-
- mView = v;
- icHandler.post(setListenerRunnable);
- }
- });
- }
-
- private boolean onIcThread() {
- return mIcRunHandler.getLooper() == Looper.myLooper();
- }
-
- private void assertOnIcThread() {
- ThreadUtils.assertOnThread(mIcRunHandler.getLooper().getThread(), AssertBehavior.THROW);
- }
-
- private void geckoPostToIc(Runnable runnable) {
- if (DEBUG) {
- ThreadUtils.assertOnGeckoThread();
- }
- mIcPostHandler.post(runnable);
- }
-
- private Object getField(Object obj, String field, Object def) {
- try {
- return obj.getClass().getField(field).get(obj);
- } catch (Exception e) {
- return def;
- }
- }
-
- /**
- * Send composition ranges to Gecko for the entire shadow text.
- */
- private void icMaybeSendComposition() {
- if (!mNeedUpdateComposition) {
- return;
- }
-
- icMaybeSendComposition(mText.getShadowText(),
- /* useEntireText */ false, /* notifyGecko */ true);
- }
-
- /**
- * Send composition ranges to Gecko if the text has composing spans.
- *
- * @param sequence Text with possible composing spans
- * @param useEntireText If text has composing spans, treat the entire text as
- * a Gecko composition, instead of just the spanned part.
- * @param notifyGecko Notify Gecko of the new composition ranges;
- * otherwise, the caller is responsible for notifying Gecko.
- * @return Whether there was a composition
- */
- private boolean icMaybeSendComposition(final CharSequence sequence,
- final boolean useEntireText,
- final boolean notifyGecko) {
- mNeedUpdateComposition = false;
-
- int selStart = Selection.getSelectionStart(sequence);
- int selEnd = Selection.getSelectionEnd(sequence);
-
- if (sequence instanceof Spanned) {
- final Spanned text = (Spanned) sequence;
- final Object[] spans = text.getSpans(0, text.length(), Object.class);
- boolean found = false;
- int composingStart = useEntireText ? 0 : Integer.MAX_VALUE;
- int composingEnd = useEntireText ? text.length() : 0;
-
- // Find existence and range of any composing spans (spans with the
- // SPAN_COMPOSING flag set).
- for (Object span : spans) {
- if ((text.getSpanFlags(span) & Spanned.SPAN_COMPOSING) == 0) {
- continue;
- }
- found = true;
- if (useEntireText) {
- break;
- }
- composingStart = Math.min(composingStart, text.getSpanStart(span));
- composingEnd = Math.max(composingEnd, text.getSpanEnd(span));
- }
-
- if (useEntireText && (selStart < 0 || selEnd < 0)) {
- selStart = composingEnd;
- selEnd = composingEnd;
- }
-
- if (found) {
- icSendComposition(text, selStart, selEnd, composingStart, composingEnd);
- if (notifyGecko) {
- onImeUpdateComposition(composingStart, composingEnd);
- }
- return true;
- }
- }
-
- if (notifyGecko) {
- // Set the selection by using a composition without ranges
- onImeUpdateComposition(selStart, selEnd);
- }
-
- if (DEBUG) {
- Log.d(LOGTAG, "icSendComposition(): no composition");
- }
- return false;
- }
-
- private void icSendComposition(final Spanned text,
- final int selStart, final int selEnd,
- final int composingStart, final int composingEnd) {
- if (DEBUG) {
- assertOnIcThread();
- Log.d(LOGTAG, "icSendComposition(\"" + text + "\", " +
- composingStart + ", " + composingEnd + ")");
- }
- if (DEBUG) {
- Log.d(LOGTAG, " range = " + composingStart + "-" + composingEnd);
- Log.d(LOGTAG, " selection = " + selStart + "-" + selEnd);
- }
-
- if (selEnd >= composingStart && selEnd <= composingEnd) {
- onImeAddCompositionRange(
- selEnd - composingStart, selEnd - composingStart,
- IME_RANGE_CARETPOSITION, 0, 0, false, 0, 0, 0);
- }
-
- int rangeStart = composingStart;
- TextPaint tp = new TextPaint();
- TextPaint emptyTp = new TextPaint();
- // set initial foreground color to 0, because we check for tp.getColor() == 0
- // below to decide whether to pass a foreground color to Gecko
- emptyTp.setColor(0);
- do {
- int rangeType, rangeStyles = 0, rangeLineStyle = IME_RANGE_LINE_NONE;
- boolean rangeBoldLine = false;
- int rangeForeColor = 0, rangeBackColor = 0, rangeLineColor = 0;
- int rangeEnd = text.nextSpanTransition(rangeStart, composingEnd, Object.class);
-
- if (selStart > rangeStart && selStart < rangeEnd) {
- rangeEnd = selStart;
- } else if (selEnd > rangeStart && selEnd < rangeEnd) {
- rangeEnd = selEnd;
- }
- CharacterStyle[] styleSpans =
- text.getSpans(rangeStart, rangeEnd, CharacterStyle.class);
-
- if (DEBUG) {
- Log.d(LOGTAG, " found " + styleSpans.length + " spans @ " +
- rangeStart + "-" + rangeEnd);
- }
-
- if (styleSpans.length == 0) {
- rangeType = (selStart == rangeStart && selEnd == rangeEnd)
- ? IME_RANGE_SELECTEDRAWTEXT
- : IME_RANGE_RAWINPUT;
- } else {
- rangeType = (selStart == rangeStart && selEnd == rangeEnd)
- ? IME_RANGE_SELECTEDCONVERTEDTEXT
- : IME_RANGE_CONVERTEDTEXT;
- tp.set(emptyTp);
- for (CharacterStyle span : styleSpans) {
- span.updateDrawState(tp);
- }
- int tpUnderlineColor = 0;
- float tpUnderlineThickness = 0.0f;
-
- // These TextPaint fields only exist on Android ICS+ and are not in the SDK.
- tpUnderlineColor = (Integer)getField(tp, "underlineColor", 0);
- tpUnderlineThickness = (Float)getField(tp, "underlineThickness", 0.0f);
- if (tpUnderlineColor != 0) {
- rangeStyles |= IME_RANGE_UNDERLINE | IME_RANGE_LINECOLOR;
- rangeLineColor = tpUnderlineColor;
- // Approximately translate underline thickness to what Gecko understands
- if (tpUnderlineThickness <= 0.5f) {
- rangeLineStyle = IME_RANGE_LINE_DOTTED;
- } else {
- rangeLineStyle = IME_RANGE_LINE_SOLID;
- if (tpUnderlineThickness >= 2.0f) {
- rangeBoldLine = true;
- }
- }
- } else if (tp.isUnderlineText()) {
- rangeStyles |= IME_RANGE_UNDERLINE;
- rangeLineStyle = IME_RANGE_LINE_SOLID;
- }
- if (tp.getColor() != 0) {
- rangeStyles |= IME_RANGE_FORECOLOR;
- rangeForeColor = tp.getColor();
- }
- if (tp.bgColor != 0) {
- rangeStyles |= IME_RANGE_BACKCOLOR;
- rangeBackColor = tp.bgColor;
- }
- }
- onImeAddCompositionRange(
- rangeStart - composingStart, rangeEnd - composingStart,
- rangeType, rangeStyles, rangeLineStyle, rangeBoldLine,
- rangeForeColor, rangeBackColor, rangeLineColor);
- rangeStart = rangeEnd;
-
- if (DEBUG) {
- Log.d(LOGTAG, " added " + rangeType +
- " : " + Integer.toHexString(rangeStyles) +
- " : " + Integer.toHexString(rangeForeColor) +
- " : " + Integer.toHexString(rangeBackColor));
- }
- } while (rangeStart < composingEnd);
- }
-
- // GeckoEditableClient interface
-
- @Override
- public void sendKeyEvent(final KeyEvent event, int action, int metaState) {
- if (DEBUG) {
- assertOnIcThread();
- Log.d(LOGTAG, "sendKeyEvent(" + event + ", " + action + ", " + metaState + ")");
- }
- /*
- We are actually sending two events to Gecko here,
- 1. Event from the event parameter (key event)
- 2. Sync event from the icOfferAction call
- The first event is a normal event that does not reply back to us,
- the second sync event will have a reply, during which we see that there is a pending
- event-type action, and update the shadow text accordingly.
- */
- icMaybeSendComposition();
- onKeyEvent(event, action, metaState, /* isSynthesizedImeKey */ false);
- icOfferAction(new Action(Action.TYPE_EVENT));
- }
-
- @Override
- public Editable getEditable() {
- if (!onIcThread()) {
- // Android may be holding an old InputConnection; ignore
- if (DEBUG) {
- Log.i(LOGTAG, "getEditable() called on non-IC thread");
- }
- return null;
- }
- if (mListener == null) {
- // We haven't initialized or we've been destroyed.
- return null;
- }
- return mProxy;
- }
-
- @Override
- public void setBatchMode(boolean inBatchMode) {
- if (!onIcThread()) {
- // Android may be holding an old InputConnection; ignore
- if (DEBUG) {
- Log.i(LOGTAG, "setBatchMode() called on non-IC thread");
- }
- return;
- }
-
- mInBatchMode = inBatchMode;
-
- if (!inBatchMode && mNeedSync) {
- icSyncShadowText();
- }
- }
-
- /* package */ void icSyncShadowText() {
- if (mListener == null) {
- // Not yet attached or already destroyed.
- return;
- }
-
- if (mInBatchMode || !mActions.isEmpty()) {
- mNeedSync = true;
- return;
- }
-
- mNeedSync = false;
- mText.syncShadowText(mListener);
- }
-
- private void geckoScheduleSyncShadowText() {
- if (DEBUG) {
- ThreadUtils.assertOnGeckoThread();
- }
- geckoPostToIc(new Runnable() {
- @Override
- public void run() {
- icSyncShadowText();
- }
- });
- }
-
- @Override
- public void setSuppressKeyUp(boolean suppress) {
- if (DEBUG) {
- assertOnIcThread();
- }
- // Suppress key up event generated as a result of
- // translating characters to key events
- mSuppressKeyUp = suppress;
- }
-
- @Override // GeckoEditableClient
- public Handler setInputConnectionHandler(final Handler handler) {
- if (handler == mIcRunHandler) {
- return mIcRunHandler;
- }
- if (DEBUG) {
- assertOnIcThread();
- }
-
- // There are three threads at this point: Gecko thread, old IC thread, and new IC
- // thread, and we want to safely switch from old IC thread to new IC thread.
- // We first send a TYPE_SET_HANDLER action to the Gecko thread; this ensures that
- // the Gecko thread is stopped at a known point. At the same time, the old IC
- // thread blocks on the action; this ensures that the old IC thread is stopped at
- // a known point. Finally, inside the Gecko thread, we post a Runnable to the old
- // IC thread; this Runnable switches from old IC thread to new IC thread. We
- // switch IC thread on the old IC thread to ensure any pending Runnables on the
- // old IC thread are processed before we switch over. Inside the Gecko thread, we
- // also post a Runnable to the new IC thread; this Runnable blocks until the
- // switch is complete; this ensures that the new IC thread won't accept
- // InputConnection calls until after the switch.
-
- handler.post(new Runnable() { // Make the new IC thread wait.
- @Override
- public void run() {
- synchronized (handler) {
- while (mIcRunHandler != handler) {
- try {
- handler.wait();
- } catch (final InterruptedException e) {
- }
- }
- }
- }
- });
-
- icOfferAction(Action.newSetHandler(handler));
- return handler;
- }
-
- @Override // GeckoEditableClient
- public void postToInputConnection(final Runnable runnable) {
- mIcPostHandler.post(runnable);
- }
-
- @Override // GeckoEditableClient
- public void requestCursorUpdates(int requestMode) {
- onImeRequestCursorUpdates(requestMode);
- }
-
- private void geckoSetIcHandler(final Handler newHandler) {
- geckoPostToIc(new Runnable() { // posting to old IC thread
- @Override
- public void run() {
- synchronized (newHandler) {
- mIcRunHandler = newHandler;
- newHandler.notify();
- }
- }
- });
-
- // At this point, all future Runnables should be posted to the new IC thread, but
- // we don't switch mIcRunHandler yet because there may be pending Runnables on the
- // old IC thread still waiting to run.
- mIcPostHandler = newHandler;
- }
-
- private void geckoActionReply(final Action action) {
- if (!mGeckoFocused) {
- if (DEBUG) {
- Log.d(LOGTAG, "discarding stale reply");
- }
- return;
- }
-
- if (DEBUG) {
- // GeckoEditableListener methods should all be called from the Gecko thread
- ThreadUtils.assertOnGeckoThread();
- Log.d(LOGTAG, "reply: Action(" +
- getConstantName(Action.class, "TYPE_", action.mType) + ")");
- }
- switch (action.mType) {
- case Action.TYPE_SET_SPAN:
- final int len = mText.getCurrentText().length();
- if (action.mStart > len || action.mEnd > len ||
- !TextUtils.substring(mText.getCurrentText(), action.mStart,
- action.mEnd).equals(action.mSequence)) {
- if (DEBUG) {
- Log.d(LOGTAG, "discarding stale set span call");
- }
- break;
- }
- mText.currentSetSpan(action.mSpanObject, action.mStart, action.mEnd, action.mSpanFlags);
- break;
-
- case Action.TYPE_REMOVE_SPAN:
- mText.currentRemoveSpan(action.mSpanObject);
- break;
-
- case Action.TYPE_SET_HANDLER:
- geckoSetIcHandler(action.mHandler);
- break;
- }
- }
-
- private void notifyCommitComposition() {
- // Gecko already committed its composition. However, Android keyboards
- // have trouble dealing with us removing the composition manually on
- // the Java side. Therefore, we keep the composition intact on the Java
- // side. The text content should still be in-sync on both sides.
- }
-
- private void notifyCancelComposition() {
- // Composition should have been canceled on our side
- // through text update notifications; verify that here.
- if (DEBUG) {
- final Spanned text = mText.getCurrentText();
- final Object[] spans = text.getSpans(0, text.length(), Object.class);
- for (Object span : spans) {
- if ((text.getSpanFlags(span) & Spanned.SPAN_COMPOSING) != 0) {
- throw new IllegalStateException("composition not cancelled");
- }
- }
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private void notifyIME(final int type) {
- if (DEBUG) {
- // GeckoEditableListener methods should all be called from the Gecko thread
- ThreadUtils.assertOnGeckoThread();
- // NOTIFY_IME_REPLY_EVENT is logged separately, inside geckoActionReply()
- if (type != GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
- Log.d(LOGTAG, "notifyIME(" +
- getConstantName(GeckoEditableListener.class, "NOTIFY_IME_", type) +
- ")");
- }
- }
-
- if (type == GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
- geckoActionReply(mActions.poll());
- if (!mGeckoFocused || !mActions.isEmpty()) {
- // Only post to IC thread below when the queue is empty.
- return;
- }
- } else if (type == GeckoEditableListener.NOTIFY_IME_TO_COMMIT_COMPOSITION) {
- notifyCommitComposition();
- return;
- } else if (type == GeckoEditableListener.NOTIFY_IME_TO_CANCEL_COMPOSITION) {
- notifyCancelComposition();
- return;
- }
-
- geckoPostToIc(new Runnable() {
- @Override
- public void run() {
- if (type == GeckoEditableListener.NOTIFY_IME_REPLY_EVENT) {
- if (mNeedSync) {
- icSyncShadowText();
- }
- return;
- }
-
- if (type == GeckoEditableListener.NOTIFY_IME_OF_FOCUS && mListener != null) {
- mNeedSync = false;
- mText.syncShadowText(/* listener */ null);
- }
-
- if (mListener != null) {
- mListener.notifyIME(type);
- }
- }
- });
-
- // Update the mGeckoFocused flag.
- if (type == GeckoEditableListener.NOTIFY_IME_OF_BLUR) {
- mGeckoFocused = false;
- } else if (type == GeckoEditableListener.NOTIFY_IME_OF_FOCUS) {
- mGeckoFocused = true;
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private void notifyIMEContext(final int state, final String typeHint,
- final String modeHint, final String actionHint) {
- if (DEBUG) {
- // GeckoEditableListener methods should all be called from the Gecko thread
- ThreadUtils.assertOnGeckoThread();
- Log.d(LOGTAG, "notifyIMEContext(" +
- getConstantName(GeckoEditableListener.class, "IME_STATE_", state) +
- ", \"" + typeHint + "\", \"" + modeHint + "\", \"" + actionHint + "\")");
- }
- geckoPostToIc(new Runnable() {
- @Override
- public void run() {
- if (mListener == null) {
- return;
- }
- mListener.notifyIMEContext(state, typeHint, modeHint, actionHint);
- }
- });
- }
-
- @WrapForJNI(calledFrom = "gecko", exceptionMode = "ignore")
- private void onSelectionChange(final int start, final int end) {
- if (DEBUG) {
- // GeckoEditableListener methods should all be called from the Gecko thread
- ThreadUtils.assertOnGeckoThread();
- Log.d(LOGTAG, "onSelectionChange(" + start + ", " + end + ")");
- }
-
- final int currentLength = mText.getCurrentText().length();
- if (start < 0 || start > currentLength || end < 0 || end > currentLength) {
- Log.e(LOGTAG, "invalid selection notification range: " +
- start + " to " + end + ", length: " + currentLength);
- throw new IllegalArgumentException("invalid selection notification range");
- }
-
- if (mIgnoreSelectionChange) {
- mIgnoreSelectionChange = false;
- } else {
- mText.currentSetSelection(start, end);
- }
-
- geckoScheduleSyncShadowText();
- }
-
- private boolean geckoIsSameText(int start, int oldEnd, CharSequence newText) {
- return oldEnd - start == newText.length() &&
- TextUtils.regionMatches(mText.getCurrentText(), start, newText, 0, oldEnd - start);
- }
-
- @WrapForJNI(calledFrom = "gecko", exceptionMode = "ignore")
- private void onTextChange(final CharSequence text, final int start,
- final int unboundedOldEnd, final int unboundedNewEnd) {
- if (DEBUG) {
- // GeckoEditableListener methods should all be called from the Gecko thread
- ThreadUtils.assertOnGeckoThread();
- StringBuilder sb = new StringBuilder("onTextChange(");
- debugAppend(sb, text);
- sb.append(", ").append(start).append(", ")
- .append(unboundedOldEnd).append(", ")
- .append(unboundedNewEnd).append(")");
- Log.d(LOGTAG, sb.toString());
- }
- if (start < 0 || start > unboundedOldEnd) {
- Log.e(LOGTAG, "invalid text notification range: " +
- start + " to " + unboundedOldEnd);
- throw new IllegalArgumentException("invalid text notification range");
- }
-
- final int currentLength = mText.getCurrentText().length();
-
- /* For the "end" parameters, Gecko can pass in a large
- number to denote "end of the text". Fix that here */
- final int oldEnd = unboundedOldEnd > currentLength ? currentLength : unboundedOldEnd;
- // new end should always match text
- if (unboundedOldEnd <= currentLength && unboundedNewEnd != (start + text.length())) {
- Log.e(LOGTAG, "newEnd does not match text: " + unboundedNewEnd + " vs " +
- (start + text.length()));
- throw new IllegalArgumentException("newEnd does not match text");
- }
-
- final int newEnd = start + text.length();
- final Action action = mActions.peek();
-
- if (start == 0 && unboundedOldEnd > currentLength) {
- // Simply replace the text for newly-focused editors. Replace in two steps to
- // properly clear composing spans that span the whole range.
- mText.currentReplace(0, currentLength, "");
- mText.currentReplace(0, 0, text);
-
- // Don't ignore the next selection change because we are re-syncing with Gecko
- mIgnoreSelectionChange = false;
-
- } else if (action != null &&
- action.mType == Action.TYPE_REPLACE_TEXT &&
- start <= action.mStart &&
- oldEnd >= action.mEnd &&
- newEnd >= action.mStart + action.mSequence.length()) {
-
- // Try to preserve both old spans and new spans in action.mSequence.
- // indexInText is where we can find waction.mSequence within the passed in text.
- final int startWithinText = action.mStart - start;
- int indexInText = TextUtils.indexOf(text, action.mSequence, startWithinText);
- if (indexInText < 0 && startWithinText >= action.mSequence.length()) {
- indexInText = text.toString().lastIndexOf(action.mSequence.toString(),
- startWithinText);
- }
-
- if (indexInText < 0) {
- // Text was changed from under us. We are forced to discard any new spans.
- mText.currentReplace(start, oldEnd, text);
-
- // Don't ignore the next selection change because we are forced to re-sync
- // with Gecko here.
- mIgnoreSelectionChange = false;
-
- } else if (indexInText == 0 && text.length() == action.mSequence.length() &&
- oldEnd - start == action.mEnd - action.mStart) {
- // The new change exactly matches our saved change, so do a direct replace.
- mText.currentReplace(start, oldEnd, action.mSequence);
-
- // Ignore the next selection change because the selection change is a
- // side-effect of the replace-text event we sent.
- mIgnoreSelectionChange = true;
-
- } else {
- // The sequence is embedded within the changed text, so we have to perform
- // replacement in parts. First replace part of text before the sequence.
- mText.currentReplace(start, action.mStart, text.subSequence(0, indexInText));
-
- // Then replace part of the text after the sequence.
- final int actionStart = indexInText + start;
- final int delta = actionStart - action.mStart;
- final int actionEnd = delta + action.mEnd;
-
- final Spanned currentText = mText.getCurrentText();
- final boolean resetSelStart = Selection.getSelectionStart(currentText) == actionEnd;
- final boolean resetSelEnd = Selection.getSelectionEnd(currentText) == actionEnd;
-
- mText.currentReplace(actionEnd, delta + oldEnd, text.subSequence(
- indexInText + action.mSequence.length(), text.length()));
-
- // The replacement above may have shifted our selection, if the selection
- // was at the start of the replacement range. If so, we need to reset
- // our selection to the previous position.
- if (resetSelStart || resetSelEnd) {
- mText.currentSetSelection(
- resetSelStart ? actionEnd : Selection.getSelectionStart(currentText),
- resetSelEnd ? actionEnd : Selection.getSelectionEnd(currentText));
- }
-
- // Finally replace the sequence itself to preserve new spans.
- mText.currentReplace(actionStart, actionEnd, action.mSequence);
-
- // Ignore the next selection change because the selection change is a
- // side-effect of the replace-text event we sent.
- mIgnoreSelectionChange = true;
- }
-
- } else if (geckoIsSameText(start, oldEnd, text)) {
- // Nothing to do because the text is the same. This could happen when
- // the composition is updated for example, in which case we want to keep the
- // Java selection.
- mIgnoreSelectionChange = mIgnoreSelectionChange ||
- (action != null && action.mType == Action.TYPE_REPLACE_TEXT);
- return;
-
- } else {
- // Gecko side initiated the text change. Replace in two steps to properly
- // clear composing spans that span the whole range.
- mText.currentReplace(start, oldEnd, "");
- mText.currentReplace(start, start, text);
-
- // Don't ignore the next selection change because we are forced to re-sync
- // with Gecko here.
- mIgnoreSelectionChange = false;
- }
-
- // onTextChange is always followed by onSelectionChange, so we let
- // onSelectionChange schedule a shadow text sync.
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private void onDefaultKeyEvent(final KeyEvent event) {
- if (DEBUG) {
- // GeckoEditableListener methods should all be called from the Gecko thread
- ThreadUtils.assertOnGeckoThread();
- StringBuilder sb = new StringBuilder("onDefaultKeyEvent(");
- sb.append("action=").append(event.getAction()).append(", ")
- .append("keyCode=").append(event.getKeyCode()).append(", ")
- .append("metaState=").append(event.getMetaState()).append(", ")
- .append("time=").append(event.getEventTime()).append(", ")
- .append("repeatCount=").append(event.getRepeatCount()).append(")");
- Log.d(LOGTAG, sb.toString());
- }
-
- geckoPostToIc(new Runnable() {
- @Override
- public void run() {
- if (mListener == null) {
- return;
- }
- mListener.onDefaultKeyEvent(event);
- }
- });
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private void updateCompositionRects(final RectF[] aRects) {
- if (DEBUG) {
- // GeckoEditableListener methods should all be called from the Gecko thread
- ThreadUtils.assertOnGeckoThread();
- Log.d(LOGTAG, "updateCompositionRects(aRects.length = " + aRects.length + ")");
- }
- geckoPostToIc(new Runnable() {
- @Override
- public void run() {
- if (mListener == null) {
- return;
- }
- mListener.updateCompositionRects(aRects);
- }
- });
- }
-
- // InvocationHandler interface
-
- static String getConstantName(Class<?> cls, String prefix, Object value) {
- for (Field fld : cls.getDeclaredFields()) {
- try {
- if (fld.getName().startsWith(prefix) &&
- fld.get(null).equals(value)) {
- return fld.getName();
- }
- } catch (IllegalAccessException e) {
- }
- }
- return String.valueOf(value);
- }
-
- static StringBuilder debugAppend(StringBuilder sb, Object obj) {
- if (obj == null) {
- sb.append("null");
- } else if (obj instanceof GeckoEditable) {
- sb.append("GeckoEditable");
- } else if (Proxy.isProxyClass(obj.getClass())) {
- debugAppend(sb, Proxy.getInvocationHandler(obj));
- } else if (obj instanceof CharSequence) {
- sb.append('"').append(obj.toString().replace('\n', '\u21b2')).append('"');
- } else if (obj.getClass().isArray()) {
- sb.append(obj.getClass().getComponentType().getSimpleName()).append('[')
- .append(Array.getLength(obj)).append(']');
- } else {
- sb.append(obj);
- }
- return sb;
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- Object target;
- final Class<?> methodInterface = method.getDeclaringClass();
- if (DEBUG) {
- // Editable methods should all be called from the IC thread
- assertOnIcThread();
- }
- if (methodInterface == Editable.class ||
- methodInterface == Appendable.class ||
- methodInterface == Spannable.class) {
- // Method alters the Editable; route calls to our implementation
- target = this;
- } else {
- target = mText.getShadowText();
- }
- Object ret;
- try {
- ret = method.invoke(target, args);
- } catch (InvocationTargetException e) {
- // Bug 817386
- // Most likely Gecko has changed the text while GeckoInputConnection is
- // trying to access the text. If we pass through the exception here, Fennec
- // will crash due to a lack of exception handler. Log the exception and
- // return an empty value instead.
- if (!(e.getCause() instanceof IndexOutOfBoundsException)) {
- // Only handle IndexOutOfBoundsException for now,
- // as other exceptions might signal other bugs
- throw e;
- }
- Log.w(LOGTAG, "Exception in GeckoEditable." + method.getName(), e.getCause());
- Class<?> retClass = method.getReturnType();
- if (retClass == Character.TYPE) {
- ret = '\0';
- } else if (retClass == Integer.TYPE) {
- ret = 0;
- } else if (retClass == String.class) {
- ret = "";
- } else {
- ret = null;
- }
- }
- if (DEBUG) {
- StringBuilder log = new StringBuilder(method.getName());
- log.append("(");
- if (args != null) {
- for (Object arg : args) {
- debugAppend(log, arg).append(", ");
- }
- if (args.length > 0) {
- log.setLength(log.length() - 2);
- }
- }
- if (method.getReturnType().equals(Void.TYPE)) {
- log.append(")");
- } else {
- debugAppend(log.append(") = "), ret);
- }
- Log.d(LOGTAG, log.toString());
- }
- return ret;
- }
-
- // Spannable interface
-
- @Override
- public void removeSpan(Object what) {
- if (what == null) {
- return;
- }
-
- if (what == Selection.SELECTION_START ||
- what == Selection.SELECTION_END) {
- Log.w(LOGTAG, "selection removed with removeSpan()");
- }
-
- icOfferAction(Action.newRemoveSpan(what));
- }
-
- @Override
- public void setSpan(Object what, int start, int end, int flags) {
- icOfferAction(Action.newSetSpan(what, start, end, flags));
- }
-
- // Appendable interface
-
- @Override
- public Editable append(CharSequence text) {
- return replace(mProxy.length(), mProxy.length(), text, 0, text.length());
- }
-
- @Override
- public Editable append(CharSequence text, int start, int end) {
- return replace(mProxy.length(), mProxy.length(), text, start, end);
- }
-
- @Override
- public Editable append(char text) {
- return replace(mProxy.length(), mProxy.length(), String.valueOf(text), 0, 1);
- }
-
- // Editable interface
-
- @Override
- public InputFilter[] getFilters() {
- return mFilters;
- }
-
- @Override
- public void setFilters(InputFilter[] filters) {
- mFilters = filters;
- }
-
- @Override
- public void clearSpans() {
- /* XXX this clears the selection spans too,
- but there is no way to clear the corresponding selection in Gecko */
- Log.w(LOGTAG, "selection cleared with clearSpans()");
- icOfferAction(Action.newRemoveSpan(/* what */ null));
- }
-
- @Override
- public Editable replace(int st, int en,
- CharSequence source, int start, int end) {
-
- CharSequence text = source;
- if (start < 0 || start > end || end > text.length()) {
- Log.e(LOGTAG, "invalid replace offsets: " +
- start + " to " + end + ", length: " + text.length());
- throw new IllegalArgumentException("invalid replace offsets");
- }
- if (start != 0 || end != text.length()) {
- text = text.subSequence(start, end);
- }
- if (mFilters != null) {
- // Filter text before sending the request to Gecko
- for (int i = 0; i < mFilters.length; ++i) {
- final CharSequence cs = mFilters[i].filter(
- text, 0, text.length(), mProxy, st, en);
- if (cs != null) {
- text = cs;
- }
- }
- }
- if (text == source) {
- // Always create a copy
- text = new SpannableString(source);
- }
- icOfferAction(Action.newReplaceText(text, Math.min(st, en), Math.max(st, en)));
- return mProxy;
- }
-
- @Override
- public void clear() {
- replace(0, mProxy.length(), "", 0, 0);
- }
-
- @Override
- public Editable delete(int st, int en) {
- return replace(st, en, "", 0, 0);
- }
-
- @Override
- public Editable insert(int where, CharSequence text,
- int start, int end) {
- return replace(where, where, text, start, end);
- }
-
- @Override
- public Editable insert(int where, CharSequence text) {
- return replace(where, where, text, 0, text.length());
- }
-
- @Override
- public Editable replace(int st, int en, CharSequence text) {
- return replace(st, en, text, 0, text.length());
- }
-
- /* GetChars interface */
-
- @Override
- public void getChars(int start, int end, char[] dest, int destoff) {
- /* overridden Editable interface methods in GeckoEditable must not be called directly
- outside of GeckoEditable. Instead, the call must go through mProxy, which ensures
- that Java is properly synchronized with Gecko */
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- /* Spanned interface */
-
- @Override
- public int getSpanEnd(Object tag) {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- @Override
- public int getSpanFlags(Object tag) {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- @Override
- public int getSpanStart(Object tag) {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- @Override
- public <T> T[] getSpans(int start, int end, Class<T> type) {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- @Override
- @SuppressWarnings("rawtypes") // nextSpanTransition uses raw Class in its Android declaration
- public int nextSpanTransition(int start, int limit, Class type) {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- /* CharSequence interface */
-
- @Override
- public char charAt(int index) {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- @Override
- public int length() {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- @Override
- public CharSequence subSequence(int start, int end) {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-
- @Override
- public String toString() {
- throw new UnsupportedOperationException("method must be called through mProxy");
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableClient.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableClient.java
deleted file mode 100644
index 5e721b3af..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableClient.java
+++ /dev/null
@@ -1,33 +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.os.Handler;
-import android.text.Editable;
-import android.view.KeyEvent;
-
-/**
- * Interface for the IC thread.
- */
-interface GeckoEditableClient {
- void sendKeyEvent(KeyEvent event, int action, int metaState);
- Editable getEditable();
- void setBatchMode(boolean isBatchMode);
- void setSuppressKeyUp(boolean suppress);
- Handler setInputConnectionHandler(Handler handler);
- void postToInputConnection(Runnable runnable);
-
- // The following value is used by requestCursorUpdates
-
- // ONE_SHOT calls updateCompositionRects() after getting current composing character rects.
- public static final int ONE_SHOT = 1;
- // START_MONITOR start the monitor for composing character rects. If is is updaed, call updateCompositionRects()
- public static final int START_MONITOR = 2;
- // ENDT_MONITOR stops the monitor for composing character rects.
- public static final int END_MONITOR = 3;
-
- void requestCursorUpdates(int requestMode);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableListener.java
deleted file mode 100644
index db594aaf7..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditableListener.java
+++ /dev/null
@@ -1,43 +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 org.mozilla.gecko.annotation.WrapForJNI;
-
-import android.graphics.RectF;
-import android.view.KeyEvent;
-
-/**
- * Interface for the Editable to listen on the Gecko thread, as well as for the IC thread to listen
- * to the Editable.
- */
-interface GeckoEditableListener {
- // IME notification type for notifyIME(), corresponding to NotificationToIME enum in Gecko
- @WrapForJNI
- int NOTIFY_IME_OPEN_VKB = -2;
- @WrapForJNI
- int NOTIFY_IME_REPLY_EVENT = -1;
- @WrapForJNI
- int NOTIFY_IME_OF_FOCUS = 1;
- @WrapForJNI
- int NOTIFY_IME_OF_BLUR = 2;
- @WrapForJNI
- int NOTIFY_IME_TO_COMMIT_COMPOSITION = 8;
- @WrapForJNI
- int NOTIFY_IME_TO_CANCEL_COMPOSITION = 9;
- // IME enabled state for notifyIMEContext()
- int IME_STATE_DISABLED = 0;
- int IME_STATE_ENABLED = 1;
- int IME_STATE_PASSWORD = 2;
- int IME_STATE_PLUGIN = 3;
-
- void notifyIME(int type);
- void notifyIMEContext(int state, String typeHint, String modeHint, String actionHint);
- void onSelectionChange();
- void onTextChange();
- void onDefaultKeyEvent(KeyEvent event);
- void updateCompositionRects(final RectF[] aRects);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java
deleted file mode 100644
index 3d9b97427..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoHalDefines.java
+++ /dev/null
@@ -1,27 +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;
-
-public class GeckoHalDefines
-{
- /*
- * Keep these values consistent with |SensorType| in HalSensor.h
- */
- public static final int SENSOR_ORIENTATION = 0;
- public static final int SENSOR_ACCELERATION = 1;
- public static final int SENSOR_PROXIMITY = 2;
- public static final int SENSOR_LINEAR_ACCELERATION = 3;
- public static final int SENSOR_GYROSCOPE = 4;
- public static final int SENSOR_LIGHT = 5;
- public static final int SENSOR_ROTATION_VECTOR = 6;
- public static final int SENSOR_GAME_ROTATION_VECTOR = 7;
-
- public static final int SENSOR_ACCURACY_UNKNOWN = -1;
- public static final int SENSOR_ACCURACY_UNRELIABLE = 0;
- public static final int SENSOR_ACCURACY_LOW = 1;
- public static final int SENSOR_ACCURACY_MED = 2;
- public static final int SENSOR_ACCURACY_HIGH = 3;
-};
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
deleted file mode 100644
index a80be0bce..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoInputConnection.java
+++ /dev/null
@@ -1,1060 +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.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.concurrent.SynchronousQueue;
-
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.gfx.DynamicToolbarAnimator;
-import org.mozilla.gecko.util.Clipboard;
-import org.mozilla.gecko.util.GamepadUtils;
-import org.mozilla.gecko.util.ThreadUtils;
-import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Matrix;
-import android.graphics.RectF;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.Selection;
-import android.text.SpannableString;
-import android.text.method.KeyListener;
-import android.text.method.TextKeyListener;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.BaseInputConnection;
-import android.view.inputmethod.CursorAnchorInfo;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-
-class GeckoInputConnection
- extends BaseInputConnection
- implements InputConnectionListener, GeckoEditableListener {
-
- private static final boolean DEBUG = false;
- protected static final String LOGTAG = "GeckoInputConnection";
-
- private static final String CUSTOM_HANDLER_TEST_METHOD = "testInputConnection";
- private static final String CUSTOM_HANDLER_TEST_CLASS =
- "org.mozilla.gecko.tests.components.GeckoViewComponent$TextInput";
-
- private static final int INLINE_IME_MIN_DISPLAY_SIZE = 480;
-
- private static Handler sBackgroundHandler;
-
- // Managed only by notifyIMEContext; see comments in notifyIMEContext
- private int mIMEState;
- private String mIMETypeHint = "";
- private String mIMEModeHint = "";
- private String mIMEActionHint = "";
- private boolean mFocused;
-
- private String mCurrentInputMethod = "";
-
- private final View mView;
- private final GeckoEditableClient mEditableClient;
- protected int mBatchEditCount;
- private ExtractedTextRequest mUpdateRequest;
- private final ExtractedText mUpdateExtract = new ExtractedText();
- private final InputConnection mKeyInputConnection;
- private CursorAnchorInfo.Builder mCursorAnchorInfoBuilder;
-
- // Prevent showSoftInput and hideSoftInput from causing reentrant calls on some devices.
- private volatile boolean mSoftInputReentrancyGuard;
-
- public static GeckoEditableListener create(View targetView,
- GeckoEditableClient editable) {
- if (DEBUG)
- return DebugGeckoInputConnection.create(targetView, editable);
- else
- return new GeckoInputConnection(targetView, editable);
- }
-
- protected GeckoInputConnection(View targetView,
- GeckoEditableClient editable) {
- super(targetView, true);
- mView = targetView;
- mEditableClient = editable;
- mIMEState = IME_STATE_DISABLED;
- // InputConnection that sends keys for plugins, which don't have full editors
- mKeyInputConnection = new BaseInputConnection(targetView, false);
- }
-
- @Override
- public synchronized boolean beginBatchEdit() {
- mBatchEditCount++;
- if (mBatchEditCount == 1) {
- mEditableClient.setBatchMode(true);
- }
- return true;
- }
-
- @Override
- public synchronized boolean endBatchEdit() {
- if (mBatchEditCount <= 0) {
- Log.w(LOGTAG, "endBatchEdit() called, but mBatchEditCount <= 0?!");
- return true;
- }
-
- mBatchEditCount--;
- if (mBatchEditCount != 0) {
- return true;
- }
-
- // setBatchMode will call onTextChange and/or onSelectionChange for us.
- mEditableClient.setBatchMode(false);
- return true;
- }
-
- @Override
- public Editable getEditable() {
- return mEditableClient.getEditable();
- }
-
- @Override
- public boolean performContextMenuAction(int id) {
- Editable editable = getEditable();
- if (editable == null) {
- return false;
- }
- int selStart = Selection.getSelectionStart(editable);
- int selEnd = Selection.getSelectionEnd(editable);
-
- switch (id) {
- case android.R.id.selectAll:
- setSelection(0, editable.length());
- break;
- case android.R.id.cut:
- // If selection is empty, we'll select everything
- if (selStart == selEnd) {
- // Fill the clipboard
- Clipboard.setText(editable);
- editable.clear();
- } else {
- Clipboard.setText(
- editable.toString().substring(
- Math.min(selStart, selEnd),
- Math.max(selStart, selEnd)));
- editable.delete(selStart, selEnd);
- }
- break;
- case android.R.id.paste:
- commitText(Clipboard.getText(), 1);
- break;
- case android.R.id.copy:
- // Copy the current selection or the empty string if nothing is selected.
- String copiedText = selStart == selEnd ? "" :
- editable.toString().substring(
- Math.min(selStart, selEnd),
- Math.max(selStart, selEnd));
- Clipboard.setText(copiedText);
- break;
- }
- return true;
- }
-
- @Override
- public ExtractedText getExtractedText(ExtractedTextRequest req, int flags) {
- if (req == null)
- return null;
-
- if ((flags & GET_EXTRACTED_TEXT_MONITOR) != 0)
- mUpdateRequest = req;
-
- Editable editable = getEditable();
- if (editable == null) {
- return null;
- }
- int selStart = Selection.getSelectionStart(editable);
- int selEnd = Selection.getSelectionEnd(editable);
-
- ExtractedText extract = new ExtractedText();
- extract.flags = 0;
- extract.partialStartOffset = -1;
- extract.partialEndOffset = -1;
- extract.selectionStart = selStart;
- extract.selectionEnd = selEnd;
- extract.startOffset = 0;
- if ((req.flags & GET_TEXT_WITH_STYLES) != 0) {
- extract.text = new SpannableString(editable);
- } else {
- extract.text = editable.toString();
- }
- return extract;
- }
-
- private View getView() {
- return mView;
- }
-
- private InputMethodManager getInputMethodManager() {
- View view = getView();
- if (view == null) {
- return null;
- }
- Context context = view.getContext();
- return InputMethods.getInputMethodManager(context);
- }
-
- private void showSoftInput() {
- if (mSoftInputReentrancyGuard) {
- return;
- }
- final View v = getView();
- final InputMethodManager imm = getInputMethodManager();
- if (v == null || imm == null) {
- return;
- }
-
- v.post(new Runnable() {
- @Override
- public void run() {
- if (v.hasFocus() && !imm.isActive(v)) {
- // Marshmallow workaround: The view has focus but it is not the active
- // view for the input method. (Bug 1211848)
- v.clearFocus();
- v.requestFocus();
- }
- GeckoAppShell.getLayerView().getDynamicToolbarAnimator().showToolbar(/*immediately*/true);
- mSoftInputReentrancyGuard = true;
- imm.showSoftInput(v, 0);
- mSoftInputReentrancyGuard = false;
- }
- });
- }
-
- private void hideSoftInput() {
- if (mSoftInputReentrancyGuard) {
- return;
- }
- final InputMethodManager imm = getInputMethodManager();
- if (imm != null) {
- final View v = getView();
- mSoftInputReentrancyGuard = true;
- imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
- mSoftInputReentrancyGuard = false;
- }
- }
-
- private void restartInput() {
-
- final InputMethodManager imm = getInputMethodManager();
- if (imm == null) {
- return;
- }
- final View v = getView();
- // InputMethodManager has internal logic to detect if we are restarting input
- // in an already focused View, which is the case here because all content text
- // fields are inside one LayerView. When this happens, InputMethodManager will
- // tell the input method to soft reset instead of hard reset. Stock latin IME
- // on Android 4.2+ has a quirk that when it soft resets, it does not clear the
- // composition. The following workaround tricks the IME into clearing the
- // composition when soft resetting.
- if (InputMethods.needsSoftResetWorkaround(mCurrentInputMethod)) {
- // Fake a selection change, because the IME clears the composition when
- // the selection changes, even if soft-resetting. Offsets here must be
- // different from the previous selection offsets, and -1 seems to be a
- // reasonable, deterministic value
- notifySelectionChange(-1, -1);
- }
- try {
- imm.restartInput(v);
- } catch (RuntimeException e) {
- Log.e(LOGTAG, "Error restarting input", e);
- }
- }
-
- private void resetInputConnection() {
- if (mBatchEditCount != 0) {
- Log.w(LOGTAG, "resetting with mBatchEditCount = " + mBatchEditCount);
- mBatchEditCount = 0;
- }
-
- // Do not reset mIMEState here; see comments in notifyIMEContext
-
- restartInput();
- }
-
- @Override // GeckoEditableListener
- public void onTextChange() {
-
- if (mUpdateRequest == null) {
- return;
- }
-
- final InputMethodManager imm = getInputMethodManager();
- final View v = getView();
- final Editable editable = getEditable();
- if (imm == null || v == null || editable == null) {
- return;
- }
- mUpdateExtract.flags = 0;
- // Update the entire Editable range
- mUpdateExtract.partialStartOffset = -1;
- mUpdateExtract.partialEndOffset = -1;
- mUpdateExtract.selectionStart = Selection.getSelectionStart(editable);
- mUpdateExtract.selectionEnd = Selection.getSelectionEnd(editable);
- mUpdateExtract.startOffset = 0;
- if ((mUpdateRequest.flags & GET_TEXT_WITH_STYLES) != 0) {
- mUpdateExtract.text = new SpannableString(editable);
- } else {
- mUpdateExtract.text = editable.toString();
- }
- imm.updateExtractedText(v, mUpdateRequest.token, mUpdateExtract);
- }
-
- @Override // GeckoEditableListener
- public void onSelectionChange() {
-
- final Editable editable = getEditable();
- if (editable != null) {
- notifySelectionChange(Selection.getSelectionStart(editable),
- Selection.getSelectionEnd(editable));
- }
- }
-
- private void notifySelectionChange(int start, int end) {
-
- final InputMethodManager imm = getInputMethodManager();
- final View v = getView();
- final Editable editable = getEditable();
- if (imm == null || v == null || editable == null) {
- return;
- }
- imm.updateSelection(v, start, end, getComposingSpanStart(editable),
- getComposingSpanEnd(editable));
- }
-
- @Override
- public void updateCompositionRects(final RectF[] aRects) {
- if (!Versions.feature21Plus) {
- return;
- }
-
- if (mCursorAnchorInfoBuilder == null) {
- mCursorAnchorInfoBuilder = new CursorAnchorInfo.Builder();
- }
- mCursorAnchorInfoBuilder.reset();
-
- // Calculate Gecko logical coords to screen coords
- final View v = getView();
- if (v == null) {
- return;
- }
-
- int[] viewCoords = new int[2];
- v.getLocationOnScreen(viewCoords);
-
- DynamicToolbarAnimator animator = GeckoAppShell.getLayerView().getDynamicToolbarAnimator();
- float toolbarHeight = animator.getMaxTranslation() - animator.getToolbarTranslation();
-
- Matrix matrix = GeckoAppShell.getLayerView().getMatrixForLayerRectToViewRect();
- if (matrix == null) {
- if (DEBUG) {
- Log.d(LOGTAG, "Cannot get Matrix to convert from Gecko coords to layer view coords");
- }
- return;
- }
- matrix.postTranslate(viewCoords[0], viewCoords[1] + toolbarHeight);
- mCursorAnchorInfoBuilder.setMatrix(matrix);
-
- final Editable content = getEditable();
- if (content == null) {
- return;
- }
- int composingStart = getComposingSpanStart(content);
- int composingEnd = getComposingSpanEnd(content);
- if (composingStart < 0 || composingEnd < 0) {
- if (DEBUG) {
- Log.d(LOGTAG, "No composition for updates");
- }
- return;
- }
-
- for (int i = 0; i < aRects.length; i++) {
- mCursorAnchorInfoBuilder.addCharacterBounds(i,
- aRects[i].left,
- aRects[i].top,
- aRects[i].right,
- aRects[i].bottom,
- CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION);
- }
-
- mCursorAnchorInfoBuilder.setComposingText(0, content.subSequence(composingStart, composingEnd));
-
- updateCursor();
- }
-
- @TargetApi(21)
- private void updateCursor() {
- if (mCursorAnchorInfoBuilder == null) {
- return;
- }
-
- final InputMethodManager imm = getInputMethodManager();
- final View v = getView();
- if (imm == null || v == null) {
- return;
- }
-
- imm.updateCursorAnchorInfo(v, mCursorAnchorInfoBuilder.build());
- }
-
- @Override
- public boolean requestCursorUpdates(int cursorUpdateMode) {
-
- if ((cursorUpdateMode & InputConnection.CURSOR_UPDATE_IMMEDIATE) != 0) {
- mEditableClient.requestCursorUpdates(GeckoEditableClient.ONE_SHOT);
- }
-
- if ((cursorUpdateMode & InputConnection.CURSOR_UPDATE_MONITOR) != 0) {
- mEditableClient.requestCursorUpdates(GeckoEditableClient.START_MONITOR);
- } else {
- mEditableClient.requestCursorUpdates(GeckoEditableClient.END_MONITOR);
- }
- return true;
- }
-
- @Override
- public void onDefaultKeyEvent(final KeyEvent event) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- GeckoInputConnection.this.performDefaultKeyAction(event);
- }
- });
- }
-
- private static synchronized Handler getBackgroundHandler() {
- if (sBackgroundHandler != null) {
- return sBackgroundHandler;
- }
- // Don't use GeckoBackgroundThread because Gecko thread may block waiting on
- // GeckoBackgroundThread. If we were to use GeckoBackgroundThread, due to IME,
- // GeckoBackgroundThread may end up also block waiting on Gecko thread and a
- // deadlock occurs
- Thread backgroundThread = new Thread(new Runnable() {
- @Override
- public void run() {
- Looper.prepare();
- synchronized (GeckoInputConnection.class) {
- sBackgroundHandler = new Handler();
- GeckoInputConnection.class.notify();
- }
- Looper.loop();
- // We should never be exiting the thread loop.
- throw new IllegalThreadStateException("unreachable code");
- }
- }, LOGTAG);
- backgroundThread.setDaemon(true);
- backgroundThread.start();
- while (sBackgroundHandler == null) {
- try {
- // wait for new thread to set sBackgroundHandler
- GeckoInputConnection.class.wait();
- } catch (InterruptedException e) {
- }
- }
- return sBackgroundHandler;
- }
-
- private boolean canReturnCustomHandler() {
- if (mIMEState == IME_STATE_DISABLED) {
- return false;
- }
- for (StackTraceElement frame : Thread.currentThread().getStackTrace()) {
- // We only return our custom Handler to InputMethodManager's InputConnection
- // proxy. For all other purposes, we return the regular Handler.
- // InputMethodManager retrieves the Handler for its InputConnection proxy
- // inside its method startInputInner(), so we check for that here. This is
- // valid from Android 2.2 to at least Android 4.2. If this situation ever
- // changes, we gracefully fall back to using the regular Handler.
- if ("startInputInner".equals(frame.getMethodName()) &&
- "android.view.inputmethod.InputMethodManager".equals(frame.getClassName())) {
- // only return our own Handler to InputMethodManager
- return true;
- }
- if (CUSTOM_HANDLER_TEST_METHOD.equals(frame.getMethodName()) &&
- CUSTOM_HANDLER_TEST_CLASS.equals(frame.getClassName())) {
- // InputConnection tests should also run on the custom handler
- return true;
- }
- }
- return false;
- }
-
- private boolean isPhysicalKeyboardPresent() {
- final View v = getView();
- if (v == null) {
- return false;
- }
- final Configuration config = v.getContext().getResources().getConfiguration();
- return config.keyboard != Configuration.KEYBOARD_NOKEYS;
- }
-
- // Android N: @Override // InputConnection
- // We need to suppress lint complaining about the lack override here in the meantime: it wants us to build
- // against sdk 24, even though we're using 23, and therefore complains about the lack of override.
- // Once we update to 24, we can use the actual override annotation and remove the lint suppression.
- @SuppressLint("Override")
- public Handler getHandler() {
- if (isPhysicalKeyboardPresent()) {
- return ThreadUtils.getUiHandler();
- }
-
- return getBackgroundHandler();
- }
-
- @Override // InputConnectionListener
- public Handler getHandler(Handler defHandler) {
- if (!canReturnCustomHandler()) {
- return defHandler;
- }
-
- return mEditableClient.setInputConnectionHandler(getHandler());
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- // Some keyboards require us to fill out outAttrs even if we return null.
- outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
- outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE;
- outAttrs.actionLabel = null;
-
- if (mIMEState == IME_STATE_DISABLED) {
- hideSoftInput();
- return null;
- }
-
- if (mIMEState == IME_STATE_PASSWORD ||
- "password".equalsIgnoreCase(mIMETypeHint))
- outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
- else if (mIMEState == IME_STATE_PLUGIN)
- outAttrs.inputType = InputType.TYPE_NULL; // "send key events" mode
- 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("tel"))
- outAttrs.inputType = InputType.TYPE_CLASS_PHONE;
- else if (mIMETypeHint.equalsIgnoreCase("number") ||
- mIMETypeHint.equalsIgnoreCase("range"))
- outAttrs.inputType = InputType.TYPE_CLASS_NUMBER
- | InputType.TYPE_NUMBER_FLAG_SIGNED
- | InputType.TYPE_NUMBER_FLAG_DECIMAL;
- else if (mIMETypeHint.equalsIgnoreCase("week") ||
- mIMETypeHint.equalsIgnoreCase("month"))
- outAttrs.inputType = InputType.TYPE_CLASS_DATETIME
- | InputType.TYPE_DATETIME_VARIATION_DATE;
- 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 {
- // TYPE_TEXT_FLAG_IME_MULTI_LINE flag makes the fullscreen IME line wrap
- outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT |
- InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE;
- if (mIMETypeHint.equalsIgnoreCase("textarea") ||
- mIMETypeHint.length() == 0) {
- // empty mIMETypeHint indicates contentEditable/designMode documents
- outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
- }
- if (mIMEModeHint.equalsIgnoreCase("uppercase"))
- outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
- else if (mIMEModeHint.equalsIgnoreCase("titlecase"))
- outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
- else if (mIMETypeHint.equalsIgnoreCase("text") &&
- !mIMEModeHint.equalsIgnoreCase("autocapitalized"))
- outAttrs.inputType |= InputType.TYPE_TEXT_VARIATION_NORMAL;
- else if (!mIMEModeHint.equalsIgnoreCase("lowercase"))
- outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
- // auto-capitalized mode is the default for types other than text
- }
-
- 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") ||
- mIMETypeHint.equalsIgnoreCase("search"))
- outAttrs.imeOptions = EditorInfo.IME_ACTION_SEARCH;
- else if (mIMEActionHint.equalsIgnoreCase("send"))
- outAttrs.imeOptions = EditorInfo.IME_ACTION_SEND;
- else if (mIMEActionHint.length() > 0) {
- if (DEBUG)
- Log.w(LOGTAG, "Unexpected mIMEActionHint=\"" + mIMEActionHint + "\"");
- outAttrs.actionLabel = mIMEActionHint;
- }
-
- Context context = getView().getContext();
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- if (Math.min(metrics.widthPixels, metrics.heightPixels) > INLINE_IME_MIN_DISPLAY_SIZE) {
- // prevent showing full-screen keyboard only when the screen is tall enough
- // to show some reasonable amount of the page (see bug 752709)
- outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI
- | EditorInfo.IME_FLAG_NO_FULLSCREEN;
- }
-
- if (DEBUG) {
- Log.d(LOGTAG, "mapped IME states to: inputType = " +
- Integer.toHexString(outAttrs.inputType) + ", imeOptions = " +
- Integer.toHexString(outAttrs.imeOptions));
- }
-
- String prevInputMethod = mCurrentInputMethod;
- mCurrentInputMethod = InputMethods.getCurrentInputMethod(context);
- if (DEBUG) {
- Log.d(LOGTAG, "IME: CurrentInputMethod=" + mCurrentInputMethod);
- }
-
- if (mIMEState == IME_STATE_PLUGIN) {
- // Since we are using a temporary string as the editable, the selection is at 0
- outAttrs.initialSelStart = 0;
- outAttrs.initialSelEnd = 0;
- return mKeyInputConnection;
- }
- Editable editable = getEditable();
- outAttrs.initialSelStart = Selection.getSelectionStart(editable);
- outAttrs.initialSelEnd = Selection.getSelectionEnd(editable);
-
- showSoftInput();
- return this;
- }
-
- private boolean replaceComposingSpanWithSelection() {
- final Editable content = getEditable();
- if (content == null) {
- return false;
- }
- int a = getComposingSpanStart(content),
- b = getComposingSpanEnd(content);
- if (a != -1 && b != -1) {
- if (DEBUG) {
- Log.d(LOGTAG, "removing composition at " + a + "-" + b);
- }
- removeComposingSpans(content);
- Selection.setSelection(content, a, b);
- }
- return true;
- }
-
- @Override
- public boolean commitText(CharSequence text, int newCursorPosition) {
- if (InputMethods.shouldCommitCharAsKey(mCurrentInputMethod) &&
- text.length() == 1 && newCursorPosition > 0) {
- if (DEBUG) {
- Log.d(LOGTAG, "committing \"" + text + "\" as key");
- }
- // mKeyInputConnection is a BaseInputConnection that commits text as keys;
- // but we first need to replace any composing span with a selection,
- // so that the new key events will generate characters to replace
- // text from the old composing span
- return replaceComposingSpanWithSelection() &&
- mKeyInputConnection.commitText(text, newCursorPosition);
- }
- return super.commitText(text, newCursorPosition);
- }
-
- @Override
- public boolean setSelection(int start, int end) {
- if (start < 0 || end < 0) {
- // Some keyboards (e.g. Samsung) can call setSelection with
- // negative offsets. In that case we ignore the call, similar to how
- // BaseInputConnection.setSelection ignores offsets that go past the length.
- return true;
- }
- return super.setSelection(start, end);
- }
-
- /* package */ void sendKeyEvent(final int action, KeyEvent event) {
- final Editable editable = getEditable();
- if (editable == null) {
- return;
- }
-
- final KeyListener keyListener = TextKeyListener.getInstance();
- event = translateKey(event.getKeyCode(), event);
-
- // We only let TextKeyListener do UI things on the UI thread.
- final View v = ThreadUtils.isOnUiThread() ? getView() : null;
- final int keyCode = event.getKeyCode();
- final boolean handled;
-
- if (shouldSkipKeyListener(keyCode, event)) {
- handled = false;
- } else if (action == KeyEvent.ACTION_DOWN) {
- mEditableClient.setSuppressKeyUp(true);
- handled = keyListener.onKeyDown(v, editable, keyCode, event);
- } else if (action == KeyEvent.ACTION_UP) {
- handled = keyListener.onKeyUp(v, editable, keyCode, event);
- } else {
- handled = keyListener.onKeyOther(v, editable, event);
- }
-
- if (!handled) {
- mEditableClient.sendKeyEvent(event, action, TextKeyListener.getMetaState(editable));
- }
-
- if (action == KeyEvent.ACTION_DOWN) {
- if (!handled) {
- // Usually, the down key listener call above adjusts meta states for us.
- // However, if the call didn't handle the event, we have to manually
- // adjust meta states so the meta states remain consistent.
- TextKeyListener.adjustMetaAfterKeypress(editable);
- }
- mEditableClient.setSuppressKeyUp(false);
- }
- }
-
- @Override
- public boolean sendKeyEvent(KeyEvent event) {
- sendKeyEvent(event.getAction(), event);
- return false; // seems to always return false
- }
-
- @Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- return false;
- }
-
- private boolean shouldProcessKey(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_MENU:
- case KeyEvent.KEYCODE_BACK:
- case KeyEvent.KEYCODE_VOLUME_UP:
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- case KeyEvent.KEYCODE_SEARCH:
- // ignore HEADSETHOOK to allow hold-for-voice-search to work
- case KeyEvent.KEYCODE_HEADSETHOOK:
- return false;
- }
- return true;
- }
-
- private boolean shouldSkipKeyListener(int keyCode, KeyEvent event) {
- if (mIMEState == IME_STATE_DISABLED ||
- mIMEState == IME_STATE_PLUGIN) {
- return true;
- }
- // Preserve enter and tab keys for the browser
- if (keyCode == KeyEvent.KEYCODE_ENTER ||
- keyCode == KeyEvent.KEYCODE_TAB) {
- return true;
- }
- // BaseKeyListener returns false even if it handled these keys for us,
- // so we skip the key listener entirely and handle these ourselves
- if (keyCode == KeyEvent.KEYCODE_DEL ||
- keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
- return true;
- }
- return false;
- }
-
- private KeyEvent translateKey(int keyCode, KeyEvent event) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_ENTER:
- if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
- mIMEActionHint.equalsIgnoreCase("next")) {
- return new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB);
- }
- break;
- }
-
- if (GamepadUtils.isSonyXperiaGamepadKeyEvent(event)) {
- return GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event);
- }
-
- return event;
- }
-
- // Called by OnDefaultKeyEvent handler, up from Gecko
- /* package */ void performDefaultKeyAction(KeyEvent event) {
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_MUTE:
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- case KeyEvent.KEYCODE_MEDIA_STOP:
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- case KeyEvent.KEYCODE_MEDIA_RECORD:
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
- case KeyEvent.KEYCODE_MEDIA_CLOSE:
- case KeyEvent.KEYCODE_MEDIA_EJECT:
- case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
- // Forward media keypresses to the registered handler so headset controls work
- // Does the same thing as Chromium
- // https://chromium.googlesource.com/chromium/src/+/49.0.2623.67/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java#445
- // These are all the keys dispatchMediaKeyEvent supports.
- if (AppConstants.Versions.feature19Plus) {
- // dispatchMediaKeyEvent is only available on Android 4.4+
- Context viewContext = getView().getContext();
- AudioManager am = (AudioManager)viewContext.getSystemService(Context.AUDIO_SERVICE);
- am.dispatchMediaKeyEvent(event);
- }
- break;
- }
- }
-
- private boolean processKey(final int action, final int keyCode, final KeyEvent event) {
-
- if (keyCode > KeyEvent.getMaxKeyCode() || !shouldProcessKey(keyCode, event)) {
- return false;
- }
-
- mEditableClient.postToInputConnection(new Runnable() {
- @Override
- public void run() {
- sendKeyEvent(action, event);
- }
- });
- return true;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- return processKey(KeyEvent.ACTION_DOWN, keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- return processKey(KeyEvent.ACTION_UP, keyCode, event);
- }
-
- /**
- * Get a key that represents a given character.
- */
- private KeyEvent getCharKeyEvent(final char c) {
- final long time = SystemClock.uptimeMillis();
- return new KeyEvent(time, time, KeyEvent.ACTION_MULTIPLE,
- KeyEvent.KEYCODE_UNKNOWN, /* repeat */ 0) {
- @Override
- public int getUnicodeChar() {
- return c;
- }
-
- @Override
- public int getUnicodeChar(int metaState) {
- return c;
- }
- };
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int repeatCount, final KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
- // KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters()
- final String str = event.getCharacters();
- for (int i = 0; i < str.length(); i++) {
- final KeyEvent charEvent = getCharKeyEvent(str.charAt(i));
- if (!processKey(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_UNKNOWN, charEvent) ||
- !processKey(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_UNKNOWN, charEvent)) {
- return false;
- }
- }
- return true;
- }
-
- while ((repeatCount--) != 0) {
- if (!processKey(KeyEvent.ACTION_DOWN, keyCode, event) ||
- !processKey(KeyEvent.ACTION_UP, keyCode, event)) {
- return false;
- }
- }
- return true;
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- View v = getView();
- switch (keyCode) {
- case KeyEvent.KEYCODE_MENU:
- InputMethodManager imm = getInputMethodManager();
- imm.toggleSoftInputFromWindow(v.getWindowToken(),
- InputMethodManager.SHOW_FORCED, 0);
- return true;
- default:
- break;
- }
- return false;
- }
-
- @Override
- public boolean isIMEEnabled() {
- // make sure this picks up PASSWORD and PLUGIN states as well
- return mIMEState != IME_STATE_DISABLED;
- }
-
- @Override
- public void notifyIME(int type) {
- switch (type) {
-
- case NOTIFY_IME_OF_FOCUS:
- // Showing/hiding vkb is done in notifyIMEContext
- mFocused = true;
- resetInputConnection();
- break;
-
- case NOTIFY_IME_OF_BLUR:
- // Showing/hiding vkb is done in notifyIMEContext
- mFocused = false;
- break;
-
- case NOTIFY_IME_OPEN_VKB:
- showSoftInput();
- break;
-
- default:
- if (DEBUG) {
- throw new IllegalArgumentException("Unexpected NOTIFY_IME=" + type);
- }
- break;
- }
- }
-
- @Override
- public void notifyIMEContext(int state, String typeHint, String modeHint, String actionHint) {
- // For some input type we will use a widget to display the ui, for those we must not
- // display the ime. We can display a widget for date and time types and, if the sdk version
- // is 11 or greater, for datetime/month/week as well.
- if (typeHint != null &&
- (typeHint.equalsIgnoreCase("date") ||
- typeHint.equalsIgnoreCase("time") ||
- typeHint.equalsIgnoreCase("datetime") ||
- typeHint.equalsIgnoreCase("month") ||
- typeHint.equalsIgnoreCase("week") ||
- typeHint.equalsIgnoreCase("datetime-local"))) {
- state = IME_STATE_DISABLED;
- }
-
- // mIMEState and the mIME*Hint fields should only be changed by notifyIMEContext,
- // and not reset anywhere else. Usually, notifyIMEContext is called right after a
- // focus or blur, so resetting mIMEState during the focus or blur seems harmless.
- // However, this behavior is not guaranteed. Gecko may call notifyIMEContext
- // independent of focus change; that is, a focus change may not be accompanied by
- // a notifyIMEContext call. So if we reset mIMEState inside focus, there may not
- // be another notifyIMEContext call to set mIMEState to a proper value (bug 829318)
- /* When IME is 'disabled', IME processing is disabled.
- In addition, the IME UI is hidden */
- mIMEState = state;
- mIMETypeHint = (typeHint == null) ? "" : typeHint;
- mIMEModeHint = (modeHint == null) ? "" : modeHint;
- mIMEActionHint = (actionHint == null) ? "" : actionHint;
-
- // These fields are reset here and will be updated when restartInput is called below
- mUpdateRequest = null;
- mCurrentInputMethod = "";
-
- View v = getView();
- if (v == null || !v.hasFocus()) {
- // When using Find In Page, we can still receive notifyIMEContext calls due to the
- // selection changing when highlighting. However in this case we don't want to reset/
- // show/hide the keyboard because the find box has the focus and is taking input from
- // the keyboard.
- return;
- }
-
- // On focus, the notifyIMEContext call comes *before* the
- // notifyIME(NOTIFY_IME_OF_FOCUS) call, but we need to call restartInput during
- // notifyIME, so we skip restartInput here. On blur, the notifyIMEContext call
- // comes *after* the notifyIME(NOTIFY_IME_OF_BLUR) call, and we need to call
- // restartInput here.
- if (mIMEState == IME_STATE_DISABLED || mFocused) {
- restartInput();
- }
- }
-}
-
-final class DebugGeckoInputConnection
- extends GeckoInputConnection
- implements InvocationHandler {
-
- private InputConnection mProxy;
- private final StringBuilder mCallLevel;
-
- private DebugGeckoInputConnection(View targetView,
- GeckoEditableClient editable) {
- super(targetView, editable);
- mCallLevel = new StringBuilder();
- }
-
- public static GeckoEditableListener create(View targetView,
- GeckoEditableClient editable) {
- final Class<?>[] PROXY_INTERFACES = { InputConnection.class,
- InputConnectionListener.class,
- GeckoEditableListener.class };
- DebugGeckoInputConnection dgic =
- new DebugGeckoInputConnection(targetView, editable);
- dgic.mProxy = (InputConnection)Proxy.newProxyInstance(
- GeckoInputConnection.class.getClassLoader(),
- PROXY_INTERFACES, dgic);
- return (GeckoEditableListener)dgic.mProxy;
- }
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
-
- StringBuilder log = new StringBuilder(mCallLevel);
- log.append("> ").append(method.getName()).append("(");
- if (args != null) {
- for (Object arg : args) {
- // translate argument values to constant names
- if ("notifyIME".equals(method.getName()) && arg == args[0]) {
- log.append(GeckoEditable.getConstantName(
- GeckoEditableListener.class, "NOTIFY_IME_", arg));
- } else if ("notifyIMEContext".equals(method.getName()) && arg == args[0]) {
- log.append(GeckoEditable.getConstantName(
- GeckoEditableListener.class, "IME_STATE_", arg));
- } else {
- GeckoEditable.debugAppend(log, arg);
- }
- log.append(", ");
- }
- if (args.length > 0) {
- log.setLength(log.length() - 2);
- }
- }
- log.append(")");
- Log.d(LOGTAG, log.toString());
-
- mCallLevel.append(' ');
- Object ret = method.invoke(this, args);
- if (ret == this) {
- ret = mProxy;
- }
- mCallLevel.setLength(Math.max(0, mCallLevel.length() - 1));
-
- log.setLength(mCallLevel.length());
- log.append("< ").append(method.getName());
- if (!method.getReturnType().equals(Void.TYPE)) {
- GeckoEditable.debugAppend(log.append(": "), ret);
- }
- Log.d(LOGTAG, log.toString());
- return ret;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoNetworkManager.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoNetworkManager.java
deleted file mode 100644
index 0cb56a7d2..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoNetworkManager.java
+++ /dev/null
@@ -1,491 +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 org.mozilla.gecko.annotation.JNITarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.util.NativeEventListener;
-import org.mozilla.gecko.util.NativeJSObject;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.NetworkUtils;
-import org.mozilla.gecko.util.NetworkUtils.ConnectionSubType;
-import org.mozilla.gecko.util.NetworkUtils.ConnectionType;
-import org.mozilla.gecko.util.NetworkUtils.NetworkStatus;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.ConnectivityManager;
-import android.net.DhcpInfo;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.telephony.TelephonyManager;
-import android.text.format.Formatter;
-import android.util.Log;
-
-/**
- * Provides connection type, subtype and general network status (up/down).
- *
- * According to spec of Network Information API version 3, connection types include:
- * bluetooth, cellular, ethernet, none, wifi and other. The objective of providing such general
- * connection is due to some security concerns. In short, we don't want to expose exact network type,
- * especially the cellular network type.
- *
- * Specific mobile subtypes are mapped to general 2G, 3G and 4G buckets.
- *
- * Logic is implemented as a state machine, so see the transition matrix to figure out what happens when.
- * This class depends on access to the context, so only use after GeckoAppShell has been initialized.
- */
-public class GeckoNetworkManager extends BroadcastReceiver implements NativeEventListener {
- private static final String LOGTAG = "GeckoNetworkManager";
-
- private static final String LINK_DATA_CHANGED = "changed";
-
- private static GeckoNetworkManager instance;
-
- // We hackishly (yet harmlessly, in this case) keep a Context reference passed in via the start method.
- // See context handling notes in handleManagerEvent, and Bug 1277333.
- private Context context;
-
- public static void destroy() {
- if (instance != null) {
- instance.onDestroy();
- instance = null;
- }
- }
-
- public enum ManagerState {
- OffNoListeners,
- OffWithListeners,
- OnNoListeners,
- OnWithListeners
- }
-
- public enum ManagerEvent {
- start,
- stop,
- enableNotifications,
- disableNotifications,
- receivedUpdate
- }
-
- private ManagerState currentState = ManagerState.OffNoListeners;
- private ConnectionType currentConnectionType = ConnectionType.NONE;
- private ConnectionType previousConnectionType = ConnectionType.NONE;
- private ConnectionSubType currentConnectionSubtype = ConnectionSubType.UNKNOWN;
- private ConnectionSubType previousConnectionSubtype = ConnectionSubType.UNKNOWN;
- private NetworkStatus currentNetworkStatus = NetworkStatus.UNKNOWN;
- private NetworkStatus previousNetworkStatus = NetworkStatus.UNKNOWN;
-
- private enum InfoType {
- MCC,
- MNC
- }
-
- private GeckoNetworkManager() {
- EventDispatcher.getInstance().registerGeckoThreadListener(this,
- "Wifi:Enable",
- "Wifi:GetIPAddress");
- }
-
- private void onDestroy() {
- handleManagerEvent(ManagerEvent.stop);
- EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
- "Wifi:Enable",
- "Wifi:GetIPAddress");
- }
-
- public static GeckoNetworkManager getInstance() {
- if (instance == null) {
- instance = new GeckoNetworkManager();
- }
-
- return instance;
- }
-
- public double[] getCurrentInformation() {
- final Context applicationContext = GeckoAppShell.getApplicationContext();
- final ConnectionType connectionType = currentConnectionType;
- return new double[] {
- connectionType.value,
- connectionType == ConnectionType.WIFI ? 1.0 : 0.0,
- connectionType == ConnectionType.WIFI ? wifiDhcpGatewayAddress(applicationContext) : 0.0
- };
- }
-
- @Override
- public void onReceive(Context aContext, Intent aIntent) {
- handleManagerEvent(ManagerEvent.receivedUpdate);
- }
-
- public void start(final Context context) {
- this.context = context;
- handleManagerEvent(ManagerEvent.start);
- }
-
- public void stop() {
- handleManagerEvent(ManagerEvent.stop);
- }
-
- public void enableNotifications() {
- handleManagerEvent(ManagerEvent.enableNotifications);
- }
-
- public void disableNotifications() {
- handleManagerEvent(ManagerEvent.disableNotifications);
- }
-
- /**
- * For a given event, figure out the next state, run any transition by-product actions, and switch
- * current state to the next state. If event is invalid for the current state, this is a no-op.
- *
- * @param event Incoming event
- * @return Boolean indicating if transition was performed.
- */
- private synchronized boolean handleManagerEvent(ManagerEvent event) {
- final ManagerState nextState = getNextState(currentState, event);
-
- Log.d(LOGTAG, "Incoming event " + event + " for state " + currentState + " -> " + nextState);
- if (nextState == null) {
- Log.w(LOGTAG, "Invalid event " + event + " for state " + currentState);
- return false;
- }
-
- // We're being deliberately careful about handling context here; it's possible that in some
- // rare cases and possibly related to timing of when this is called (seems to be early in the startup phase),
- // GeckoAppShell.getApplicationContext() will be null, and .start() wasn't called yet,
- // so we don't have a local Context reference either. If both of these are true, we have to drop the event.
- // NB: this is hacky (and these checks attempt to isolate the hackiness), and root cause
- // seems to be how this class fits into the larger ecosystem and general flow of events.
- // See Bug 1277333.
- final Context contextForAction;
- if (context != null) {
- contextForAction = context;
- } else {
- contextForAction = GeckoAppShell.getApplicationContext();
- }
-
- if (contextForAction == null) {
- Log.w(LOGTAG, "Context is not available while processing event " + event + " for state " + currentState);
- return false;
- }
-
- performActionsForStateEvent(contextForAction, currentState, event);
- currentState = nextState;
-
- return true;
- }
-
- /**
- * Defines a transition matrix for our state machine. For a given state/event pair, returns nextState.
- *
- * @param currentState Current state against which we have an incoming event
- * @param event Incoming event for which we'd like to figure out the next state
- * @return State into which we should transition as result of given event
- */
- @Nullable
- public static ManagerState getNextState(@NonNull ManagerState currentState, @NonNull ManagerEvent event) {
- switch (currentState) {
- case OffNoListeners:
- switch (event) {
- case start:
- return ManagerState.OnNoListeners;
- case enableNotifications:
- return ManagerState.OffWithListeners;
- default:
- return null;
- }
- case OnNoListeners:
- switch (event) {
- case stop:
- return ManagerState.OffNoListeners;
- case enableNotifications:
- return ManagerState.OnWithListeners;
- case receivedUpdate:
- return ManagerState.OnNoListeners;
- default:
- return null;
- }
- case OnWithListeners:
- switch (event) {
- case stop:
- return ManagerState.OffWithListeners;
- case disableNotifications:
- return ManagerState.OnNoListeners;
- case receivedUpdate:
- return ManagerState.OnWithListeners;
- default:
- return null;
- }
- case OffWithListeners:
- switch (event) {
- case start:
- return ManagerState.OnWithListeners;
- case disableNotifications:
- return ManagerState.OffNoListeners;
- default:
- return null;
- }
- default:
- throw new IllegalStateException("Unknown current state: " + currentState.name());
- }
- }
-
- /**
- * For a given state/event combination, run any actions which are by-products of leaving the state
- * because of a given event. Since this is a deterministic state machine, we can easily do that
- * without any additional information.
- *
- * @param currentState State which we are leaving
- * @param event Event which is causing us to leave the state
- */
- private void performActionsForStateEvent(final Context context, final ManagerState currentState, final ManagerEvent event) {
- // NB: network state might be queried via getCurrentInformation at any time; pre-rewrite behaviour was
- // that network state was updated whenever enableNotifications was called. To avoid deviating
- // from previous behaviour and causing weird side-effects, we call updateNetworkStateAndConnectionType
- // whenever notifications are enabled.
- switch (currentState) {
- case OffNoListeners:
- if (event == ManagerEvent.start) {
- updateNetworkStateAndConnectionType(context);
- registerBroadcastReceiver(context, this);
- }
- if (event == ManagerEvent.enableNotifications) {
- updateNetworkStateAndConnectionType(context);
- }
- break;
- case OnNoListeners:
- if (event == ManagerEvent.receivedUpdate) {
- updateNetworkStateAndConnectionType(context);
- sendNetworkStateToListeners(context);
- }
- if (event == ManagerEvent.enableNotifications) {
- updateNetworkStateAndConnectionType(context);
- registerBroadcastReceiver(context, this);
- }
- if (event == ManagerEvent.stop) {
- unregisterBroadcastReceiver(context, this);
- }
- break;
- case OnWithListeners:
- if (event == ManagerEvent.receivedUpdate) {
- updateNetworkStateAndConnectionType(context);
- sendNetworkStateToListeners(context);
- }
- if (event == ManagerEvent.stop) {
- unregisterBroadcastReceiver(context, this);
- }
- /* no-op event: ManagerEvent.disableNotifications */
- break;
- case OffWithListeners:
- if (event == ManagerEvent.start) {
- registerBroadcastReceiver(context, this);
- }
- /* no-op event: ManagerEvent.disableNotifications */
- break;
- default:
- throw new IllegalStateException("Unknown current state: " + currentState.name());
- }
- }
-
- /**
- * Update current network state and connection types.
- */
- private void updateNetworkStateAndConnectionType(final Context context) {
- final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- // Type/status getters below all have a defined behaviour for when connectivityManager == null
- if (connectivityManager == null) {
- Log.e(LOGTAG, "ConnectivityManager does not exist.");
- }
- currentConnectionType = NetworkUtils.getConnectionType(connectivityManager);
- currentNetworkStatus = NetworkUtils.getNetworkStatus(connectivityManager);
- currentConnectionSubtype = NetworkUtils.getConnectionSubType(connectivityManager);
- Log.d(LOGTAG, "New network state: " + currentNetworkStatus + ", " + currentConnectionType + ", " + currentConnectionSubtype);
- }
-
- @WrapForJNI(dispatchTo = "gecko")
- private static native void onConnectionChanged(int type, String subType,
- boolean isWifi, int DHCPGateway);
-
- @WrapForJNI(dispatchTo = "gecko")
- private static native void onStatusChanged(String status);
-
- /**
- * Send current network state and connection type to whomever is listening.
- */
- private void sendNetworkStateToListeners(final Context context) {
- if (currentConnectionType != previousConnectionType ||
- currentConnectionSubtype != previousConnectionSubtype) {
- previousConnectionType = currentConnectionType;
- previousConnectionSubtype = currentConnectionSubtype;
-
- final boolean isWifi = currentConnectionType == ConnectionType.WIFI;
- final int gateway = !isWifi ? 0 :
- wifiDhcpGatewayAddress(context);
-
- if (GeckoThread.isRunning()) {
- onConnectionChanged(currentConnectionType.value,
- currentConnectionSubtype.value, isWifi, gateway);
- } else {
- GeckoThread.queueNativeCall(GeckoNetworkManager.class, "onConnectionChanged",
- currentConnectionType.value,
- String.class, currentConnectionSubtype.value,
- isWifi, gateway);
- }
- }
-
- final String status;
-
- if (currentNetworkStatus != previousNetworkStatus) {
- previousNetworkStatus = currentNetworkStatus;
- status = currentNetworkStatus.value;
- } else {
- status = LINK_DATA_CHANGED;
- }
-
- if (GeckoThread.isRunning()) {
- onStatusChanged(status);
- } else {
- GeckoThread.queueNativeCall(GeckoNetworkManager.class, "onStatusChanged",
- String.class, status);
- }
- }
-
- /**
- * Stop listening for network state updates.
- */
- private static void unregisterBroadcastReceiver(final Context context, final BroadcastReceiver receiver) {
- context.unregisterReceiver(receiver);
- }
-
- /**
- * Start listening for network state updates.
- */
- private static void registerBroadcastReceiver(final Context context, final BroadcastReceiver receiver) {
- final IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
- context.registerReceiver(receiver, filter);
- }
-
- private static int wifiDhcpGatewayAddress(final Context context) {
- if (context == null) {
- return 0;
- }
-
- try {
- WifiManager mgr = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- DhcpInfo d = mgr.getDhcpInfo();
- if (d == null) {
- return 0;
- }
-
- return d.gateway;
-
- } catch (Exception ex) {
- // getDhcpInfo() is not documented to require any permissions, but on some devices
- // requires android.permission.ACCESS_WIFI_STATE. Just catch the generic exception
- // here and returning 0. Not logging because this could be noisy.
- return 0;
- }
- }
-
- @Override
- /**
- * Handles native messages, not part of the state machine flow.
- */
- public void handleMessage(final String event, final NativeJSObject message,
- final EventCallback callback) {
- final Context applicationContext = GeckoAppShell.getApplicationContext();
- switch (event) {
- case "Wifi:Enable":
- final WifiManager mgr = (WifiManager) applicationContext.getSystemService(Context.WIFI_SERVICE);
-
- if (!mgr.isWifiEnabled()) {
- mgr.setWifiEnabled(true);
- } else {
- // If Wifi is enabled, maybe you need to select a network
- Intent intent = new Intent(android.provider.Settings.ACTION_WIFI_SETTINGS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- applicationContext.startActivity(intent);
- }
- break;
- case "Wifi:GetIPAddress":
- getWifiIPAddress(callback);
- break;
- }
- }
-
- // This function only works for IPv4; not part of the state machine flow.
- private void getWifiIPAddress(final EventCallback callback) {
- final WifiManager mgr = (WifiManager) GeckoAppShell.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
-
- if (mgr == null) {
- callback.sendError("Cannot get WifiManager");
- return;
- }
-
- final WifiInfo info = mgr.getConnectionInfo();
- if (info == null) {
- callback.sendError("Cannot get connection info");
- return;
- }
-
- int ip = info.getIpAddress();
- if (ip == 0) {
- callback.sendError("Cannot get IPv4 address");
- return;
- }
- callback.sendSuccess(Formatter.formatIpAddress(ip));
- }
-
- private static int getNetworkOperator(InfoType type, Context context) {
- if (null == context) {
- return -1;
- }
-
- TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- if (tel == null) {
- Log.e(LOGTAG, "Telephony service does not exist");
- return -1;
- }
-
- String networkOperator = tel.getNetworkOperator();
- if (networkOperator == null || networkOperator.length() <= 3) {
- return -1;
- }
-
- if (type == InfoType.MNC) {
- return Integer.parseInt(networkOperator.substring(3));
- }
-
- if (type == InfoType.MCC) {
- return Integer.parseInt(networkOperator.substring(0, 3));
- }
-
- return -1;
- }
-
- /**
- * These are called from JavaScript ctypes. Avoid letting ProGuard delete them.
- *
- * Note that these methods must only be called after GeckoAppShell has been
- * initialized: they depend on access to the context.
- *
- * Not part of the state machine flow.
- */
- @JNITarget
- public static int getMCC() {
- return getNetworkOperator(InfoType.MCC, GeckoAppShell.getApplicationContext());
- }
-
- @JNITarget
- public static int getMNC() {
- return getNetworkOperator(InfoType.MNC, GeckoAppShell.getApplicationContext());
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java
deleted file mode 100644
index 27ec4f1dd..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfile.java
+++ /dev/null
@@ -1,1002 +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.app.Activity;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.support.annotation.WorkerThread;
-import android.text.TextUtils;
-import android.util.Log;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
-import org.mozilla.gecko.GeckoProfileDirectories.NoSuchProfileException;
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.util.FileUtils;
-import org.mozilla.gecko.util.INIParser;
-import org.mozilla.gecko.util.INISection;
-import org.mozilla.gecko.util.IntentUtils;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-import java.util.Enumeration;
-import java.util.Hashtable;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public final class GeckoProfile {
- private static final String LOGTAG = "GeckoProfile";
-
- // The path in the profile to the file containing the client ID.
- private static final String CLIENT_ID_FILE_PATH = "datareporting/state.json";
- private static final String FHR_CLIENT_ID_FILE_PATH = "healthreport/state.json";
- // In the client ID file, the attribute title in the JSON object containing the client ID value.
- private static final String CLIENT_ID_JSON_ATTR = "clientID";
-
- private static final String TIMES_PATH = "times.json";
- private static final String PROFILE_CREATION_DATE_JSON_ATTR = "created";
-
- // Only tests should need to do this.
- // We can default this to AppConstants.RELEASE_OR_BETA once we fix Bug 1069687.
- private static volatile boolean sAcceptDirectoryChanges = true;
-
- @RobocopTarget
- public static void enableDirectoryChanges() {
- Log.w(LOGTAG, "Directory changes should only be enabled for tests. And even then it's a bad idea.");
- sAcceptDirectoryChanges = true;
- }
-
- public static final String DEFAULT_PROFILE = "default";
- // Profile is using a custom directory outside of the Mozilla directory.
- public static final String CUSTOM_PROFILE = "";
-
- public static final String GUEST_PROFILE_DIR = "guest";
- public static final String GUEST_MODE_PREF = "guestMode";
-
- // Session store
- private static final String SESSION_FILE = "sessionstore.js";
- private static final String SESSION_FILE_BACKUP = "sessionstore.bak";
- private static final String SESSION_FILE_PREVIOUS = "sessionstore.old";
- private static final long MAX_PREVIOUS_FILE_AGE = 1000 * 3600 * 24; // 24 hours
-
- private boolean mOldSessionDataProcessed = false;
-
- private static final ConcurrentHashMap<String, GeckoProfile> sProfileCache =
- new ConcurrentHashMap<String, GeckoProfile>(
- /* capacity */ 4, /* load factor */ 0.75f, /* concurrency */ 2);
- private static String sDefaultProfileName;
-
- private final String mName;
- private final File mMozillaDir;
- private final Context mApplicationContext;
-
- private Object mData;
-
- /**
- * Access to this member should be synchronized to avoid
- * races during creation -- particularly between getDir and GeckoView#init.
- *
- * Not final because this is lazily computed.
- */
- private File mProfileDir;
-
- private Boolean mInGuestMode;
-
- public static boolean shouldUseGuestMode(final Context context) {
- return GeckoSharedPrefs.forApp(context).getBoolean(GUEST_MODE_PREF, false);
- }
-
- public static void enterGuestMode(final Context context) {
- GeckoSharedPrefs.forApp(context).edit().putBoolean(GUEST_MODE_PREF, true).commit();
- }
-
- public static void leaveGuestMode(final Context context) {
- GeckoSharedPrefs.forApp(context).edit().putBoolean(GUEST_MODE_PREF, false).commit();
- }
-
- public static GeckoProfile initFromArgs(final Context context, final String args) {
- if (shouldUseGuestMode(context)) {
- final GeckoProfile guestProfile = getGuestProfile(context);
- if (guestProfile != null) {
- return guestProfile;
- }
- // Failed to create guest profile; leave guest mode.
- leaveGuestMode(context);
- }
-
- // We never want to use the guest mode profile concurrently with a normal profile
- // -- no syncing to it, no dual-profile usage, nothing. GeckoThread startup with
- // a conventional GeckoProfile will cause the guest profile to be deleted and
- // guest mode to reset.
- if (getGuestDir(context).isDirectory()) {
- final GeckoProfile guestProfile = getGuestProfile(context);
- if (guestProfile != null) {
- removeProfile(context, guestProfile);
- }
- }
-
- String profileName = null;
- String profilePath = null;
-
- if (args != null && args.contains("-P")) {
- final Pattern p = Pattern.compile("(?:-P\\s*)(\\w*)(\\s*)");
- final Matcher m = p.matcher(args);
- if (m.find()) {
- profileName = m.group(1);
- }
- }
-
- if (args != null && args.contains("-profile")) {
- final Pattern p = Pattern.compile("(?:-profile\\s*)(\\S*)(\\s*)");
- final Matcher m = p.matcher(args);
- if (m.find()) {
- profilePath = m.group(1);
- }
- }
-
- if (profileName == null && profilePath == null) {
- // Get the default profile for the Activity.
- return getDefaultProfile(context);
- }
-
- return GeckoProfile.get(context, profileName, profilePath);
- }
-
- private static GeckoProfile getDefaultProfile(Context context) {
- try {
- return get(context, getDefaultProfileName(context));
-
- } catch (final NoMozillaDirectoryException e) {
- // If this failed, we're screwed.
- Log.wtf(LOGTAG, "Unable to get default profile name.", e);
- throw new RuntimeException(e);
- }
- }
-
- public static GeckoProfile get(Context context) {
- return get(context, null, (File) null);
- }
-
- public static GeckoProfile get(Context context, String profileName) {
- if (profileName != null) {
- GeckoProfile profile = sProfileCache.get(profileName);
- if (profile != null)
- return profile;
- }
- return get(context, profileName, (File)null);
- }
-
- @RobocopTarget
- public static GeckoProfile get(Context context, String profileName, String profilePath) {
- File dir = null;
- if (!TextUtils.isEmpty(profilePath)) {
- dir = new File(profilePath);
- if (!dir.exists() || !dir.isDirectory()) {
- Log.w(LOGTAG, "requested profile directory missing: " + profilePath);
- }
- }
- return get(context, profileName, dir);
- }
-
- // Note that the profile cache respects only the profile name!
- // If the directory changes, the returned GeckoProfile instance will be mutated.
- @RobocopTarget
- public static GeckoProfile get(Context context, String profileName, File profileDir) {
- if (context == null) {
- throw new IllegalArgumentException("context must be non-null");
- }
-
- // Null name? | Null dir? | Returned profile
- // ------------------------------------------
- // Yes | Yes | Active profile or default profile.
- // No | Yes | Profile with specified name at default dir.
- // Yes | No | Custom (anonymous) profile with specified dir.
- // No | No | Profile with specified name at specified dir.
-
- if (profileName == null && profileDir == null) {
- // If no profile info was passed in, look for the active profile or a default profile.
- final GeckoProfile profile = GeckoThread.getActiveProfile();
- if (profile != null) {
- return profile;
- }
-
- final String args;
- if (context instanceof Activity) {
- args = IntentUtils.getStringExtraSafe(((Activity) context).getIntent(), "args");
- } else {
- args = null;
- }
-
- return GeckoProfile.initFromArgs(context, args);
-
- } else if (profileName == null) {
- // If only profile dir was passed in, use custom (anonymous) profile.
- profileName = CUSTOM_PROFILE;
-
- } else if (AppConstants.DEBUG_BUILD) {
- Log.v(LOGTAG, "Fetching profile: '" + profileName + "', '" + profileDir + "'");
- }
-
- // We require the profile dir to exist if specified, so create it here if needed.
- final boolean init = profileDir != null && profileDir.mkdirs();
-
- // Actually try to look up the profile.
- GeckoProfile profile = sProfileCache.get(profileName);
- GeckoProfile newProfile = null;
-
- if (profile == null) {
- try {
- newProfile = new GeckoProfile(context, profileName, profileDir);
- } catch (NoMozillaDirectoryException e) {
- // We're unable to do anything sane here.
- throw new RuntimeException(e);
- }
-
- profile = sProfileCache.putIfAbsent(profileName, newProfile);
- }
-
- if (profile == null) {
- profile = newProfile;
-
- } else if (profileDir != null) {
- // We have an existing profile but was given an alternate directory.
- boolean consistent = false;
- try {
- consistent = profile.mProfileDir != null &&
- profile.mProfileDir.getCanonicalPath().equals(profileDir.getCanonicalPath());
- } catch (final IOException e) {
- }
-
- if (!consistent) {
- if (!sAcceptDirectoryChanges || !profileDir.isDirectory()) {
- throw new IllegalStateException(
- "Refusing to reuse profile with a different directory.");
- }
-
- if (AppConstants.RELEASE_OR_BETA) {
- Log.e(LOGTAG, "Release build trying to switch out profile dir. " +
- "This is an error, but let's do what we can.");
- }
- profile.setDir(profileDir);
- }
- }
-
- if (init) {
- // Initialize the profile directory if we had to create it.
- profile.enqueueInitialization(profileDir);
- }
-
- return profile;
- }
-
- // Currently unused outside of testing.
- @RobocopTarget
- public static boolean removeProfile(final Context context, final GeckoProfile profile) {
- final boolean success = profile.remove();
-
- if (success) {
- // Clear all shared prefs for the given profile.
- GeckoSharedPrefs.forProfileName(context, profile.getName())
- .edit().clear().apply();
- }
-
- return success;
- }
-
- private static File getGuestDir(final Context context) {
- return context.getFileStreamPath(GUEST_PROFILE_DIR);
- }
-
- @RobocopTarget
- public static GeckoProfile getGuestProfile(final Context context) {
- return get(context, CUSTOM_PROFILE, getGuestDir(context));
- }
-
- public static boolean isGuestProfile(final Context context, final String profileName,
- final File profileDir) {
- // Guest profile is just a custom profile with a special path.
- if (profileDir == null || !CUSTOM_PROFILE.equals(profileName)) {
- return false;
- }
-
- try {
- return profileDir.getCanonicalPath().equals(getGuestDir(context).getCanonicalPath());
- } catch (final IOException e) {
- return false;
- }
- }
-
- private GeckoProfile(Context context, String profileName, File profileDir) throws NoMozillaDirectoryException {
- if (profileName == null) {
- throw new IllegalArgumentException("Unable to create GeckoProfile for empty profile name.");
- } else if (CUSTOM_PROFILE.equals(profileName) && profileDir == null) {
- throw new IllegalArgumentException("Custom profile must have a directory");
- }
-
- mApplicationContext = context.getApplicationContext();
- mName = profileName;
- mMozillaDir = GeckoProfileDirectories.getMozillaDirectory(context);
-
- mProfileDir = profileDir;
- if (profileDir != null && !profileDir.isDirectory()) {
- throw new IllegalArgumentException("Profile directory must exist if specified.");
- }
- }
-
- /**
- * Return the custom data object associated with this profile, which was set by the
- * previous {@link #setData(Object)} call. This association is valid for the duration
- * of the process lifetime. The caller must ensure proper synchronization, typically
- * by synchronizing on the object returned by {@link #getLock()}.
- *
- * The data object is usually a database object that stores per-profile data such as
- * page history. However, it can be any other object that needs to maintain
- * profile-specific state.
- *
- * @return Associated data object
- */
- public Object getData() {
- return mData;
- }
-
- /**
- * Associate this profile with a custom data object, which can be retrieved by
- * subsequent {@link #getData()} calls. The caller must ensure proper
- * synchronization, typically by synchronizing on the object returned by {@link
- * #getLock()}.
- *
- * @param data Custom data object
- */
- public void setData(final Object data) {
- mData = data;
- }
-
- private void setDir(File dir) {
- if (dir != null && dir.exists() && dir.isDirectory()) {
- synchronized (this) {
- mProfileDir = dir;
- mInGuestMode = null;
- }
- }
- }
-
- @RobocopTarget
- public String getName() {
- return mName;
- }
-
- public boolean isCustomProfile() {
- return CUSTOM_PROFILE.equals(mName);
- }
-
- @RobocopTarget
- public boolean inGuestMode() {
- if (mInGuestMode == null) {
- mInGuestMode = isGuestProfile(GeckoAppShell.getApplicationContext(),
- mName, mProfileDir);
- }
- return mInGuestMode;
- }
-
- /**
- * Return an Object that can be used with a synchronized statement to allow
- * exclusive access to the profile.
- */
- public Object getLock() {
- return this;
- }
-
- /**
- * Retrieves the directory backing the profile. This method acts
- * as a lazy initializer for the GeckoProfile instance.
- */
- @RobocopTarget
- public synchronized File getDir() {
- forceCreateLocked();
- return mProfileDir;
- }
-
- /**
- * Forces profile creation. Consider using {@link #getDir()} to initialize the profile instead - it is the
- * lazy initializer and, for our code reasoning abilities, we should initialize the profile in one place.
- */
- private void forceCreateLocked() {
- if (mProfileDir != null) {
- return;
- }
-
- try {
- // Check if a profile with this name already exists.
- try {
- mProfileDir = findProfileDir();
- Log.d(LOGTAG, "Found profile dir.");
- } catch (NoSuchProfileException noSuchProfile) {
- // If it doesn't exist, create it.
- mProfileDir = createProfileDir();
- }
- } catch (IOException ioe) {
- Log.e(LOGTAG, "Error getting profile dir", ioe);
- }
- }
-
- public File getFile(String aFile) {
- File f = getDir();
- if (f == null)
- return null;
-
- return new File(f, aFile);
- }
-
- /**
- * Retrieves the Gecko client ID from the filesystem. If the client ID does not exist, we attempt to migrate and
- * persist it from FHR and, if that fails, we attempt to create a new one ourselves.
- *
- * This method assumes the client ID is located in a file at a hard-coded path within the profile. The format of
- * this file is a JSONObject which at the bottom level contains a String -> String mapping containing the client ID.
- *
- * WARNING: the platform provides a JSM to retrieve the client ID [1] and this would be a
- * robust way to access it. However, we don't want to rely on Gecko running in order to get
- * the client ID so instead we access the file this module accesses directly. However, it's
- * possible the format of this file (and the access calls in the jsm) will change, leaving
- * this code to fail. There are tests in TestGeckoProfile to verify the file format but be
- * warned: THIS IS NOT FOOLPROOF.
- *
- * [1]: https://dxr.mozilla.org/mozilla-central/source/toolkit/modules/ClientID.jsm
- *
- * @throws IOException if the client ID could not be retrieved.
- */
- // Mimics ClientID.jsm – _doLoadClientID.
- @WorkerThread
- public String getClientId() throws IOException {
- try {
- return getValidClientIdFromDisk(CLIENT_ID_FILE_PATH);
- } catch (final IOException e) {
- // Avoid log spam: don't log the full Exception w/ the stack trace.
- Log.d(LOGTAG, "Could not get client ID - attempting to migrate ID from FHR: " + e.getLocalizedMessage());
- }
-
- String clientIdToWrite;
- try {
- clientIdToWrite = getValidClientIdFromDisk(FHR_CLIENT_ID_FILE_PATH);
- } catch (final IOException e) {
- // Avoid log spam: don't log the full Exception w/ the stack trace.
- Log.d(LOGTAG, "Could not migrate client ID from FHR – creating a new one: " + e.getLocalizedMessage());
- clientIdToWrite = generateNewClientId();
- }
-
- // There is a possibility Gecko is running and the Gecko telemetry implementation decided it's time to generate
- // the client ID, writing client ID underneath us. Since it's highly unlikely (e.g. we run in onStart before
- // Gecko is started), we don't handle that possibility besides writing the ID and then reading from the file
- // again (rather than just returning the value we generated before writing).
- //
- // In the event it does happen, any discrepancy will be resolved after a restart. In the mean time, both this
- // implementation and the Gecko implementation could upload documents with inconsistent IDs.
- //
- // In any case, if we get an exception, intentionally throw - there's nothing more to do here.
- persistClientId(clientIdToWrite);
- return getValidClientIdFromDisk(CLIENT_ID_FILE_PATH);
- }
-
- protected static String generateNewClientId() {
- return UUID.randomUUID().toString();
- }
-
- /**
- * @return a valid client ID
- * @throws IOException if a valid client ID could not be retrieved
- */
- @WorkerThread
- private String getValidClientIdFromDisk(final String filePath) throws IOException {
- final JSONObject obj = readJSONObjectFromFile(filePath);
- final String clientId = obj.optString(CLIENT_ID_JSON_ATTR);
- if (isClientIdValid(clientId)) {
- return clientId;
- }
- throw new IOException("Received client ID is invalid: " + clientId);
- }
-
- /**
- * Persists the given client ID to disk. This will overwrite any existing files.
- */
- @WorkerThread
- private void persistClientId(final String clientId) throws IOException {
- if (!ensureParentDirs(CLIENT_ID_FILE_PATH)) {
- throw new IOException("Could not create client ID parent directories");
- }
-
- final JSONObject obj = new JSONObject();
- try {
- obj.put(CLIENT_ID_JSON_ATTR, clientId);
- } catch (final JSONException e) {
- throw new IOException("Could not create client ID JSON object", e);
- }
-
- // ClientID.jsm overwrites the file to store the client ID so it's okay if we do it too.
- Log.d(LOGTAG, "Attempting to write new client ID");
- writeFile(CLIENT_ID_FILE_PATH, obj.toString()); // Logs errors within function: ideally we'd throw.
- }
-
- // From ClientID.jsm - isValidClientID.
- public static boolean isClientIdValid(final String clientId) {
- // We could use UUID.fromString but, for consistency, we take the implementation from ClientID.jsm.
- if (TextUtils.isEmpty(clientId)) {
- return false;
- }
- return clientId.matches("(?i:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})");
- }
-
- /**
- * Gets the profile creation date and persists it if it had to be generated.
- *
- * To get this value, we first look in times.json. If that could not be accessed, we
- * return the package's first install date. This is not a perfect solution because a
- * user may have large gap between install time and first use.
- *
- * A more correct algorithm could be the one performed by the JS code in ProfileAge.jsm
- * getOldestProfileTimestamp: walk the tree and return the oldest timestamp on the files
- * within the profile. However, since times.json will only not exist for the small
- * number of really old profiles, we're okay with the package install date compromise for
- * simplicity.
- *
- * @return the profile creation date in the format returned by {@link System#currentTimeMillis()}
- * or -1 if the value could not be persisted.
- */
- @WorkerThread
- public long getAndPersistProfileCreationDate(final Context context) {
- try {
- return getProfileCreationDateFromTimesFile();
- } catch (final IOException e) {
- Log.d(LOGTAG, "Unable to retrieve profile creation date from times.json. Getting from system...");
- final long packageInstallMillis = org.mozilla.gecko.util.ContextUtils.getCurrentPackageInfo(context).firstInstallTime;
- try {
- persistProfileCreationDateToTimesFile(packageInstallMillis);
- } catch (final IOException ioEx) {
- // We return -1 to ensure the profileCreationDate
- // will either be an error (-1) or a consistent value.
- Log.w(LOGTAG, "Unable to persist profile creation date - returning -1");
- return -1;
- }
-
- return packageInstallMillis;
- }
- }
-
- @WorkerThread
- private long getProfileCreationDateFromTimesFile() throws IOException {
- final JSONObject obj = readJSONObjectFromFile(TIMES_PATH);
- try {
- return obj.getLong(PROFILE_CREATION_DATE_JSON_ATTR);
- } catch (final JSONException e) {
- // Don't log to avoid leaking data in JSONObject.
- throw new IOException("Profile creation does not exist in JSONObject");
- }
- }
-
- @WorkerThread
- private void persistProfileCreationDateToTimesFile(final long profileCreationMillis) throws IOException {
- final JSONObject obj = new JSONObject();
- try {
- obj.put(PROFILE_CREATION_DATE_JSON_ATTR, profileCreationMillis);
- } catch (final JSONException e) {
- // Don't log to avoid leaking data in JSONObject.
- throw new IOException("Unable to persist profile creation date to times file");
- }
- Log.d(LOGTAG, "Attempting to write new profile creation date");
- writeFile(TIMES_PATH, obj.toString()); // Ideally we'd throw here too.
- }
-
- /**
- * Updates the state of the old session data file.
- *
- * sessionstore.js should hold the current session, and sessionstore.old should
- * hold the previous session (where it is used to read the "tabs from last time").
- * If we're not restoring tabs automatically, sessionstore.js needs to be moved to
- * sessionstore.old, so we can display the correct "tabs from last time".
- * If we *are* restoring tabs, we need to delete outdated copies of sessionstore.old,
- * so we don't continue showing stale "tabs from last time" indefinitely.
- *
- * @param shouldRestore Pass true if we are automatically restoring last session's tabs.
- */
- public void updateSessionFile(boolean shouldRestore) {
- File sessionFilePrevious = getFile(SESSION_FILE_PREVIOUS);
- if (!shouldRestore) {
- File sessionFile = getFile(SESSION_FILE);
- if (sessionFile != null && sessionFile.exists()) {
- sessionFile.renameTo(sessionFilePrevious);
- }
- } else {
- if (sessionFilePrevious != null && sessionFilePrevious.exists() &&
- System.currentTimeMillis() - sessionFilePrevious.lastModified() > MAX_PREVIOUS_FILE_AGE) {
- sessionFilePrevious.delete();
- }
- }
- synchronized (this) {
- mOldSessionDataProcessed = true;
- notifyAll();
- }
- }
-
- public void waitForOldSessionDataProcessing() {
- synchronized (this) {
- while (!mOldSessionDataProcessed) {
- try {
- wait();
- } catch (final InterruptedException e) {
- // Ignore and wait again.
- }
- }
- }
- }
-
- /**
- * Get the string from a session file.
- *
- * The session can either be read from sessionstore.js or sessionstore.bak.
- * In general, sessionstore.js holds the current session, and
- * sessionstore.bak holds a backup copy in case of interrupted writes.
- *
- * @param readBackup if true, the session is read from sessionstore.bak;
- * otherwise, the session is read from sessionstore.js
- *
- * @return the session string
- */
- public String readSessionFile(boolean readBackup) {
- return readSessionFile(readBackup ? SESSION_FILE_BACKUP : SESSION_FILE);
- }
-
- /**
- * Get the string from last session's session file.
- *
- * If we are not restoring tabs automatically, sessionstore.old will contain
- * the previous session.
- *
- * @return the session string
- */
- public String readPreviousSessionFile() {
- return readSessionFile(SESSION_FILE_PREVIOUS);
- }
-
- private String readSessionFile(String fileName) {
- File sessionFile = getFile(fileName);
-
- try {
- if (sessionFile != null && sessionFile.exists()) {
- return readFile(sessionFile);
- }
- } catch (IOException ioe) {
- Log.e(LOGTAG, "Unable to read session file", ioe);
- }
- return null;
- }
-
- /**
- * Checks whether the session store file exists.
- */
- public boolean sessionFileExists() {
- File sessionFile = getFile(SESSION_FILE);
-
- return sessionFile != null && sessionFile.exists();
- }
-
- /**
- * Ensures the parent director(y|ies) of the given filename exist by making them
- * if they don't already exist..
- *
- * @param filename The path to the file whose parents should be made directories
- * @return true if the parent directory exists, false otherwise
- */
- @WorkerThread
- protected boolean ensureParentDirs(final String filename) {
- final File file = new File(getDir(), filename);
- final File parentFile = file.getParentFile();
- return parentFile.mkdirs() || parentFile.isDirectory();
- }
-
- public void writeFile(final String filename, final String data) {
- File file = new File(getDir(), filename);
- BufferedWriter bufferedWriter = null;
- try {
- bufferedWriter = new BufferedWriter(new FileWriter(file, false));
- bufferedWriter.write(data);
- } catch (IOException e) {
- Log.e(LOGTAG, "Unable to write to file", e);
- } finally {
- try {
- if (bufferedWriter != null) {
- bufferedWriter.close();
- }
- } catch (IOException e) {
- Log.e(LOGTAG, "Error closing writer while writing to file", e);
- }
- }
- }
-
- @WorkerThread
- public JSONObject readJSONObjectFromFile(final String filename) throws IOException {
- final String fileContents;
- try {
- fileContents = readFile(filename);
- } catch (final IOException e) {
- // Don't log exception to avoid leaking profile path.
- throw new IOException("Could not access given file to retrieve JSONObject");
- }
-
- try {
- return new JSONObject(fileContents);
- } catch (final JSONException e) {
- // Don't log exception to avoid leaking profile path.
- throw new IOException("Could not parse JSON to retrieve JSONObject");
- }
- }
-
- public JSONArray readJSONArrayFromFile(final String filename) {
- String fileContent;
- try {
- fileContent = readFile(filename);
- } catch (IOException expected) {
- return new JSONArray();
- }
-
- JSONArray jsonArray;
- try {
- jsonArray = new JSONArray(fileContent);
- } catch (JSONException e) {
- jsonArray = new JSONArray();
- }
- return jsonArray;
- }
-
- public String readFile(String filename) throws IOException {
- File dir = getDir();
- if (dir == null) {
- throw new IOException("No profile directory found");
- }
- File target = new File(dir, filename);
- return readFile(target);
- }
-
- private String readFile(File target) throws IOException {
- FileReader fr = new FileReader(target);
- try {
- StringBuilder sb = new StringBuilder();
- char[] buf = new char[8192];
- int read = fr.read(buf);
- while (read >= 0) {
- sb.append(buf, 0, read);
- read = fr.read(buf);
- }
- return sb.toString();
- } finally {
- fr.close();
- }
- }
-
- public boolean deleteFileFromProfileDir(String fileName) throws IllegalArgumentException {
- if (TextUtils.isEmpty(fileName)) {
- throw new IllegalArgumentException("Filename cannot be empty.");
- }
- File file = new File(getDir(), fileName);
- return file.delete();
- }
-
- private boolean remove() {
- try {
- synchronized (this) {
- if (mProfileDir != null && mProfileDir.exists()) {
- FileUtils.delete(mProfileDir);
- }
-
- if (isCustomProfile()) {
- // Custom profiles don't have profile.ini sections that we need to remove.
- return true;
- }
-
- try {
- // If findProfileDir() succeeds, it means the profile was created
- // through forceCreate(), so we set mProfileDir to null to enable
- // forceCreate() to create the profile again.
- findProfileDir();
- mProfileDir = null;
-
- } catch (final NoSuchProfileException e) {
- // If findProfileDir() throws, it means the profile was not created
- // through forceCreate(), and we have to preserve mProfileDir because
- // it was given to us. In that case, there's nothing left to do here.
- return true;
- }
- }
-
- final INIParser parser = GeckoProfileDirectories.getProfilesINI(mMozillaDir);
- final Hashtable<String, INISection> sections = parser.getSections();
- for (Enumeration<INISection> e = sections.elements(); e.hasMoreElements();) {
- final INISection section = e.nextElement();
- String name = section.getStringProperty("Name");
-
- if (name == null || !name.equals(mName)) {
- continue;
- }
-
- if (section.getName().startsWith("Profile")) {
- // ok, we have stupid Profile#-named things. Rename backwards.
- try {
- int sectionNumber = Integer.parseInt(section.getName().substring("Profile".length()));
- String curSection = "Profile" + sectionNumber;
- String nextSection = "Profile" + (sectionNumber + 1);
-
- sections.remove(curSection);
-
- while (sections.containsKey(nextSection)) {
- parser.renameSection(nextSection, curSection);
- sectionNumber++;
-
- curSection = nextSection;
- nextSection = "Profile" + (sectionNumber + 1);
- }
- } catch (NumberFormatException nex) {
- // uhm, malformed Profile thing; we can't do much.
- Log.e(LOGTAG, "Malformed section name in profiles.ini: " + section.getName());
- return false;
- }
- } else {
- // this really shouldn't be the case, but handle it anyway
- parser.removeSection(mName);
- }
-
- break;
- }
-
- parser.write();
- return true;
- } catch (IOException ex) {
- Log.w(LOGTAG, "Failed to remove profile.", ex);
- return false;
- }
- }
-
- /**
- * @return the default profile name for this application, or
- * {@link GeckoProfile#DEFAULT_PROFILE} if none could be found.
- *
- * @throws NoMozillaDirectoryException
- * if the Mozilla directory did not exist and could not be
- * created.
- */
- public static String getDefaultProfileName(final Context context) throws NoMozillaDirectoryException {
- // Have we read the default profile from the INI already?
- // Changing the default profile requires a restart, so we don't
- // need to worry about runtime changes.
- if (sDefaultProfileName != null) {
- return sDefaultProfileName;
- }
-
- final String profileName = GeckoProfileDirectories.findDefaultProfileName(context);
- if (profileName == null) {
- // Note that we don't persist this back to profiles.ini.
- sDefaultProfileName = DEFAULT_PROFILE;
- return DEFAULT_PROFILE;
- }
-
- sDefaultProfileName = profileName;
- return sDefaultProfileName;
- }
-
- private File findProfileDir() throws NoSuchProfileException {
- if (isCustomProfile()) {
- return mProfileDir;
- }
- return GeckoProfileDirectories.findProfileDir(mMozillaDir, mName);
- }
-
- @WorkerThread
- private File createProfileDir() throws IOException {
- if (isCustomProfile()) {
- // Custom profiles must already exist.
- return mProfileDir;
- }
-
- INIParser parser = GeckoProfileDirectories.getProfilesINI(mMozillaDir);
-
- // Salt the name of our requested profile
- String saltedName;
- File profileDir;
- do {
- saltedName = GeckoProfileDirectories.saltProfileName(mName);
- profileDir = new File(mMozillaDir, saltedName);
- } while (profileDir.exists());
-
- // Attempt to create the salted profile dir
- if (!profileDir.mkdirs()) {
- throw new IOException("Unable to create profile.");
- }
- Log.d(LOGTAG, "Created new profile dir.");
-
- // Now update profiles.ini
- // If this is the first time its created, we also add a General section
- // look for the first profile number that isn't taken yet
- int profileNum = 0;
- boolean isDefaultSet = false;
- INISection profileSection;
- while ((profileSection = parser.getSection("Profile" + profileNum)) != null) {
- profileNum++;
- if (profileSection.getProperty("Default") != null) {
- isDefaultSet = true;
- }
- }
-
- profileSection = new INISection("Profile" + profileNum);
- profileSection.setProperty("Name", mName);
- profileSection.setProperty("IsRelative", 1);
- profileSection.setProperty("Path", saltedName);
-
- if (parser.getSection("General") == null) {
- INISection generalSection = new INISection("General");
- generalSection.setProperty("StartWithLastProfile", 1);
- parser.addSection(generalSection);
- }
-
- if (!isDefaultSet) {
- // only set as default if this is the first profile we're creating
- profileSection.setProperty("Default", 1);
- }
-
- parser.addSection(profileSection);
- parser.write();
-
- enqueueInitialization(profileDir);
-
- // Write out profile creation time, mirroring the logic in nsToolkitProfileService.
- try {
- FileOutputStream stream = new FileOutputStream(profileDir.getAbsolutePath() + File.separator + TIMES_PATH);
- OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
- try {
- writer.append("{\"created\": " + System.currentTimeMillis() + "}\n");
- } finally {
- writer.close();
- }
- } catch (Exception e) {
- // Best-effort.
- Log.w(LOGTAG, "Couldn't write " + TIMES_PATH, e);
- }
-
- // Create the client ID file before Gecko starts (we assume this method
- // is called before Gecko starts). If we let Gecko start, the JS telemetry
- // code may try to write to the file at the same time Java does.
- persistClientId(generateNewClientId());
-
- return profileDir;
- }
-
- /**
- * This method is called once, immediately before creation of the profile
- * directory completes.
- *
- * It queues up work to be done in the background to prepare the profile,
- * such as adding default bookmarks.
- *
- * This is public for use *from tests only*!
- */
- @RobocopTarget
- public void enqueueInitialization(final File profileDir) {
- Log.i(LOGTAG, "Enqueuing profile init.");
-
- final Bundle message = new Bundle(2);
- message.putCharSequence("name", getName());
- message.putCharSequence("path", profileDir.getAbsolutePath());
- EventDispatcher.getInstance().dispatch("Profile:Create", message);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfileDirectories.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfileDirectories.java
deleted file mode 100644
index 2afb54bc4..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoProfileDirectories.java
+++ /dev/null
@@ -1,230 +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 java.io.File;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.util.INIParser;
-import org.mozilla.gecko.util.INISection;
-
-import android.content.Context;
-
-/**
- * <code>GeckoProfileDirectories</code> manages access to mappings from profile
- * names to salted profile directory paths, as well as the default profile name.
- *
- * This class will eventually come to encapsulate the remaining logic embedded
- * in profiles.ini; for now it's a read-only wrapper.
- */
-public class GeckoProfileDirectories {
- @SuppressWarnings("serial")
- public static class NoMozillaDirectoryException extends Exception {
- public NoMozillaDirectoryException(Throwable cause) {
- super(cause);
- }
-
- public NoMozillaDirectoryException(String reason) {
- super(reason);
- }
-
- public NoMozillaDirectoryException(String reason, Throwable cause) {
- super(reason, cause);
- }
- }
-
- @SuppressWarnings("serial")
- public static class NoSuchProfileException extends Exception {
- public NoSuchProfileException(String detailMessage, Throwable cause) {
- super(detailMessage, cause);
- }
-
- public NoSuchProfileException(String detailMessage) {
- super(detailMessage);
- }
- }
-
- private interface INISectionPredicate {
- public boolean matches(INISection section);
- }
-
- private static final String MOZILLA_DIR_NAME = "mozilla";
-
- /**
- * Returns true if the supplied profile entry represents the default profile.
- */
- private static final INISectionPredicate sectionIsDefault = new INISectionPredicate() {
- @Override
- public boolean matches(INISection section) {
- return section.getIntProperty("Default") == 1;
- }
- };
-
- /**
- * Returns true if the supplied profile entry has a 'Name' field.
- */
- private static final INISectionPredicate sectionHasName = new INISectionPredicate() {
- @Override
- public boolean matches(INISection section) {
- final String name = section.getStringProperty("Name");
- return name != null;
- }
- };
-
- @RobocopTarget
- public static INIParser getProfilesINI(File mozillaDir) {
- return new INIParser(new File(mozillaDir, "profiles.ini"));
- }
-
- /**
- * Utility method to compute a salted profile name: eight random alphanumeric
- * characters, followed by a period, followed by the profile name.
- */
- public static String saltProfileName(final String name) {
- if (name == null) {
- throw new IllegalArgumentException("Cannot salt null profile name.");
- }
-
- final String allowedChars = "abcdefghijklmnopqrstuvwxyz0123456789";
- final int scale = allowedChars.length();
- final int saltSize = 8;
-
- final StringBuilder saltBuilder = new StringBuilder(saltSize + 1 + name.length());
- for (int i = 0; i < saltSize; i++) {
- saltBuilder.append(allowedChars.charAt((int)(Math.random() * scale)));
- }
- saltBuilder.append('.');
- saltBuilder.append(name);
- return saltBuilder.toString();
- }
-
- /**
- * Return the Mozilla directory within the files directory of the provided
- * context. This should always be the same within a running application.
- *
- * This method is package-scoped so that new {@link GeckoProfile} instances can
- * contextualize themselves.
- *
- * @return a new File object for the Mozilla directory.
- * @throws NoMozillaDirectoryException
- * if the directory did not exist and could not be created.
- */
- @RobocopTarget
- public static File getMozillaDirectory(Context context) throws NoMozillaDirectoryException {
- final File mozillaDir = new File(context.getFilesDir(), MOZILLA_DIR_NAME);
- if (mozillaDir.mkdirs() || mozillaDir.isDirectory()) {
- return mozillaDir;
- }
-
- // Although this leaks a path to the system log, the path is
- // predictable (unlike a profile directory), so this is fine.
- throw new NoMozillaDirectoryException("Unable to create mozilla directory at " + mozillaDir.getAbsolutePath());
- }
-
- /**
- * Discover the default profile name by examining profiles.ini.
- *
- * Package-scoped because {@link GeckoProfile} needs access to it.
- *
- * @return null if there is no "Default" entry in profiles.ini, or the profile
- * name if there is.
- * @throws NoMozillaDirectoryException
- * if the Mozilla directory did not exist and could not be created.
- */
- static String findDefaultProfileName(final Context context) throws NoMozillaDirectoryException {
- final INIParser parser = GeckoProfileDirectories.getProfilesINI(getMozillaDirectory(context));
- if (parser.getSections() != null) {
- for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
- final INISection section = e.nextElement();
- if (section.getIntProperty("Default") == 1) {
- return section.getStringProperty("Name");
- }
- }
- }
- return null;
- }
-
- static Map<String, String> getDefaultProfile(final File mozillaDir) {
- return getMatchingProfiles(mozillaDir, sectionIsDefault, true);
- }
-
- static Map<String, String> getProfilesNamed(final File mozillaDir, final String name) {
- final INISectionPredicate predicate = new INISectionPredicate() {
- @Override
- public boolean matches(final INISection section) {
- return name.equals(section.getStringProperty("Name"));
- }
- };
- return getMatchingProfiles(mozillaDir, predicate, true);
- }
-
- /**
- * Calls {@link GeckoProfileDirectories#getMatchingProfiles(File, INISectionPredicate, boolean)}
- * with a filter to ensure that all profiles are named.
- */
- static Map<String, String> getAllProfiles(final File mozillaDir) {
- return getMatchingProfiles(mozillaDir, sectionHasName, false);
- }
-
- /**
- * Return a mapping from the names of all matching profiles (that is,
- * profiles appearing in profiles.ini that match the supplied predicate) to
- * their absolute paths on disk.
- *
- * @param mozillaDir
- * a directory containing profiles.ini.
- * @param predicate
- * a predicate to use when evaluating whether to include a
- * particular INI section.
- * @param stopOnSuccess
- * if true, this method will return with the first result that
- * matches the predicate; if false, all matching results are
- * included.
- * @return a {@link Map} from name to path.
- */
- public static Map<String, String> getMatchingProfiles(final File mozillaDir, INISectionPredicate predicate, boolean stopOnSuccess) {
- final HashMap<String, String> result = new HashMap<String, String>();
- final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
-
- if (parser.getSections() != null) {
- for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
- final INISection section = e.nextElement();
- if (predicate == null || predicate.matches(section)) {
- final String name = section.getStringProperty("Name");
- final String pathString = section.getStringProperty("Path");
- final boolean isRelative = section.getIntProperty("IsRelative") == 1;
- final File path = isRelative ? new File(mozillaDir, pathString) : new File(pathString);
- result.put(name, path.getAbsolutePath());
-
- if (stopOnSuccess) {
- return result;
- }
- }
- }
- }
- return result;
- }
-
- public static File findProfileDir(final File mozillaDir, final String profileName) throws NoSuchProfileException {
- // Open profiles.ini to find the correct path.
- final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
- if (parser.getSections() != null) {
- for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements(); ) {
- final INISection section = e.nextElement();
- final String name = section.getStringProperty("Name");
- if (name != null && name.equals(profileName)) {
- if (section.getIntProperty("IsRelative") == 1) {
- return new File(mozillaDir, section.getStringProperty("Path"));
- }
- return new File(section.getStringProperty("Path"));
- }
- }
- }
- throw new NoSuchProfileException("No profile " + profileName);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
deleted file mode 100644
index 23f84f52a..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
+++ /dev/null
@@ -1,423 +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 org.mozilla.gecko.annotation.WrapForJNI;
-
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.util.Log;
-import android.view.Surface;
-import android.app.Activity;
-
-import java.util.Arrays;
-import java.util.List;
-
-/*
- * Updates, locks and unlocks the screen orientation.
- *
- * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation
- * event handling.
- */
-public class GeckoScreenOrientation {
- private static final String LOGTAG = "GeckoScreenOrientation";
-
- // Make sure that any change in dom/base/ScreenOrientation.h happens here too.
- public enum ScreenOrientation {
- NONE(0),
- PORTRAIT_PRIMARY(1 << 0),
- PORTRAIT_SECONDARY(1 << 1),
- PORTRAIT(PORTRAIT_PRIMARY.value | PORTRAIT_SECONDARY.value),
- LANDSCAPE_PRIMARY(1 << 2),
- LANDSCAPE_SECONDARY(1 << 3),
- LANDSCAPE(LANDSCAPE_PRIMARY.value | LANDSCAPE_SECONDARY.value),
- DEFAULT(1 << 4);
-
- public final short value;
-
- private ScreenOrientation(int value) {
- this.value = (short)value;
- }
-
- private final static ScreenOrientation[] sValues = ScreenOrientation.values();
-
- public static ScreenOrientation get(int value) {
- for (ScreenOrientation orient: sValues) {
- if (orient.value == value) {
- return orient;
- }
- }
- return NONE;
- }
- }
-
- // Singleton instance.
- private static GeckoScreenOrientation sInstance;
- // Default screen orientation, used for initialization and unlocking.
- private static final ScreenOrientation DEFAULT_SCREEN_ORIENTATION = ScreenOrientation.DEFAULT;
- // Default rotation, used when device rotation is unknown.
- private static final int DEFAULT_ROTATION = Surface.ROTATION_0;
- // Default orientation, used if screen orientation is unspecified.
- private ScreenOrientation mDefaultScreenOrientation;
- // Last updated screen orientation.
- private ScreenOrientation mScreenOrientation;
- // Whether the update should notify Gecko about screen orientation changes.
- private boolean mShouldNotify = true;
- // Configuration screen orientation preference path.
- private static final String DEFAULT_SCREEN_ORIENTATION_PREF = "app.orientation.default";
-
- public GeckoScreenOrientation() {
- PrefsHelper.getPref(DEFAULT_SCREEN_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() {
- @Override public void prefValue(String pref, String value) {
- // Read and update the configuration default preference.
- mDefaultScreenOrientation = screenOrientationFromArrayString(value);
- setRequestedOrientation(mDefaultScreenOrientation);
- }
- });
-
- mDefaultScreenOrientation = DEFAULT_SCREEN_ORIENTATION;
- update();
- }
-
- public static GeckoScreenOrientation getInstance() {
- if (sInstance == null) {
- sInstance = new GeckoScreenOrientation();
- }
- return sInstance;
- }
-
- /*
- * Enable Gecko screen orientation events on update.
- */
- public void enableNotifications() {
- update();
- mShouldNotify = true;
- }
-
- /*
- * Disable Gecko screen orientation events on update.
- */
- public void disableNotifications() {
- mShouldNotify = false;
- }
-
- /*
- * Update screen orientation.
- * Retrieve orientation and rotation via GeckoAppShell.
- *
- * @return Whether the screen orientation has changed.
- */
- public boolean update() {
- Activity activity = getActivity();
- if (activity == null) {
- return false;
- }
- Configuration config = activity.getResources().getConfiguration();
- return update(config.orientation);
- }
-
- /*
- * Update screen orientation given the android orientation.
- * Retrieve rotation via GeckoAppShell.
- *
- * @param aAndroidOrientation
- * Android screen orientation from Configuration.orientation.
- *
- * @return Whether the screen orientation has changed.
- */
- public boolean update(int aAndroidOrientation) {
- return update(getScreenOrientation(aAndroidOrientation, getRotation()));
- }
-
- @WrapForJNI(dispatchTo = "gecko")
- private static native void onOrientationChange(short screenOrientation, short angle);
-
- /*
- * Update screen orientation given the screen orientation.
- *
- * @param aScreenOrientation
- * Gecko screen orientation based on android orientation and rotation.
- *
- * @return Whether the screen orientation has changed.
- */
- public boolean update(ScreenOrientation aScreenOrientation) {
- if (mScreenOrientation == aScreenOrientation) {
- return false;
- }
- mScreenOrientation = aScreenOrientation;
- Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation);
- if (mShouldNotify) {
- // Gecko expects a definite screen orientation, so we default to the
- // primary orientations.
- if (aScreenOrientation == ScreenOrientation.PORTRAIT) {
- aScreenOrientation = ScreenOrientation.PORTRAIT_PRIMARY;
- } else if (aScreenOrientation == ScreenOrientation.LANDSCAPE) {
- aScreenOrientation = ScreenOrientation.LANDSCAPE_PRIMARY;
- }
-
- if (GeckoThread.isRunning()) {
- onOrientationChange(aScreenOrientation.value, getAngle());
- } else {
- GeckoThread.queueNativeCall(GeckoScreenOrientation.class, "onOrientationChange",
- aScreenOrientation.value, getAngle());
- }
- }
- GeckoAppShell.resetScreenSize();
- return true;
- }
-
- /*
- * @return The Android orientation (Configuration.orientation).
- */
- public int getAndroidOrientation() {
- return screenOrientationToAndroidOrientation(getScreenOrientation());
- }
-
- /*
- * @return The Gecko screen orientation derived from Android orientation and
- * rotation.
- */
- public ScreenOrientation getScreenOrientation() {
- return mScreenOrientation;
- }
-
- /*
- * Lock screen orientation given the Gecko screen orientation.
- *
- * @param aGeckoOrientation
- * The Gecko orientation provided.
- */
- public void lock(int aGeckoOrientation) {
- lock(ScreenOrientation.get(aGeckoOrientation));
- }
-
- /*
- * Lock screen orientation given the Gecko screen orientation.
- * Retrieve rotation via GeckoAppShell.
- *
- * @param aScreenOrientation
- * Gecko screen orientation derived from Android orientation and
- * rotation.
- *
- * @return Whether the locking was successful.
- */
- public boolean lock(ScreenOrientation aScreenOrientation) {
- Log.d(LOGTAG, "locking to " + aScreenOrientation);
- update(aScreenOrientation);
- return setRequestedOrientation(aScreenOrientation);
- }
-
- /*
- * Unlock and update screen orientation.
- *
- * @return Whether the unlocking was successful.
- */
- public boolean unlock() {
- Log.d(LOGTAG, "unlocking");
- setRequestedOrientation(mDefaultScreenOrientation);
- return update();
- }
-
- private Activity getActivity() {
- if (GeckoAppShell.getGeckoInterface() == null) {
- return null;
- }
- return GeckoAppShell.getGeckoInterface().getActivity();
- }
-
- /*
- * Set the given requested orientation for the current activity.
- * This is essentially an unlock without an update.
- *
- * @param aScreenOrientation
- * Gecko screen orientation.
- *
- * @return Whether the requested orientation was set. This can only fail if
- * the current activity cannot be retrieved via GeckoAppShell.
- *
- */
- private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) {
- int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation);
- Activity activity = getActivity();
- if (activity == null) {
- Log.w(LOGTAG, "setRequestOrientation: failed to get activity");
- return false;
- }
- if (activity.getRequestedOrientation() == activityOrientation) {
- return false;
- }
- activity.setRequestedOrientation(activityOrientation);
- return true;
- }
-
- /*
- * Combine the Android orientation and rotation to the Gecko orientation.
- *
- * @param aAndroidOrientation
- * Android orientation from Configuration.orientation.
- * @param aRotation
- * Device rotation from Display.getRotation().
- *
- * @return Gecko screen orientation.
- */
- private ScreenOrientation getScreenOrientation(int aAndroidOrientation, int aRotation) {
- boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90;
- if (aAndroidOrientation == Configuration.ORIENTATION_PORTRAIT) {
- if (isPrimary) {
- // Non-rotated portrait device or landscape device rotated
- // to primary portrait mode counter-clockwise.
- return ScreenOrientation.PORTRAIT_PRIMARY;
- }
- return ScreenOrientation.PORTRAIT_SECONDARY;
- }
- if (aAndroidOrientation == Configuration.ORIENTATION_LANDSCAPE) {
- if (isPrimary) {
- // Non-rotated landscape device or portrait device rotated
- // to primary landscape mode counter-clockwise.
- return ScreenOrientation.LANDSCAPE_PRIMARY;
- }
- return ScreenOrientation.LANDSCAPE_SECONDARY;
- }
- return ScreenOrientation.NONE;
- }
-
- /*
- * @return Device rotation converted to an angle.
- */
- public short getAngle() {
- switch (getRotation()) {
- case Surface.ROTATION_0:
- return 0;
- case Surface.ROTATION_90:
- return 90;
- case Surface.ROTATION_180:
- return 180;
- case Surface.ROTATION_270:
- return 270;
- default:
- Log.w(LOGTAG, "getAngle: unexpected rotation value");
- return 0;
- }
- }
-
- /*
- * @return Device rotation from Display.getRotation().
- */
- private int getRotation() {
- Activity activity = getActivity();
- if (activity == null) {
- Log.w(LOGTAG, "getRotation: failed to get activity");
- return DEFAULT_ROTATION;
- }
- return activity.getWindowManager().getDefaultDisplay().getRotation();
- }
-
- /*
- * Retrieve the screen orientation from an array string.
- *
- * @param aArray
- * String containing comma-delimited strings.
- *
- * @return First parsed Gecko screen orientation.
- */
- public static ScreenOrientation screenOrientationFromArrayString(String aArray) {
- List<String> orientations = Arrays.asList(aArray.split(","));
- if (orientations.size() == 0) {
- // If nothing is listed, return default.
- Log.w(LOGTAG, "screenOrientationFromArrayString: no orientation in string");
- return DEFAULT_SCREEN_ORIENTATION;
- }
-
- // We don't support multiple orientations yet. To avoid developer
- // confusion, just take the first one listed.
- return screenOrientationFromString(orientations.get(0));
- }
-
- /*
- * Retrieve the screen orientation from a string.
- *
- * @param aStr
- * String hopefully containing a screen orientation name.
- * @return Gecko screen orientation if matched, DEFAULT_SCREEN_ORIENTATION
- * otherwise.
- */
- public static ScreenOrientation screenOrientationFromString(String aStr) {
- switch (aStr) {
- case "portrait":
- return ScreenOrientation.PORTRAIT;
- case "landscape":
- return ScreenOrientation.LANDSCAPE;
- case "portrait-primary":
- return ScreenOrientation.PORTRAIT_PRIMARY;
- case "portrait-secondary":
- return ScreenOrientation.PORTRAIT_SECONDARY;
- case "landscape-primary":
- return ScreenOrientation.LANDSCAPE_PRIMARY;
- case "landscape-secondary":
- return ScreenOrientation.LANDSCAPE_SECONDARY;
- }
-
- Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string: " + aStr);
- return DEFAULT_SCREEN_ORIENTATION;
- }
-
- /*
- * Convert Gecko screen orientation to Android orientation.
- *
- * @param aScreenOrientation
- * Gecko screen orientation.
- * @return Android orientation. This conversion is lossy, the Android
- * orientation does not differentiate between primary and secondary
- * orientations.
- */
- public static int screenOrientationToAndroidOrientation(ScreenOrientation aScreenOrientation) {
- switch (aScreenOrientation) {
- case PORTRAIT:
- case PORTRAIT_PRIMARY:
- case PORTRAIT_SECONDARY:
- return Configuration.ORIENTATION_PORTRAIT;
- case LANDSCAPE:
- case LANDSCAPE_PRIMARY:
- case LANDSCAPE_SECONDARY:
- return Configuration.ORIENTATION_LANDSCAPE;
- case NONE:
- case DEFAULT:
- default:
- return Configuration.ORIENTATION_UNDEFINED;
- }
- }
-
-
- /*
- * Convert Gecko screen orientation to Android ActivityInfo orientation.
- * This is yet another orientation used by Android, but it's more detailed
- * than the Android orientation.
- * It is required for screen orientation locking and unlocking.
- *
- * @param aScreenOrientation
- * Gecko screen orientation.
- * @return Android ActivityInfo orientation.
- */
- public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) {
- switch (aScreenOrientation) {
- case PORTRAIT:
- case PORTRAIT_PRIMARY:
- return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- case PORTRAIT_SECONDARY:
- return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
- case LANDSCAPE:
- case LANDSCAPE_PRIMARY:
- return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- case LANDSCAPE_SECONDARY:
- return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
- case DEFAULT:
- case NONE:
- return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
- default:
- return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSharedPrefs.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSharedPrefs.java
deleted file mode 100644
index ec928dd86..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoSharedPrefs.java
+++ /dev/null
@@ -1,318 +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 java.util.Arrays;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Map;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.os.StrictMode;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-/**
- * {@code GeckoSharedPrefs} provides scoped SharedPreferences instances.
- * You should use this API instead of using Context.getSharedPreferences()
- * directly. There are four methods to get scoped SharedPreferences instances:
- *
- * forApp()
- * Use it for app-wide, cross-profile pref keys.
- * forCrashReporter()
- * For the crash reporter, which runs in its own process.
- * forProfile()
- * Use it to fetch and store keys for the current profile.
- * forProfileName()
- * Use it to fetch and store keys from/for a specific profile.
- *
- * {@code GeckoSharedPrefs} has a notion of migrations. Migrations can used to
- * migrate keys from one scope to another. You can trigger a new migration by
- * incrementing PREFS_VERSION and updating migrateIfNecessary() accordingly.
- *
- * Migration history:
- * 1: Move all PreferenceManager keys to app/profile scopes
- * 2: Move the crash reporter's private preferences into their own scope
- */
-@RobocopTarget
-public final class GeckoSharedPrefs {
- private static final String LOGTAG = "GeckoSharedPrefs";
-
- // Increment it to trigger a new migration
- public static final int PREFS_VERSION = 2;
-
- // Name for app-scoped prefs
- public static final String APP_PREFS_NAME = "GeckoApp";
-
- // Name for crash reporter prefs
- public static final String CRASH_PREFS_NAME = "CrashReporter";
-
- // Used when fetching profile-scoped prefs.
- public static final String PROFILE_PREFS_NAME_PREFIX = "GeckoProfile-";
-
- // The prefs key that holds the current migration
- private static final String PREFS_VERSION_KEY = "gecko_shared_prefs_migration";
-
- // For disabling migration when getting a SharedPreferences instance
- private static final EnumSet<Flags> disableMigrations = EnumSet.of(Flags.DISABLE_MIGRATIONS);
-
- // The keys that have to be moved from ProfileManager's default
- // shared prefs to the profile from version 0 to 1.
- private static final String[] PROFILE_MIGRATIONS_0_TO_1 = {
- "home_panels",
- "home_locale"
- };
-
- // The keys that have to be moved from the app prefs
- // into the crash reporter's own prefs.
- private static final String[] PROFILE_MIGRATIONS_1_TO_2 = {
- "sendReport",
- "includeUrl",
- "allowContact",
- "contactEmail"
- };
-
- // For optimizing the migration check in subsequent get() calls
- private static volatile boolean migrationDone;
-
- public enum Flags {
- DISABLE_MIGRATIONS
- }
-
- public static SharedPreferences forApp(Context context) {
- return forApp(context, EnumSet.noneOf(Flags.class));
- }
-
- /**
- * Returns an app-scoped SharedPreferences instance. You can disable
- * migrations by using the DISABLE_MIGRATIONS flag.
- */
- public static SharedPreferences forApp(Context context, EnumSet<Flags> flags) {
- if (flags != null && !flags.contains(Flags.DISABLE_MIGRATIONS)) {
- migrateIfNecessary(context);
- }
-
- return context.getSharedPreferences(APP_PREFS_NAME, 0);
- }
-
- public static SharedPreferences forCrashReporter(Context context) {
- return forCrashReporter(context, EnumSet.noneOf(Flags.class));
- }
-
- /**
- * Returns a crash-reporter-scoped SharedPreferences instance. You can disable
- * migrations by using the DISABLE_MIGRATIONS flag.
- */
- public static SharedPreferences forCrashReporter(Context context, EnumSet<Flags> flags) {
- if (flags != null && !flags.contains(Flags.DISABLE_MIGRATIONS)) {
- migrateIfNecessary(context);
- }
-
- return context.getSharedPreferences(CRASH_PREFS_NAME, 0);
- }
-
- public static SharedPreferences forProfile(Context context) {
- return forProfile(context, EnumSet.noneOf(Flags.class));
- }
-
- /**
- * Returns a SharedPreferences instance scoped to the current profile
- * in the app. You can disable migrations by using the DISABLE_MIGRATIONS
- * flag.
- */
- public static SharedPreferences forProfile(Context context, EnumSet<Flags> flags) {
- String profileName = GeckoProfile.get(context).getName();
- if (profileName == null) {
- throw new IllegalStateException("Could not get current profile name");
- }
-
- return forProfileName(context, profileName, flags);
- }
-
- public static SharedPreferences forProfileName(Context context, String profileName) {
- return forProfileName(context, profileName, EnumSet.noneOf(Flags.class));
- }
-
- /**
- * Returns an SharedPreferences instance scoped to the given profile name.
- * You can disable migrations by using the DISABLE_MIGRATION flag.
- */
- public static SharedPreferences forProfileName(Context context, String profileName,
- EnumSet<Flags> flags) {
- if (flags != null && !flags.contains(Flags.DISABLE_MIGRATIONS)) {
- migrateIfNecessary(context);
- }
-
- final String prefsName = PROFILE_PREFS_NAME_PREFIX + profileName;
- return context.getSharedPreferences(prefsName, 0);
- }
-
- /**
- * Returns the current version of the prefs.
- */
- public static int getVersion(Context context) {
- return forApp(context, disableMigrations).getInt(PREFS_VERSION_KEY, 0);
- }
-
- /**
- * Resets migration flag. Should only be used in tests.
- */
- public static synchronized void reset() {
- migrationDone = false;
- }
-
- /**
- * Performs all prefs migrations in the background thread to avoid StrictMode
- * exceptions from reading/writing in the UI thread. This method will block
- * the current thread until the migration is finished.
- */
- private static synchronized void migrateIfNecessary(final Context context) {
- if (migrationDone) {
- return;
- }
-
- // We deliberately perform the migration in the current thread (which
- // is likely the UI thread) as this is actually cheaper than enforcing a
- // context switch to another thread (see bug 940575).
- // Avoid strict mode warnings when doing so.
- final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
- StrictMode.allowThreadDiskWrites();
- try {
- performMigration(context);
- } finally {
- StrictMode.setThreadPolicy(savedPolicy);
- }
-
- migrationDone = true;
- }
-
- private static void performMigration(Context context) {
- final SharedPreferences appPrefs = forApp(context, disableMigrations);
-
- final int currentVersion = appPrefs.getInt(PREFS_VERSION_KEY, 0);
- Log.d(LOGTAG, "Current version = " + currentVersion + ", prefs version = " + PREFS_VERSION);
-
- if (currentVersion == PREFS_VERSION) {
- return;
- }
-
- Log.d(LOGTAG, "Performing migration");
-
- final Editor appEditor = appPrefs.edit();
-
- // The migration always moves prefs to the default profile, not
- // the current one. We might have to revisit this if we ever support
- // multiple profiles.
- final String defaultProfileName;
- try {
- defaultProfileName = GeckoProfile.getDefaultProfileName(context);
- } catch (Exception e) {
- throw new IllegalStateException("Failed to get default profile name for migration");
- }
-
- final Editor profileEditor = forProfileName(context, defaultProfileName, disableMigrations).edit();
- final Editor crashEditor = forCrashReporter(context, disableMigrations).edit();
-
- List<String> profileKeys;
- Editor pmEditor = null;
-
- for (int v = currentVersion + 1; v <= PREFS_VERSION; v++) {
- Log.d(LOGTAG, "Migrating to version = " + v);
-
- switch (v) {
- case 1:
- profileKeys = Arrays.asList(PROFILE_MIGRATIONS_0_TO_1);
- pmEditor = migrateFromPreferenceManager(context, appEditor, profileEditor, profileKeys);
- break;
- case 2:
- profileKeys = Arrays.asList(PROFILE_MIGRATIONS_1_TO_2);
- migrateCrashReporterSettings(appPrefs, appEditor, crashEditor, profileKeys);
- break;
- }
- }
-
- // Update prefs version accordingly.
- appEditor.putInt(PREFS_VERSION_KEY, PREFS_VERSION);
-
- appEditor.apply();
- profileEditor.apply();
- crashEditor.apply();
- if (pmEditor != null) {
- pmEditor.apply();
- }
-
- Log.d(LOGTAG, "All keys have been migrated");
- }
-
- /**
- * Moves all preferences stored in PreferenceManager's default prefs
- * to either app or profile scopes. The profile-scoped keys are defined
- * in given profileKeys list, all other keys are moved to the app scope.
- */
- public static Editor migrateFromPreferenceManager(Context context, Editor appEditor,
- Editor profileEditor, List<String> profileKeys) {
- Log.d(LOGTAG, "Migrating from PreferenceManager");
-
- final SharedPreferences pmPrefs =
- PreferenceManager.getDefaultSharedPreferences(context);
-
- for (Map.Entry<String, ?> entry : pmPrefs.getAll().entrySet()) {
- final String key = entry.getKey();
-
- final Editor to;
- if (profileKeys.contains(key)) {
- to = profileEditor;
- } else {
- to = appEditor;
- }
-
- putEntry(to, key, entry.getValue());
- }
-
- // Clear PreferenceManager's prefs once we're done
- // and return the Editor to be committed.
- return pmPrefs.edit().clear();
- }
-
- /**
- * Moves the crash reporter's preferences from the app-wide prefs
- * into its own shared prefs to avoid cross-process pref accesses.
- */
- public static void migrateCrashReporterSettings(SharedPreferences appPrefs, Editor appEditor,
- Editor crashEditor, List<String> profileKeys) {
- Log.d(LOGTAG, "Migrating crash reporter settings");
-
- for (Map.Entry<String, ?> entry : appPrefs.getAll().entrySet()) {
- final String key = entry.getKey();
-
- if (profileKeys.contains(key)) {
- putEntry(crashEditor, key, entry.getValue());
- appEditor.remove(key);
- }
- }
- }
-
- private static void putEntry(Editor to, String key, Object value) {
- Log.d(LOGTAG, "Migrating key = " + key + " with value = " + value);
-
- if (value instanceof String) {
- to.putString(key, (String) value);
- } else if (value instanceof Boolean) {
- to.putBoolean(key, (Boolean) value);
- } else if (value instanceof Long) {
- to.putLong(key, (Long) value);
- } else if (value instanceof Float) {
- to.putFloat(key, (Float) value);
- } else if (value instanceof Integer) {
- to.putInt(key, (Integer) value);
- } else {
- throw new IllegalStateException("Unrecognized value type for key: " + key);
- }
- }
-} \ No newline at end of file
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
deleted file mode 100644
index b57222a31..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoThread.java
+++ /dev/null
@@ -1,677 +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 org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.mozglue.GeckoLoader;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue;
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-public class GeckoThread extends Thread {
- private static final String LOGTAG = "GeckoThread";
-
- public enum State {
- // After being loaded by class loader.
- @WrapForJNI INITIAL(0),
- // After launching Gecko thread
- @WrapForJNI LAUNCHED(1),
- // After loading the mozglue library.
- @WrapForJNI MOZGLUE_READY(2),
- // After loading the libxul library.
- @WrapForJNI LIBS_READY(3),
- // After initializing nsAppShell and JNI calls.
- @WrapForJNI JNI_READY(4),
- // After initializing profile and prefs.
- @WrapForJNI PROFILE_READY(5),
- // After initializing frontend JS
- @WrapForJNI RUNNING(6),
- // After leaving Gecko event loop
- @WrapForJNI EXITING(3),
- // After exiting GeckoThread (corresponding to "Gecko:Exited" event)
- @WrapForJNI EXITED(0);
-
- /* The rank is an arbitrary value reflecting the amount of components or features
- * that are available for use. During startup and up to the RUNNING state, the
- * rank value increases because more components are initialized and available for
- * use. During shutdown and up to the EXITED state, the rank value decreases as
- * components are shut down and become unavailable. EXITING has the same rank as
- * LIBS_READY because both states have a similar amount of components available.
- */
- private final int rank;
-
- private State(int rank) {
- this.rank = rank;
- }
-
- public boolean is(final State other) {
- return this == other;
- }
-
- public boolean isAtLeast(final State other) {
- return this.rank >= other.rank;
- }
-
- public boolean isAtMost(final State other) {
- return this.rank <= other.rank;
- }
-
- // Inclusive
- public boolean isBetween(final State min, final State max) {
- return this.rank >= min.rank && this.rank <= max.rank;
- }
- }
-
- public static final State MIN_STATE = State.INITIAL;
- public static final State MAX_STATE = State.EXITED;
-
- private static volatile State sState = State.INITIAL;
-
- private static class QueuedCall {
- public Method method;
- public Object target;
- public Object[] args;
- public State state;
-
- public QueuedCall(final Method method, final Object target,
- final Object[] args, final State state) {
- this.method = method;
- this.target = target;
- this.args = args;
- this.state = state;
- }
- }
-
- private static final int QUEUED_CALLS_COUNT = 16;
- private static final ArrayList<QueuedCall> QUEUED_CALLS = new ArrayList<>(QUEUED_CALLS_COUNT);
-
- private static final Runnable UI_THREAD_CALLBACK = new Runnable() {
- @Override
- public void run() {
- ThreadUtils.assertOnUiThread();
- long nextDelay = runUiThreadCallback();
- if (nextDelay >= 0) {
- ThreadUtils.getUiHandler().postDelayed(this, nextDelay);
- }
- }
- };
-
- private static GeckoThread sGeckoThread;
-
- @WrapForJNI
- private static final ClassLoader clsLoader = GeckoThread.class.getClassLoader();
- @WrapForJNI
- private static MessageQueue msgQueue;
-
- private GeckoProfile mProfile;
-
- private final String mArgs;
- private final String mAction;
- private final boolean mDebugging;
-
- GeckoThread(GeckoProfile profile, String args, String action, boolean debugging) {
- mProfile = profile;
- mArgs = args;
- mAction = action;
- mDebugging = debugging;
-
- setName("Gecko");
- }
-
- public static boolean init(GeckoProfile profile, String args, String action, boolean debugging) {
- ThreadUtils.assertOnUiThread();
- if (isState(State.INITIAL) && sGeckoThread == null) {
- sGeckoThread = new GeckoThread(profile, args, action, debugging);
- return true;
- }
- return false;
- }
-
- private static boolean canUseProfile(final Context context, final GeckoProfile profile,
- final String profileName, final File profileDir) {
- if (profileDir != null && !profileDir.isDirectory()) {
- return false;
- }
-
- if (profile == null) {
- // We haven't initialized; any profile is okay as long as we follow the guest mode setting.
- return GeckoProfile.shouldUseGuestMode(context) ==
- GeckoProfile.isGuestProfile(context, profileName, profileDir);
- }
-
- // We already initialized and have a profile; see if it matches ours.
- try {
- return profileDir == null ? profileName.equals(profile.getName()) :
- profile.getDir().getCanonicalPath().equals(profileDir.getCanonicalPath());
- } catch (final IOException e) {
- Log.e(LOGTAG, "Cannot compare profile " + profileName);
- return false;
- }
- }
-
- public static boolean canUseProfile(final String profileName, final File profileDir) {
- if (profileName == null) {
- throw new IllegalArgumentException("Null profile name");
- }
- return canUseProfile(GeckoAppShell.getApplicationContext(), getActiveProfile(),
- profileName, profileDir);
- }
-
- public static boolean initWithProfile(final String profileName, final File profileDir) {
- if (profileName == null) {
- throw new IllegalArgumentException("Null profile name");
- }
-
- final Context context = GeckoAppShell.getApplicationContext();
- final GeckoProfile profile = getActiveProfile();
-
- if (!canUseProfile(context, profile, profileName, profileDir)) {
- // Profile is incompatible with current profile.
- return false;
- }
-
- if (profile != null) {
- // We already have a compatible profile.
- return true;
- }
-
- // We haven't initialized yet; okay to initialize now.
- return init(GeckoProfile.get(context, profileName, profileDir),
- /* args */ null, /* action */ null, /* debugging */ false);
- }
-
- public static boolean launch() {
- ThreadUtils.assertOnUiThread();
- if (checkAndSetState(State.INITIAL, State.LAUNCHED)) {
- sGeckoThread.start();
- return true;
- }
- return false;
- }
-
- public static boolean isLaunched() {
- return !isState(State.INITIAL);
- }
-
- @RobocopTarget
- public static boolean isRunning() {
- return isState(State.RUNNING);
- }
-
- // Invoke the given Method and handle checked Exceptions.
- private static void invokeMethod(final Method method, final Object obj, final Object[] args) {
- try {
- method.setAccessible(true);
- method.invoke(obj, args);
- } catch (final IllegalAccessException e) {
- throw new IllegalStateException("Unexpected exception", e);
- } catch (final InvocationTargetException e) {
- throw new UnsupportedOperationException("Cannot make call", e.getCause());
- }
- }
-
- // Queue a call to the given method.
- private static void queueNativeCallLocked(final Class<?> cls, final String methodName,
- final Object obj, final Object[] args,
- final State state) {
- final ArrayList<Class<?>> argTypes = new ArrayList<>(args.length);
- final ArrayList<Object> argValues = new ArrayList<>(args.length);
-
- for (int i = 0; i < args.length; i++) {
- if (args[i] instanceof Class) {
- argTypes.add((Class<?>) args[i]);
- argValues.add(args[++i]);
- continue;
- }
- Class<?> argType = args[i].getClass();
- if (argType == Boolean.class) argType = Boolean.TYPE;
- else if (argType == Byte.class) argType = Byte.TYPE;
- else if (argType == Character.class) argType = Character.TYPE;
- else if (argType == Double.class) argType = Double.TYPE;
- else if (argType == Float.class) argType = Float.TYPE;
- else if (argType == Integer.class) argType = Integer.TYPE;
- else if (argType == Long.class) argType = Long.TYPE;
- else if (argType == Short.class) argType = Short.TYPE;
- argTypes.add(argType);
- argValues.add(args[i]);
- }
- final Method method;
- try {
- method = cls.getDeclaredMethod(
- methodName, argTypes.toArray(new Class<?>[argTypes.size()]));
- } catch (final NoSuchMethodException e) {
- throw new IllegalArgumentException("Cannot find method", e);
- }
-
- if (!Modifier.isNative(method.getModifiers())) {
- // As a precaution, we disallow queuing non-native methods. Queuing non-native
- // methods is dangerous because the method could end up being called on either
- // the original thread or the Gecko thread depending on timing. Native methods
- // usually handle this by posting an event to the Gecko thread automatically,
- // but there is no automatic mechanism for non-native methods.
- throw new UnsupportedOperationException("Not allowed to queue non-native methods");
- }
-
- if (isStateAtLeast(state)) {
- invokeMethod(method, obj, argValues.toArray());
- return;
- }
-
- QUEUED_CALLS.add(new QueuedCall(
- method, obj, argValues.toArray(), state));
- }
-
- /**
- * Queue a call to the given static method until Gecko is in the given state.
- *
- * @param state The Gecko state in which the native call could be executed.
- * Default is State.RUNNING, which means this queued call will
- * run when Gecko is at or after RUNNING state.
- * @param cls Class that declares the static method.
- * @param methodName Name of the static method.
- * @param args Args to call the static method with; to specify a parameter type,
- * pass in a Class instance first, followed by the value.
- */
- public static void queueNativeCallUntil(final State state, final Class<?> cls,
- final String methodName, final Object... args) {
- synchronized (QUEUED_CALLS) {
- queueNativeCallLocked(cls, methodName, null, args, state);
- }
- }
-
- /**
- * Queue a call to the given static method until Gecko is in the RUNNING state.
- */
- public static void queueNativeCall(final Class<?> cls, final String methodName,
- final Object... args) {
- synchronized (QUEUED_CALLS) {
- queueNativeCallLocked(cls, methodName, null, args, State.RUNNING);
- }
- }
-
- /**
- * Queue a call to the given instance method until Gecko is in the given state.
- *
- * @param state The Gecko state in which the native call could be executed.
- * @param obj Object that declares the instance method.
- * @param methodName Name of the instance method.
- * @param args Args to call the instance method with; to specify a parameter type,
- * pass in a Class instance first, followed by the value.
- */
- public static void queueNativeCallUntil(final State state, final Object obj,
- final String methodName, final Object... args) {
- synchronized (QUEUED_CALLS) {
- queueNativeCallLocked(obj.getClass(), methodName, obj, args, state);
- }
- }
-
- /**
- * Queue a call to the given instance method until Gecko is in the RUNNING state.
- */
- public static void queueNativeCall(final Object obj, final String methodName,
- final Object... args) {
- synchronized (QUEUED_CALLS) {
- queueNativeCallLocked(obj.getClass(), methodName, obj, args, State.RUNNING);
- }
- }
-
- // Run all queued methods
- private static void flushQueuedNativeCallsLocked(final State state) {
- int lastSkipped = -1;
- for (int i = 0; i < QUEUED_CALLS.size(); i++) {
- final QueuedCall call = QUEUED_CALLS.get(i);
- if (call == null) {
- // We already handled the call.
- continue;
- }
- if (!state.isAtLeast(call.state)) {
- // The call is not ready yet; skip it.
- lastSkipped = i;
- continue;
- }
- // Mark as handled.
- QUEUED_CALLS.set(i, null);
-
- invokeMethod(call.method, call.target, call.args);
- }
- if (lastSkipped < 0) {
- // We're done here; release the memory
- QUEUED_CALLS.clear();
- QUEUED_CALLS.trimToSize();
- } else if (lastSkipped < QUEUED_CALLS.size() - 1) {
- // We skipped some; free up null entries at the end,
- // but keep all the previous entries for later.
- QUEUED_CALLS.subList(lastSkipped + 1, QUEUED_CALLS.size()).clear();
- }
- }
-
- private static String initGeckoEnvironment() {
- final Context context = GeckoAppShell.getApplicationContext();
- GeckoLoader.loadMozGlue(context);
- setState(State.MOZGLUE_READY);
-
- final Locale locale = Locale.getDefault();
- final Resources res = context.getResources();
- if (locale.toString().equalsIgnoreCase("zh_hk")) {
- final Locale mappedLocale = Locale.TRADITIONAL_CHINESE;
- Locale.setDefault(mappedLocale);
- Configuration config = res.getConfiguration();
- config.locale = mappedLocale;
- res.updateConfiguration(config, null);
- }
-
- String[] pluginDirs = null;
- try {
- pluginDirs = GeckoAppShell.getPluginDirectories();
- } catch (Exception e) {
- Log.w(LOGTAG, "Caught exception getting plugin dirs.", e);
- }
-
- final String resourcePath = context.getPackageResourcePath();
- GeckoLoader.setupGeckoEnvironment(context, pluginDirs, context.getFilesDir().getPath());
-
- GeckoLoader.loadSQLiteLibs(context, resourcePath);
- GeckoLoader.loadNSSLibs(context, resourcePath);
- GeckoLoader.loadGeckoLibs(context, resourcePath);
- setState(State.LIBS_READY);
-
- return resourcePath;
- }
-
- private String addCustomProfileArg(String args) {
- String profileArg = "";
-
- // Make sure a profile exists.
- final GeckoProfile profile = getProfile();
- profile.getDir(); // call the lazy initializer
-
- // If args don't include the profile, make sure it's included.
- if (args == null || !args.matches(".*\\B-(P|profile)\\s+\\S+.*")) {
- if (profile.isCustomProfile()) {
- profileArg = " -profile " + profile.getDir().getAbsolutePath();
- } else {
- profileArg = " -P " + profile.getName();
- }
- }
-
- return (args != null ? args : "") + profileArg;
- }
-
- private String getGeckoArgs(final String apkPath) {
- // argv[0] is the program name, which for us is the package name.
- final Context context = GeckoAppShell.getApplicationContext();
- final StringBuilder args = new StringBuilder(context.getPackageName());
- args.append(" -greomni ").append(apkPath);
-
- final String userArgs = addCustomProfileArg(mArgs);
- if (userArgs != null) {
- args.append(' ').append(userArgs);
- }
-
- // In un-official builds, we want to load Javascript resources fresh
- // with each build. In official builds, the startup cache is purged by
- // the buildid mechanism, but most un-official builds don't bump the
- // buildid, so we purge here instead.
- if (!AppConstants.MOZILLA_OFFICIAL) {
- Log.w(LOGTAG, "STARTUP PERFORMANCE WARNING: un-official build: purging the " +
- "startup (JavaScript) caches.");
- args.append(" -purgecaches");
- }
-
- return args.toString();
- }
-
- public static GeckoProfile getActiveProfile() {
- if (sGeckoThread == null) {
- return null;
- }
- final GeckoProfile profile = sGeckoThread.mProfile;
- if (profile != null) {
- return profile;
- }
- return sGeckoThread.getProfile();
- }
-
- public synchronized GeckoProfile getProfile() {
- if (mProfile == null) {
- final Context context = GeckoAppShell.getApplicationContext();
- mProfile = GeckoProfile.initFromArgs(context, mArgs);
- }
- return mProfile;
- }
-
- @Override
- public void run() {
- Log.i(LOGTAG, "preparing to run Gecko");
-
- Looper.prepare();
- GeckoThread.msgQueue = Looper.myQueue();
- ThreadUtils.sGeckoThread = this;
- ThreadUtils.sGeckoHandler = new Handler();
-
- // Preparation for pumpMessageLoop()
- final MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
- @Override public boolean queueIdle() {
- final Handler geckoHandler = ThreadUtils.sGeckoHandler;
- Message idleMsg = Message.obtain(geckoHandler);
- // Use |Message.obj == GeckoHandler| to identify our "queue is empty" message
- idleMsg.obj = geckoHandler;
- geckoHandler.sendMessageAtFrontOfQueue(idleMsg);
- // Keep this IdleHandler
- return true;
- }
- };
- Looper.myQueue().addIdleHandler(idleHandler);
-
- if (mDebugging) {
- try {
- Thread.sleep(5 * 1000 /* 5 seconds */);
- } catch (final InterruptedException e) {
- }
- }
-
- final String args = getGeckoArgs(initGeckoEnvironment());
-
- // This can only happen after the call to initGeckoEnvironment
- // above, because otherwise the JNI code hasn't been loaded yet.
- ThreadUtils.postToUiThread(new Runnable() {
- @Override public void run() {
- registerUiThread();
- }
- });
-
- Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - runGecko");
-
- if (!AppConstants.MOZILLA_OFFICIAL) {
- Log.i(LOGTAG, "RunGecko - args = " + args);
- }
-
- // And go.
- GeckoLoader.nativeRun(args);
-
- // And... we're done.
- setState(State.EXITED);
-
- try {
- final JSONObject msg = new JSONObject();
- msg.put("type", "Gecko:Exited");
- GeckoAppShell.getGeckoInterface().getAppEventDispatcher().dispatchEvent(msg, null);
- EventDispatcher.getInstance().dispatchEvent(msg, null);
- } catch (final JSONException e) {
- Log.e(LOGTAG, "unable to dispatch event", e);
- }
-
- // Remove pumpMessageLoop() idle handler
- Looper.myQueue().removeIdleHandler(idleHandler);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean pumpMessageLoop(final Message msg) {
- final Handler geckoHandler = ThreadUtils.sGeckoHandler;
-
- if (msg.obj == geckoHandler && msg.getTarget() == geckoHandler) {
- // Our "queue is empty" message; see runGecko()
- return false;
- }
-
- if (msg.getTarget() == null) {
- Looper.myLooper().quit();
- } else {
- msg.getTarget().dispatchMessage(msg);
- }
-
- return true;
- }
-
- /**
- * Check that the current Gecko thread state matches the given state.
- *
- * @param state State to check
- * @return True if the current Gecko thread state matches
- */
- public static boolean isState(final State state) {
- return sState.is(state);
- }
-
- /**
- * Check that the current Gecko thread state is at the given state or further along,
- * according to the order defined in the State enum.
- *
- * @param state State to check
- * @return True if the current Gecko thread state matches
- */
- public static boolean isStateAtLeast(final State state) {
- return sState.isAtLeast(state);
- }
-
- /**
- * Check that the current Gecko thread state is at the given state or prior,
- * according to the order defined in the State enum.
- *
- * @param state State to check
- * @return True if the current Gecko thread state matches
- */
- public static boolean isStateAtMost(final State state) {
- return sState.isAtMost(state);
- }
-
- /**
- * Check that the current Gecko thread state falls into an inclusive range of states,
- * according to the order defined in the State enum.
- *
- * @param minState Lower range of allowable states
- * @param maxState Upper range of allowable states
- * @return True if the current Gecko thread state matches
- */
- public static boolean isStateBetween(final State minState, final State maxState) {
- return sState.isBetween(minState, maxState);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void setState(final State newState) {
- ThreadUtils.assertOnGeckoThread();
- synchronized (QUEUED_CALLS) {
- flushQueuedNativeCallsLocked(newState);
- sState = newState;
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static boolean checkAndSetState(final State currentState, final State newState) {
- synchronized (QUEUED_CALLS) {
- if (sState == currentState) {
- flushQueuedNativeCallsLocked(newState);
- sState = newState;
- return true;
- }
- }
- return false;
- }
-
- @WrapForJNI(stubName = "SpeculativeConnect")
- private static native void speculativeConnectNative(String uri);
-
- public static void speculativeConnect(final String uri) {
- // This is almost always called before Gecko loads, so we don't
- // bother checking here if Gecko is actually loaded or not.
- // Speculative connection depends on proxy settings,
- // so the earliest it can happen is after profile is ready.
- queueNativeCallUntil(State.PROFILE_READY, GeckoThread.class,
- "speculativeConnectNative", uri);
- }
-
- @WrapForJNI @RobocopTarget
- public static native void waitOnGecko();
-
- @WrapForJNI(stubName = "OnPause", dispatchTo = "gecko")
- private static native void nativeOnPause();
-
- public static void onPause() {
- if (isStateAtLeast(State.PROFILE_READY)) {
- nativeOnPause();
- } else {
- queueNativeCallUntil(State.PROFILE_READY, GeckoThread.class,
- "nativeOnPause");
- }
- }
-
- @WrapForJNI(stubName = "OnResume", dispatchTo = "gecko")
- private static native void nativeOnResume();
-
- public static void onResume() {
- if (isStateAtLeast(State.PROFILE_READY)) {
- nativeOnResume();
- } else {
- queueNativeCallUntil(State.PROFILE_READY, GeckoThread.class,
- "nativeOnResume");
- }
- }
-
- @WrapForJNI(stubName = "CreateServices", dispatchTo = "gecko")
- private static native void nativeCreateServices(String category, String data);
-
- public static void createServices(final String category, final String data) {
- if (isStateAtLeast(State.PROFILE_READY)) {
- nativeCreateServices(category, data);
- } else {
- queueNativeCallUntil(State.PROFILE_READY, GeckoThread.class, "nativeCreateServices",
- String.class, category, String.class, data);
- }
- }
-
- // Implemented in mozglue/android/APKOpen.cpp.
- /* package */ static native void registerUiThread();
-
- @WrapForJNI(calledFrom = "ui")
- /* package */ static native long runUiThreadCallback();
-
- @WrapForJNI
- private static void requestUiThreadCallback(long delay) {
- ThreadUtils.getUiHandler().postDelayed(UI_THREAD_CALLBACK, delay);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
deleted file mode 100644
index 93d738361..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java
+++ /dev/null
@@ -1,736 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
- * vim: ts=4 sw=4 expandtab:
- * 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.util.Set;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.annotation.ReflectionTarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.gfx.LayerView;
-import org.mozilla.gecko.mozglue.JNIObject;
-import org.mozilla.gecko.util.EventCallback;
-import org.mozilla.gecko.util.GeckoEventListener;
-import org.mozilla.gecko.util.NativeEventListener;
-import org.mozilla.gecko.util.NativeJSObject;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-public class GeckoView extends LayerView
- implements ContextGetter, GeckoEventListener, NativeEventListener {
-
- private static final String DEFAULT_SHARED_PREFERENCES_FILE = "GeckoView";
- private static final String LOGTAG = "GeckoView";
-
- private ChromeDelegate mChromeDelegate;
- private ContentDelegate mContentDelegate;
-
- private InputConnectionListener mInputConnectionListener;
-
- protected boolean onAttachedToWindowCalled;
- protected String chromeURI = getGeckoInterface().getDefaultChromeURI();
- protected int screenId = 0; // default to the primary screen
-
- @Override
- public void handleMessage(final String event, final JSONObject message) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- if (event.equals("Gecko:Ready")) {
- handleReady(message);
- } else if (event.equals("Content:StateChange")) {
- handleStateChange(message);
- } else if (event.equals("Content:LoadError")) {
- handleLoadError(message);
- } else if (event.equals("Content:PageShow")) {
- handlePageShow(message);
- } else if (event.equals("DOMTitleChanged")) {
- handleTitleChanged(message);
- } else if (event.equals("Link:Favicon")) {
- handleLinkFavicon(message);
- } else if (event.equals("Prompt:Show") || event.equals("Prompt:ShowTop")) {
- handlePrompt(message);
- } else if (event.equals("Accessibility:Event")) {
- int mode = getImportantForAccessibility();
- if (mode == View.IMPORTANT_FOR_ACCESSIBILITY_YES ||
- mode == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- GeckoAccessibility.sendAccessibilityEvent(message);
- }
- }
- } catch (Exception e) {
- Log.e(LOGTAG, "handleMessage threw for " + event, e);
- }
- }
- });
- }
-
- @Override
- public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) {
- try {
- if ("Accessibility:Ready".equals(event)) {
- GeckoAccessibility.updateAccessibilitySettings(getContext());
- } else if ("GeckoView:Message".equals(event)) {
- // We need to pull out the bundle while on the Gecko thread.
- NativeJSObject json = message.optObject("data", null);
- if (json == null) {
- // Must have payload to call the message handler.
- return;
- }
- final Bundle data = json.toBundle();
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- handleScriptMessage(data, callback);
- }
- });
- }
- } catch (Exception e) {
- Log.w(LOGTAG, "handleMessage threw for " + event, e);
- }
- }
-
- @WrapForJNI(dispatchTo = "proxy")
- protected static final class Window extends JNIObject {
- @WrapForJNI(skip = true)
- /* package */ Window() {}
-
- static native void open(Window instance, GeckoView view, Object compositor,
- String chromeURI, int screenId);
-
- @Override protected native void disposeNative();
- native void close();
- native void reattach(GeckoView view, Object compositor);
- native void loadUri(String uri, int flags);
- }
-
- // Object to hold onto our nsWindow connection when GeckoView gets destroyed.
- private static class StateBinder extends Binder implements Parcelable {
- public final Parcelable superState;
- public final Window window;
-
- public StateBinder(Parcelable superState, Window window) {
- this.superState = superState;
- this.window = window;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- // Always write out the super-state, so that even if we lose this binder, we
- // will still have something to pass into super.onRestoreInstanceState.
- out.writeParcelable(superState, flags);
- out.writeStrongBinder(this);
- }
-
- @ReflectionTarget
- public static final Parcelable.Creator<StateBinder> CREATOR
- = new Parcelable.Creator<StateBinder>() {
- @Override
- public StateBinder createFromParcel(Parcel in) {
- final Parcelable superState = in.readParcelable(null);
- final IBinder binder = in.readStrongBinder();
- if (binder instanceof StateBinder) {
- return (StateBinder) binder;
- }
- // Not the original object we saved; return null state.
- return new StateBinder(superState, null);
- }
-
- @Override
- public StateBinder[] newArray(int size) {
- return new StateBinder[size];
- }
- };
- }
-
- protected Window window;
- private boolean stateSaved;
-
- public GeckoView(Context context) {
- super(context);
- init(context);
- }
-
- public GeckoView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- private void init(Context context) {
- if (GeckoAppShell.getApplicationContext() == null) {
- GeckoAppShell.setApplicationContext(context.getApplicationContext());
- }
-
- // Set the GeckoInterface if the context is an activity and the GeckoInterface
- // has not already been set
- if (context instanceof Activity && getGeckoInterface() == null) {
- setGeckoInterface(new BaseGeckoInterface(context));
- GeckoAppShell.setContextGetter(this);
- }
-
- // Perform common initialization for Fennec/GeckoView.
- GeckoAppShell.setLayerView(this);
-
- initializeView(EventDispatcher.getInstance());
- }
-
- @Override
- protected Parcelable onSaveInstanceState()
- {
- final Parcelable superState = super.onSaveInstanceState();
- stateSaved = true;
- return new StateBinder(superState, this.window);
- }
-
- @Override
- protected void onRestoreInstanceState(final Parcelable state)
- {
- final StateBinder stateBinder = (StateBinder) state;
-
- if (stateBinder.window != null) {
- this.window = stateBinder.window;
- }
- stateSaved = false;
-
- if (onAttachedToWindowCalled) {
- reattachWindow();
- }
-
- // We have to always call super.onRestoreInstanceState because View keeps
- // track of these calls and throws an exception when we don't call it.
- super.onRestoreInstanceState(stateBinder.superState);
- }
-
- protected void openWindow() {
-
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- Window.open(window, this, getCompositor(),
- chromeURI, screenId);
- } else {
- GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, Window.class,
- "open", window, GeckoView.class, this, Object.class, getCompositor(),
- String.class, chromeURI, screenId);
- }
- }
-
- protected void reattachWindow() {
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- window.reattach(this, getCompositor());
- } else {
- GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
- window, "reattach", GeckoView.class, this, Object.class, getCompositor());
- }
- }
-
- @Override
- public void onAttachedToWindow()
- {
- final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
-
- if (window == null) {
- // Open a new nsWindow if we didn't have one from before.
- window = new Window();
- openWindow();
- } else {
- reattachWindow();
- }
-
- super.onAttachedToWindow();
-
- onAttachedToWindowCalled = true;
- }
-
- @Override
- public void onDetachedFromWindow()
- {
- super.onDetachedFromWindow();
- super.destroy();
-
- if (stateSaved) {
- // If we saved state earlier, we don't want to close the nsWindow.
- return;
- }
-
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- window.close();
- window.disposeNative();
- } else {
- GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
- window, "close");
- GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
- window, "disposeNative");
- }
-
- onAttachedToWindowCalled = false;
- }
-
- @WrapForJNI public static final int LOAD_DEFAULT = 0;
- @WrapForJNI public static final int LOAD_NEW_TAB = 1;
- @WrapForJNI public static final int LOAD_SWITCH_TAB = 2;
-
- public void loadUri(String uri, int flags) {
- if (window == null) {
- throw new IllegalStateException("Not attached to window");
- }
-
- if (GeckoThread.isRunning()) {
- window.loadUri(uri, flags);
- } else {
- GeckoThread.queueNativeCall(window, "loadUri", String.class, uri, flags);
- }
- }
-
- /* package */ void setInputConnectionListener(final InputConnectionListener icl) {
- mInputConnectionListener = icl;
- }
-
- @Override
- public Handler getHandler() {
- if (mInputConnectionListener != null) {
- return mInputConnectionListener.getHandler(super.getHandler());
- }
- return super.getHandler();
- }
-
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- if (mInputConnectionListener != null) {
- return mInputConnectionListener.onCreateInputConnection(outAttrs);
- }
- return null;
- }
-
- @Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- if (super.onKeyPreIme(keyCode, event)) {
- return true;
- }
- return mInputConnectionListener != null &&
- mInputConnectionListener.onKeyPreIme(keyCode, event);
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (super.onKeyUp(keyCode, event)) {
- return true;
- }
- return mInputConnectionListener != null &&
- mInputConnectionListener.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (super.onKeyDown(keyCode, event)) {
- return true;
- }
- return mInputConnectionListener != null &&
- mInputConnectionListener.onKeyDown(keyCode, event);
- }
-
- @Override
- public boolean onKeyLongPress(int keyCode, KeyEvent event) {
- if (super.onKeyLongPress(keyCode, event)) {
- return true;
- }
- return mInputConnectionListener != null &&
- mInputConnectionListener.onKeyLongPress(keyCode, event);
- }
-
- @Override
- public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
- if (super.onKeyMultiple(keyCode, repeatCount, event)) {
- return true;
- }
- return mInputConnectionListener != null &&
- mInputConnectionListener.onKeyMultiple(keyCode, repeatCount, event);
- }
-
- /* package */ boolean isIMEEnabled() {
- return mInputConnectionListener != null &&
- mInputConnectionListener.isIMEEnabled();
- }
-
- public void importScript(final String url) {
- if (url.startsWith("resource://android/assets/")) {
- GeckoAppShell.notifyObservers("GeckoView:ImportScript", url);
- return;
- }
-
- throw new IllegalArgumentException("Must import script from 'resources://android/assets/' location.");
- }
-
- private void handleReady(final JSONObject message) {
- if (mChromeDelegate != null) {
- mChromeDelegate.onReady(this);
- }
- }
-
- private void handleStateChange(final JSONObject message) throws JSONException {
- int state = message.getInt("state");
- if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
- if ((state & GeckoAppShell.WPL_STATE_START) != 0) {
- if (mContentDelegate != null) {
- int id = message.getInt("tabID");
- mContentDelegate.onPageStart(this, new Browser(id), message.getString("uri"));
- }
- } else if ((state & GeckoAppShell.WPL_STATE_STOP) != 0) {
- if (mContentDelegate != null) {
- int id = message.getInt("tabID");
- mContentDelegate.onPageStop(this, new Browser(id), message.getBoolean("success"));
- }
- }
- }
- }
-
- private void handleLoadError(final JSONObject message) throws JSONException {
- if (mContentDelegate != null) {
- int id = message.getInt("tabID");
- mContentDelegate.onPageStop(GeckoView.this, new Browser(id), false);
- }
- }
-
- private void handlePageShow(final JSONObject message) throws JSONException {
- if (mContentDelegate != null) {
- int id = message.getInt("tabID");
- mContentDelegate.onPageShow(GeckoView.this, new Browser(id));
- }
- }
-
- private void handleTitleChanged(final JSONObject message) throws JSONException {
- if (mContentDelegate != null) {
- int id = message.getInt("tabID");
- mContentDelegate.onReceivedTitle(GeckoView.this, new Browser(id), message.getString("title"));
- }
- }
-
- private void handleLinkFavicon(final JSONObject message) throws JSONException {
- if (mContentDelegate != null) {
- int id = message.getInt("tabID");
- mContentDelegate.onReceivedFavicon(GeckoView.this, new Browser(id), message.getString("href"), message.getInt("size"));
- }
- }
-
- private void handlePrompt(final JSONObject message) throws JSONException {
- if (mChromeDelegate != null) {
- String hint = message.optString("hint");
- if ("alert".equals(hint)) {
- String text = message.optString("text");
- mChromeDelegate.onAlert(GeckoView.this, null, text, new PromptResult(message));
- } else if ("confirm".equals(hint)) {
- String text = message.optString("text");
- mChromeDelegate.onConfirm(GeckoView.this, null, text, new PromptResult(message));
- } else if ("prompt".equals(hint)) {
- String text = message.optString("text");
- String defaultValue = message.optString("textbox0");
- mChromeDelegate.onPrompt(GeckoView.this, null, text, defaultValue, new PromptResult(message));
- } else if ("remotedebug".equals(hint)) {
- mChromeDelegate.onDebugRequest(GeckoView.this, new PromptResult(message));
- }
- }
- }
-
- private void handleScriptMessage(final Bundle data, final EventCallback callback) {
- if (mChromeDelegate != null) {
- MessageResult result = null;
- if (callback != null) {
- result = new MessageResult(callback);
- }
- mChromeDelegate.onScriptMessage(GeckoView.this, data, result);
- }
- }
-
- /**
- * Set the chrome callback handler.
- * This will replace the current handler.
- * @param chrome An implementation of GeckoViewChrome.
- */
- public void setChromeDelegate(ChromeDelegate chrome) {
- mChromeDelegate = chrome;
- }
-
- /**
- * Set the content callback handler.
- * This will replace the current handler.
- * @param content An implementation of ContentDelegate.
- */
- public void setContentDelegate(ContentDelegate content) {
- mContentDelegate = content;
- }
-
- public static void setGeckoInterface(final BaseGeckoInterface geckoInterface) {
- GeckoAppShell.setGeckoInterface(geckoInterface);
- }
-
- public static GeckoAppShell.GeckoInterface getGeckoInterface() {
- return GeckoAppShell.getGeckoInterface();
- }
-
- protected String getSharedPreferencesFile() {
- return DEFAULT_SHARED_PREFERENCES_FILE;
- }
-
- @Override
- public SharedPreferences getSharedPreferences() {
- return getContext().getSharedPreferences(getSharedPreferencesFile(), 0);
- }
-
- /**
- * Wrapper for a browser in the GeckoView container. Associated with a browser
- * element in the Gecko system.
- */
- public class Browser {
- private final int mId;
- private Browser(int Id) {
- mId = Id;
- }
-
- /**
- * Get the ID of the Browser. This is the same ID used by Gecko for it's underlying
- * browser element.
- * @return The integer ID of the Browser.
- */
- private int getId() {
- return mId;
- }
-
- /**
- * Load a URL resource into the Browser.
- * @param url The URL string.
- */
- public void loadUrl(String url) {
- JSONObject args = new JSONObject();
- try {
- args.put("url", url);
- args.put("parentId", -1);
- args.put("newTab", false);
- args.put("tabID", mId);
- } catch (Exception e) {
- Log.w(LOGTAG, "Error building JSON arguments for loadUrl.", e);
- }
- GeckoAppShell.notifyObservers("Tab:Load", args.toString());
- }
- }
-
- /* Provides a means for the client to indicate whether a JavaScript
- * dialog request should proceed. An instance of this class is passed to
- * various GeckoViewChrome callback actions.
- */
- public class PromptResult {
- private final int RESULT_OK = 0;
- private final int RESULT_CANCEL = 1;
-
- private final JSONObject mMessage;
-
- public PromptResult(JSONObject message) {
- mMessage = message;
- }
-
- private JSONObject makeResult(int resultCode) {
- JSONObject result = new JSONObject();
- try {
- result.put("button", resultCode);
- } catch (JSONException ex) { }
- return result;
- }
-
- /**
- * Handle a confirmation response from the user.
- */
- public void confirm() {
- JSONObject result = makeResult(RESULT_OK);
- EventDispatcher.sendResponse(mMessage, result);
- }
-
- /**
- * Handle a confirmation response from the user.
- * @param value String value to return to the browser context.
- */
- public void confirmWithValue(String value) {
- JSONObject result = makeResult(RESULT_OK);
- try {
- result.put("textbox0", value);
- } catch (JSONException ex) { }
- EventDispatcher.sendResponse(mMessage, result);
- }
-
- /**
- * Handle a cancellation response from the user.
- */
- public void cancel() {
- JSONObject result = makeResult(RESULT_CANCEL);
- EventDispatcher.sendResponse(mMessage, result);
- }
- }
-
- /* Provides a means for the client to respond to a script message with some data.
- * An instance of this class is passed to GeckoViewChrome.onScriptMessage.
- */
- public class MessageResult {
- private final EventCallback mCallback;
-
- public MessageResult(EventCallback callback) {
- if (callback == null) {
- throw new IllegalArgumentException("EventCallback should not be null.");
- }
- mCallback = callback;
- }
-
- private JSONObject bundleToJSON(Bundle data) {
- JSONObject result = new JSONObject();
- if (data == null) {
- return result;
- }
-
- final Set<String> keys = data.keySet();
- for (String key : keys) {
- try {
- result.put(key, data.get(key));
- } catch (JSONException e) {
- }
- }
- return result;
- }
-
- /**
- * Handle a successful response to a script message.
- * @param value Bundle value to return to the script context.
- */
- public void success(Bundle data) {
- mCallback.sendSuccess(bundleToJSON(data));
- }
-
- /**
- * Handle a failure response to a script message.
- */
- public void failure(Bundle data) {
- mCallback.sendError(bundleToJSON(data));
- }
- }
-
- public interface ChromeDelegate {
- /**
- * Tell the host application that Gecko is ready to handle requests.
- * @param view The GeckoView that initiated the callback.
- */
- public void onReady(GeckoView view);
-
- /**
- * Tell the host application to display an alert dialog.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param message The string to display in the dialog.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result);
-
- /**
- * Tell the host application to display a confirmation dialog.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param message The string to display in the dialog.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result);
-
- /**
- * Tell the host application to display an input prompt dialog.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param message The string to display in the dialog.
- * @param defaultValue The string to use as default input.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result);
-
- /**
- * Tell the host application to display a remote debugging request dialog.
- * @param view The GeckoView that initiated the callback.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- public void onDebugRequest(GeckoView view, GeckoView.PromptResult result);
-
- /**
- * Receive a message from an imported script.
- * @param view The GeckoView that initiated the callback.
- * @param data Bundle of data sent with the message. Never null.
- * @param result A MessageResult used to send back a response without blocking. Can be null.
- * Defaults to do nothing.
- */
- public void onScriptMessage(GeckoView view, Bundle data, GeckoView.MessageResult result);
- }
-
- public interface ContentDelegate {
- /**
- * A Browser has started loading content from the network.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param url The resource being loaded.
- */
- public void onPageStart(GeckoView view, GeckoView.Browser browser, String url);
-
- /**
- * A Browser has finished loading content from the network.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that was loading the content.
- * @param success Whether the page loaded successfully or an error occurred.
- */
- public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success);
-
- /**
- * A Browser is displaying content. This page could have been loaded via
- * network or from the session history.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is showing the content.
- */
- public void onPageShow(GeckoView view, GeckoView.Browser browser);
-
- /**
- * A page title was discovered in the content or updated after the content
- * loaded.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is showing the content.
- * @param title The title sent from the content.
- */
- public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title);
-
- /**
- * A link element was discovered in the content or updated after the content
- * loaded that specifies a favicon.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is showing the content.
- * @param url The href of the link element specifying the favicon.
- * @param size The maximum size specified for the favicon, or -1 for any size.
- */
- public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewChrome.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewChrome.java
deleted file mode 100644
index 403c6dbca..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewChrome.java
+++ /dev/null
@@ -1,81 +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.os.Bundle;
-
-public class GeckoViewChrome implements GeckoView.ChromeDelegate {
- /**
- * Tell the host application that Gecko is ready to handle requests.
- * @param view The GeckoView that initiated the callback.
- */
- @Override
- public void onReady(GeckoView view) {}
-
- /**
- * Tell the host application to display an alert dialog.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param message The string to display in the dialog.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- @Override
- public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result) {
- result.cancel();
- }
-
- /**
- * Tell the host application to display a confirmation dialog.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param message The string to display in the dialog.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- @Override
- public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result) {
- result.cancel();
- }
-
- /**
- * Tell the host application to display an input prompt dialog.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param message The string to display in the dialog.
- * @param defaultValue The string to use as default input.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- @Override
- public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result) {
- result.cancel();
- }
-
- /**
- * Tell the host application to display a remote debugging request dialog.
- * @param view The GeckoView that initiated the callback.
- * @param result A PromptResult used to send back the result without blocking.
- * Defaults to cancel requests.
- */
- @Override
- public void onDebugRequest(GeckoView view, GeckoView.PromptResult result) {
- result.cancel();
- }
-
- /**
- * Receive a message from an imported script.
- * @param view The GeckoView that initiated the callback.
- * @param data Bundle of data sent with the message. Never null.
- * @param result A MessageResult used to send back a response without blocking. Can be null.
- * Defaults to cancel requests with a failed response.
- */
- public void onScriptMessage(GeckoView view, Bundle data, GeckoView.MessageResult result) {
- if (result != null) {
- result.failure(null);
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewContent.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewContent.java
deleted file mode 100644
index 22d0ede75..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewContent.java
+++ /dev/null
@@ -1,56 +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;
-
-public class GeckoViewContent implements GeckoView.ContentDelegate {
- /**
- * A Browser has started loading content from the network.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is loading the content.
- * @param url The resource being loaded.
- */
- @Override
- public void onPageStart(GeckoView view, GeckoView.Browser browser, String url) {}
-
- /**
- * A Browser has finished loading content from the network.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that was loading the content.
- * @param success Whether the page loaded successfully or an error occurred.
- */
- @Override
- public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success) {}
-
- /**
- * A Browser is displaying content. This page could have been loaded via
- * network or from the session history.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is showing the content.
- */
- @Override
- public void onPageShow(GeckoView view, GeckoView.Browser browser) {}
-
- /**
- * A page title was discovered in the content or updated after the content
- * loaded.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is showing the content.
- * @param title The title sent from the content.
- */
- @Override
- public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title) {}
-
- /**
- * A link element was discovered in the content or updated after the content
- * loaded that specifies a favicon.
- * @param view The GeckoView that initiated the callback.
- * @param browser The Browser that is showing the content.
- * @param url The href of the link element specifying the favicon.
- * @param size The maximum size specified for the favicon, or -1 for any size.
- */
- @Override
- public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size) {}
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewFragment.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewFragment.java
deleted file mode 100644
index 51320636e..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewFragment.java
+++ /dev/null
@@ -1,52 +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.support.v4.app.Fragment;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-public class GeckoViewFragment extends android.support.v4.app.Fragment {
- private static final String LOGTAG = "GeckoViewFragment";
-
- private static Parcelable state = null;
- private static GeckoViewFragment lastUsed = null;
- private GeckoView geckoView = null;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- setRetainInstance(true);
- super.onCreate(savedInstanceState);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- geckoView = new GeckoView(getContext());
- return geckoView;
- }
-
- @Override
- public void onResume() {
- if (state != null && lastUsed != this) {
- // "Restore" the window from the previously used GeckoView to this GeckoView and attach it
- geckoView.onRestoreInstanceState(state);
- state = null;
- }
- super.onResume();
- }
-
- @Override
- public void onPause() {
- state = geckoView.onSaveInstanceState();
- lastUsed = this;
- super.onPause();
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputConnectionListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputConnectionListener.java
deleted file mode 100644
index baddc4ed2..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputConnectionListener.java
+++ /dev/null
@@ -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/. */
-
-package org.mozilla.gecko;
-
-import android.os.Handler;
-import android.view.KeyEvent;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-
-/**
- * Interface for interacting with GeckoInputConnection from GeckoView.
- */
-interface InputConnectionListener
-{
- Handler getHandler(Handler defHandler);
- InputConnection onCreateInputConnection(EditorInfo outAttrs);
- boolean onKeyPreIme(int keyCode, KeyEvent event);
- boolean onKeyDown(int keyCode, KeyEvent event);
- boolean onKeyLongPress(int keyCode, KeyEvent event);
- boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
- boolean onKeyUp(int keyCode, KeyEvent event);
- boolean isIMEEnabled();
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputMethods.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputMethods.java
deleted file mode 100644
index 57649b0da..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/InputMethods.java
+++ /dev/null
@@ -1,76 +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.util.Collection;
-
-import org.mozilla.gecko.AppConstants.Versions;
-
-import android.content.Context;
-import android.provider.Settings.Secure;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
-
-final public class InputMethods {
- public static final String METHOD_ANDROID_LATINIME = "com.android.inputmethod.latin/.LatinIME";
- public static final String METHOD_ATOK = "com.justsystems.atokmobile.service/.AtokInputMethodService";
- public static final String METHOD_GOOGLE_JAPANESE_INPUT = "com.google.android.inputmethod.japanese/.MozcService";
- public static final String METHOD_GOOGLE_LATINIME = "com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME";
- public static final String METHOD_HTC_TOUCH_INPUT = "com.htc.android.htcime/.HTCIMEService";
- public static final String METHOD_IWNN = "jp.co.omronsoft.iwnnime.ml/.standardcommon.IWnnLanguageSwitcher";
- public static final String METHOD_OPENWNN_PLUS = "com.owplus.ime.openwnnplus/.OpenWnnJAJP";
- public static final String METHOD_SAMSUNG = "com.sec.android.inputmethod/.SamsungKeypad";
- public static final String METHOD_SIMEJI = "com.adamrocker.android.input.simeji/.OpenWnnSimeji";
- public static final String METHOD_SWIFTKEY = "com.touchtype.swiftkey/com.touchtype.KeyboardService";
- public static final String METHOD_SWYPE = "com.swype.android.inputmethod/.SwypeInputMethod";
- public static final String METHOD_SWYPE_BETA = "com.nuance.swype.input/.IME";
- public static final String METHOD_TOUCHPAL_KEYBOARD = "com.cootek.smartinputv5/com.cootek.smartinput5.TouchPalIME";
-
- private InputMethods() {}
-
- public static String getCurrentInputMethod(Context context) {
- String inputMethod = Secure.getString(context.getContentResolver(), Secure.DEFAULT_INPUT_METHOD);
- return (inputMethod != null ? inputMethod : "");
- }
-
- public static InputMethodInfo getInputMethodInfo(Context context, String inputMethod) {
- InputMethodManager imm = getInputMethodManager(context);
- Collection<InputMethodInfo> infos = imm.getEnabledInputMethodList();
- for (InputMethodInfo info : infos) {
- if (info.getId().equals(inputMethod)) {
- return info;
- }
- }
- return null;
- }
-
- public static InputMethodManager getInputMethodManager(Context context) {
- return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- }
-
- public static boolean needsSoftResetWorkaround(String inputMethod) {
- // Stock latin IME on Android 4.2 and above
- return Versions.feature17Plus &&
- (METHOD_ANDROID_LATINIME.equals(inputMethod) ||
- METHOD_GOOGLE_LATINIME.equals(inputMethod));
- }
-
- public static boolean shouldCommitCharAsKey(String inputMethod) {
- return METHOD_HTC_TOUCH_INPUT.equals(inputMethod);
- }
-
- public static boolean isGestureKeyboard(Context context) {
- // SwiftKey is a gesture keyboard, but it doesn't seem to need any special-casing
- // to do AwesomeBar auto-spacing.
- String inputMethod = getCurrentInputMethod(context);
- return (Versions.feature17Plus &&
- (METHOD_ANDROID_LATINIME.equals(inputMethod) ||
- METHOD_GOOGLE_LATINIME.equals(inputMethod))) ||
- METHOD_SWYPE.equals(inputMethod) ||
- METHOD_SWYPE_BETA.equals(inputMethod) ||
- METHOD_TOUCHPAL_KEYBOARD.equals(inputMethod);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NSSBridge.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NSSBridge.java
deleted file mode 100644
index 8d525b0ba..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NSSBridge.java
+++ /dev/null
@@ -1,55 +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 org.mozilla.gecko.mozglue.GeckoLoader;
-
-import android.content.Context;
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-public class NSSBridge {
- private static final String LOGTAG = "NSSBridge";
-
- private static native String nativeEncrypt(String aDb, String aValue);
- private static native String nativeDecrypt(String aDb, String aValue);
-
- @RobocopTarget
- static public String encrypt(Context context, String aValue)
- throws Exception {
- String resourcePath = context.getPackageResourcePath();
- GeckoLoader.loadNSSLibs(context, resourcePath);
-
- String path = GeckoProfile.get(context).getDir().toString();
- return nativeEncrypt(path, aValue);
- }
-
- @RobocopTarget
- static public String encrypt(Context context, String profilePath, String aValue)
- throws Exception {
- String resourcePath = context.getPackageResourcePath();
- GeckoLoader.loadNSSLibs(context, resourcePath);
-
- return nativeEncrypt(profilePath, aValue);
- }
-
- @RobocopTarget
- static public String decrypt(Context context, String aValue)
- throws Exception {
- String resourcePath = context.getPackageResourcePath();
- GeckoLoader.loadNSSLibs(context, resourcePath);
-
- String path = GeckoProfile.get(context).getDir().toString();
- return nativeDecrypt(path, aValue);
- }
-
- @RobocopTarget
- static public String decrypt(Context context, String profilePath, String aValue)
- throws Exception {
- String resourcePath = context.getPackageResourcePath();
- GeckoLoader.loadNSSLibs(context, resourcePath);
-
- return nativeDecrypt(profilePath, aValue);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NotificationListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NotificationListener.java
deleted file mode 100644
index 85a68768f..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/NotificationListener.java
+++ /dev/null
@@ -1,17 +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;
-
-public interface NotificationListener
-{
- void showNotification(String name, String cookie, String title, String text,
- String host, String imageUrl);
-
- void showPersistentNotification(String name, String cookie, String title, String text,
- String host, String imageUrl, String data);
-
- void closeNotification(String name);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/PrefsHelper.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/PrefsHelper.java
deleted file mode 100644
index b60f6fd88..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/PrefsHelper.java
+++ /dev/null
@@ -1,308 +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 org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-
-import android.support.v4.util.SimpleArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * Helper class to get/set gecko prefs.
- */
-public final class PrefsHelper {
- private static final String LOGTAG = "GeckoPrefsHelper";
-
- // Map pref name to ArrayList for multiple observers or PrefHandler for single observer.
- private static final SimpleArrayMap<String, Object> OBSERVERS = new SimpleArrayMap<>();
- private static final HashSet<String> INT_TO_STRING_PREFS = new HashSet<>(8);
- private static final HashSet<String> INT_TO_BOOL_PREFS = new HashSet<>(2);
-
- static {
- INT_TO_STRING_PREFS.add("browser.chrome.titlebarMode");
- INT_TO_STRING_PREFS.add("network.cookie.cookieBehavior");
- INT_TO_STRING_PREFS.add("font.size.inflation.minTwips");
- INT_TO_STRING_PREFS.add("home.sync.updateMode");
- INT_TO_STRING_PREFS.add("browser.image_blocking");
- INT_TO_BOOL_PREFS.add("browser.display.use_document_fonts");
- }
-
- @WrapForJNI
- private static final int PREF_INVALID = -1;
- @WrapForJNI
- private static final int PREF_FINISH = 0;
- @WrapForJNI
- private static final int PREF_BOOL = 1;
- @WrapForJNI
- private static final int PREF_INT = 2;
- @WrapForJNI
- private static final int PREF_STRING = 3;
-
- @WrapForJNI(stubName = "GetPrefs", dispatchTo = "gecko")
- private static native void nativeGetPrefs(String[] prefNames, PrefHandler handler);
- @WrapForJNI(stubName = "SetPref", dispatchTo = "gecko")
- private static native void nativeSetPref(String prefName, boolean flush, int type,
- boolean boolVal, int intVal, String strVal);
- @WrapForJNI(stubName = "AddObserver", dispatchTo = "gecko")
- private static native void nativeAddObserver(String[] prefNames, PrefHandler handler,
- String[] prefsToObserve);
- @WrapForJNI(stubName = "RemoveObserver", dispatchTo = "gecko")
- private static native void nativeRemoveObserver(String[] prefToUnobserve);
-
- @RobocopTarget
- public static void getPrefs(final String[] prefNames, final PrefHandler callback) {
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- nativeGetPrefs(prefNames, callback);
- } else {
- GeckoThread.queueNativeCallUntil(
- GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeGetPrefs",
- String[].class, prefNames, PrefHandler.class, callback);
- }
- }
-
- public static void getPref(final String prefName, final PrefHandler callback) {
- getPrefs(new String[] { prefName }, callback);
- }
-
- public static void getPrefs(final ArrayList<String> prefNames, final PrefHandler callback) {
- getPrefs(prefNames.toArray(new String[prefNames.size()]), callback);
- }
-
- @RobocopTarget
- public static void setPref(final String pref, final Object value, final boolean flush) {
- final int type;
- boolean boolVal = false;
- int intVal = 0;
- String strVal = null;
-
- if (INT_TO_STRING_PREFS.contains(pref)) {
- // When sending to Java, we normalized special preferences that use integers
- // and strings to represent booleans. Here, we convert them back to their
- // actual types so we can store them.
- type = PREF_INT;
- intVal = Integer.parseInt(String.valueOf(value));
- } else if (INT_TO_BOOL_PREFS.contains(pref)) {
- type = PREF_INT;
- intVal = (Boolean) value ? 1 : 0;
- } else if (value instanceof Boolean) {
- type = PREF_BOOL;
- boolVal = (Boolean) value;
- } else if (value instanceof Integer) {
- type = PREF_INT;
- intVal = (Integer) value;
- } else {
- type = PREF_STRING;
- strVal = String.valueOf(value);
- }
-
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- nativeSetPref(pref, flush, type, boolVal, intVal, strVal);
- } else {
- GeckoThread.queueNativeCallUntil(
- GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeSetPref",
- String.class, pref, flush, type, boolVal, intVal, String.class, strVal);
- }
- }
-
- public static void setPref(final String pref, final Object value) {
- setPref(pref, value, /* flush */ false);
- }
-
- @RobocopTarget
- public synchronized static void addObserver(final String[] prefNames,
- final PrefHandler handler) {
- List<String> prefsToObserve = null;
-
- for (String pref : prefNames) {
- final Object existing = OBSERVERS.get(pref);
-
- if (existing == null) {
- // Not observing yet, so add observer.
- if (prefsToObserve == null) {
- prefsToObserve = new ArrayList<>(prefNames.length);
- }
- prefsToObserve.add(pref);
- OBSERVERS.put(pref, handler);
-
- } else if (existing instanceof PrefHandler) {
- // Already observing one, so turn it into an array.
- final List<PrefHandler> handlerList = new ArrayList<>(2);
- handlerList.add((PrefHandler) existing);
- handlerList.add(handler);
- OBSERVERS.put(pref, handlerList);
-
- } else {
- // Already observing multiple, so add to existing array.
- @SuppressWarnings("unchecked")
- final List<PrefHandler> handlerList = (List) existing;
- handlerList.add(handler);
- }
- }
-
- final String[] namesToObserve = prefsToObserve == null ? null :
- prefsToObserve.toArray(new String[prefsToObserve.size()]);
-
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- nativeAddObserver(prefNames, handler, namesToObserve);
- } else {
- GeckoThread.queueNativeCallUntil(
- GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeAddObserver",
- String[].class, prefNames, PrefHandler.class, handler,
- String[].class, namesToObserve);
- }
- }
-
- @RobocopTarget
- public synchronized static void removeObserver(final PrefHandler handler) {
- List<String> prefsToUnobserve = null;
-
- for (int i = OBSERVERS.size() - 1; i >= 0; i--) {
- final Object existing = OBSERVERS.valueAt(i);
- boolean removeObserver = false;
-
- if (existing == handler) {
- removeObserver = true;
-
- } else if (!(existing instanceof PrefHandler)) {
- // Removing existing handler from list.
- @SuppressWarnings("unchecked")
- final List<PrefHandler> handlerList = (List) existing;
- if (handlerList.remove(handler) && handlerList.isEmpty()) {
- removeObserver = true;
- }
- }
-
- if (removeObserver) {
- // Removed last handler, so remove observer.
- if (prefsToUnobserve == null) {
- prefsToUnobserve = new ArrayList<>();
- }
- prefsToUnobserve.add(OBSERVERS.keyAt(i));
- OBSERVERS.removeAt(i);
- }
- }
-
- if (prefsToUnobserve == null) {
- return;
- }
-
- final String[] namesToUnobserve =
- prefsToUnobserve.toArray(new String[prefsToUnobserve.size()]);
-
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- nativeRemoveObserver(namesToUnobserve);
- } else {
- GeckoThread.queueNativeCallUntil(
- GeckoThread.State.PROFILE_READY, PrefsHelper.class, "nativeRemoveObserver",
- String[].class, namesToUnobserve);
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private static void callPrefHandler(final PrefHandler handler, int type, final String pref,
- boolean boolVal, int intVal, String strVal) {
-
- // Some Gecko preferences use integers or strings to reference state instead of
- // directly representing the value. Since the Java UI uses the type to determine
- // which ui elements to show and how to handle them, we need to normalize these
- // preferences to the correct type.
- if (INT_TO_STRING_PREFS.contains(pref)) {
- type = PREF_STRING;
- strVal = String.valueOf(intVal);
- } else if (INT_TO_BOOL_PREFS.contains(pref)) {
- type = PREF_BOOL;
- boolVal = intVal == 1;
- }
-
- switch (type) {
- case PREF_FINISH:
- handler.finish();
- return;
- case PREF_BOOL:
- handler.prefValue(pref, boolVal);
- return;
- case PREF_INT:
- handler.prefValue(pref, intVal);
- return;
- case PREF_STRING:
- handler.prefValue(pref, strVal);
- return;
- }
- throw new IllegalArgumentException();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private synchronized static void onPrefChange(final String pref, final int type,
- final boolean boolVal, final int intVal,
- final String strVal) {
- final Object existing = OBSERVERS.get(pref);
-
- if (existing == null) {
- return;
- }
-
- final Iterator<PrefHandler> itor;
- PrefHandler handler;
-
- if (existing instanceof PrefHandler) {
- itor = null;
- handler = (PrefHandler) existing;
- } else {
- @SuppressWarnings("unchecked")
- final List<PrefHandler> handlerList = (List) existing;
- if (handlerList.isEmpty()) {
- return;
- }
- itor = handlerList.iterator();
- handler = itor.next();
- }
-
- do {
- callPrefHandler(handler, type, pref, boolVal, intVal, strVal);
- handler.finish();
-
- handler = itor != null && itor.hasNext() ? itor.next() : null;
- } while (handler != null);
- }
-
- public interface PrefHandler {
- void prefValue(String pref, boolean value);
- void prefValue(String pref, int value);
- void prefValue(String pref, String value);
- void finish();
- }
-
- public static abstract class PrefHandlerBase implements PrefHandler {
- @Override
- public void prefValue(String pref, boolean value) {
- throw new UnsupportedOperationException(
- "Unhandled boolean pref " + pref + "; wrong type?");
- }
-
- @Override
- public void prefValue(String pref, int value) {
- throw new UnsupportedOperationException(
- "Unhandled int pref " + pref + "; wrong type?");
- }
-
- @Override
- public void prefValue(String pref, String value) {
- throw new UnsupportedOperationException(
- "Unhandled String pref " + pref + "; wrong type?");
- }
-
- @Override
- public void finish() {
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/SysInfo.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/SysInfo.java
deleted file mode 100644
index 5c53ef465..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/SysInfo.java
+++ /dev/null
@@ -1,237 +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.os.StrictMode;
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
-import java.util.regex.Pattern;
-
-/**
- * A collection of system info values, broadly mirroring a subset of
- * nsSystemInfo. See also the constants in AppConstants, which reflect
- * much of nsIXULAppInfo.
- */
-// Normally, we'd annotate with @RobocopTarget. Since SysInfo is compiled
-// before RobocopTarget, we instead add o.m.g.SysInfo directly to the Proguard
-// configuration.
-public final class SysInfo {
- private static final String LOG_TAG = "GeckoSysInfo";
-
- // Number of bytes of /proc/meminfo to read in one go.
- private static final int MEMINFO_BUFFER_SIZE_BYTES = 256;
-
- // We don't mind an instant of possible duplicate work, we only wish to
- // avoid inconsistency, so we don't bother with synchronization for
- // these.
- private static volatile int cpuCount = -1;
-
- private static volatile int totalRAM = -1;
-
- /**
- * Get the number of cores on the device.
- *
- * We can't use a nice tidy API call, because they're all wrong:
- *
- * <http://stackoverflow.com/questions/7962155/how-can-you-detect-a-dual-core-
- * cpu-on-an-android-device-from-code>
- *
- * This method is based on that code.
- *
- * @return the number of CPU cores, or 1 if the number could not be
- * determined.
- */
- public static int getCPUCount() {
- if (cpuCount > 0) {
- return cpuCount;
- }
-
- // Avoid a strict mode warning.
- StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
- try {
- return readCPUCount();
- } finally {
- StrictMode.setThreadPolicy(savedPolicy);
- }
- }
-
- private static int readCPUCount() {
- class CpuFilter implements FileFilter {
- @Override
- public boolean accept(File pathname) {
- return Pattern.matches("cpu[0-9]+", pathname.getName());
- }
- }
- try {
- final File dir = new File("/sys/devices/system/cpu/");
- return cpuCount = dir.listFiles(new CpuFilter()).length;
- } catch (Exception e) {
- Log.w(LOG_TAG, "Assuming 1 CPU; got exception.", e);
- return cpuCount = 1;
- }
- }
-
- /**
- * Helper functions used to extract key/value data from /proc/meminfo
- * Pulled from:
- * http://androidxref.com/4.2_r1/xref/frameworks/base/core/java/com/android/internal/util/MemInfoReader.java
- */
- private static boolean matchMemText(byte[] buffer, int index, int bufferLength, byte[] text) {
- final int N = text.length;
- if ((index + N) >= bufferLength) {
- return false;
- }
- for (int i = 0; i < N; i++) {
- if (buffer[index + i] != text[i]) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Parses a line like:
- *
- * MemTotal: 1605324 kB
- *
- * into 1605324.
- *
- * @return the first uninterrupted sequence of digits following the
- * specified index, parsed as an integer value in KB.
- */
- private static int extractMemValue(byte[] buffer, int offset, int length) {
- if (offset >= length) {
- return 0;
- }
-
- while (offset < length && buffer[offset] != '\n') {
- if (buffer[offset] >= '0' && buffer[offset] <= '9') {
- int start = offset++;
- while (offset < length &&
- buffer[offset] >= '0' &&
- buffer[offset] <= '9') {
- ++offset;
- }
- return Integer.parseInt(new String(buffer, start, offset - start), 10);
- }
- ++offset;
- }
- return 0;
- }
-
- /**
- * Fetch the total memory of the device in MB by parsing /proc/meminfo.
- *
- * Of course, Android doesn't have a neat and tidy way to find total
- * RAM, so we do it by parsing /proc/meminfo.
- *
- * @return 0 if a problem occurred, or memory size in MB.
- */
- public static int getMemSize() {
- if (totalRAM >= 0) {
- return totalRAM;
- }
-
- // This is the string "MemTotal" that we're searching for in the buffer.
- final byte[] MEMTOTAL = {'M', 'e', 'm', 'T', 'o', 't', 'a', 'l'};
-
- // `/proc/meminfo` is not a real file and thus safe to read on the main thread.
- final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
- try {
- final byte[] buffer = new byte[MEMINFO_BUFFER_SIZE_BYTES];
- final FileInputStream is = new FileInputStream("/proc/meminfo");
- try {
- final int length = is.read(buffer);
-
- for (int i = 0; i < length; i++) {
- if (matchMemText(buffer, i, length, MEMTOTAL)) {
- i += 8;
- totalRAM = extractMemValue(buffer, i, length) / 1024;
- Log.d(LOG_TAG, "System memory: " + totalRAM + "MB.");
- return totalRAM;
- }
- }
- } finally {
- is.close();
- }
-
- Log.w(LOG_TAG, "Did not find MemTotal line in /proc/meminfo.");
- return totalRAM = 0;
- } catch (FileNotFoundException f) {
- return totalRAM = 0;
- } catch (IOException e) {
- return totalRAM = 0;
- } finally {
- StrictMode.setThreadPolicy(savedPolicy);
- }
- }
-
- /**
- * @return the SDK version supported by this device, such as '16'.
- */
- public static int getVersion() {
- return android.os.Build.VERSION.SDK_INT;
- }
-
- /**
- * @return the release version string, such as "4.1.2".
- */
- public static String getReleaseVersion() {
- return android.os.Build.VERSION.RELEASE;
- }
-
- /**
- * @return the kernel version string, such as "3.4.10-geb45596".
- */
- public static String getKernelVersion() {
- return System.getProperty("os.version", "");
- }
-
- /**
- * @return the device manufacturer, such as "HTC".
- */
- public static String getManufacturer() {
- return android.os.Build.MANUFACTURER;
- }
-
- /**
- * @return the device name, such as "HTC One".
- */
- public static String getDevice() {
- // No, not android.os.Build.DEVICE.
- return android.os.Build.MODEL;
- }
-
- /**
- * @return the Android "hardware" identifier, such as "m7".
- */
- public static String getHardware() {
- return android.os.Build.HARDWARE;
- }
-
- /**
- * @return the system OS name. Hardcoded to "Android".
- */
- public static String getName() {
- // We deliberately differ from PR_SI_SYSNAME, which is "Linux".
- return "Android";
- }
-
- /**
- * @return the Android architecture string, including ABI.
- */
- public static String getArchABI() {
- // Android likes to include the ABI, too ("armeabiv7"), so we
- // differ to add value.
- return android.os.Build.CPU_ABI;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/TouchEventInterceptor.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/TouchEventInterceptor.java
deleted file mode 100644
index 41a71dfa5..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/TouchEventInterceptor.java
+++ /dev/null
@@ -1,14 +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.view.MotionEvent;
-import android.view.View;
-
-public interface TouchEventInterceptor extends View.OnTouchListener {
- /** Override this method for a chance to consume events before the view or its children */
- public boolean onInterceptTouchEvent(View view, MotionEvent event);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/JNITarget.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/JNITarget.java
deleted file mode 100644
index d6140a1ff..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/JNITarget.java
+++ /dev/null
@@ -1,14 +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.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
-@Retention(RetentionPolicy.CLASS)
-public @interface JNITarget {}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/ReflectionTarget.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/ReflectionTarget.java
deleted file mode 100644
index e873ebeb9..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/ReflectionTarget.java
+++ /dev/null
@@ -1,18 +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.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/*
- * Used to indicate to ProGuard that this definition is accessed
- * via reflection and should not be stripped from the source.
- */
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
-@Retention(RetentionPolicy.CLASS)
-public @interface ReflectionTarget {}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/RobocopTarget.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/RobocopTarget.java
deleted file mode 100644
index e15130674..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/RobocopTarget.java
+++ /dev/null
@@ -1,15 +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.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
-@Retention(RetentionPolicy.CLASS)
-public @interface RobocopTarget {}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WebRTCJNITarget.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WebRTCJNITarget.java
deleted file mode 100644
index f58dea148..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WebRTCJNITarget.java
+++ /dev/null
@@ -1,14 +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.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Target({ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD})
-@Retention(RetentionPolicy.CLASS)
-public @interface WebRTCJNITarget {}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WrapForJNI.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WrapForJNI.java
deleted file mode 100644
index 358ed5d56..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/WrapForJNI.java
+++ /dev/null
@@ -1,51 +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.annotation;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * This annotation is used to tag methods that are to have wrapper methods generated.
- * Such methods will be protected from destruction by ProGuard, and allow us to avoid
- * writing by hand large amounts of boring boilerplate.
- */
-@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
-@Retention(RetentionPolicy.RUNTIME)
-public @interface WrapForJNI {
- /**
- * Skip this member when generating wrappers for a whole class.
- */
- boolean skip() default false;
-
- /**
- * Optional parameter specifying the name of the generated method stub. If omitted,
- * the capitalized name of the Java method will be used.
- */
- String stubName() default "";
-
- /**
- * Action to take if member access returns an exception.
- * One of "abort", "ignore", or "nsresult". "nsresult" is not supported for native
- * methods.
- */
- String exceptionMode() default "abort";
-
- /**
- * The thread that the method will be called from.
- * One of "any", "gecko", or "ui". Not supported for fields.
- */
- String calledFrom() default "any";
-
- /**
- * The thread that the method call will be dispatched to.
- * One of "current", "gecko", or "proxy". Not supported for non-native methods,
- * fields, and constructors. Only void-return methods are supported for anything other
- * than current thread.
- */
- String dispatchTo() default "current";
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BitmapUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BitmapUtils.java
deleted file mode 100644
index a4b516519..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BitmapUtils.java
+++ /dev/null
@@ -1,290 +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.gfx;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.util.Base64;
-import android.util.Log;
-
-public final class BitmapUtils {
- private static final String LOGTAG = "GeckoBitmapUtils";
-
- private BitmapUtils() {}
-
- public static Bitmap decodeByteArray(byte[] bytes) {
- return decodeByteArray(bytes, null);
- }
-
- public static Bitmap decodeByteArray(byte[] bytes, BitmapFactory.Options options) {
- return decodeByteArray(bytes, 0, bytes.length, options);
- }
-
- public static Bitmap decodeByteArray(byte[] bytes, int offset, int length) {
- return decodeByteArray(bytes, offset, length, null);
- }
-
- public static Bitmap decodeByteArray(byte[] bytes, int offset, int length, BitmapFactory.Options options) {
- if (bytes.length <= 0) {
- throw new IllegalArgumentException("bytes.length " + bytes.length
- + " must be a positive number");
- }
-
- Bitmap bitmap = null;
- try {
- bitmap = BitmapFactory.decodeByteArray(bytes, offset, length, options);
- } catch (OutOfMemoryError e) {
- Log.e(LOGTAG, "decodeByteArray(bytes.length=" + bytes.length
- + ", options= " + options + ") OOM!", e);
- return null;
- }
-
- if (bitmap == null) {
- Log.w(LOGTAG, "decodeByteArray() returning null because BitmapFactory returned null");
- return null;
- }
-
- if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
- Log.w(LOGTAG, "decodeByteArray() returning null because BitmapFactory returned "
- + "a bitmap with dimensions " + bitmap.getWidth()
- + "x" + bitmap.getHeight());
- return null;
- }
-
- return bitmap;
- }
-
- public static Bitmap decodeStream(InputStream inputStream) {
- try {
- return BitmapFactory.decodeStream(inputStream);
- } catch (OutOfMemoryError e) {
- Log.e(LOGTAG, "decodeStream() OOM!", e);
- return null;
- }
- }
-
- public static Bitmap decodeUrl(Uri uri) {
- return decodeUrl(uri.toString());
- }
-
- public static Bitmap decodeUrl(String urlString) {
- URL url;
-
- try {
- url = new URL(urlString);
- } catch (MalformedURLException e) {
- Log.w(LOGTAG, "decodeUrl: malformed URL " + urlString);
- return null;
- }
-
- return decodeUrl(url);
- }
-
- public static Bitmap decodeUrl(URL url) {
- InputStream stream = null;
-
- try {
- stream = url.openStream();
- } catch (IOException e) {
- Log.w(LOGTAG, "decodeUrl: IOException downloading " + url);
- return null;
- }
-
- if (stream == null) {
- Log.w(LOGTAG, "decodeUrl: stream not found downloading " + url);
- return null;
- }
-
- Bitmap bitmap = decodeStream(stream);
-
- try {
- stream.close();
- } catch (IOException e) {
- Log.w(LOGTAG, "decodeUrl: IOException closing stream " + url, e);
- }
-
- return bitmap;
- }
-
- public static Bitmap decodeResource(Context context, int id) {
- return decodeResource(context, id, null);
- }
-
- public static Bitmap decodeResource(Context context, int id, BitmapFactory.Options options) {
- Resources resources = context.getResources();
- try {
- return BitmapFactory.decodeResource(resources, id, options);
- } catch (OutOfMemoryError e) {
- Log.e(LOGTAG, "decodeResource() OOM! Resource id=" + id, e);
- return null;
- }
- }
-
- public static int getDominantColor(Bitmap source) {
- return getDominantColor(source, true);
- }
-
- public static int getDominantColor(Bitmap source, boolean applyThreshold) {
- if (source == null)
- return Color.argb(255, 255, 255, 255);
-
- // Keep track of how many times a hue in a given bin appears in the image.
- // Hue values range [0 .. 360), so dividing by 10, we get 36 bins.
- int[] colorBins = new int[36];
-
- // The bin with the most colors. Initialize to -1 to prevent accidentally
- // thinking the first bin holds the dominant color.
- int maxBin = -1;
-
- // Keep track of sum hue/saturation/value per hue bin, which we'll use to
- // compute an average to for the dominant color.
- float[] sumHue = new float[36];
- float[] sumSat = new float[36];
- float[] sumVal = new float[36];
- float[] hsv = new float[3];
-
- int height = source.getHeight();
- int width = source.getWidth();
- int[] pixels = new int[width * height];
- source.getPixels(pixels, 0, width, 0, 0, width, height);
- for (int row = 0; row < height; row++) {
- for (int col = 0; col < width; col++) {
- int c = pixels[col + row * width];
- // Ignore pixels with a certain transparency.
- if (Color.alpha(c) < 128)
- continue;
-
- Color.colorToHSV(c, hsv);
-
- // If a threshold is applied, ignore arbitrarily chosen values for "white" and "black".
- if (applyThreshold && (hsv[1] <= 0.35f || hsv[2] <= 0.35f))
- continue;
-
- // We compute the dominant color by putting colors in bins based on their hue.
- int bin = (int) Math.floor(hsv[0] / 10.0f);
-
- // Update the sum hue/saturation/value for this bin.
- sumHue[bin] = sumHue[bin] + hsv[0];
- sumSat[bin] = sumSat[bin] + hsv[1];
- sumVal[bin] = sumVal[bin] + hsv[2];
-
- // Increment the number of colors in this bin.
- colorBins[bin]++;
-
- // Keep track of the bin that holds the most colors.
- if (maxBin < 0 || colorBins[bin] > colorBins[maxBin])
- maxBin = bin;
- }
- }
-
- // maxBin may never get updated if the image holds only transparent and/or black/white pixels.
- if (maxBin < 0)
- return Color.argb(255, 255, 255, 255);
-
- // Return a color with the average hue/saturation/value of the bin with the most colors.
- hsv[0] = sumHue[maxBin] / colorBins[maxBin];
- hsv[1] = sumSat[maxBin] / colorBins[maxBin];
- hsv[2] = sumVal[maxBin] / colorBins[maxBin];
- return Color.HSVToColor(hsv);
- }
-
- /**
- * Decodes a bitmap from a Base64 data URI.
- *
- * @param dataURI a Base64-encoded data URI string
- * @return the decoded bitmap, or null if the data URI is invalid
- */
- public static Bitmap getBitmapFromDataURI(String dataURI) {
- if (dataURI == null) {
- return null;
- }
-
- byte[] raw = getBytesFromDataURI(dataURI);
- if (raw == null || raw.length == 0) {
- return null;
- }
-
- return decodeByteArray(raw);
- }
-
- /**
- * Return a byte[] containing the bytes in a given base64 string, or null if this is not a valid
- * base64 string.
- */
- public static byte[] getBytesFromBase64(String base64) {
- try {
- return Base64.decode(base64, Base64.DEFAULT);
- } catch (Exception e) {
- Log.e(LOGTAG, "exception decoding bitmap from data URI: " + base64, e);
- }
-
- return null;
- }
-
- public static byte[] getBytesFromDataURI(String dataURI) {
- final String base64 = dataURI.substring(dataURI.indexOf(',') + 1);
- return getBytesFromBase64(base64);
- }
-
- public static Bitmap getBitmapFromDrawable(Drawable drawable) {
- if (drawable instanceof BitmapDrawable) {
- return ((BitmapDrawable) drawable).getBitmap();
- }
-
- int width = drawable.getIntrinsicWidth();
- width = width > 0 ? width : 1;
- int height = drawable.getIntrinsicHeight();
- height = height > 0 ? height : 1;
-
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
-
- return bitmap;
- }
-
- public static int getResource(final Context context, final Uri resourceUrl) {
- final String scheme = resourceUrl.getScheme();
- if (!"drawable".equals(scheme)) {
- // Return a "not found" default icon that's easy to spot.
- return android.R.drawable.sym_def_app_icon;
- }
-
- String resource = resourceUrl.getSchemeSpecificPart();
- if (resource.startsWith("//")) {
- resource = resource.substring(2);
- }
-
- final Resources res = context.getResources();
- int id = res.getIdentifier(resource, "drawable", context.getPackageName());
- if (id != 0) {
- return id;
- }
-
- // For backwards compatibility, we also search in system resources.
- id = res.getIdentifier(resource, "drawable", "android");
- if (id != 0) {
- return id;
- }
-
- Log.w(LOGTAG, "Cannot find drawable/" + resource);
- // Return a "not found" default icon that's easy to spot.
- return android.R.drawable.sym_def_app_icon;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImage.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImage.java
deleted file mode 100644
index 4dbcf61bb..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImage.java
+++ /dev/null
@@ -1,94 +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.gfx;
-
-import org.mozilla.gecko.mozglue.DirectBufferAllocator;
-
-import android.graphics.Bitmap;
-import android.util.Log;
-
-import java.nio.ByteBuffer;
-
-/** A buffered image that simply saves a buffer of pixel data. */
-public class BufferedImage {
- private ByteBuffer mBuffer;
- private Bitmap mBitmap;
- private IntSize mSize;
- private int mFormat;
-
- private static final String LOGTAG = "GeckoBufferedImage";
-
- /** Creates an empty buffered image */
- public BufferedImage() {
- mSize = new IntSize(0, 0);
- }
-
- /** Creates a buffered image from an Android bitmap. */
- public BufferedImage(Bitmap bitmap) {
- mFormat = bitmapConfigToFormat(bitmap.getConfig());
- mSize = new IntSize(bitmap.getWidth(), bitmap.getHeight());
- mBitmap = bitmap;
- }
-
- private synchronized void freeBuffer() {
- if (mBuffer != null) {
- mBuffer = DirectBufferAllocator.free(mBuffer);
- }
- }
-
- public void destroy() {
- try {
- freeBuffer();
- } catch (Exception ex) {
- Log.e(LOGTAG, "error clearing buffer: ", ex);
- }
- }
-
- public ByteBuffer getBuffer() {
- if (mBuffer == null) {
- int bpp = bitsPerPixelForFormat(mFormat);
- mBuffer = DirectBufferAllocator.allocate(mSize.getArea() * bpp);
- mBitmap.copyPixelsToBuffer(mBuffer.asIntBuffer());
- mBitmap = null;
- }
- return mBuffer;
- }
-
- public IntSize getSize() { return mSize; }
- public int getFormat() { return mFormat; }
-
- public static final int FORMAT_INVALID = -1;
- public static final int FORMAT_ARGB32 = 0;
- public static final int FORMAT_RGB24 = 1;
- public static final int FORMAT_A8 = 2;
- public static final int FORMAT_A1 = 3;
- public static final int FORMAT_RGB16_565 = 4;
-
- private static int bitsPerPixelForFormat(int format) {
- switch (format) {
- case FORMAT_A1: return 1;
- case FORMAT_A8: return 8;
- case FORMAT_RGB16_565: return 16;
- case FORMAT_RGB24: return 24;
- case FORMAT_ARGB32: return 32;
- default:
- throw new RuntimeException("Unknown Cairo format");
- }
- }
-
- private static int bitmapConfigToFormat(Bitmap.Config config) {
- if (config == null)
- return FORMAT_ARGB32; /* Droid Pro fix. */
-
- switch (config) {
- case ALPHA_8: return FORMAT_A8;
- case ARGB_4444: throw new RuntimeException("ARGB_444 unsupported");
- case ARGB_8888: return FORMAT_ARGB32;
- case RGB_565: return FORMAT_RGB16_565;
- default: throw new RuntimeException("Unknown Skia bitmap config");
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImageGLInfo.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImageGLInfo.java
deleted file mode 100644
index 41f38e1ba..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/BufferedImageGLInfo.java
+++ /dev/null
@@ -1,35 +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.gfx;
-
-import javax.microedition.khronos.opengles.GL10;
-
-/** Information needed to render buffered bitmaps using OpenGL ES. */
-public class BufferedImageGLInfo {
- public final int internalFormat;
- public final int format;
- public final int type;
-
- public BufferedImageGLInfo(int bufferedImageFormat) {
- switch (bufferedImageFormat) {
- case BufferedImage.FORMAT_ARGB32:
- internalFormat = format = GL10.GL_RGBA; type = GL10.GL_UNSIGNED_BYTE;
- break;
- case BufferedImage.FORMAT_RGB24:
- internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_BYTE;
- break;
- case BufferedImage.FORMAT_RGB16_565:
- internalFormat = format = GL10.GL_RGB; type = GL10.GL_UNSIGNED_SHORT_5_6_5;
- break;
- case BufferedImage.FORMAT_A8:
- case BufferedImage.FORMAT_A1:
- throw new RuntimeException("BufferedImage FORMAT_A1 and FORMAT_A8 unsupported");
- default:
- throw new RuntimeException("Unknown BufferedImage format");
- }
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
deleted file mode 100644
index e299b5744..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/DynamicToolbarAnimator.java
+++ /dev/null
@@ -1,605 +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.gfx;
-
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.PrefsHelper;
-import org.mozilla.gecko.util.FloatUtils;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.graphics.PointF;
-import android.support.v4.view.ViewCompat;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.animation.LinearInterpolator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-
-public class DynamicToolbarAnimator {
- private static final String LOGTAG = "GeckoDynamicToolbarAnimator";
- private static final String PREF_SCROLL_TOOLBAR_THRESHOLD = "browser.ui.scroll-toolbar-threshold";
-
- public static enum PinReason {
- RELAYOUT,
- ACTION_MODE,
- FULL_SCREEN,
- CARET_DRAG
- }
-
- private final Set<PinReason> pinFlags = Collections.synchronizedSet(EnumSet.noneOf(PinReason.class));
-
- // The duration of the animation in ns
- private static final long ANIMATION_DURATION = 150000000;
-
- private final GeckoLayerClient mTarget;
- private final List<LayerView.DynamicToolbarListener> mListeners;
-
- /* The translation to be applied to the toolbar UI view. This is the
- * distance from the default/initial location (at the top of the screen,
- * visible to the user) to where we want it to be. This variable should
- * always be between 0 (toolbar fully visible) and the height of the toolbar
- * (toolbar fully hidden), inclusive.
- */
- private float mToolbarTranslation;
-
- /* The translation to be applied to the LayerView. This is the distance from
- * the default/initial location (just below the toolbar, with the bottom
- * extending past the bottom of the screen) to where we want it to be.
- * This variable should always be between 0 and the height of the toolbar,
- * inclusive.
- */
- private float mLayerViewTranslation;
-
- /* This stores the maximum translation that can be applied to the toolbar
- * and layerview when scrolling. This is populated with the height of the
- * toolbar. */
- private float mMaxTranslation;
-
- /* This interpolator is used for the above mentioned animation */
- private LinearInterpolator mInterpolator;
-
- /* This is the proportion of the viewport rect that needs to be travelled
- * while scrolling before the translation will start taking effect.
- */
- private float SCROLL_TOOLBAR_THRESHOLD = 0.20f;
- /* The ID of the prefs listener for the scroll-toolbar threshold */
- private final PrefsHelper.PrefHandler mPrefObserver;
-
- /* While we are resizing the viewport to account for the toolbar, the Java
- * code and painted layer metrics in the compositor have different notions
- * of the CSS viewport height. The Java value is stored in the
- * GeckoLayerClient's viewport metrics, and the Gecko one is stored here.
- * This allows us to adjust fixed-pos items correctly.
- * You must synchronize on mTarget.getLock() to read/write this. */
- private Integer mHeightDuringResize;
-
- /* This tracks if we should trigger a "snap" on the next composite. A "snap"
- * is when we simultaneously move the LayerView and change the scroll offset
- * in the compositor so that everything looks the same on the screen but
- * has really been shifted.
- * You must synchronize on |this| to read/write this. */
- private boolean mSnapRequired = false;
-
- /* The task that handles showing/hiding toolbar */
- private DynamicToolbarAnimationTask mAnimationTask;
-
- /* The start point of a drag, used for scroll-based dynamic toolbar
- * behaviour. */
- private PointF mTouchStart;
- private float mLastTouch;
-
- /* Set to true when root content is being scrolled */
- private boolean mScrollingRootContent;
-
- public DynamicToolbarAnimator(GeckoLayerClient aTarget) {
- mTarget = aTarget;
- mListeners = new ArrayList<LayerView.DynamicToolbarListener>();
-
- mInterpolator = new LinearInterpolator();
-
- // Listen to the dynamic toolbar pref
- mPrefObserver = new PrefsHelper.PrefHandlerBase() {
- @Override
- public void prefValue(String pref, int value) {
- SCROLL_TOOLBAR_THRESHOLD = value / 100.0f;
- }
- };
- PrefsHelper.addObserver(new String[] { PREF_SCROLL_TOOLBAR_THRESHOLD }, mPrefObserver);
- }
-
- public void destroy() {
- PrefsHelper.removeObserver(mPrefObserver);
- }
-
- public void addTranslationListener(LayerView.DynamicToolbarListener aListener) {
- mListeners.add(aListener);
- }
-
- public void removeTranslationListener(LayerView.DynamicToolbarListener aListener) {
- mListeners.remove(aListener);
- }
-
- private void fireListeners() {
- for (LayerView.DynamicToolbarListener listener : mListeners) {
- listener.onTranslationChanged(mToolbarTranslation, mLayerViewTranslation);
- }
- }
-
- void onPanZoomStopped() {
- for (LayerView.DynamicToolbarListener listener : mListeners) {
- listener.onPanZoomStopped();
- }
- }
-
- void onMetricsChanged(ImmutableViewportMetrics aMetrics) {
- for (LayerView.DynamicToolbarListener listener : mListeners) {
- listener.onMetricsChanged(aMetrics);
- }
- }
-
- public void setMaxTranslation(float maxTranslation) {
- ThreadUtils.assertOnUiThread();
- if (maxTranslation < 0) {
- Log.e(LOGTAG, "Got a negative max-translation value: " + maxTranslation + "; clamping to zero");
- mMaxTranslation = 0;
- } else {
- mMaxTranslation = maxTranslation;
- }
- }
-
- public float getMaxTranslation() {
- return mMaxTranslation;
- }
-
- public float getToolbarTranslation() {
- return mToolbarTranslation;
- }
-
- /**
- * If true, scroll changes will not affect translation.
- */
- public boolean isPinned() {
- return !pinFlags.isEmpty();
- }
-
- public boolean isPinnedBy(PinReason reason) {
- return pinFlags.contains(reason);
- }
-
- public void setPinned(boolean pinned, PinReason reason) {
- if (pinned) {
- pinFlags.add(reason);
- } else {
- pinFlags.remove(reason);
- }
- }
-
- public void showToolbar(boolean immediately) {
- animateToolbar(true, immediately);
- }
-
- public void hideToolbar(boolean immediately) {
- animateToolbar(false, immediately);
- }
-
- public void setScrollingRootContent(boolean isRootContent) {
- mScrollingRootContent = isRootContent;
- }
-
- private void animateToolbar(final boolean showToolbar, boolean immediately) {
- ThreadUtils.assertOnUiThread();
-
- if (mAnimationTask != null) {
- mTarget.getView().removeRenderTask(mAnimationTask);
- mAnimationTask = null;
- }
-
- float desiredTranslation = (showToolbar ? 0 : mMaxTranslation);
- Log.v(LOGTAG, "Requested " + (immediately ? "immediate " : "") + "toolbar animation to translation " + desiredTranslation);
- if (FloatUtils.fuzzyEquals(mToolbarTranslation, desiredTranslation)) {
- // If we're already pretty much in the desired position, don't bother
- // with a full animation; do an immediate jump
- immediately = true;
- Log.v(LOGTAG, "Changing animation to immediate jump");
- }
-
- if (showToolbar && immediately) {
- // Special case for showing the toolbar immediately: some of the call
- // sites expect this to happen synchronously, so let's do that. This
- // is safe because if we are showing the toolbar from a hidden state
- // there is no chance of showing garbage
- mToolbarTranslation = desiredTranslation;
- fireListeners();
- // And then proceed with the normal flow (some of which will be
- // a no-op now)...
- }
-
- if (!showToolbar) {
- // If we are hiding the toolbar, we need to move the LayerView first,
- // so that we don't end up showing garbage under the toolbar when
- // it is hidden. In the case that we are showing the toolbar, we
- // move the LayerView after the toolbar is shown - the
- // DynamicToolbarAnimationTask calls that upon completion.
- shiftLayerView(desiredTranslation);
- }
-
- mAnimationTask = new DynamicToolbarAnimationTask(desiredTranslation, immediately, showToolbar);
- mTarget.getView().postRenderTask(mAnimationTask);
- }
-
- private synchronized void shiftLayerView(float desiredTranslation) {
- float layerViewTranslationNeeded = desiredTranslation - mLayerViewTranslation;
- mLayerViewTranslation = desiredTranslation;
- synchronized (mTarget.getLock()) {
- if (layerViewTranslationNeeded == 0 && isResizing()) {
- // We're already in the middle of a snap, so this new call is
- // redundant as it's snapping to the same place. Ignore it.
- return;
- }
- mHeightDuringResize = new Integer(mTarget.getViewportMetrics().viewportRectHeight);
- mSnapRequired = mTarget.setViewportSize(
- mTarget.getView().getWidth(),
- mTarget.getView().getHeight() - Math.round(mMaxTranslation - mLayerViewTranslation),
- new PointF(0, -layerViewTranslationNeeded));
- if (!mSnapRequired) {
- mHeightDuringResize = null;
- ThreadUtils.postToUiThread(new Runnable() {
- // Post to run it outside of the synchronize blocks. The
- // delay shouldn't hurt.
- @Override
- public void run() {
- fireListeners();
- }
- });
- }
- // Request a composite, which will trigger the snap.
- mTarget.getView().requestRender();
- }
- }
-
- IntSize getViewportSize() {
- ThreadUtils.assertOnUiThread();
-
- int viewWidth = mTarget.getView().getWidth();
- int viewHeight = mTarget.getView().getHeight();
- float toolbarTranslation = mToolbarTranslation;
- if (mAnimationTask != null) {
- // If we have an animation going, mToolbarTranslation may be in flux
- // and we should use the final value it will settle on.
- toolbarTranslation = mAnimationTask.getFinalToolbarTranslation();
- }
- int viewHeightVisible = viewHeight - Math.round(mMaxTranslation - toolbarTranslation);
- return new IntSize(viewWidth, viewHeightVisible);
- }
-
- boolean isResizing() {
- return mHeightDuringResize != null;
- }
-
- private final Runnable mSnapRunnable = new Runnable() {
- private int mFrame = 0;
-
- @Override
- public final void run() {
- // It takes 2 frames for the view translation to take effect, at
- // least on a Nexus 4 device running Android 4.2.2. So we wait for
- // two frames before doing the notifyAll(), otherwise we get a
- // short user-visible glitch.
- // TODO: find a better way to do this, if possible.
- if (mFrame == 1) {
- synchronized (this) {
- this.notifyAll();
- }
- mFrame = 0;
- return;
- }
-
- if (mFrame == 0) {
- fireListeners();
- }
-
- ViewCompat.postOnAnimation(mTarget.getView(), this);
- mFrame++;
- }
- };
-
- void scrollChangeResizeCompleted() {
- synchronized (mTarget.getLock()) {
- Log.v(LOGTAG, "Scrollchange resize completed");
- mHeightDuringResize = null;
- }
- }
-
- /**
- * "Shrinks" the absolute value of aValue by moving it closer to zero by
- * aShrinkAmount, but prevents it from crossing over zero. If aShrinkAmount
- * is negative it is ignored.
- * @return The shrunken value.
- */
- private static float shrinkAbs(float aValue, float aShrinkAmount) {
- if (aShrinkAmount <= 0) {
- return aValue;
- }
- float shrinkBy = Math.min(Math.abs(aValue), aShrinkAmount);
- return (aValue < 0 ? aValue + shrinkBy : aValue - shrinkBy);
- }
-
- /**
- * This function takes in a scroll amount and decides how much of that
- * should be used up to translate things on screen because of the dynamic
- * toolbar behaviour. It returns the maximum amount that could be used
- * for translation purposes; the rest must be used for scrolling.
- */
- private float decideTranslation(float aDelta,
- ImmutableViewportMetrics aMetrics,
- float aTouchTravelDistance) {
-
- float exposeThreshold = aMetrics.getHeight() * SCROLL_TOOLBAR_THRESHOLD;
- float translation = aDelta;
-
- if (translation < 0) { // finger moving upwards
- translation = shrinkAbs(translation, aMetrics.getOverscroll().top);
-
- // If the toolbar is in a state between fully hidden and fully shown
- // (i.e. the user is actively translating it), then we want the
- // translation to take effect right away. Or if the user has moved
- // their finger past the required threshold (and is not trying to
- // scroll past the bottom of the page) then also we want the touch
- // to cause translation. If the toolbar is fully visible, we only
- // want the toolbar to hide if the user is scrolling the root content.
- boolean inBetween = (mToolbarTranslation != 0 && mToolbarTranslation != mMaxTranslation);
- boolean reachedThreshold = -aTouchTravelDistance >= exposeThreshold;
- boolean atBottomOfPage = aMetrics.viewportRectBottom() >= aMetrics.pageRectBottom;
- if (inBetween || (mScrollingRootContent && reachedThreshold && !atBottomOfPage)) {
- return translation;
- }
- } else { // finger moving downwards
- translation = shrinkAbs(translation, aMetrics.getOverscroll().bottom);
-
- // Ditto above comment, but in this case if they reached the top and
- // the toolbar is not shown, then we do want to allow translation
- // right away.
- boolean inBetween = (mToolbarTranslation != 0 && mToolbarTranslation != mMaxTranslation);
- boolean reachedThreshold = aTouchTravelDistance >= exposeThreshold;
- boolean atTopOfPage = aMetrics.viewportRectTop <= aMetrics.pageRectTop;
- boolean isToolbarTranslated = (mToolbarTranslation != 0);
- if (inBetween || reachedThreshold || (atTopOfPage && isToolbarTranslated)) {
- return translation;
- }
- }
-
- return 0;
- }
-
- // Timestamp of the start of the touch event used to calculate toolbar velocity
- private long mLastEventTime;
- // Current velocity of the toolbar. Used to populate the velocity queue in C++APZ.
- private float mVelocity;
-
- boolean onInterceptTouchEvent(MotionEvent event) {
- if (isPinned()) {
- return false;
- }
-
- // Animations should never co-exist with the user touching the screen.
- if (mAnimationTask != null) {
- mTarget.getView().removeRenderTask(mAnimationTask);
- mAnimationTask = null;
- }
-
- // we only care about single-finger drags here; any other kind of event
- // should reset and cause us to start over.
- if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE ||
- event.getActionMasked() != MotionEvent.ACTION_MOVE ||
- event.getPointerCount() != 1)
- {
- if (mTouchStart != null) {
- Log.v(LOGTAG, "Resetting touch sequence due to non-move");
- mTouchStart = null;
- mVelocity = 0.0f;
- }
-
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- // We need to do this even if the toolbar is already fully
- // visible or fully hidden, because this is what triggers the
- // viewport resize in content and updates the viewport metrics.
- boolean toolbarMostlyVisible = mToolbarTranslation < (mMaxTranslation / 2);
- Log.v(LOGTAG, "All fingers lifted, completing " + (toolbarMostlyVisible ? "show" : "hide"));
- animateToolbar(toolbarMostlyVisible, false);
- }
- return false;
- }
-
- if (mTouchStart != null) {
- float prevDir = mLastTouch - mTouchStart.y;
- float newDir = event.getRawY() - mLastTouch;
- if (prevDir != 0 && newDir != 0 && ((prevDir < 0) != (newDir < 0))) {
- // If the direction of movement changed, reset the travel
- // distance properties.
- mTouchStart = null;
- mVelocity = 0.0f;
- }
- }
-
- if (mTouchStart == null) {
- mTouchStart = new PointF(event.getRawX(), event.getRawY());
- mLastTouch = event.getRawY();
- mLastEventTime = event.getEventTime();
- return false;
- }
-
- float deltaY = event.getRawY() - mLastTouch;
- long currentTime = event.getEventTime();
- float deltaTime = (float)(currentTime - mLastEventTime);
- mLastEventTime = currentTime;
- if (deltaTime > 0.0f) {
- mVelocity = -deltaY / deltaTime;
- } else {
- mVelocity = 0.0f;
- }
- mLastTouch = event.getRawY();
- float travelDistance = event.getRawY() - mTouchStart.y;
-
- ImmutableViewportMetrics metrics = mTarget.getViewportMetrics();
-
- if (metrics.getPageHeight() <= mTarget.getView().getHeight() &&
- mToolbarTranslation == 0) {
- // If the page is short and the toolbar is already visible, don't
- // allow translating it out of view.
- return false;
- }
-
- float translation = decideTranslation(deltaY, metrics, travelDistance);
-
- float oldToolbarTranslation = mToolbarTranslation;
- float oldLayerViewTranslation = mLayerViewTranslation;
- mToolbarTranslation = FloatUtils.clamp(mToolbarTranslation - translation, 0, mMaxTranslation);
- mLayerViewTranslation = FloatUtils.clamp(mLayerViewTranslation - translation, 0, mMaxTranslation);
-
- if (oldToolbarTranslation == mToolbarTranslation &&
- oldLayerViewTranslation == mLayerViewTranslation) {
- return false;
- }
-
- if (mToolbarTranslation == mMaxTranslation) {
- Log.v(LOGTAG, "Toolbar at maximum translation, calling shiftLayerView(" + mMaxTranslation + ")");
- shiftLayerView(mMaxTranslation);
- } else if (mToolbarTranslation == 0) {
- Log.v(LOGTAG, "Toolbar at minimum translation, calling shiftLayerView(0)");
- shiftLayerView(0);
- }
-
- fireListeners();
- mTarget.getView().requestRender();
- return true;
- }
-
- // Get the current velocity of the toolbar.
- float getVelocity() {
- return mVelocity;
- }
-
- public PointF getVisibleEndOfLayerView() {
- return new PointF(mTarget.getView().getWidth(),
- mTarget.getView().getHeight() - mMaxTranslation + mLayerViewTranslation);
- }
-
- private float bottomOfCssViewport(ImmutableViewportMetrics aMetrics) {
- return (isResizing() ? mHeightDuringResize : aMetrics.getHeight())
- + mMaxTranslation - mLayerViewTranslation;
- }
-
- private synchronized boolean getAndClearSnapRequired() {
- boolean snapRequired = mSnapRequired;
- mSnapRequired = false;
- return snapRequired;
- }
-
- void populateViewTransform(ViewTransform aTransform, ImmutableViewportMetrics aMetrics) {
- if (getAndClearSnapRequired()) {
- synchronized (mSnapRunnable) {
- ViewCompat.postOnAnimation(mTarget.getView(), mSnapRunnable);
- try {
- // hold the in-progress composite until the views have been
- // translated because otherwise there is a visible glitch.
- // don't hold for more than 100ms just in case.
- mSnapRunnable.wait(100);
- } catch (InterruptedException ie) {
- }
- }
- }
-
- aTransform.x = aMetrics.viewportRectLeft;
- aTransform.y = aMetrics.viewportRectTop;
- aTransform.width = aMetrics.viewportRectWidth;
- aTransform.height = aMetrics.viewportRectHeight;
- aTransform.scale = aMetrics.zoomFactor;
-
- aTransform.fixedLayerMarginTop = mLayerViewTranslation - mToolbarTranslation;
- float bottomOfScreen = mTarget.getView().getHeight();
- // We want to move a fixed item from "bottomOfCssViewport" to
- // "bottomOfScreen". But also the bottom margin > 0 means that bottom
- // fixed-pos items will move upwards.
- aTransform.fixedLayerMarginBottom = bottomOfCssViewport(aMetrics) - bottomOfScreen;
- //Log.v(LOGTAG, "ViewTransform is x=" + aTransform.x + " y=" + aTransform.y
- // + " z=" + aTransform.scale + " t=" + aTransform.fixedLayerMarginTop
- // + " b=" + aTransform.fixedLayerMarginBottom);
- }
-
- class DynamicToolbarAnimationTask extends RenderTask {
- private final float mStartTranslation;
- private final float mEndTranslation;
- private final boolean mImmediate;
- private final boolean mShiftLayerView;
- private boolean mContinueAnimation;
-
- public DynamicToolbarAnimationTask(float aTranslation, boolean aImmediate, boolean aShiftLayerView) {
- super(false);
- mContinueAnimation = true;
- mStartTranslation = mToolbarTranslation;
- mEndTranslation = aTranslation;
- mImmediate = aImmediate;
- mShiftLayerView = aShiftLayerView;
- }
-
- float getFinalToolbarTranslation() {
- return mEndTranslation;
- }
-
- @Override
- public boolean internalRun(long timeDelta, long currentFrameStartTime) {
- if (!mContinueAnimation) {
- return false;
- }
-
- // Calculate the progress (between 0 and 1)
- final float progress = mImmediate
- ? 1.0f
- : mInterpolator.getInterpolation(
- Math.min(1.0f, (System.nanoTime() - getStartTime())
- / (float)ANIMATION_DURATION));
-
- // This runs on the compositor thread, so we need to post the
- // actual work to the UI thread.
- ThreadUtils.assertNotOnUiThread();
-
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- // Move the toolbar as per the animation
- mToolbarTranslation = FloatUtils.interpolate(mStartTranslation, mEndTranslation, progress);
- fireListeners();
-
- if (mShiftLayerView && progress >= 1.0f) {
- shiftLayerView(mEndTranslation);
- }
- }
- });
-
- mTarget.getView().requestRender();
- if (progress >= 1.0f) {
- mContinueAnimation = false;
- }
- return mContinueAnimation;
- }
- }
-
- class SnapMetrics {
- public final int viewportWidth;
- public final int viewportHeight;
- public final float scrollChangeY;
-
- SnapMetrics(ImmutableViewportMetrics aMetrics, float aScrollChange) {
- viewportWidth = aMetrics.viewportRectWidth;
- viewportHeight = aMetrics.viewportRectHeight;
- scrollChangeY = aScrollChange;
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FloatSize.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FloatSize.java
deleted file mode 100644
index 4b495ab77..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FloatSize.java
+++ /dev/null
@@ -1,54 +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.gfx;
-
-import org.mozilla.gecko.util.FloatUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-public class FloatSize {
- public final float width, height;
-
- public FloatSize(FloatSize size) { width = size.width; height = size.height; }
- public FloatSize(IntSize size) { width = size.width; height = size.height; }
- public FloatSize(float aWidth, float aHeight) { width = aWidth; height = aHeight; }
-
- public FloatSize(JSONObject json) {
- try {
- width = (float)json.getDouble("width");
- height = (float)json.getDouble("height");
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public String toString() { return "(" + width + "," + height + ")"; }
-
- public boolean isPositive() {
- return (width > 0 && height > 0);
- }
-
- public boolean fuzzyEquals(FloatSize size) {
- return (FloatUtils.fuzzyEquals(size.width, width) &&
- FloatUtils.fuzzyEquals(size.height, height));
- }
-
- public FloatSize scale(float factor) {
- return new FloatSize(width * factor, height * factor);
- }
-
- /*
- * Returns the size that represents a linear transition between this size and `to` at time `t`,
- * which is on the scale [0, 1).
- */
- public FloatSize interpolate(FloatSize to, float t) {
- return new FloatSize(FloatUtils.interpolate(width, to.width, t),
- FloatUtils.interpolate(height, to.height, t));
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FullScreenState.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FullScreenState.java
deleted file mode 100644
index 9574bbe0e..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FullScreenState.java
+++ /dev/null
@@ -1,12 +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.gfx;
-
-public enum FullScreenState {
- NONE,
- ROOT_ELEMENT,
- NON_ROOT_ELEMENT
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
deleted file mode 100644
index d504fe13e..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ /dev/null
@@ -1,694 +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.gfx;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.gfx.LayerView.DrawListener;
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.util.FloatUtils;
-import org.mozilla.gecko.AppConstants;
-
-import android.content.Context;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.os.SystemClock;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.List;
-
-class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
-{
- private static final String LOGTAG = "GeckoLayerClient";
- private static int sPaintSyncId = 1;
-
- private LayerRenderer mLayerRenderer;
- private boolean mLayerRendererInitialized;
-
- private final Context mContext;
- private IntSize mScreenSize;
- private IntSize mWindowSize;
-
- /*
- * The viewport metrics being used to draw the current frame. This is only
- * accessed by the compositor thread, and so needs no synchronisation.
- */
- private ImmutableViewportMetrics mFrameMetrics;
-
- private final List<DrawListener> mDrawListeners;
-
- /* Used as temporaries by syncViewportInfo */
- private final ViewTransform mCurrentViewTransform;
-
- private boolean mForceRedraw;
-
- /* The current viewport metrics.
- * This is volatile so that we can read and write to it from different threads.
- * We avoid synchronization to make getting the viewport metrics from
- * the compositor as cheap as possible. The viewport is immutable so
- * we don't need to worry about anyone mutating it while we're reading from it.
- * Specifically:
- * 1) reading mViewportMetrics from any thread is fine without synchronization
- * 2) writing to mViewportMetrics requires synchronizing on the layer controller object
- * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in
- * case 1 above) you should always first grab a local copy of the reference, and then use
- * that because mViewportMetrics might get reassigned in between reading the different
- * fields. */
- private volatile ImmutableViewportMetrics mViewportMetrics;
-
- private volatile boolean mGeckoIsReady;
-
- /* package */ final PanZoomController mPanZoomController;
- private final DynamicToolbarAnimator mToolbarAnimator;
- /* package */ final LayerView mView;
-
- /* This flag is true from the time that browser.js detects a first-paint is about to start,
- * to the time that we receive the first-paint composite notification from the compositor.
- * Note that there is a small race condition with this; if there are two paints that both
- * have the first-paint flag set, and the second paint happens concurrently with the
- * composite for the first paint, then this flag may be set to true prematurely. Fixing this
- * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751
- */
- private volatile boolean mContentDocumentIsDisplayed;
-
- private SynthesizedEventState mPointerState;
-
- @WrapForJNI(stubName = "ClearColor")
- private volatile int mClearColor = Color.WHITE;
-
- public GeckoLayerClient(Context context, LayerView view, EventDispatcher eventDispatcher) {
- // we can fill these in with dummy values because they are always written
- // to before being read
- mContext = context;
- mScreenSize = new IntSize(0, 0);
- mWindowSize = new IntSize(0, 0);
- mCurrentViewTransform = new ViewTransform(0, 0, 1);
-
- mForceRedraw = true;
- DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
- mViewportMetrics = new ImmutableViewportMetrics(displayMetrics)
- .setViewportSize(view.getWidth(), view.getHeight());
-
- mFrameMetrics = mViewportMetrics;
-
- mDrawListeners = new ArrayList<DrawListener>();
- mToolbarAnimator = new DynamicToolbarAnimator(this);
- mPanZoomController = PanZoomController.Factory.create(this, view, eventDispatcher);
- mView = view;
- mView.setListener(this);
- mContentDocumentIsDisplayed = true;
- }
-
- public void setOverscrollHandler(final Overscroll listener) {
- mPanZoomController.setOverscrollHandler(listener);
- }
-
- public void setGeckoReady(boolean ready) {
- mGeckoIsReady = ready;
- }
-
- @Override // PanZoomTarget
- public boolean isGeckoReady() {
- return mGeckoIsReady;
- }
-
- /** Attaches to root layer so that Gecko appears. */
- @WrapForJNI(calledFrom = "gecko")
- private void onGeckoReady() {
- mGeckoIsReady = true;
-
- mLayerRenderer = mView.getRenderer();
-
- sendResizeEventIfNecessary(true, null);
-
- // Gecko being ready is one of the two conditions (along with having an available
- // surface) that cause us to create the compositor. So here, now that we know gecko
- // is ready, call updateCompositor() to see if we can actually do the creation.
- // This needs to run on the UI thread so that the surface validity can't change on
- // us while we're in the middle of creating the compositor.
- mView.post(new Runnable() {
- @Override
- public void run() {
- mPanZoomController.attach();
- mView.updateCompositor();
- }
- });
- }
-
- public void destroy() {
- mPanZoomController.destroy();
- mToolbarAnimator.destroy();
- mDrawListeners.clear();
- mGeckoIsReady = false;
- }
-
- public LayerView getView() {
- return mView;
- }
-
- public FloatSize getViewportSize() {
- return mViewportMetrics.getSize();
- }
-
- /**
- * The view calls this function to indicate that the viewport changed size. It must hold the
- * monitor while calling it.
- *
- * TODO: Refactor this to use an interface. Expose that interface only to the view and not
- * to the layer client. That way, the layer client won't be tempted to call this, which might
- * result in an infinite loop.
- */
- boolean setViewportSize(int width, int height, PointF scrollChange) {
- if (mViewportMetrics.viewportRectWidth == width &&
- mViewportMetrics.viewportRectHeight == height &&
- (scrollChange == null || (scrollChange.x == 0 && scrollChange.y == 0))) {
- return false;
- }
- mViewportMetrics = mViewportMetrics.setViewportSize(width, height);
- if (scrollChange != null) {
- mViewportMetrics = mPanZoomController.adjustScrollForSurfaceShift(mViewportMetrics, scrollChange);
- }
-
- if (mGeckoIsReady) {
- // here we send gecko a resize message. The code in browser.js is responsible for
- // picking up on that resize event, modifying the viewport as necessary, and informing
- // us of the new viewport.
- sendResizeEventIfNecessary(true, scrollChange);
-
- // the following call also sends gecko a message, which will be processed after the resize
- // message above has updated the viewport. this message ensures that if we have just put
- // focus in a text field, we scroll the content so that the text field is in view.
- GeckoAppShell.viewSizeChanged();
- }
- return true;
- }
-
- PanZoomController getPanZoomController() {
- return mPanZoomController;
- }
-
- DynamicToolbarAnimator getDynamicToolbarAnimator() {
- return mToolbarAnimator;
- }
-
- /* Informs Gecko that the screen size has changed. */
- private void sendResizeEventIfNecessary(boolean force, PointF scrollChange) {
- DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
-
- IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
- IntSize newWindowSize = new IntSize(mViewportMetrics.viewportRectWidth,
- mViewportMetrics.viewportRectHeight);
-
- boolean screenSizeChanged = !mScreenSize.equals(newScreenSize);
- boolean windowSizeChanged = !mWindowSize.equals(newWindowSize);
-
- if (!force && !screenSizeChanged && !windowSizeChanged) {
- return;
- }
-
- mScreenSize = newScreenSize;
- mWindowSize = newWindowSize;
-
- if (screenSizeChanged) {
- Log.d(LOGTAG, "Screen-size changed to " + mScreenSize);
- }
-
- if (windowSizeChanged) {
- Log.d(LOGTAG, "Window-size changed to " + mWindowSize);
- }
-
- if (mView != null) {
- mView.notifySizeChanged(mWindowSize.width, mWindowSize.height,
- mScreenSize.width, mScreenSize.height);
- }
-
- String json = "";
- try {
- if (scrollChange != null) {
- int id = ++sPaintSyncId;
- if (id == 0) {
- // never use 0 as that is the default value for "this is not
- // a special transaction"
- id = ++sPaintSyncId;
- }
- JSONObject jsonObj = new JSONObject();
- jsonObj.put("x", scrollChange.x / mViewportMetrics.zoomFactor);
- jsonObj.put("y", scrollChange.y / mViewportMetrics.zoomFactor);
- jsonObj.put("id", id);
- json = jsonObj.toString();
- }
- } catch (Exception e) {
- Log.e(LOGTAG, "Unable to convert point to JSON", e);
- }
- GeckoAppShell.notifyObservers("Window:Resize", json);
- }
-
- /**
- * The different types of Viewport messages handled. All viewport events
- * expect a display-port to be returned, but can handle one not being
- * returned.
- */
- private enum ViewportMessageType {
- UPDATE, // The viewport has changed and should be entirely updated
- PAGE_SIZE // The viewport's page-size has changed
- }
-
- @WrapForJNI(calledFrom = "gecko")
- void contentDocumentChanged() {
- mContentDocumentIsDisplayed = false;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- boolean isContentDocumentDisplayed() {
- return mContentDocumentIsDisplayed;
- }
-
- /** The compositor invokes this function just before compositing a frame where the document
- * is different from the document composited on the last frame. In these cases, the viewport
- * information we have in Java is no longer valid and needs to be replaced with the new
- * viewport information provided.
- */
- @WrapForJNI
- public void setFirstPaintViewport(float offsetX, float offsetY, float zoom,
- float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
- synchronized (getLock()) {
- ImmutableViewportMetrics currentMetrics = getViewportMetrics();
-
- RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
- RectF pageRect = RectUtils.scaleAndRound(cssPageRect, zoom);
-
- final ImmutableViewportMetrics newMetrics = currentMetrics
- .setViewportOrigin(offsetX, offsetY)
- .setZoomFactor(zoom)
- .setPageRect(pageRect, cssPageRect);
- // Since we have switched to displaying a different document, we need to update any
- // viewport-related state we have lying around (i.e. mViewportMetrics).
- // Usually this information is updated via handleViewportMessage
- // while we remain on the same document.
- setViewportMetrics(newMetrics, true);
-
- // Indicate that the document is about to be composited so the
- // LayerView background can be removed.
- if (mView.getPaintState() == LayerView.PAINT_START) {
- mView.setPaintState(LayerView.PAINT_BEFORE_FIRST);
- }
- }
-
- mContentDocumentIsDisplayed = true;
- }
-
- /** The compositor invokes this function on every frame to figure out what part of the
- * page to display, and to inform Java of the current display port. Since it is called
- * on every frame, it needs to be ultra-fast.
- * It avoids taking any locks or allocating any objects. We keep around a
- * mCurrentViewTransform so we don't need to allocate a new ViewTransform
- * every time we're called. NOTE: we might be able to return a ImmutableViewportMetrics
- * which would avoid the copy into mCurrentViewTransform.
- */
- private ViewTransform syncViewportInfo(int x, int y, int width, int height, float resolution, boolean layersUpdated,
- int paintSyncId) {
- // getViewportMetrics is thread safe so we don't need to synchronize.
- // We save the viewport metrics here, so we later use it later in
- // createFrame (which will be called by nsWindow::DrawWindowUnderlay on
- // the native side, by the compositor). The viewport
- // metrics can change between here and there, as it's accessed outside
- // of the compositor thread.
- mFrameMetrics = getViewportMetrics();
-
- if (paintSyncId == sPaintSyncId) {
- mToolbarAnimator.scrollChangeResizeCompleted();
- }
- mToolbarAnimator.populateViewTransform(mCurrentViewTransform, mFrameMetrics);
-
- if (layersUpdated) {
- for (DrawListener listener : mDrawListeners) {
- listener.drawFinished();
- }
- }
-
- return mCurrentViewTransform;
- }
-
- @WrapForJNI
- public ViewTransform syncFrameMetrics(float scrollX, float scrollY, float zoom,
- float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom,
- int dpX, int dpY, int dpWidth, int dpHeight, float paintedResolution,
- boolean layersUpdated, int paintSyncId)
- {
- // TODO: optimize this so it doesn't create so much garbage - it's a
- // hot path
- RectF cssPageRect = new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom);
- synchronized (getLock()) {
- mViewportMetrics = mViewportMetrics.setViewportOrigin(scrollX, scrollY)
- .setZoomFactor(zoom)
- .setPageRect(RectUtils.scale(cssPageRect, zoom), cssPageRect);
- }
- return syncViewportInfo(dpX, dpY, dpWidth, dpHeight, paintedResolution,
- layersUpdated, paintSyncId);
- }
-
- class PointerInfo {
- // We reserve one pointer ID for the mouse, so that tests don't have
- // to worry about tracking pointer IDs if they just want to test mouse
- // event synthesization. If somebody tries to use this ID for a
- // synthesized touch event we'll throw an exception.
- public static final int RESERVED_MOUSE_POINTER_ID = 100000;
-
- public int pointerId;
- public int source;
- public int screenX;
- public int screenY;
- public double pressure;
- public int orientation;
-
- public MotionEvent.PointerCoords getCoords() {
- MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
- coords.orientation = orientation;
- coords.pressure = (float)pressure;
- coords.x = screenX;
- coords.y = screenY;
- return coords;
- }
- }
-
- class SynthesizedEventState {
- public final ArrayList<PointerInfo> pointers;
- public long downTime;
-
- SynthesizedEventState() {
- pointers = new ArrayList<PointerInfo>();
- }
-
- int getPointerIndex(int pointerId) {
- for (int i = 0; i < pointers.size(); i++) {
- if (pointers.get(i).pointerId == pointerId) {
- return i;
- }
- }
- return -1;
- }
-
- int addPointer(int pointerId, int source) {
- PointerInfo info = new PointerInfo();
- info.pointerId = pointerId;
- info.source = source;
- pointers.add(info);
- return pointers.size() - 1;
- }
-
- int getPointerCount(int source) {
- int count = 0;
- for (int i = 0; i < pointers.size(); i++) {
- if (pointers.get(i).source == source) {
- count++;
- }
- }
- return count;
- }
-
- MotionEvent.PointerProperties[] getPointerProperties(int source) {
- MotionEvent.PointerProperties[] props = new MotionEvent.PointerProperties[getPointerCount(source)];
- int index = 0;
- for (int i = 0; i < pointers.size(); i++) {
- if (pointers.get(i).source == source) {
- MotionEvent.PointerProperties p = new MotionEvent.PointerProperties();
- p.id = pointers.get(i).pointerId;
- switch (source) {
- case InputDevice.SOURCE_TOUCHSCREEN:
- p.toolType = MotionEvent.TOOL_TYPE_FINGER;
- break;
- case InputDevice.SOURCE_MOUSE:
- p.toolType = MotionEvent.TOOL_TYPE_MOUSE;
- break;
- }
- props[index++] = p;
- }
- }
- return props;
- }
-
- MotionEvent.PointerCoords[] getPointerCoords(int source) {
- MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[getPointerCount(source)];
- int index = 0;
- for (int i = 0; i < pointers.size(); i++) {
- if (pointers.get(i).source == source) {
- coords[index++] = pointers.get(i).getCoords();
- }
- }
- return coords;
- }
- }
-
- private void synthesizeNativePointer(int source, int pointerId,
- int eventType, int screenX, int screenY, double pressure,
- int orientation)
- {
- Log.d(LOGTAG, "Synthesizing pointer from " + source + " id " + pointerId + " at " + screenX + ", " + screenY);
-
- if (mPointerState == null) {
- mPointerState = new SynthesizedEventState();
- }
-
- // Find the pointer if it already exists
- int pointerIndex = mPointerState.getPointerIndex(pointerId);
-
- // Event-specific handling
- switch (eventType) {
- case MotionEvent.ACTION_POINTER_UP:
- if (pointerIndex < 0) {
- Log.d(LOGTAG, "Requested synthesis of a pointer-up for a pointer that doesn't exist!");
- return;
- }
- if (mPointerState.pointers.size() == 1) {
- // Last pointer is going up
- eventType = MotionEvent.ACTION_UP;
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- if (pointerIndex < 0) {
- Log.d(LOGTAG, "Requested synthesis of a pointer-cancel for a pointer that doesn't exist!");
- return;
- }
- break;
- case MotionEvent.ACTION_POINTER_DOWN:
- if (pointerIndex < 0) {
- // Adding a new pointer
- pointerIndex = mPointerState.addPointer(pointerId, source);
- if (pointerIndex == 0) {
- // first pointer
- eventType = MotionEvent.ACTION_DOWN;
- mPointerState.downTime = SystemClock.uptimeMillis();
- }
- } else {
- // We're moving an existing pointer
- eventType = MotionEvent.ACTION_MOVE;
- }
- break;
- case MotionEvent.ACTION_HOVER_MOVE:
- if (pointerIndex < 0) {
- // Mouse-move a pointer without it going "down". However
- // in order to send the right MotionEvent without a lot of
- // duplicated code, we add the pointer to mPointerState,
- // and then remove it at the bottom of this function.
- pointerIndex = mPointerState.addPointer(pointerId, source);
- } else {
- // We're moving an existing mouse pointer that went down.
- eventType = MotionEvent.ACTION_MOVE;
- }
- break;
- }
-
- // Update the pointer with the new info
- PointerInfo info = mPointerState.pointers.get(pointerIndex);
- info.screenX = screenX;
- info.screenY = screenY;
- info.pressure = pressure;
- info.orientation = orientation;
-
- // Dispatch the event
- int action = (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT);
- action &= MotionEvent.ACTION_POINTER_INDEX_MASK;
- action |= (eventType & MotionEvent.ACTION_MASK);
- boolean isButtonDown = (source == InputDevice.SOURCE_MOUSE) &&
- (eventType == MotionEvent.ACTION_DOWN || eventType == MotionEvent.ACTION_MOVE);
- final MotionEvent event = MotionEvent.obtain(
- /*downTime*/ mPointerState.downTime,
- /*eventTime*/ SystemClock.uptimeMillis(),
- /*action*/ action,
- /*pointerCount*/ mPointerState.getPointerCount(source),
- /*pointerProperties*/ mPointerState.getPointerProperties(source),
- /*pointerCoords*/ mPointerState.getPointerCoords(source),
- /*metaState*/ 0,
- /*buttonState*/ (isButtonDown ? MotionEvent.BUTTON_PRIMARY : 0),
- /*xPrecision*/ 0,
- /*yPrecision*/ 0,
- /*deviceId*/ 0,
- /*edgeFlags*/ 0,
- /*source*/ source,
- /*flags*/ 0);
- mView.post(new Runnable() {
- @Override
- public void run() {
- mView.dispatchTouchEvent(event);
- }
- });
-
- // Forget about removed pointers
- if (eventType == MotionEvent.ACTION_POINTER_UP ||
- eventType == MotionEvent.ACTION_UP ||
- eventType == MotionEvent.ACTION_CANCEL ||
- eventType == MotionEvent.ACTION_HOVER_MOVE)
- {
- mPointerState.pointers.remove(pointerIndex);
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public void synthesizeNativeTouchPoint(int pointerId, int eventType, int screenX,
- int screenY, double pressure, int orientation)
- {
- if (pointerId == PointerInfo.RESERVED_MOUSE_POINTER_ID) {
- throw new IllegalArgumentException("Use a different pointer ID in your test, this one is reserved for mouse");
- }
- synthesizeNativePointer(InputDevice.SOURCE_TOUCHSCREEN, pointerId,
- eventType, screenX, screenY, pressure, orientation);
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public void synthesizeNativeMouseEvent(int eventType, int screenX, int screenY) {
- synthesizeNativePointer(InputDevice.SOURCE_MOUSE, PointerInfo.RESERVED_MOUSE_POINTER_ID,
- eventType, screenX, screenY, 0, 0);
- }
-
- @WrapForJNI
- public LayerRenderer.Frame createFrame() {
- // Create the shaders and textures if necessary.
- if (!mLayerRendererInitialized) {
- if (mLayerRenderer == null) {
- return null;
- }
- mLayerRenderer.createDefaultProgram();
- mLayerRendererInitialized = true;
- }
-
- try {
- return mLayerRenderer.createFrame(mFrameMetrics);
- } catch (Exception e) {
- Log.w(LOGTAG, e);
- return null;
- }
- }
-
- private void geometryChanged() {
- /* Let Gecko know if the screensize has changed */
- sendResizeEventIfNecessary(false, null);
- }
-
- /** Implementation of LayerView.Listener */
- @Override
- public void surfaceChanged(int width, int height) {
- IntSize viewportSize = mToolbarAnimator.getViewportSize();
- setViewportSize(viewportSize.width, viewportSize.height, null);
- }
-
- ImmutableViewportMetrics getViewportMetrics() {
- return mViewportMetrics;
- }
-
- /*
- * You must hold the monitor while calling this.
- */
- private void setViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko) {
- // This class owns the viewport size and the fixed layer margins; don't let other pieces
- // of code clobber either of them. The only place the viewport size should ever be
- // updated is in GeckoLayerClient.setViewportSize, and the only place the margins should
- // ever be updated is in GeckoLayerClient.setFixedLayerMargins; both of these assign to
- // mViewportMetrics directly.
- metrics = metrics.setViewportSize(mViewportMetrics.viewportRectWidth, mViewportMetrics.viewportRectHeight);
- mViewportMetrics = metrics;
-
- viewportMetricsChanged(notifyGecko);
- }
-
- /*
- * You must hold the monitor while calling this.
- */
- private void viewportMetricsChanged(boolean notifyGecko) {
- mToolbarAnimator.onMetricsChanged(mViewportMetrics);
-
- mView.requestRender();
- if (notifyGecko && mGeckoIsReady) {
- geometryChanged();
- }
- }
-
- /*
- * Updates the viewport metrics, overriding the viewport size and margins
- * which are normally retained when calling setViewportMetrics.
- * You must hold the monitor while calling this.
- */
- void forceViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko, boolean forceRedraw) {
- if (forceRedraw) {
- mForceRedraw = true;
- }
- mViewportMetrics = metrics;
- viewportMetricsChanged(notifyGecko);
- }
-
- /** Implementation of PanZoomTarget */
- @Override
- public void panZoomStopped() {
- mToolbarAnimator.onPanZoomStopped();
- }
-
- Object getLock() {
- return this;
- }
-
- Matrix getMatrixForLayerRectToViewRect() {
- if (!mGeckoIsReady) {
- return null;
- }
-
- ImmutableViewportMetrics viewportMetrics = mViewportMetrics;
- PointF origin = viewportMetrics.getOrigin();
- float zoom = viewportMetrics.zoomFactor;
- ImmutableViewportMetrics geckoViewport = mViewportMetrics;
- PointF geckoOrigin = geckoViewport.getOrigin();
- float geckoZoom = geckoViewport.zoomFactor;
-
- Matrix matrix = new Matrix();
- matrix.postTranslate(geckoOrigin.x / geckoZoom, geckoOrigin.y / geckoZoom);
- matrix.postScale(zoom, zoom);
- matrix.postTranslate(-origin.x, -origin.y);
- return matrix;
- }
-
- @Override
- public void setScrollingRootContent(boolean isRootContent) {
- mToolbarAnimator.setScrollingRootContent(isRootContent);
- }
-
- public void addDrawListener(DrawListener listener) {
- mDrawListeners.add(listener);
- }
-
- public void removeDrawListener(DrawListener listener) {
- mDrawListeners.remove(listener);
- }
-
- public void setClearColor(int color) {
- mClearColor = color;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
deleted file mode 100644
index 8072deecc..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ImmutableViewportMetrics.java
+++ /dev/null
@@ -1,282 +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.gfx;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.util.FloatUtils;
-
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.util.DisplayMetrics;
-
-/**
- * ImmutableViewportMetrics are used to store the viewport metrics
- * in way that we can access a version of them from multiple threads
- * without having to take a lock
- */
-public class ImmutableViewportMetrics {
-
- // We need to flatten the RectF and FloatSize structures
- // because Java doesn't have the concept of const classes
- public final float pageRectLeft;
- public final float pageRectTop;
- public final float pageRectRight;
- public final float pageRectBottom;
- public final float cssPageRectLeft;
- public final float cssPageRectTop;
- public final float cssPageRectRight;
- public final float cssPageRectBottom;
- public final float viewportRectLeft;
- public final float viewportRectTop;
- public final int viewportRectWidth;
- public final int viewportRectHeight;
-
- public final float zoomFactor;
-
- public ImmutableViewportMetrics(DisplayMetrics metrics) {
- viewportRectLeft = pageRectLeft = cssPageRectLeft = 0;
- viewportRectTop = pageRectTop = cssPageRectTop = 0;
- viewportRectWidth = metrics.widthPixels;
- viewportRectHeight = metrics.heightPixels;
- pageRectRight = cssPageRectRight = metrics.widthPixels;
- pageRectBottom = cssPageRectBottom = metrics.heightPixels;
- zoomFactor = 1.0f;
- }
-
- /** This constructor is used by native code in AndroidJavaWrappers.cpp, be
- * careful when modifying the signature.
- */
- @WrapForJNI(calledFrom = "gecko")
- private ImmutableViewportMetrics(float aPageRectLeft, float aPageRectTop,
- float aPageRectRight, float aPageRectBottom, float aCssPageRectLeft,
- float aCssPageRectTop, float aCssPageRectRight, float aCssPageRectBottom,
- float aViewportRectLeft, float aViewportRectTop, int aViewportRectWidth,
- int aViewportRectHeight, float aZoomFactor)
- {
- pageRectLeft = aPageRectLeft;
- pageRectTop = aPageRectTop;
- pageRectRight = aPageRectRight;
- pageRectBottom = aPageRectBottom;
- cssPageRectLeft = aCssPageRectLeft;
- cssPageRectTop = aCssPageRectTop;
- cssPageRectRight = aCssPageRectRight;
- cssPageRectBottom = aCssPageRectBottom;
- viewportRectLeft = aViewportRectLeft;
- viewportRectTop = aViewportRectTop;
- viewportRectWidth = aViewportRectWidth;
- viewportRectHeight = aViewportRectHeight;
- zoomFactor = aZoomFactor;
- }
-
- public float getWidth() {
- return viewportRectWidth;
- }
-
- public float getHeight() {
- return viewportRectHeight;
- }
-
- public float viewportRectRight() {
- return viewportRectLeft + viewportRectWidth;
- }
-
- public float viewportRectBottom() {
- return viewportRectTop + viewportRectHeight;
- }
-
- public PointF getOrigin() {
- return new PointF(viewportRectLeft, viewportRectTop);
- }
-
- public FloatSize getSize() {
- return new FloatSize(viewportRectWidth, viewportRectHeight);
- }
-
- public RectF getViewport() {
- return new RectF(viewportRectLeft,
- viewportRectTop,
- viewportRectRight(),
- viewportRectBottom());
- }
-
- public RectF getCssViewport() {
- return RectUtils.scale(getViewport(), 1 / zoomFactor);
- }
-
- public RectF getPageRect() {
- return new RectF(pageRectLeft, pageRectTop, pageRectRight, pageRectBottom);
- }
-
- public float getPageWidth() {
- return pageRectRight - pageRectLeft;
- }
-
- public float getPageHeight() {
- return pageRectBottom - pageRectTop;
- }
-
- public RectF getCssPageRect() {
- return new RectF(cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom);
- }
-
- public RectF getOverscroll() {
- return new RectF(Math.max(0, pageRectLeft - viewportRectLeft),
- Math.max(0, pageRectTop - viewportRectTop),
- Math.max(0, viewportRectRight() - pageRectRight),
- Math.max(0, viewportRectBottom() - pageRectBottom));
- }
-
- /*
- * Returns the viewport metrics that represent a linear transition between "this" and "to" at
- * time "t", which is on the scale [0, 1). This function interpolates all values stored in
- * the viewport metrics.
- */
- public ImmutableViewportMetrics interpolate(ImmutableViewportMetrics to, float t) {
- return new ImmutableViewportMetrics(
- FloatUtils.interpolate(pageRectLeft, to.pageRectLeft, t),
- FloatUtils.interpolate(pageRectTop, to.pageRectTop, t),
- FloatUtils.interpolate(pageRectRight, to.pageRectRight, t),
- FloatUtils.interpolate(pageRectBottom, to.pageRectBottom, t),
- FloatUtils.interpolate(cssPageRectLeft, to.cssPageRectLeft, t),
- FloatUtils.interpolate(cssPageRectTop, to.cssPageRectTop, t),
- FloatUtils.interpolate(cssPageRectRight, to.cssPageRectRight, t),
- FloatUtils.interpolate(cssPageRectBottom, to.cssPageRectBottom, t),
- FloatUtils.interpolate(viewportRectLeft, to.viewportRectLeft, t),
- FloatUtils.interpolate(viewportRectTop, to.viewportRectTop, t),
- (int)FloatUtils.interpolate(viewportRectWidth, to.viewportRectWidth, t),
- (int)FloatUtils.interpolate(viewportRectHeight, to.viewportRectHeight, t),
- FloatUtils.interpolate(zoomFactor, to.zoomFactor, t));
- }
-
- public ImmutableViewportMetrics setViewportSize(int width, int height) {
- if (width == viewportRectWidth && height == viewportRectHeight) {
- return this;
- }
-
- return new ImmutableViewportMetrics(
- pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
- cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
- viewportRectLeft, viewportRectTop, width, height,
- zoomFactor);
- }
-
- public ImmutableViewportMetrics setViewportOrigin(float newOriginX, float newOriginY) {
- return new ImmutableViewportMetrics(
- pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
- cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
- newOriginX, newOriginY, viewportRectWidth, viewportRectHeight,
- zoomFactor);
- }
-
- public ImmutableViewportMetrics setZoomFactor(float newZoomFactor) {
- return new ImmutableViewportMetrics(
- pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
- cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
- viewportRectLeft, viewportRectTop, viewportRectWidth, viewportRectHeight,
- newZoomFactor);
- }
-
- public ImmutableViewportMetrics offsetViewportBy(float dx, float dy) {
- return setViewportOrigin(viewportRectLeft + dx, viewportRectTop + dy);
- }
-
- public ImmutableViewportMetrics offsetViewportByAndClamp(float dx, float dy) {
- return setViewportOrigin(
- Math.max(pageRectLeft, Math.min(viewportRectLeft + dx, pageRectRight - getWidth())),
- Math.max(pageRectTop, Math.min(viewportRectTop + dy, pageRectBottom - getHeight())));
- }
-
- public ImmutableViewportMetrics setPageRect(RectF pageRect, RectF cssPageRect) {
- return new ImmutableViewportMetrics(
- pageRect.left, pageRect.top, pageRect.right, pageRect.bottom,
- cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom,
- viewportRectLeft, viewportRectTop, viewportRectWidth, viewportRectHeight,
- zoomFactor);
- }
-
- public ImmutableViewportMetrics setPageRectFrom(ImmutableViewportMetrics aMetrics) {
- if (aMetrics.cssPageRectLeft == cssPageRectLeft &&
- aMetrics.cssPageRectTop == cssPageRectTop &&
- aMetrics.cssPageRectRight == cssPageRectRight &&
- aMetrics.cssPageRectBottom == cssPageRectBottom) {
- return this;
- }
- RectF css = aMetrics.getCssPageRect();
- return setPageRect(RectUtils.scale(css, zoomFactor), css);
- }
-
- /* This will set the zoom factor and re-scale page-size and viewport offset
- * accordingly. The given focus will remain at the same point on the screen
- * after scaling.
- */
- public ImmutableViewportMetrics scaleTo(float newZoomFactor, PointF focus) {
- // cssPageRect* is invariant, since we're setting the scale factor
- // here. The page rect is based on the CSS page rect.
- float newPageRectLeft = cssPageRectLeft * newZoomFactor;
- float newPageRectTop = cssPageRectTop * newZoomFactor;
- float newPageRectRight = cssPageRectLeft + ((cssPageRectRight - cssPageRectLeft) * newZoomFactor);
- float newPageRectBottom = cssPageRectTop + ((cssPageRectBottom - cssPageRectTop) * newZoomFactor);
-
- PointF origin = getOrigin();
- origin.offset(focus.x, focus.y);
- origin = PointUtils.scale(origin, newZoomFactor / zoomFactor);
- origin.offset(-focus.x, -focus.y);
-
- return new ImmutableViewportMetrics(
- newPageRectLeft, newPageRectTop, newPageRectRight, newPageRectBottom,
- cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
- origin.x, origin.y, viewportRectWidth, viewportRectHeight,
- newZoomFactor);
- }
-
- /** Clamps the viewport to remain within the page rect. */
- public ImmutableViewportMetrics clamp() {
- RectF newViewport = getViewport();
-
- // The viewport bounds ought to never exceed the page bounds.
- if (newViewport.right > pageRectRight)
- newViewport.offset((pageRectRight) - newViewport.right, 0);
- if (newViewport.left < pageRectLeft)
- newViewport.offset(pageRectLeft - newViewport.left, 0);
-
- if (newViewport.bottom > pageRectBottom)
- newViewport.offset(0, (pageRectBottom) - newViewport.bottom);
- if (newViewport.top < pageRectTop)
- newViewport.offset(0, pageRectTop - newViewport.top);
-
- // Note that since newViewport is only translated around, the viewport's
- // width and height are unchanged.
- return new ImmutableViewportMetrics(
- pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
- cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
- newViewport.left, newViewport.top, viewportRectWidth, viewportRectHeight,
- zoomFactor);
- }
-
- public boolean fuzzyEquals(ImmutableViewportMetrics other) {
- // Don't bother checking the pageRectXXX values because they are a product
- // of the cssPageRectXXX values and the zoomFactor, except with more rounding
- // error. Checking those is both inefficient and can lead to false negatives.
- return FloatUtils.fuzzyEquals(cssPageRectLeft, other.cssPageRectLeft)
- && FloatUtils.fuzzyEquals(cssPageRectTop, other.cssPageRectTop)
- && FloatUtils.fuzzyEquals(cssPageRectRight, other.cssPageRectRight)
- && FloatUtils.fuzzyEquals(cssPageRectBottom, other.cssPageRectBottom)
- && FloatUtils.fuzzyEquals(viewportRectLeft, other.viewportRectLeft)
- && FloatUtils.fuzzyEquals(viewportRectTop, other.viewportRectTop)
- && viewportRectWidth == other.viewportRectWidth
- && viewportRectHeight == other.viewportRectHeight
- && FloatUtils.fuzzyEquals(zoomFactor, other.zoomFactor);
- }
-
- @Override
- public String toString() {
- return "ImmutableViewportMetrics v=(" + viewportRectLeft + "," + viewportRectTop + ","
- + viewportRectWidth + "x" + viewportRectHeight + ") p=(" + pageRectLeft + ","
- + pageRectTop + "," + pageRectRight + "," + pageRectBottom + ") c=("
- + cssPageRectLeft + "," + cssPageRectTop + "," + cssPageRectRight + ","
- + cssPageRectBottom + ") z=" + zoomFactor;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/IntSize.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/IntSize.java
deleted file mode 100644
index 0e847158d..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/IntSize.java
+++ /dev/null
@@ -1,89 +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.gfx;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-public class IntSize {
- public final int width, height;
-
- public IntSize(IntSize size) { width = size.width; height = size.height; }
- public IntSize(int inWidth, int inHeight) { width = inWidth; height = inHeight; }
-
- public IntSize(FloatSize size) {
- width = Math.round(size.width);
- height = Math.round(size.height);
- }
-
- public IntSize(JSONObject json) {
- try {
- width = json.getInt("width");
- height = json.getInt("height");
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- }
-
- public int getArea() {
- return width * height;
- }
-
- public boolean equals(IntSize size) {
- return ((size.width == width) && (size.height == height));
- }
-
- public boolean isPositive() {
- return (width > 0 && height > 0);
- }
-
- @Override
- public String toString() { return "(" + width + "," + height + ")"; }
-
- public IntSize scale(float factor) {
- return new IntSize(Math.round(width * factor),
- Math.round(height * factor));
- }
-
- /* Returns the power of two that is greater than or equal to value */
- public static int nextPowerOfTwo(int value) {
- // code taken from http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html
- if (0 == value--) {
- return 1;
- }
- value = (value >> 1) | value;
- value = (value >> 2) | value;
- value = (value >> 4) | value;
- value = (value >> 8) | value;
- value = (value >> 16) | value;
- return value + 1;
- }
-
- public IntSize nextPowerOfTwo() {
- return new IntSize(nextPowerOfTwo(width), nextPowerOfTwo(height));
- }
-
- public static boolean isPowerOfTwo(int value) {
- if (value == 0)
- return false;
- return (value & (value - 1)) == 0;
- }
-
- public static int largestPowerOfTwoLessThan(float value) {
- int val = (int) Math.floor(value);
- if (val <= 0) {
- throw new IllegalArgumentException("Error: value must be > 0");
- }
- // keep dropping the least-significant set bits until only one is left
- int bestVal = val;
- while (val != 0) {
- bestVal = val;
- val &= (val - 1);
- }
- return bestVal;
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java
deleted file mode 100644
index 1a087cc2a..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerRenderer.java
+++ /dev/null
@@ -1,275 +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.gfx;
-
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.mozglue.DirectBufferAllocator;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.opengl.GLES20;
-import android.os.SystemClock;
-import android.util.Log;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.nio.IntBuffer;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.microedition.khronos.egl.EGLConfig;
-
-/**
- * The layer renderer implements the rendering logic for a layer view.
- */
-public class LayerRenderer {
- private static final String LOGTAG = "GeckoLayerRenderer";
-
- /*
- * The amount of time a frame is allowed to take to render before we declare it a dropped
- * frame.
- */
- private static final int MAX_FRAME_TIME = 16; /* 1000 ms / 60 FPS */
- private static final long NANOS_PER_MS = 1000000;
- private static final int MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER = 5;
-
- private final LayerView mView;
- private ByteBuffer mCoordByteBuffer;
- private FloatBuffer mCoordBuffer;
- private int mMaxTextureSize;
-
- private long mLastFrameTime;
- private final CopyOnWriteArrayList<RenderTask> mTasks;
-
- // Dropped frames display
- private final int[] mFrameTimings;
- private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames;
-
- private IntBuffer mPixelBuffer;
- private List<LayerView.ZoomedViewListener> mZoomedViewListeners;
- private float mLastViewLeft;
- private float mLastViewTop;
-
- public LayerRenderer(LayerView view) {
- mView = view;
-
- mTasks = new CopyOnWriteArrayList<RenderTask>();
- mLastFrameTime = System.nanoTime();
-
- mFrameTimings = new int[60];
- mCurrentFrame = mFrameTimingsSum = mDroppedFrames = 0;
-
- mZoomedViewListeners = new ArrayList<LayerView.ZoomedViewListener>();
- }
-
- public void destroy() {
- if (mCoordByteBuffer != null) {
- DirectBufferAllocator.free(mCoordByteBuffer);
- mCoordByteBuffer = null;
- mCoordBuffer = null;
- }
- mZoomedViewListeners.clear();
- }
-
- void onSurfaceCreated(EGLConfig config) {
- createDefaultProgram();
- }
-
- public void createDefaultProgram() {
- int maxTextureSizeResult[] = new int[1];
- GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0);
- mMaxTextureSize = maxTextureSizeResult[0];
- }
-
- public int getMaxTextureSize() {
- return mMaxTextureSize;
- }
-
- public void postRenderTask(RenderTask aTask) {
- mTasks.add(aTask);
- mView.requestRender();
- }
-
- public void removeRenderTask(RenderTask aTask) {
- mTasks.remove(aTask);
- }
-
- private void runRenderTasks(CopyOnWriteArrayList<RenderTask> tasks, boolean after, long frameStartTime) {
- for (RenderTask task : tasks) {
- if (task.runAfter != after) {
- continue;
- }
-
- boolean stillRunning = task.run(frameStartTime - mLastFrameTime, frameStartTime);
-
- // Remove the task from the list if its finished
- if (!stillRunning) {
- tasks.remove(task);
- }
- }
- }
-
- /** Used by robocop for testing purposes. Not for production use! */
- IntBuffer getPixels() {
- IntBuffer pixelBuffer = IntBuffer.allocate(mView.getWidth() * mView.getHeight());
- synchronized (pixelBuffer) {
- mPixelBuffer = pixelBuffer;
- mView.requestRender();
- try {
- pixelBuffer.wait();
- } catch (InterruptedException ie) {
- }
- mPixelBuffer = null;
- }
- return pixelBuffer;
- }
-
- private void updateDroppedFrames(long frameStartTime) {
- int frameElapsedTime = (int)((System.nanoTime() - frameStartTime) / NANOS_PER_MS);
-
- /* Update the running statistics. */
- mFrameTimingsSum -= mFrameTimings[mCurrentFrame];
- mFrameTimingsSum += frameElapsedTime;
- mDroppedFrames -= (mFrameTimings[mCurrentFrame] + 1) / MAX_FRAME_TIME;
- mDroppedFrames += (frameElapsedTime + 1) / MAX_FRAME_TIME;
-
- mFrameTimings[mCurrentFrame] = frameElapsedTime;
- mCurrentFrame = (mCurrentFrame + 1) % mFrameTimings.length;
-
- int averageTime = mFrameTimingsSum / mFrameTimings.length;
- }
-
- public Frame createFrame(ImmutableViewportMetrics metrics) {
- return new Frame(metrics);
- }
-
- public class Frame {
- // The timestamp recording the start of this frame.
- private long mFrameStartTime;
- // A fixed snapshot of the viewport metrics that this frame is using to render content.
- private final ImmutableViewportMetrics mFrameMetrics;
-
- public Frame(ImmutableViewportMetrics metrics) {
- mFrameMetrics = metrics;
- }
-
- /** This function is invoked via JNI; be careful when modifying signature. */
- @WrapForJNI
- public void beginDrawing() {
- mFrameStartTime = System.nanoTime();
-
- // Run through pre-render tasks
- runRenderTasks(mTasks, false, mFrameStartTime);
- }
-
-
- private void maybeRequestZoomedViewRender() {
- // Concurrently update of mZoomedViewListeners should not be an issue here
- // because the following line is just a short-circuit
- if (mZoomedViewListeners.size() == 0) {
- return;
- }
-
- // When scrolling fast, do not request zoomed view render to avoid to slow down
- // the scroll in the main view.
- // Speed is estimated using the offset changes between 2 display frame calls
- final float viewLeft = Math.round(mFrameMetrics.getViewport().left);
- final float viewTop = Math.round(mFrameMetrics.getViewport().top);
- boolean shouldWaitToRender = false;
-
- if (Math.abs(mLastViewLeft - viewLeft) > MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER ||
- Math.abs(mLastViewTop - viewTop) > MAX_SCROLL_SPEED_TO_REQUEST_ZOOM_RENDER) {
- shouldWaitToRender = true;
- }
-
- mLastViewLeft = viewLeft;
- mLastViewTop = viewTop;
-
- if (shouldWaitToRender) {
- return;
- }
-
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- for (LayerView.ZoomedViewListener listener : mZoomedViewListeners) {
- listener.requestZoomedViewRender();
- }
- }
- });
- }
-
-
- /** This function is invoked via JNI; be careful when modifying signature. */
- @WrapForJNI
- public void endDrawing() {
- PanningPerfAPI.recordFrameTime();
-
- runRenderTasks(mTasks, true, mFrameStartTime);
- maybeRequestZoomedViewRender();
-
- /* Used by robocop for testing purposes */
- IntBuffer pixelBuffer = mPixelBuffer;
- if (pixelBuffer != null) {
- synchronized (pixelBuffer) {
- pixelBuffer.position(0);
- GLES20.glReadPixels(0, 0, Math.round(mFrameMetrics.getWidth()),
- Math.round(mFrameMetrics.getHeight()), GLES20.GL_RGBA,
- GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
- pixelBuffer.notify();
- }
- }
-
- // Remove background color once we've painted. GeckoLayerClient is
- // responsible for setting this flag before current document is
- // composited.
- if (mView.getPaintState() == LayerView.PAINT_BEFORE_FIRST) {
- mView.post(new Runnable() {
- @Override
- public void run() {
- mView.setSurfaceBackgroundColor(Color.TRANSPARENT);
- }
- });
- mView.setPaintState(LayerView.PAINT_AFTER_FIRST);
- }
- mLastFrameTime = mFrameStartTime;
- }
- }
-
- public void updateZoomedView(final ByteBuffer data) {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- for (LayerView.ZoomedViewListener listener : mZoomedViewListeners) {
- data.position(0);
- listener.updateView(data);
- }
- }
- });
- }
-
- public void addZoomedViewListener(LayerView.ZoomedViewListener listener) {
- ThreadUtils.assertOnUiThread();
- mZoomedViewListeners.add(listener);
- }
-
- public void removeZoomedViewListener(LayerView.ZoomedViewListener listener) {
- ThreadUtils.assertOnUiThread();
- mZoomedViewListeners.remove(listener);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
deleted file mode 100644
index 969aa3f24..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
+++ /dev/null
@@ -1,711 +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.gfx;
-
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
-
-import org.mozilla.gecko.AndroidGamepadManager;
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.AppConstants.Versions;
-import org.mozilla.gecko.EventDispatcher;
-import org.mozilla.gecko.GeckoAccessibility;
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoThread;
-import org.mozilla.gecko.mozglue.JNIObject;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.SurfaceTexture;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.TextureView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.InputDevice;
-import android.widget.FrameLayout;
-
-/**
- * A view rendered by the layer compositor.
- */
-public class LayerView extends FrameLayout {
- private static final String LOGTAG = "GeckoLayerView";
-
- private GeckoLayerClient mLayerClient;
- private PanZoomController mPanZoomController;
- private DynamicToolbarAnimator mToolbarAnimator;
- private LayerRenderer mRenderer;
- /* Must be a PAINT_xxx constant */
- private int mPaintState;
- private FullScreenState mFullScreenState;
-
- private SurfaceView mSurfaceView;
- private TextureView mTextureView;
-
- private Listener mListener;
-
- /* This should only be modified on the Java UI thread. */
- private final Overscroll mOverscroll;
-
- private boolean mServerSurfaceValid;
- private int mWidth, mHeight;
-
- private boolean onAttachedToWindowCalled;
-
- /* This is written by the Gecko thread and the UI thread, and read by the UI thread. */
- @WrapForJNI(stubName = "CompositorCreated", calledFrom = "ui")
- /* package */ volatile boolean mCompositorCreated;
-
- private class Compositor extends JNIObject {
- public Compositor() {
- }
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko")
- @Override protected native void disposeNative();
-
- // Gecko thread sets its Java instances; does not block UI thread.
- @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
- /* package */ native void attachToJava(GeckoLayerClient layerClient,
- NativePanZoomController npzc);
-
- @WrapForJNI(calledFrom = "any", dispatchTo = "gecko")
- /* package */ native void onSizeChanged(int windowWidth, int windowHeight,
- int screenWidth, int screenHeight);
-
- // Gecko thread creates compositor; blocks UI thread.
- @WrapForJNI(calledFrom = "ui", dispatchTo = "proxy")
- /* package */ native void createCompositor(int width, int height, Object surface);
-
- // Gecko thread pauses compositor; blocks UI thread.
- @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
- /* package */ native void syncPauseCompositor();
-
- // UI thread resumes compositor and notifies Gecko thread; does not block UI thread.
- @WrapForJNI(calledFrom = "ui", dispatchTo = "current")
- /* package */ native void syncResumeResizeCompositor(int width, int height, Object surface);
-
- @WrapForJNI(calledFrom = "any", dispatchTo = "current")
- /* package */ native void syncInvalidateAndScheduleComposite();
-
- @WrapForJNI(calledFrom = "gecko")
- private void reattach() {
- mCompositorCreated = true;
- }
-
- @WrapForJNI(calledFrom = "gecko")
- private void destroy() {
- // The nsWindow has been closed. First mark our compositor as destroyed.
- LayerView.this.mCompositorCreated = false;
-
- LayerView.this.mLayerClient.setGeckoReady(false);
-
- // Then clear out any pending calls on the UI thread by disposing on the UI thread.
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- disposeNative();
- }
- });
- }
- }
-
- private final Compositor mCompositor = new Compositor();
-
- /* Flags used to determine when to show the painted surface. */
- public static final int PAINT_START = 0;
- public static final int PAINT_BEFORE_FIRST = 1;
- public static final int PAINT_AFTER_FIRST = 2;
-
- public boolean shouldUseTextureView() {
- // Disable TextureView support for now as it causes panning/zooming
- // performance regressions (see bug 792259). Uncomment the code below
- // once this bug is fixed.
- return false;
-
- /*
- // we can only use TextureView on ICS or higher
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- Log.i(LOGTAG, "Not using TextureView: not on ICS+");
- return false;
- }
-
- try {
- // and then we can only use it if we have a hardware accelerated window
- Method m = View.class.getMethod("isHardwareAccelerated", (Class[]) null);
- return (Boolean) m.invoke(this);
- } catch (Exception e) {
- Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString());
- return false;
- } */
- }
-
- public LayerView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mPaintState = PAINT_START;
- mFullScreenState = FullScreenState.NONE;
-
- mOverscroll = new OverscrollEdgeEffect(this);
- }
-
- public LayerView(Context context) {
- this(context, null);
- }
-
- public void initializeView(EventDispatcher eventDispatcher) {
- mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher);
- if (mOverscroll != null) {
- mLayerClient.setOverscrollHandler(mOverscroll);
- }
-
- mPanZoomController = mLayerClient.getPanZoomController();
- mToolbarAnimator = mLayerClient.getDynamicToolbarAnimator();
-
- mRenderer = new LayerRenderer(this);
-
- setFocusable(true);
- setFocusableInTouchMode(true);
-
- GeckoAccessibility.setDelegate(this);
- }
-
- /**
- * MotionEventHelper dragAsync() robocop tests can instruct
- * PanZoomController not to generate longpress events.
- */
- public void setIsLongpressEnabled(boolean isLongpressEnabled) {
- mPanZoomController.setIsLongpressEnabled(isLongpressEnabled);
- }
-
- private static Point getEventRadius(MotionEvent event) {
- return new Point((int)event.getToolMajor() / 2,
- (int)event.getToolMinor() / 2);
- }
-
- public void showSurface() {
- // Fix this if TextureView support is turned back on above
- mSurfaceView.setVisibility(View.VISIBLE);
- }
-
- public void hideSurface() {
- // Fix this if TextureView support is turned back on above
- mSurfaceView.setVisibility(View.INVISIBLE);
- }
-
- public void destroy() {
- if (mLayerClient != null) {
- mLayerClient.destroy();
- }
- if (mRenderer != null) {
- mRenderer.destroy();
- }
- }
-
- @Override
- public void dispatchDraw(final Canvas canvas) {
- super.dispatchDraw(canvas);
-
- // We must have a layer client to get valid viewport metrics
- if (mLayerClient != null && mOverscroll != null) {
- mOverscroll.draw(canvas, getViewportMetrics());
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- requestFocus();
- }
-
- if (mToolbarAnimator != null && mToolbarAnimator.onInterceptTouchEvent(event)) {
- if (mPanZoomController != null) {
- mPanZoomController.onMotionEventVelocity(event.getEventTime(), mToolbarAnimator.getVelocity());
- }
- return true;
- }
- if (!mLayerClient.isGeckoReady()) {
- // If gecko isn't loaded yet, don't try sending events to the
- // native code because it's just going to crash
- return true;
- }
- if (mPanZoomController != null && mPanZoomController.onTouchEvent(event)) {
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- // If we get a touchscreen hover event, and accessibility is not enabled,
- // don't send it to gecko.
- if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN &&
- !GeckoAccessibility.isEnabled()) {
- return false;
- }
-
- if (!mLayerClient.isGeckoReady()) {
- // If gecko isn't loaded yet, don't try sending events to the
- // native code because it's just going to crash
- return true;
- } else if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean onGenericMotionEvent(MotionEvent event) {
- if (AndroidGamepadManager.handleMotionEvent(event)) {
- return true;
- }
- if (!mLayerClient.isGeckoReady()) {
- // If gecko isn't loaded yet, don't try sending events to the
- // native code because it's just going to crash
- return true;
- }
- if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
- return true;
- }
- return false;
- }
-
- @Override
- protected void onRestoreInstanceState(final Parcelable state) {
- if (onAttachedToWindowCalled) {
- attachCompositor();
- }
- super.onRestoreInstanceState(state);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- // We are adding descendants to this LayerView, but we don't want the
- // descendants to affect the way LayerView retains its focus.
- setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
-
- // This check should not be done before the view is attached to a window
- // as hardware acceleration will not be enabled at that point.
- // We must create and add the SurfaceView instance before the view tree
- // is fully created to avoid flickering (see bug 801477).
- if (shouldUseTextureView()) {
- mTextureView = new TextureView(getContext());
- mTextureView.setSurfaceTextureListener(new SurfaceTextureListener());
-
- // The background is set to this color when the LayerView is
- // created, and it will be shown immediately at startup. Shortly
- // after, the tab's background color will be used before any content
- // is shown.
- mTextureView.setBackgroundColor(Color.WHITE);
- addView(mTextureView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- } else {
- // This will stop PropertyAnimator from creating a drawing cache (i.e. a bitmap)
- // from a SurfaceView, which is just not possible (the bitmap will be transparent).
- setWillNotCacheDrawing(false);
-
- mSurfaceView = new LayerSurfaceView(getContext(), this);
- mSurfaceView.setBackgroundColor(Color.WHITE);
- addView(mSurfaceView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
-
- SurfaceHolder holder = mSurfaceView.getHolder();
- holder.addCallback(new SurfaceListener());
- }
-
- attachCompositor();
-
- onAttachedToWindowCalled = true;
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- onAttachedToWindowCalled = false;
- }
-
- // Don't expose GeckoLayerClient to things outside this package; only expose it as an Object
- GeckoLayerClient getLayerClient() { return mLayerClient; }
-
- public PanZoomController getPanZoomController() { return mPanZoomController; }
- public DynamicToolbarAnimator getDynamicToolbarAnimator() { return mToolbarAnimator; }
-
- public ImmutableViewportMetrics getViewportMetrics() {
- return mLayerClient.getViewportMetrics();
- }
-
- public Matrix getMatrixForLayerRectToViewRect() {
- return mLayerClient.getMatrixForLayerRectToViewRect();
- }
-
- public void setSurfaceBackgroundColor(int newColor) {
- if (mSurfaceView != null) {
- mSurfaceView.setBackgroundColor(newColor);
- }
- }
-
- public void requestRender() {
- if (mCompositorCreated) {
- mCompositor.syncInvalidateAndScheduleComposite();
- }
- }
-
- public void postRenderTask(RenderTask task) {
- mRenderer.postRenderTask(task);
- }
-
- public void removeRenderTask(RenderTask task) {
- mRenderer.removeRenderTask(task);
- }
-
- public int getMaxTextureSize() {
- return mRenderer.getMaxTextureSize();
- }
-
- /** Used by robocop for testing purposes. Not for production use! */
- @RobocopTarget
- public IntBuffer getPixels() {
- return mRenderer.getPixels();
- }
-
- /* paintState must be a PAINT_xxx constant. */
- public void setPaintState(int paintState) {
- mPaintState = paintState;
- }
-
- public int getPaintState() {
- return mPaintState;
- }
-
- public LayerRenderer getRenderer() {
- return mRenderer;
- }
-
- public void setListener(Listener listener) {
- mListener = listener;
- }
-
- Listener getListener() {
- return mListener;
- }
-
- private void attachCompositor() {
- final NativePanZoomController npzc = (NativePanZoomController) mPanZoomController;
-
- if (GeckoThread.isStateAtLeast(GeckoThread.State.PROFILE_READY)) {
- mCompositor.attachToJava(mLayerClient, npzc);
- } else {
- GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY,
- mCompositor, "attachToJava",
- GeckoLayerClient.class, mLayerClient,
- NativePanZoomController.class, npzc);
- }
- }
-
- @WrapForJNI(calledFrom = "ui")
- protected Object getCompositor() {
- return mCompositor;
- }
-
- void serverSurfaceChanged(int newWidth, int newHeight) {
- ThreadUtils.assertOnUiThread();
-
- mWidth = newWidth;
- mHeight = newHeight;
- mServerSurfaceValid = true;
-
- updateCompositor();
- }
-
- void updateCompositor() {
- ThreadUtils.assertOnUiThread();
-
- if (mCompositorCreated) {
- // If the compositor has already been created, just resume it instead. We don't need
- // to block here because if the surface is destroyed before the compositor grabs it,
- // we can handle that gracefully (i.e. the compositor will remain paused).
- if (!mServerSurfaceValid) {
- return;
- }
- // Asking Gecko to resume the compositor takes too long (see
- // https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we
- // resume the compositor directly. We still need to inform Gecko about
- // the compositor resuming, so that Gecko knows that it can now draw.
- // It is important to not notify Gecko until after the compositor has
- // been resumed, otherwise Gecko may send updates that get dropped.
- mCompositor.syncResumeResizeCompositor(mWidth, mHeight, getSurface());
- return;
- }
-
- // Only try to create the compositor if we have a valid surface and gecko is up. When these
- // two conditions are satisfied, we can be relatively sure that the compositor creation will
- // happen without needing to block anywhere.
- if (mServerSurfaceValid && getLayerClient().isGeckoReady()) {
- mCompositorCreated = true;
- mCompositor.createCompositor(mWidth, mHeight, getSurface());
- }
- }
-
- /* When using a SurfaceView (mSurfaceView != null), resizing happens in two
- * phases. First, the LayerView changes size, then, often some frames later,
- * the SurfaceView changes size. Because of this, we need to split the
- * resize into two phases to avoid jittering.
- *
- * The first phase is the LayerView size change. mListener is notified so
- * that a synchronous draw can be performed (otherwise a blank frame will
- * appear).
- *
- * The second phase is the SurfaceView size change. At this point, the
- * backing GL surface is resized and another synchronous draw is performed.
- * Gecko is also sent the new window size, and this will likely cause an
- * extra draw a few frames later, after it's re-rendered and caught up.
- *
- * In the case that there is no valid GL surface (for example, when
- * resuming, or when coming back from the awesomescreen), or we're using a
- * TextureView instead of a SurfaceView, the first phase is skipped.
- */
- private void onSizeChanged(int width, int height) {
- if (!mServerSurfaceValid || mSurfaceView == null) {
- surfaceChanged(width, height);
- return;
- }
-
- if (mCompositorCreated) {
- mCompositor.syncResumeResizeCompositor(width, height, getSurface());
- }
-
- if (mOverscroll != null) {
- mOverscroll.setSize(width, height);
- }
- }
-
- private void surfaceChanged(int width, int height) {
- serverSurfaceChanged(width, height);
-
- if (mListener != null) {
- mListener.surfaceChanged(width, height);
- }
-
- if (mOverscroll != null) {
- mOverscroll.setSize(width, height);
- }
- }
-
- void notifySizeChanged(int windowWidth, int windowHeight, int screenWidth, int screenHeight) {
- mCompositor.onSizeChanged(windowWidth, windowHeight, screenWidth, screenHeight);
- }
-
- void serverSurfaceDestroyed() {
- ThreadUtils.assertOnUiThread();
-
- // We need to coordinate with Gecko when pausing composition, to ensure
- // that Gecko never executes a draw event while the compositor is paused.
- // This is sent synchronously to make sure that we don't attempt to use
- // any outstanding Surfaces after we call this (such as from a
- // serverSurfaceDestroyed notification), and to make sure that any in-flight
- // Gecko draw events have been processed. When this returns, composition is
- // definitely paused -- it'll synchronize with the Gecko event loop, which
- // in turn will synchronize with the compositor thread.
- if (mCompositorCreated) {
- mCompositor.syncPauseCompositor();
- }
-
- mServerSurfaceValid = false;
- }
-
- private void onDestroyed() {
- serverSurfaceDestroyed();
- }
-
- public Object getNativeWindow() {
- if (mSurfaceView != null)
- return mSurfaceView.getHolder();
-
- return mTextureView.getSurfaceTexture();
- }
-
- public Object getSurface() {
- if (mSurfaceView != null) {
- return mSurfaceView.getHolder().getSurface();
- }
- return null;
- }
-
- // This method is called on the Gecko main thread.
- @WrapForJNI(calledFrom = "gecko")
- private static void updateZoomedView(ByteBuffer data) {
- LayerView layerView = GeckoAppShell.getLayerView();
- if (layerView != null) {
- LayerRenderer layerRenderer = layerView.getRenderer();
- if (layerRenderer != null) {
- layerRenderer.updateZoomedView(data);
- }
- }
- }
-
- public interface Listener {
- void surfaceChanged(int width, int height);
- }
-
- private class SurfaceListener implements SurfaceHolder.Callback {
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width,
- int height) {
- onSizeChanged(width, height);
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- onDestroyed();
- }
- }
-
- /* A subclass of SurfaceView to listen to layout changes, as
- * View.OnLayoutChangeListener requires API level 11.
- */
- private class LayerSurfaceView extends SurfaceView {
- private LayerView mParent;
-
- public LayerSurfaceView(Context aContext, LayerView aParent) {
- super(aContext);
- mParent = aParent;
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- if (changed && mParent.mServerSurfaceValid) {
- mParent.surfaceChanged(right - left, bottom - top);
- }
- }
- }
-
- private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- // We don't do this for surfaceCreated above because it is always followed by a surfaceChanged,
- // but that is not the case here.
- onSizeChanged(width, height);
- }
-
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- onDestroyed();
- return true; // allow Android to call release() on the SurfaceTexture, we are done drawing to it
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- onSizeChanged(width, height);
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-
- }
- }
-
- @RobocopTarget
- public void addDrawListener(DrawListener listener) {
- mLayerClient.addDrawListener(listener);
- }
-
- @RobocopTarget
- public void removeDrawListener(DrawListener listener) {
- mLayerClient.removeDrawListener(listener);
- }
-
- @RobocopTarget
- public static interface DrawListener {
- public void drawFinished();
- }
-
- @Override
- public void setOverScrollMode(int overscrollMode) {
- super.setOverScrollMode(overscrollMode);
- }
-
- @Override
- public int getOverScrollMode() {
- return super.getOverScrollMode();
- }
-
- public float getZoomFactor() {
- return getLayerClient().getViewportMetrics().zoomFactor;
- }
-
- @Override
- public void onFocusChanged (boolean gainFocus, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- GeckoAccessibility.onLayerViewFocusChanged(gainFocus);
- }
-
- public void setFullScreenState(FullScreenState state) {
- mFullScreenState = state;
- }
-
- public boolean isFullScreen() {
- return mFullScreenState != FullScreenState.NONE;
- }
-
- public void setMaxTranslation(float aMaxTranslation) {
- mToolbarAnimator.setMaxTranslation(aMaxTranslation);
- }
-
- public void setSurfaceTranslation(float translation) {
- setTranslationY(translation);
- }
-
- public float getSurfaceTranslation() {
- return getTranslationY();
- }
-
- // Public hooks for dynamic toolbar translation
-
- public interface DynamicToolbarListener {
- public void onTranslationChanged(float aToolbarTranslation, float aLayerViewTranslation);
- public void onPanZoomStopped();
- public void onMetricsChanged(ImmutableViewportMetrics viewport);
- }
-
- // Public hooks for zoomed view
-
- public interface ZoomedViewListener {
- public void requestZoomedViewRender();
- public void updateView(ByteBuffer data);
- }
-
- public void addZoomedViewListener(ZoomedViewListener listener) {
- mRenderer.addZoomedViewListener(listener);
- }
-
- public void removeZoomedViewListener(ZoomedViewListener listener) {
- mRenderer.removeZoomedViewListener(listener);
- }
-
- public void setClearColor(int color) {
- if (mLayerClient != null) {
- mLayerClient.setClearColor(color);
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
deleted file mode 100644
index 6b643c85b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ /dev/null
@@ -1,300 +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.gfx;
-
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.GeckoThread;
-import org.mozilla.gecko.PrefsHelper;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.gfx.DynamicToolbarAnimator.PinReason;
-import org.mozilla.gecko.mozglue.JNIObject;
-import org.mozilla.gecko.util.ThreadUtils;
-
-import org.json.JSONObject;
-
-import android.graphics.PointF;
-import android.util.TypedValue;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.InputDevice;
-
-class NativePanZoomController extends JNIObject implements PanZoomController {
- private final PanZoomTarget mTarget;
- private final LayerView mView;
- private boolean mDestroyed;
- private Overscroll mOverscroll;
- boolean mNegateWheelScroll;
- private float mPointerScrollFactor;
- private PrefsHelper.PrefHandler mPrefsObserver;
- private long mLastDownTime;
- private static final float MAX_SCROLL = 0.075f * GeckoAppShell.getDpi();
-
- @WrapForJNI(calledFrom = "ui")
- private native boolean handleMotionEvent(
- int action, int actionIndex, long time, int metaState,
- int pointerId[], float x[], float y[], float orientation[], float pressure[],
- float toolMajor[], float toolMinor[]);
-
- @WrapForJNI(calledFrom = "ui")
- private native boolean handleScrollEvent(
- long time, int metaState,
- float x, float y,
- float hScroll, float vScroll);
-
- @WrapForJNI(calledFrom = "ui")
- private native boolean handleMouseEvent(
- int action, long time, int metaState,
- float x, float y, int buttons);
-
- @WrapForJNI(calledFrom = "ui")
- private native void handleMotionEventVelocity(long time, float ySpeed);
-
- private boolean handleMotionEvent(MotionEvent event) {
- if (mDestroyed) {
- return false;
- }
-
- final int action = event.getActionMasked();
- final int count = event.getPointerCount();
-
- if (action == MotionEvent.ACTION_DOWN) {
- mLastDownTime = event.getDownTime();
- } else if (mLastDownTime != event.getDownTime()) {
- return false;
- }
-
- final int[] pointerId = new int[count];
- final float[] x = new float[count];
- final float[] y = new float[count];
- final float[] orientation = new float[count];
- final float[] pressure = new float[count];
- final float[] toolMajor = new float[count];
- final float[] toolMinor = new float[count];
-
- final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
-
- for (int i = 0; i < count; i++) {
- pointerId[i] = event.getPointerId(i);
- event.getPointerCoords(i, coords);
-
- x[i] = coords.x;
- y[i] = coords.y;
-
- orientation[i] = coords.orientation;
- pressure[i] = coords.pressure;
-
- // If we are converting to CSS pixels, we should adjust the radii as well.
- toolMajor[i] = coords.toolMajor;
- toolMinor[i] = coords.toolMinor;
- }
-
- return handleMotionEvent(action, event.getActionIndex(), event.getEventTime(),
- event.getMetaState(), pointerId, x, y, orientation, pressure,
- toolMajor, toolMinor);
- }
-
- private boolean handleScrollEvent(MotionEvent event) {
- if (mDestroyed) {
- return false;
- }
-
- final int count = event.getPointerCount();
-
- if (count <= 0) {
- return false;
- }
-
- final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
- event.getPointerCoords(0, coords);
- final float x = coords.x;
- final float y = coords.y;
-
- final float flipFactor = mNegateWheelScroll ? -1.0f : 1.0f;
- final float hScroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL) * flipFactor * mPointerScrollFactor;
- final float vScroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL) * flipFactor * mPointerScrollFactor;
-
- return handleScrollEvent(event.getEventTime(), event.getMetaState(), x, y, hScroll, vScroll);
- }
-
- private boolean handleMouseEvent(MotionEvent event) {
- if (mDestroyed) {
- return false;
- }
-
- final int count = event.getPointerCount();
-
- if (count <= 0) {
- return false;
- }
-
- final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
- event.getPointerCoords(0, coords);
- final float x = coords.x;
- final float y = coords.y;
-
- return handleMouseEvent(event.getActionMasked(), event.getEventTime(), event.getMetaState(), x, y, event.getButtonState());
- }
-
-
- NativePanZoomController(PanZoomTarget target, View view) {
- mTarget = target;
- mView = (LayerView) view;
- mDestroyed = true;
-
- String[] prefs = { "ui.scrolling.negate_wheel_scroll" };
- mPrefsObserver = new PrefsHelper.PrefHandlerBase() {
- @Override public void prefValue(String pref, boolean value) {
- if (pref.equals("ui.scrolling.negate_wheel_scroll")) {
- mNegateWheelScroll = value;
- }
- }
- };
- PrefsHelper.addObserver(prefs, mPrefsObserver);
-
- TypedValue outValue = new TypedValue();
- if (view.getContext().getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, outValue, true)) {
- mPointerScrollFactor = outValue.getDimension(view.getContext().getResources().getDisplayMetrics());
- } else {
- mPointerScrollFactor = MAX_SCROLL;
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
-// NOTE: This commented out block of code allows Fennec to generate
-// mouse event instead of converting them to touch events.
-// This gives Fennec similar behaviour to desktop when using
-// a mouse.
-//
-// if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
-// return handleMouseEvent(event);
-// } else {
-// return handleMotionEvent(event);
-// }
- return handleMotionEvent(event);
- }
-
- @Override
- public boolean onMotionEvent(MotionEvent event) {
- final int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_SCROLL) {
- if (event.getDownTime() >= mLastDownTime) {
- mLastDownTime = event.getDownTime();
- } else if ((InputDevice.getDevice(event.getDeviceId()).getSources() & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD) {
- return false;
- }
- return handleScrollEvent(event);
- } else if ((action == MotionEvent.ACTION_HOVER_MOVE) ||
- (action == MotionEvent.ACTION_HOVER_ENTER) ||
- (action == MotionEvent.ACTION_HOVER_EXIT)) {
- return handleMouseEvent(event);
- } else {
- return false;
- }
- }
-
- @Override
- public void onMotionEventVelocity(final long aEventTime, final float aSpeedY) {
- handleMotionEventVelocity(aEventTime, aSpeedY);
- }
-
- @Override @WrapForJNI(calledFrom = "ui") // PanZoomController
- public void destroy() {
- if (mPrefsObserver != null) {
- PrefsHelper.removeObserver(mPrefsObserver);
- mPrefsObserver = null;
- }
- if (mDestroyed || !mTarget.isGeckoReady()) {
- return;
- }
- mDestroyed = true;
- disposeNative();
- }
-
- @Override
- public void attach() {
- mDestroyed = false;
- }
-
- @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") @Override // JNIObject
- protected native void disposeNative();
-
- @Override
- public void setOverscrollHandler(final Overscroll handler) {
- mOverscroll = handler;
- }
-
- @WrapForJNI(stubName = "SetIsLongpressEnabled") // Called from test thread.
- private native void nativeSetIsLongpressEnabled(boolean isLongpressEnabled);
-
- @Override // PanZoomController
- public void setIsLongpressEnabled(boolean isLongpressEnabled) {
- if (!mDestroyed) {
- nativeSetIsLongpressEnabled(isLongpressEnabled);
- }
- }
-
- @WrapForJNI(calledFrom = "ui")
- private native void adjustScrollForSurfaceShift(float aX, float aY);
-
- @Override // PanZoomController
- public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift) {
- adjustScrollForSurfaceShift(aShift.x, aShift.y);
- return aMetrics.offsetViewportByAndClamp(aShift.x, aShift.y);
- }
-
- @WrapForJNI
- private void updateOverscrollVelocity(final float x, final float y) {
- if (mOverscroll != null) {
- if (ThreadUtils.isOnUiThread() == true) {
- mOverscroll.setVelocity(x * 1000.0f, Overscroll.Axis.X);
- mOverscroll.setVelocity(y * 1000.0f, Overscroll.Axis.Y);
- } else {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- // Multiply the velocity by 1000 to match what was done in JPZ.
- mOverscroll.setVelocity(x * 1000.0f, Overscroll.Axis.X);
- mOverscroll.setVelocity(y * 1000.0f, Overscroll.Axis.Y);
- }
- });
- }
- }
- }
-
- @WrapForJNI
- private void updateOverscrollOffset(final float x, final float y) {
- if (mOverscroll != null) {
- if (ThreadUtils.isOnUiThread() == true) {
- mOverscroll.setDistance(x, Overscroll.Axis.X);
- mOverscroll.setDistance(y, Overscroll.Axis.Y);
- } else {
- ThreadUtils.postToUiThread(new Runnable() {
- @Override
- public void run() {
- mOverscroll.setDistance(x, Overscroll.Axis.X);
- mOverscroll.setDistance(y, Overscroll.Axis.Y);
- }
- });
- }
- }
- }
-
- @WrapForJNI(calledFrom = "ui")
- private void setScrollingRootContent(final boolean isRootContent) {
- mTarget.setScrollingRootContent(isRootContent);
- }
-
- /**
- * Active SelectionCaretDrag requires DynamicToolbarAnimator to be pinned
- * to avoid unwanted scroll interactions.
- */
- @WrapForJNI(calledFrom = "gecko")
- private void onSelectionDragState(boolean state) {
- mView.getDynamicToolbarAnimator().setPinned(state, PinReason.CARET_DRAG);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/Overscroll.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/Overscroll.java
deleted file mode 100644
index e442444d5..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/Overscroll.java
+++ /dev/null
@@ -1,21 +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.gfx;
-
-import android.graphics.Canvas;
-
-public interface Overscroll {
- // The axis to show overscroll on.
- public enum Axis {
- X,
- Y,
- };
-
- public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics);
- public void setSize(final int width, final int height);
- public void setVelocity(final float velocity, final Axis axis);
- public void setDistance(final float distance, final Axis axis);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/OverscrollEdgeEffect.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/OverscrollEdgeEffect.java
deleted file mode 100644
index 85e04d9f2..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/OverscrollEdgeEffect.java
+++ /dev/null
@@ -1,162 +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.gfx;
-
-import org.mozilla.gecko.AppConstants.Versions;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PointF;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.widget.EdgeEffect;
-
-import java.lang.reflect.Field;
-
-public class OverscrollEdgeEffect implements Overscroll {
- // Used to index particular edges in the edges array
- private static final int TOP = 0;
- private static final int BOTTOM = 1;
- private static final int LEFT = 2;
- private static final int RIGHT = 3;
-
- // All four edges of the screen
- private final EdgeEffect[] mEdges = new EdgeEffect[4];
-
- // The view we're showing this overscroll on.
- private final LayerView mView;
-
- public OverscrollEdgeEffect(final LayerView v) {
- Field paintField = null;
- if (Versions.feature21Plus) {
- try {
- paintField = EdgeEffect.class.getDeclaredField("mPaint");
- paintField.setAccessible(true);
- } catch (NoSuchFieldException e) {
- }
- }
-
- mView = v;
- Context context = v.getContext();
- for (int i = 0; i < 4; i++) {
- mEdges[i] = new EdgeEffect(context);
-
- try {
- if (paintField != null) {
- final Paint p = (Paint) paintField.get(mEdges[i]);
-
- // The Android EdgeEffect class uses a mode of SRC_ATOP here, which means it will only
- // draw the effect where there are non-transparent pixels in the destination. Since the LayerView
- // itself is fully transparent, it doesn't display at all. We need to use SRC instead.
- p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
- }
- } catch (IllegalAccessException e) {
- }
- }
- }
-
- @Override
- public void setSize(final int width, final int height) {
- mEdges[LEFT].setSize(height, width);
- mEdges[RIGHT].setSize(height, width);
- mEdges[TOP].setSize(width, height);
- mEdges[BOTTOM].setSize(width, height);
- }
-
- private EdgeEffect getEdgeForAxisAndSide(final Axis axis, final float side) {
- if (axis == Axis.Y) {
- if (side < 0) {
- return mEdges[TOP];
- } else {
- return mEdges[BOTTOM];
- }
- } else {
- if (side < 0) {
- return mEdges[LEFT];
- } else {
- return mEdges[RIGHT];
- }
- }
- }
-
- private void invalidate() {
- if (Versions.feature16Plus) {
- mView.postInvalidateOnAnimation();
- } else {
- mView.postInvalidateDelayed(10);
- }
- }
-
- @Override
- public void setVelocity(final float velocity, final Axis axis) {
- final EdgeEffect edge = getEdgeForAxisAndSide(axis, velocity);
-
- // If we're showing overscroll already, start fading it out.
- if (!edge.isFinished()) {
- edge.onRelease();
- } else {
- // Otherwise, show an absorb effect
- edge.onAbsorb((int)velocity);
- }
-
- invalidate();
- }
-
- @Override
- public void setDistance(final float distance, final Axis axis) {
- // The first overscroll event often has zero distance. Throw it out
- if (distance == 0.0f) {
- return;
- }
-
- final EdgeEffect edge = getEdgeForAxisAndSide(axis, (int)distance);
- edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight()));
- invalidate();
- }
-
- @Override
- public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) {
- if (metrics == null) {
- return;
- }
-
- PointF visibleEnd = mView.getDynamicToolbarAnimator().getVisibleEndOfLayerView();
-
- // If we're pulling an edge, or fading it out, draw!
- boolean invalidate = false;
- if (!mEdges[TOP].isFinished()) {
- invalidate |= draw(mEdges[TOP], canvas, 0, 0, 0);
- }
-
- if (!mEdges[BOTTOM].isFinished()) {
- invalidate |= draw(mEdges[BOTTOM], canvas, visibleEnd.x, visibleEnd.y, 180);
- }
-
- if (!mEdges[LEFT].isFinished()) {
- invalidate |= draw(mEdges[LEFT], canvas, 0, visibleEnd.y, 270);
- }
-
- if (!mEdges[RIGHT].isFinished()) {
- invalidate |= draw(mEdges[RIGHT], canvas, visibleEnd.x, 0, 90);
- }
-
- // If the edge effect is animating off screen, invalidate.
- if (invalidate) {
- invalidate();
- }
- }
-
- private static boolean draw(final EdgeEffect edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) {
- final int state = canvas.save();
- canvas.translate(translateX, translateY);
- canvas.rotate(rotation);
- boolean invalidate = edge.draw(canvas);
- canvas.restoreToCount(state);
-
- return invalidate;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java
deleted file mode 100644
index fbd07c69b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java
+++ /dev/null
@@ -1,38 +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.gfx;
-
-import org.mozilla.gecko.GeckoAppShell;
-import org.mozilla.gecko.EventDispatcher;
-
-import android.graphics.PointF;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-
-public interface PanZoomController {
- // Threshold for sending touch move events to content
- public static final float CLICK_THRESHOLD = 1 / 50f * GeckoAppShell.getDpi();
-
- static class Factory {
- static PanZoomController create(PanZoomTarget target, View view, EventDispatcher dispatcher) {
- return new NativePanZoomController(target, view);
- }
- }
-
- public void destroy();
- public void attach();
-
- public boolean onTouchEvent(MotionEvent event);
- public boolean onMotionEvent(MotionEvent event);
- public void onMotionEventVelocity(final long aEventTime, final float aSpeedY);
-
- public void setOverscrollHandler(final Overscroll controller);
-
- public void setIsLongpressEnabled(boolean isLongpressEnabled);
-
- public ImmutableViewportMetrics adjustScrollForSurfaceShift(ImmutableViewportMetrics aMetrics, PointF aShift);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java
deleted file mode 100644
index 0896674fc..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomTarget.java
+++ /dev/null
@@ -1,15 +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.gfx;
-
-import android.graphics.Matrix;
-import android.graphics.PointF;
-
-public interface PanZoomTarget {
- public void panZoomStopped();
- public boolean isGeckoReady();
- public void setScrollingRootContent(boolean isRootContent);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java
deleted file mode 100644
index 42eb2b88b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanningPerfAPI.java
+++ /dev/null
@@ -1,73 +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.gfx;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-import android.os.SystemClock;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PanningPerfAPI {
- private static final String LOGTAG = "GeckoPanningPerfAPI";
-
- // make this large enough to avoid having to resize the frame time
- // list, as that may be expensive and impact the thing we're trying
- // to measure.
- private static final int EXPECTED_FRAME_COUNT = 2048;
-
- private static boolean mRecordingFrames;
- private static List<Long> mFrameTimes;
- private static long mFrameStartTime;
-
- private static void initialiseRecordingArrays() {
- if (mFrameTimes == null) {
- mFrameTimes = new ArrayList<Long>(EXPECTED_FRAME_COUNT);
- } else {
- mFrameTimes.clear();
- }
- }
-
- @RobocopTarget
- public static void startFrameTimeRecording() {
- if (mRecordingFrames) {
- Log.e(LOGTAG, "Error: startFrameTimeRecording() called while already recording!");
- return;
- }
- mRecordingFrames = true;
- initialiseRecordingArrays();
- mFrameStartTime = SystemClock.uptimeMillis();
- }
-
- @RobocopTarget
- public static List<Long> stopFrameTimeRecording() {
- if (!mRecordingFrames) {
- Log.e(LOGTAG, "Error: stopFrameTimeRecording() called when not recording!");
- return null;
- }
- mRecordingFrames = false;
- return mFrameTimes;
- }
-
- public static void recordFrameTime() {
- // this will be called often, so try to make it as quick as possible
- if (mRecordingFrames) {
- mFrameTimes.add(SystemClock.uptimeMillis() - mFrameStartTime);
- }
- }
-
- @RobocopTarget
- public static void startCheckerboardRecording() {
- throw new UnsupportedOperationException();
- }
-
- @RobocopTarget
- public static List<Float> stopCheckerboardRecording() {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PointUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PointUtils.java
deleted file mode 100644
index 8db329c9f..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PointUtils.java
+++ /dev/null
@@ -1,51 +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.gfx;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.graphics.Point;
-import android.graphics.PointF;
-
-public final class PointUtils {
- public static PointF add(PointF one, PointF two) {
- return new PointF(one.x + two.x, one.y + two.y);
- }
-
- public static PointF subtract(PointF one, PointF two) {
- return new PointF(one.x - two.x, one.y - two.y);
- }
-
- public static PointF scale(PointF point, float factor) {
- return new PointF(point.x * factor, point.y * factor);
- }
-
- public static Point round(PointF point) {
- return new Point(Math.round(point.x), Math.round(point.y));
- }
-
- /* Computes the magnitude of the given vector. */
- public static float distance(PointF point) {
- return (float)Math.sqrt(point.x * point.x + point.y * point.y);
- }
-
- /** Computes the scalar distance between two points. */
- public static float distance(PointF one, PointF two) {
- return PointF.length(one.x - two.x, one.y - two.y);
- }
-
- public static JSONObject toJSON(PointF point) throws JSONException {
- // Ensure we put ints, not longs, because Gecko message handlers call getInt().
- int x = Math.round(point.x);
- int y = Math.round(point.y);
- JSONObject json = new JSONObject();
- json.put("x", x);
- json.put("y", y);
- return json;
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java
deleted file mode 100644
index d961a2569..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ProgressiveUpdateData.java
+++ /dev/null
@@ -1,29 +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.gfx;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-
-/**
- * This is the data structure that's returned by the progressive tile update
- * callback function. It encompasses the current viewport and a boolean value
- * representing whether the front-end is interested in the current progressive
- * update continuing.
- */
-@WrapForJNI
-public class ProgressiveUpdateData {
- public float x;
- public float y;
- public float scale;
- public boolean abort;
-
- public void setViewport(ImmutableViewportMetrics viewport) {
- this.x = viewport.viewportRectLeft;
- this.y = viewport.viewportRectTop;
- this.scale = viewport.zoomFactor;
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RectUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RectUtils.java
deleted file mode 100644
index 22151db76..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RectUtils.java
+++ /dev/null
@@ -1,126 +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.gfx;
-
-import org.mozilla.gecko.util.FloatUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.RectF;
-
-public final class RectUtils {
- private RectUtils() {}
-
- public static Rect create(JSONObject json) {
- try {
- int x = json.getInt("x");
- int y = json.getInt("y");
- int width = json.getInt("width");
- int height = json.getInt("height");
- return new Rect(x, y, x + width, y + height);
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- }
-
- public static String toJSON(RectF rect) {
- StringBuilder sb = new StringBuilder(256);
- sb.append("{ \"left\": ").append(rect.left)
- .append(", \"top\": ").append(rect.top)
- .append(", \"right\": ").append(rect.right)
- .append(", \"bottom\": ").append(rect.bottom)
- .append('}');
- return sb.toString();
- }
-
- public static RectF expand(RectF rect, float moreWidth, float moreHeight) {
- float halfMoreWidth = moreWidth / 2;
- float halfMoreHeight = moreHeight / 2;
- return new RectF(rect.left - halfMoreWidth,
- rect.top - halfMoreHeight,
- rect.right + halfMoreWidth,
- rect.bottom + halfMoreHeight);
- }
-
- public static RectF contract(RectF rect, float lessWidth, float lessHeight) {
- float halfLessWidth = lessWidth / 2.0f;
- float halfLessHeight = lessHeight / 2.0f;
- return new RectF(rect.left + halfLessWidth,
- rect.top + halfLessHeight,
- rect.right - halfLessWidth,
- rect.bottom - halfLessHeight);
- }
-
- public static RectF intersect(RectF one, RectF two) {
- float left = Math.max(one.left, two.left);
- float top = Math.max(one.top, two.top);
- float right = Math.min(one.right, two.right);
- float bottom = Math.min(one.bottom, two.bottom);
- return new RectF(left, top, Math.max(right, left), Math.max(bottom, top));
- }
-
- public static RectF scale(RectF rect, float scale) {
- float x = rect.left * scale;
- float y = rect.top * scale;
- return new RectF(x, y,
- x + (rect.width() * scale),
- y + (rect.height() * scale));
- }
-
- public static RectF scaleAndRound(RectF rect, float scale) {
- float left = rect.left * scale;
- float top = rect.top * scale;
- return new RectF(Math.round(left),
- Math.round(top),
- Math.round(left + (rect.width() * scale)),
- Math.round(top + (rect.height() * scale)));
- }
-
- /** Returns the nearest integer rect of the given rect. */
- public static Rect round(RectF rect) {
- Rect r = new Rect();
- round(rect, r);
- return r;
- }
-
- public static void round(RectF rect, Rect dest) {
- dest.set(Math.round(rect.left), Math.round(rect.top),
- Math.round(rect.right), Math.round(rect.bottom));
- }
-
- public static Rect roundIn(RectF rect) {
- return new Rect((int)Math.ceil(rect.left), (int)Math.ceil(rect.top),
- (int)Math.floor(rect.right), (int)Math.floor(rect.bottom));
- }
-
- public static IntSize getSize(Rect rect) {
- return new IntSize(rect.width(), rect.height());
- }
-
- public static Point getOrigin(Rect rect) {
- return new Point(rect.left, rect.top);
- }
-
- public static PointF getOrigin(RectF rect) {
- return new PointF(rect.left, rect.top);
- }
-
- public static boolean fuzzyEquals(RectF a, RectF b) {
- if (a == null && b == null)
- return true;
- else if ((a == null && b != null) || (a != null && b == null))
- return false;
- else
- return FloatUtils.fuzzyEquals(a.top, b.top)
- && FloatUtils.fuzzyEquals(a.left, b.left)
- && FloatUtils.fuzzyEquals(a.right, b.right)
- && FloatUtils.fuzzyEquals(a.bottom, b.bottom);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RenderTask.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RenderTask.java
deleted file mode 100644
index 80cbf77f0..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/RenderTask.java
+++ /dev/null
@@ -1,80 +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.gfx;
-
-/**
- * A class used to schedule a callback to occur when the next frame is drawn.
- * Subclasses must redefine the internalRun method, not the run method.
- */
-public abstract class RenderTask {
- /**
- * Whether to run the task after the render, or before.
- */
- public final boolean runAfter;
-
- /**
- * Time when this task has first run, in ns. Useful for tasks which run for a specific duration.
- */
- private long mStartTime;
-
- /**
- * Whether we should initialise mStartTime on the next frame run.
- */
- private boolean mResetStartTime = true;
-
- /**
- * The callback to run on each frame. timeDelta is the time elapsed since
- * the last call, in nanoseconds. Returns true if it should continue
- * running, or false if it should be removed from the task queue. Returning
- * true implicitly schedules a redraw.
- *
- * This method first initializes the start time if resetStartTime has been invoked,
- * then calls internalRun.
- *
- * Note : subclasses should override internalRun.
- *
- * @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns.
- * @param currentFrameStartTime the startTime of the current frame, in ns.
- * @return true if animation should be run at the next frame, false otherwise
- * @see RenderTask#internalRun(long, long)
- */
- public final boolean run(long timeDelta, long currentFrameStartTime) {
- if (mResetStartTime) {
- mStartTime = currentFrameStartTime;
- mResetStartTime = false;
- }
- return internalRun(timeDelta, currentFrameStartTime);
- }
-
- /**
- * Abstract method to be overridden by subclasses.
- * @param timeDelta the time between the beginning of last frame and the beginning of this frame, in ns
- * @param currentFrameStartTime the startTime of the current frame, in ns.
- * @return true if animation should be run at the next frame, false otherwise
- */
- protected abstract boolean internalRun(long timeDelta, long currentFrameStartTime);
-
- public RenderTask(boolean aRunAfter) {
- runAfter = aRunAfter;
- }
-
- /**
- * Get the start time of this task.
- * It is the start time of the first frame this task was run on.
- * @return the start time in ns
- */
- public long getStartTime() {
- return mStartTime;
- }
-
- /**
- * Schedule a reset of the recorded start time next time {@link RenderTask#run(long, long)} is run.
- * @see RenderTask#getStartTime()
- */
- public void resetStartTime() {
- mResetStartTime = true;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/StackScroller.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/StackScroller.java
deleted file mode 100644
index 293268cba..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/StackScroller.java
+++ /dev/null
@@ -1,695 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.mozilla.gecko.gfx;
-
-import android.content.Context;
-import android.hardware.SensorManager;
-import android.util.Log;
-import android.view.ViewConfiguration;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-
-/**
- * This class is vastly copied from {@link android.widget.OverScroller} but decouples the time
- * from the app time so it can be specified manually.
- */
-@WrapForJNI(exceptionMode = "nsresult")
-public class StackScroller {
- private int mMode;
-
- private final SplineStackScroller mScrollerX;
- private final SplineStackScroller mScrollerY;
-
- private final boolean mFlywheel;
-
- private static final int SCROLL_MODE = 0;
- private static final int FLING_MODE = 1;
-
- private static float sViscousFluidScale;
- private static float sViscousFluidNormalize;
-
- /**
- * Creates an StackScroller with a viscous fluid scroll interpolator and flywheel.
- * @param context
- */
- public StackScroller(Context context) {
- mFlywheel = true;
- mScrollerX = new SplineStackScroller(context);
- mScrollerY = new SplineStackScroller(context);
- initContants();
- }
-
- private static void initContants() {
- // This controls the viscous fluid effect (how much of it)
- sViscousFluidScale = 8.0f;
- // must be set to 1.0 (used in viscousFluid())
- sViscousFluidNormalize = 1.0f;
- sViscousFluidNormalize = 1.0f / viscousFluid(1.0f);
- }
-
- /**
- *
- * Returns whether the scroller has finished scrolling.
- *
- * @return True if the scroller has finished scrolling, false otherwise.
- */
- public final boolean isFinished() {
- return mScrollerX.mFinished && mScrollerY.mFinished;
- }
-
- /**
- * Force the finished field to a particular value. Contrary to
- * {@link #abortAnimation()}, forcing the animation to finished
- * does NOT cause the scroller to move to the final x and y
- * position.
- *
- * @param finished The new finished value.
- */
- public final void forceFinished(boolean finished) {
- mScrollerX.mFinished = mScrollerY.mFinished = finished;
- }
-
- /**
- * Returns the current X offset in the scroll.
- *
- * @return The new X offset as an absolute distance from the origin.
- */
- public final int getCurrX() {
- return mScrollerX.mCurrentPosition;
- }
-
- /**
- * Returns the current Y offset in the scroll.
- *
- * @return The new Y offset as an absolute distance from the origin.
- */
- public final int getCurrY() {
- return mScrollerY.mCurrentPosition;
- }
-
- /**
- * Returns where the scroll will end. Valid only for "fling" scrolls.
- *
- * @return The final X offset as an absolute distance from the origin.
- */
- public final int getFinalX() {
- return mScrollerX.mFinal;
- }
-
- public final float getCurrSpeedX() {
- return mScrollerX.mCurrVelocity;
- }
-
- public final float getCurrSpeedY() {
- return mScrollerY.mCurrVelocity;
- }
-
- /**
- * Returns where the scroll will end. Valid only for "fling" scrolls.
- *
- * @return The final Y offset as an absolute distance from the origin.
- */
- public final int getFinalY() {
- return mScrollerY.mFinal;
- }
-
- /**
- * Sets where the scroll will end. Valid only for "fling" scrolls.
- *
- * @param x The final X offset as an absolute distance from the origin.
- */
- public final void setFinalX(int x) {
- mScrollerX.setFinalPosition(x);
- }
-
- private static float viscousFluid(float x) {
- x *= sViscousFluidScale;
- if (x < 1.0f) {
- x -= (1.0f - (float) Math.exp(-x));
- } else {
- float start = 0.36787944117f; // 1/e == exp(-1)
- x = 1.0f - (float) Math.exp(1.0f - x);
- x = start + x * (1.0f - start);
- }
- x *= sViscousFluidNormalize;
- return x;
- }
-
- /**
- * Call this when you want to know the new location. If it returns true, the
- * animation is not yet finished.
- */
- public boolean computeScrollOffset(long time) {
- if (isFinished()) {
- return false;
- }
-
- switch (mMode) {
- case SCROLL_MODE:
- // Any scroller can be used for time, since they were started
- // together in scroll mode. We use X here.
- final long elapsedTime = time - mScrollerX.mStartTime;
-
- final int duration = mScrollerX.mDuration;
- if (elapsedTime < duration) {
- float q = (float) (elapsedTime) / duration;
- q = viscousFluid(q);
- mScrollerX.updateScroll(q);
- mScrollerY.updateScroll(q);
- } else {
- abortAnimation();
- }
- break;
-
- case FLING_MODE:
- if (!mScrollerX.mFinished) {
- if (!mScrollerX.update(time)) {
- if (!mScrollerX.continueWhenFinished(time)) {
- mScrollerX.finish();
- }
- }
- }
-
- if (!mScrollerY.mFinished) {
- if (!mScrollerY.update(time)) {
- if (!mScrollerY.continueWhenFinished(time)) {
- mScrollerY.finish();
- }
- }
- }
-
- break;
-
- default:
- break;
- }
-
- return true;
- }
-
- /**
- * Start scrolling by providing a starting point and the distance to travel.
- *
- * @param startX Starting horizontal scroll offset in pixels. Positive
- * numbers will scroll the content to the left.
- * @param startY Starting vertical scroll offset in pixels. Positive numbers
- * will scroll the content up.
- * @param dx Horizontal distance to travel. Positive numbers will scroll the
- * content to the left.
- * @param dy Vertical distance to travel. Positive numbers will scroll the
- * content up.
- * @param duration Duration of the scroll in milliseconds.
- */
- public void startScroll(int startX, int startY, int dx, int dy, long startTime, int duration) {
- mMode = SCROLL_MODE;
- mScrollerX.startScroll(startX, dx, startTime, duration);
- mScrollerY.startScroll(startY, dy, startTime, duration);
- }
-
- /**
- * Call this when you want to 'spring back' into a valid coordinate range.
- *
- * @param startX Starting X coordinate
- * @param startY Starting Y coordinate
- * @param minX Minimum valid X value
- * @param maxX Maximum valid X value
- * @param minY Minimum valid Y value
- * @param maxY Minimum valid Y value
- * @return true if a springback was initiated, false if startX and startY were
- * already within the valid range.
- */
- public boolean springBack(
- int startX, int startY, int minX, int maxX, int minY, int maxY, long time) {
- mMode = FLING_MODE;
-
- // Make sure both methods are called.
- final boolean spingbackX = mScrollerX.springback(startX, minX, maxX, time);
- final boolean spingbackY = mScrollerY.springback(startY, minY, maxY, time);
- return spingbackX || spingbackY;
- }
-
- /**
- * Start scrolling based on a fling gesture. The distance traveled will
- * depend on the initial velocity of the fling.
- *
- * @param startX Starting point of the scroll (X)
- * @param startY Starting point of the scroll (Y)
- * @param velocityX Initial velocity of the fling (X) measured in pixels per second.
- * @param velocityY Initial velocity of the fling (Y) measured in pixels per second
- * @param minX Minimum X value. The scroller will not scroll past this point
- * unless overX > 0. If overfling is allowed, it will use minX as
- * a springback boundary.
- * @param maxX Maximum X value. The scroller will not scroll past this point
- * unless overX > 0. If overfling is allowed, it will use maxX as
- * a springback boundary.
- * @param minY Minimum Y value. The scroller will not scroll past this point
- * unless overY > 0. If overfling is allowed, it will use minY as
- * a springback boundary.
- * @param maxY Maximum Y value. The scroller will not scroll past this point
- * unless overY > 0. If overfling is allowed, it will use maxY as
- * a springback boundary.
- * @param overX Overfling range. If > 0, horizontal overfling in either
- * direction will be possible.
- * @param overY Overfling range. If > 0, vertical overfling in either
- * direction will be possible.
- */
- public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX,
- int minY, int maxY, int overX, int overY, long time) {
- // Continue a scroll or fling in progress
- if (mFlywheel && !isFinished()) {
- float oldVelocityX = mScrollerX.mCurrVelocity;
- float oldVelocityY = mScrollerY.mCurrVelocity;
- boolean sameXDirection = (velocityX == 0) || (oldVelocityX == 0) ||
- ((velocityX < 0) == (oldVelocityX < 0));
- boolean sameYDirection = (velocityY == 0) || (oldVelocityY == 0) ||
- ((velocityY < 0) == (oldVelocityY < 0));
- if (sameXDirection) {
- velocityX += oldVelocityX;
- }
- if (sameYDirection) {
- velocityY += oldVelocityY;
- }
- }
-
- mMode = FLING_MODE;
- mScrollerX.fling(startX, velocityX, minX, maxX, overX, time);
- mScrollerY.fling(startY, velocityY, minY, maxY, overY, time);
- }
-
- /**
- * Stops the animation. Contrary to {@link #forceFinished(boolean)},
- * aborting the animating causes the scroller to move to the final x and y
- * positions.
- *
- * @see #forceFinished(boolean)
- */
- public void abortAnimation() {
- mScrollerX.finish();
- mScrollerY.finish();
- }
-
- static class SplineStackScroller {
- // Initial position
- private int mStart;
-
- // Current position
- private int mCurrentPosition;
-
- // Final position
- private int mFinal;
-
- // Initial velocity
- private int mVelocity;
-
- // Current velocity
- private float mCurrVelocity;
-
- // Constant current deceleration
- private float mDeceleration;
-
- // Animation starting time, in system milliseconds
- private long mStartTime;
-
- // Animation duration, in milliseconds
- private int mDuration;
-
- // Duration to complete spline component of animation
- private int mSplineDuration;
-
- // Distance to travel along spline animation
- private int mSplineDistance;
-
- // Whether the animation is currently in progress
- private boolean mFinished;
-
- // The allowed overshot distance before boundary is reached.
- private int mOver;
-
- // Fling friction
- private final float mFlingFriction = ViewConfiguration.getScrollFriction();
-
- // Current state of the animation.
- private int mState = SPLINE;
-
- // Constant gravity value, used in the deceleration phase.
- private static final float GRAVITY = 2000.0f;
-
- // A context-specific coefficient adjusted to physical values.
- private final float mPhysicalCoeff;
-
- private static final float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));
- private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1)
- private static final float START_TENSION = 0.5f;
- private static final float END_TENSION = 1.0f;
- private static final float P1 = START_TENSION * INFLEXION;
- private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION);
-
- private static final int NB_SAMPLES = 100;
- private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1];
- private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1];
-
- private static final int SPLINE = 0;
- private static final int CUBIC = 1;
- private static final int BALLISTIC = 2;
-
- static {
- float xMin = 0.0f;
- float yMin = 0.0f;
- for (int i = 0; i < NB_SAMPLES; i++) {
- final float alpha = (float) i / NB_SAMPLES;
-
- float xMax = 1.0f;
- float x, tx, coef;
- while (true) {
- x = xMin + (xMax - xMin) / 2.0f;
- coef = 3.0f * x * (1.0f - x);
- tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x;
- if (Math.abs(tx - alpha) < 1E-5) break;
- if (tx > alpha) {
- xMax = x;
- } else {
- xMin = x;
- }
- }
- SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x;
-
- float yMax = 1.0f;
- float y, dy;
- while (true) {
- y = yMin + (yMax - yMin) / 2.0f;
- coef = 3.0f * y * (1.0f - y);
- dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y;
- if (Math.abs(dy - alpha) < 1E-5) break;
- if (dy > alpha) {
- yMax = y;
- } else {
- yMin = y;
- }
- }
- SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y;
- }
- SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f;
- }
-
- SplineStackScroller(Context context) {
- mFinished = true;
- final float ppi = context.getResources().getDisplayMetrics().density * 160.0f;
- mPhysicalCoeff = SensorManager.GRAVITY_EARTH // g (m/s^2)
- * 39.37f // inch/meter
- * ppi * 0.84f; // look and feel tuning
- }
-
- void updateScroll(float q) {
- mCurrentPosition = mStart + Math.round(q * (mFinal - mStart));
- }
-
- /*
- * Get a signed deceleration that will reduce the velocity.
- */
- private static float getDeceleration(int velocity) {
- return velocity > 0 ? -GRAVITY : GRAVITY;
- }
-
- /*
- * Modifies mDuration to the duration it takes to get from start to newFinal using the
- * spline interpolation. The previous duration was needed to get to oldFinal.
- */
- private void adjustDuration(int start, int oldFinal, int newFinal) {
- final int oldDistance = oldFinal - start;
- final int newDistance = newFinal - start;
- final float x = Math.abs((float) newDistance / oldDistance);
- final int index = (int) (NB_SAMPLES * x);
- if (index < NB_SAMPLES) {
- final float xInf = (float) index / NB_SAMPLES;
- final float xSup = (float) (index + 1) / NB_SAMPLES;
- final float tInf = SPLINE_TIME[index];
- final float tSup = SPLINE_TIME[index + 1];
- final float timeCoef = tInf + (x - xInf) / (xSup - xInf) * (tSup - tInf);
- mDuration *= timeCoef;
- }
- }
-
- void startScroll(int start, int distance, long startTime, int duration) {
- mFinished = false;
-
- mStart = start;
- mFinal = start + distance;
-
- mStartTime = startTime;
- mDuration = duration;
-
- // Unused
- mDeceleration = 0.0f;
- mVelocity = 0;
- }
-
- void finish() {
- mCurrentPosition = mFinal;
- // Not reset since WebView relies on this value for fast fling.
- // TODO: restore when WebView uses the fast fling implemented in this class.
- // mCurrVelocity = 0.0f;
- mFinished = true;
- }
-
- void setFinalPosition(int position) {
- mFinal = position;
- mFinished = false;
- }
-
- boolean springback(int start, int min, int max, long time) {
- mFinished = true;
-
- mStart = mFinal = start;
- mVelocity = 0;
-
- mStartTime = time;
- mDuration = 0;
-
- if (start < min) {
- startSpringback(start, min, 0);
- } else if (start > max) {
- startSpringback(start, max, 0);
- }
-
- return !mFinished;
- }
-
- private void startSpringback(int start, int end, int velocity) {
- // mStartTime has been set
- mFinished = false;
- mState = CUBIC;
- mStart = start;
- mFinal = end;
- final int delta = start - end;
- mDeceleration = getDeceleration(delta);
- // TODO take velocity into account
- mVelocity = -delta; // only sign is used
- mOver = Math.abs(delta);
- mDuration = (int) (1000.0 * Math.sqrt(-2.0 * delta / mDeceleration));
- }
-
- void fling(int start, int velocity, int min, int max, int over, long time) {
- mOver = over;
- mFinished = false;
- mCurrVelocity = mVelocity = velocity;
- mDuration = mSplineDuration = 0;
- mStartTime = time;
- mCurrentPosition = mStart = start;
-
- if (start > max || start < min) {
- startAfterEdge(start, min, max, velocity, time);
- return;
- }
-
- mState = SPLINE;
- double totalDistance = 0.0;
-
- if (velocity != 0) {
- mDuration = mSplineDuration = getSplineFlingDuration(velocity);
- totalDistance = getSplineFlingDistance(velocity);
- }
-
- mSplineDistance = (int) (totalDistance * Math.signum(velocity));
- mFinal = start + mSplineDistance;
-
- // Clamp to a valid final position
- if (mFinal < min) {
- adjustDuration(mStart, mFinal, min);
- mFinal = min;
- }
-
- if (mFinal > max) {
- adjustDuration(mStart, mFinal, max);
- mFinal = max;
- }
- }
-
- private double getSplineDeceleration(int velocity) {
- return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
- }
-
- private double getSplineFlingDistance(int velocity) {
- final double l = getSplineDeceleration(velocity);
- final double decelMinusOne = DECELERATION_RATE - 1.0;
- return mFlingFriction * mPhysicalCoeff
- * Math.exp(DECELERATION_RATE / decelMinusOne * l);
- }
-
- /* Returns the duration, expressed in milliseconds */
- private int getSplineFlingDuration(int velocity) {
- final double l = getSplineDeceleration(velocity);
- final double decelMinusOne = DECELERATION_RATE - 1.0;
- return (int) (1000.0 * Math.exp(l / decelMinusOne));
- }
-
- private void fitOnBounceCurve(int start, int end, int velocity) {
- // Simulate a bounce that started from edge
- final float durationToApex = -velocity / mDeceleration;
- final float distanceToApex = velocity * velocity / 2.0f / Math.abs(mDeceleration);
- final float distanceToEdge = Math.abs(end - start);
- final float totalDuration = (float) Math.sqrt(
- 2.0 * (distanceToApex + distanceToEdge) / Math.abs(mDeceleration));
- mStartTime -= (int) (1000.0f * (totalDuration - durationToApex));
- mStart = end;
- mVelocity = (int) (-mDeceleration * totalDuration);
- }
-
- private void startBounceAfterEdge(int start, int end, int velocity) {
- mDeceleration = getDeceleration(velocity == 0 ? start - end : velocity);
- fitOnBounceCurve(start, end, velocity);
- onEdgeReached();
- }
-
- private void startAfterEdge(int start, int min, int max, int velocity, long time) {
- if (start > min && start < max) {
- Log.e("StackScroller", "startAfterEdge called from a valid position");
- mFinished = true;
- return;
- }
- final boolean positive = start > max;
- final int edge = positive ? max : min;
- final int overDistance = start - edge;
- boolean keepIncreasing = overDistance * velocity >= 0;
- if (keepIncreasing) {
- // Will result in a bounce or a to_boundary depending on velocity.
- startBounceAfterEdge(start, edge, velocity);
- } else {
- final double totalDistance = getSplineFlingDistance(velocity);
- if (totalDistance > Math.abs(overDistance)) {
- fling(start, velocity, positive ? min : start, positive ? start : max, mOver,
- time);
- } else {
- startSpringback(start, edge, velocity);
- }
- }
- }
-
- private void onEdgeReached() {
- // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached.
- float distance = mVelocity * mVelocity / (2.0f * Math.abs(mDeceleration));
- final float sign = Math.signum(mVelocity);
-
- if (distance > mOver) {
- // Default deceleration is not sufficient to slow us down before boundary
- mDeceleration = -sign * mVelocity * mVelocity / (2.0f * mOver);
- distance = mOver;
- }
-
- mOver = (int) distance;
- mState = BALLISTIC;
- mFinal = mStart + (int) (mVelocity > 0 ? distance : -distance);
- mDuration = -(int) (1000.0f * mVelocity / mDeceleration);
- }
-
- boolean continueWhenFinished(long time) {
- switch (mState) {
- case SPLINE:
- // Duration from start to null velocity
- if (mDuration < mSplineDuration) {
- // If the animation was clamped, we reached the edge
- mStart = mFinal;
- // TODO Better compute speed when edge was reached
- mVelocity = (int) mCurrVelocity;
- mDeceleration = getDeceleration(mVelocity);
- mStartTime += mDuration;
- onEdgeReached();
- } else {
- // Normal stop, no need to continue
- return false;
- }
- break;
- case BALLISTIC:
- mStartTime += mDuration;
- startSpringback(mFinal, mStart, 0);
- break;
- case CUBIC:
- return false;
- }
-
- update(time);
- return true;
- }
-
- /*
- * Update the current position and velocity for current time. Returns
- * true if update has been done and false if animation duration has been
- * reached.
- */
- boolean update(long time) {
- final long currentTime = time - mStartTime;
-
- if (((mState == SPLINE) && (mSplineDuration <= 0)) ||
- ((mState == CUBIC) && (mDuration <= 0))) {
- return false;
- }
-
- if (currentTime > mDuration) {
- return false;
- }
-
- double distance = 0.0;
- switch (mState) {
- case SPLINE: {
- final float t = (float) currentTime / mSplineDuration;
- final int index = (int) (NB_SAMPLES * t);
- float distanceCoef = 1.f;
- float velocityCoef = 0.f;
- if (index < NB_SAMPLES) {
- final float tInf = (float) index / NB_SAMPLES;
- final float tSup = (float) (index + 1) / NB_SAMPLES;
- final float dInf = SPLINE_POSITION[index];
- final float dSup = SPLINE_POSITION[index + 1];
- velocityCoef = (dSup - dInf) / (tSup - tInf);
- distanceCoef = dInf + (t - tInf) * velocityCoef;
- }
-
- distance = distanceCoef * mSplineDistance;
- mCurrVelocity = velocityCoef * mSplineDistance / mSplineDuration * 1000.0f;
- break;
- }
-
- case BALLISTIC: {
- final float t = currentTime / 1000.0f;
- mCurrVelocity = mVelocity + mDeceleration * t;
- distance = mVelocity * t + mDeceleration * t * t / 2.0f;
- break;
- }
-
- case CUBIC: {
- final float t = (float) (currentTime) / mDuration;
- final float t2 = t * t;
- final float sign = Math.signum(mVelocity);
- distance = sign * mOver * (3.0f * t2 - 2.0f * t * t2);
- mCurrVelocity = sign * mOver * 6.0f * (-t + t2);
- break;
- }
- }
-
- mCurrentPosition = mStart + (int) Math.round(distance);
-
- return true;
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/SurfaceTextureListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/SurfaceTextureListener.java
deleted file mode 100644
index 560674e4f..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/SurfaceTextureListener.java
+++ /dev/null
@@ -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;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.mozglue.JNIObject;
-
-import android.graphics.SurfaceTexture;
-
-final class SurfaceTextureListener
- extends JNIObject implements SurfaceTexture.OnFrameAvailableListener
-{
- @WrapForJNI(calledFrom = "gecko")
- private SurfaceTextureListener() {
- }
-
- @Override
- protected void disposeNative() {
- // SurfaceTextureListener is disposed inside AndroidSurfaceTexture.
- throw new IllegalStateException("unreachable code");
- }
-
- @WrapForJNI(stubName = "OnFrameAvailable")
- private native void nativeOnFrameAvailable();
-
- @Override // SurfaceTexture.OnFrameAvailableListener
- public void onFrameAvailable(SurfaceTexture surfaceTexture) {
- try {
- nativeOnFrameAvailable();
- } catch (final NullPointerException e) {
- // Ignore exceptions caused by a disposed object, i.e.
- // getting a callback after this listener is no longer in use.
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java
deleted file mode 100644
index e6685f066..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/ViewTransform.java
+++ /dev/null
@@ -1,28 +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.gfx;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-
-@WrapForJNI
-public class ViewTransform {
- public float x;
- public float y;
- public float width;
- public float height;
- public float scale;
- public float fixedLayerMarginLeft;
- public float fixedLayerMarginTop;
- public float fixedLayerMarginRight;
- public float fixedLayerMarginBottom;
-
- public ViewTransform(float inX, float inY, float inScale) {
- x = inX;
- y = inY;
- scale = inScale;
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/ByteBufferInputStream.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/ByteBufferInputStream.java
deleted file mode 100644
index bc9e0a143..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/ByteBufferInputStream.java
+++ /dev/null
@@ -1,64 +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.mozglue;
-
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-class ByteBufferInputStream extends InputStream {
-
- protected ByteBuffer mBuf;
- // Reference to a native object holding the data backing the ByteBuffer.
- private final NativeReference mNativeRef;
-
- protected ByteBufferInputStream(ByteBuffer buffer, NativeReference ref) {
- mBuf = buffer;
- mNativeRef = ref;
- }
-
- @Override
- public int available() {
- return mBuf.remaining();
- }
-
- @Override
- public void close() {
- // Do nothing, we need to keep the native references around for child
- // buffers.
- }
-
- @Override
- public int read() {
- if (!mBuf.hasRemaining() || mNativeRef.isReleased()) {
- return -1;
- }
-
- return mBuf.get() & 0xff; // Avoid sign extension
- }
-
- @Override
- public int read(byte[] buffer, int offset, int length) {
- if (!mBuf.hasRemaining() || mNativeRef.isReleased()) {
- return -1;
- }
-
- length = Math.min(length, mBuf.remaining());
- mBuf.get(buffer, offset, length);
- return length;
- }
-
- @Override
- public long skip(long byteCount) {
- if (byteCount < 0 || mNativeRef.isReleased()) {
- return 0;
- }
-
- byteCount = Math.min(byteCount, mBuf.remaining());
- mBuf.position(mBuf.position() + (int)byteCount);
- return byteCount;
- }
-
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/DirectBufferAllocator.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/DirectBufferAllocator.java
deleted file mode 100644
index b3fb24291..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/DirectBufferAllocator.java
+++ /dev/null
@@ -1,52 +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.mozglue;
-
-import java.nio.ByteBuffer;
-
-//
-// We must manually allocate direct buffers in JNI to work around a bug where Honeycomb's
-// ByteBuffer.allocateDirect() grossly overallocates the direct buffer size.
-// https://code.google.com/p/android/issues/detail?id=16941
-//
-
-public final class DirectBufferAllocator {
- private DirectBufferAllocator() {}
-
- public static ByteBuffer allocate(int size) {
- if (size <= 0) {
- throw new IllegalArgumentException("Invalid size " + size);
- }
-
- ByteBuffer directBuffer = nativeAllocateDirectBuffer(size);
- if (directBuffer == null) {
- throw new OutOfMemoryError("allocateDirectBuffer() returned null");
- }
-
- if (!directBuffer.isDirect()) {
- throw new AssertionError("allocateDirectBuffer() did not return a direct buffer");
- }
-
- return directBuffer;
- }
-
- public static ByteBuffer free(ByteBuffer buffer) {
- if (buffer == null) {
- return null;
- }
-
- if (!buffer.isDirect()) {
- throw new IllegalArgumentException("buffer must be direct");
- }
-
- nativeFreeDirectBuffer(buffer);
- return null;
- }
-
- // These JNI methods are implemented in mozglue/android/nsGeckoUtils.cpp.
- private static native ByteBuffer nativeAllocateDirectBuffer(long size);
- private static native void nativeFreeDirectBuffer(ByteBuffer buf);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
deleted file mode 100644
index 0bef2435b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/GeckoLoader.java
+++ /dev/null
@@ -1,549 +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.mozglue;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
-import java.text.NumberFormat;
-import java.util.Locale;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-
-import android.content.Context;
-import android.os.Build;
-import android.os.Environment;
-import android.util.Log;
-
-import org.mozilla.gecko.annotation.JNITarget;
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.AppConstants;
-
-public final class GeckoLoader {
- private static final String LOGTAG = "GeckoLoader";
-
- private static volatile SafeIntent sIntent;
- private static File sCacheFile;
- private static File sGREDir;
-
- /* Synchronized on GeckoLoader.class. */
- private static boolean sSQLiteLibsLoaded;
- private static boolean sNSSLibsLoaded;
- private static boolean sMozGlueLoaded;
-
- private GeckoLoader() {
- // prevent instantiation
- }
-
- public static File getCacheDir(Context context) {
- if (sCacheFile == null) {
- sCacheFile = context.getCacheDir();
- }
- return sCacheFile;
- }
-
- public static File getGREDir(Context context) {
- if (sGREDir == null) {
- sGREDir = new File(context.getApplicationInfo().dataDir);
- }
- return sGREDir;
- }
-
- private static void setupPluginEnvironment(Context context, String[] pluginDirs) {
- // setup plugin path directories
- try {
- // Check to see if plugins were blocked.
- if (pluginDirs == null) {
- putenv("MOZ_PLUGINS_BLOCKED=1");
- putenv("MOZ_PLUGIN_PATH=");
- return;
- }
-
- StringBuilder pluginSearchPath = new StringBuilder();
- for (int i = 0; i < pluginDirs.length; i++) {
- pluginSearchPath.append(pluginDirs[i]);
- pluginSearchPath.append(":");
- }
- putenv("MOZ_PLUGIN_PATH=" + pluginSearchPath);
-
- File pluginDataDir = context.getDir("plugins", 0);
- putenv("ANDROID_PLUGIN_DATADIR=" + pluginDataDir.getPath());
-
- File pluginPrivateDataDir = context.getDir("plugins_private", 0);
- putenv("ANDROID_PLUGIN_DATADIR_PRIVATE=" + pluginPrivateDataDir.getPath());
-
- } catch (Exception ex) {
- Log.w(LOGTAG, "Caught exception getting plugin dirs.", ex);
- }
- }
-
- private static void setupDownloadEnvironment(final Context context) {
- try {
- File downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
- File updatesDir = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
- if (downloadDir == null) {
- downloadDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
- }
- if (updatesDir == null) {
- updatesDir = downloadDir;
- }
- putenv("DOWNLOADS_DIRECTORY=" + downloadDir.getPath());
- putenv("UPDATES_DIRECTORY=" + updatesDir.getPath());
- } catch (Exception e) {
- Log.w(LOGTAG, "No download directory found.", e);
- }
- }
-
- private static void delTree(File file) {
- if (file.isDirectory()) {
- File children[] = file.listFiles();
- for (File child : children) {
- delTree(child);
- }
- }
- file.delete();
- }
-
- private static File getTmpDir(Context context) {
- File tmpDir = context.getDir("tmpdir", Context.MODE_PRIVATE);
- // check if the old tmp dir is there
- File oldDir = new File(tmpDir.getParentFile(), "app_tmp");
- if (oldDir.exists()) {
- delTree(oldDir);
- }
- return tmpDir;
- }
-
- public static void setLastIntent(SafeIntent intent) {
- sIntent = intent;
- }
-
- public static void setupGeckoEnvironment(Context context, String[] pluginDirs, String profilePath) {
- // if we have an intent (we're being launched by an activity)
- // read in any environmental variables from it here
- final SafeIntent intent = sIntent;
- if (intent != null) {
- String env = intent.getStringExtra("env0");
- Log.d(LOGTAG, "Gecko environment env0: " + env);
- for (int c = 1; env != null; c++) {
- putenv(env);
- env = intent.getStringExtra("env" + c);
- Log.d(LOGTAG, "env" + c + ": " + env);
- }
- }
-
- putenv("MOZ_ANDROID_PACKAGE_NAME=" + context.getPackageName());
-
- setupPluginEnvironment(context, pluginDirs);
- setupDownloadEnvironment(context);
-
- // profile home path
- putenv("HOME=" + profilePath);
-
- // setup the tmp path
- File f = getTmpDir(context);
- if (!f.exists()) {
- f.mkdirs();
- }
- putenv("TMPDIR=" + f.getPath());
-
- // setup the downloads path
- f = Environment.getDownloadCacheDirectory();
- putenv("EXTERNAL_STORAGE=" + f.getPath());
-
- // setup the app-specific cache path
- f = context.getCacheDir();
- putenv("CACHE_DIRECTORY=" + f.getPath());
-
- if (AppConstants.Versions.feature17Plus) {
- android.os.UserManager um = (android.os.UserManager)context.getSystemService(Context.USER_SERVICE);
- if (um != null) {
- putenv("MOZ_ANDROID_USER_SERIAL_NUMBER=" + um.getSerialNumberForUser(android.os.Process.myUserHandle()));
- } else {
- Log.d(LOGTAG, "Unable to obtain user manager service on a device with SDK version " + Build.VERSION.SDK_INT);
- }
- }
- setupLocaleEnvironment();
-
- // We don't need this any more.
- sIntent = null;
- }
-
- private static void loadLibsSetupLocked(Context context) {
- // The package data lib directory isn't placed in ld.so's
- // search path, so we have to manually load libraries that
- // libxul will depend on. Not ideal.
-
- File cacheFile = getCacheDir(context);
- putenv("GRE_HOME=" + getGREDir(context).getPath());
-
- // setup the libs cache
- String linkerCache = System.getenv("MOZ_LINKER_CACHE");
- if (linkerCache == null) {
- linkerCache = cacheFile.getPath();
- putenv("MOZ_LINKER_CACHE=" + linkerCache);
- }
-
- // Disable on-demand decompression of the linker on devices where it
- // is known to cause crashes.
- String forced_ondemand = System.getenv("MOZ_LINKER_ONDEMAND");
- if (forced_ondemand == null) {
- if ("HTC".equals(android.os.Build.MANUFACTURER) &&
- "HTC Vision".equals(android.os.Build.MODEL)) {
- putenv("MOZ_LINKER_ONDEMAND=0");
- }
- }
-
- putenv("MOZ_LINKER_EXTRACT=1");
- }
-
- @RobocopTarget
- public synchronized static void loadSQLiteLibs(final Context context, final String apkName) {
- if (sSQLiteLibsLoaded) {
- return;
- }
-
- loadMozGlue(context);
- loadLibsSetupLocked(context);
- loadSQLiteLibsNative(apkName);
- sSQLiteLibsLoaded = true;
- }
-
- public synchronized static void loadNSSLibs(final Context context, final String apkName) {
- if (sNSSLibsLoaded) {
- return;
- }
-
- loadMozGlue(context);
- loadLibsSetupLocked(context);
- loadNSSLibsNative(apkName);
- sNSSLibsLoaded = true;
- }
-
- @SuppressWarnings("deprecation")
- private static final String getCPUABI() {
- return android.os.Build.CPU_ABI;
- }
-
- /**
- * Copy a library out of our APK.
- *
- * @param context a Context.
- * @param lib the name of the library; e.g., "mozglue".
- * @param outDir the output directory for the .so. No trailing slash.
- * @return true on success, false on failure.
- */
- private static boolean extractLibrary(final Context context, final String lib, final String outDir) {
- final String apkPath = context.getApplicationInfo().sourceDir;
-
- // Sanity check.
- if (!apkPath.endsWith(".apk")) {
- Log.w(LOGTAG, "sourceDir is not an APK.");
- return false;
- }
-
- // Try to extract the named library from the APK.
- File outDirFile = new File(outDir);
- if (!outDirFile.isDirectory()) {
- if (!outDirFile.mkdirs()) {
- Log.e(LOGTAG, "Couldn't create " + outDir);
- return false;
- }
- }
-
- if (AppConstants.Versions.feature21Plus) {
- String[] abis = Build.SUPPORTED_ABIS;
- for (String abi : abis) {
- if (tryLoadWithABI(lib, outDir, apkPath, abi)) {
- return true;
- }
- }
- return false;
- } else {
- final String abi = getCPUABI();
- return tryLoadWithABI(lib, outDir, apkPath, abi);
- }
- }
-
- private static boolean tryLoadWithABI(String lib, String outDir, String apkPath, String abi) {
- try {
- final ZipFile zipFile = new ZipFile(new File(apkPath));
- try {
- final String libPath = "lib/" + abi + "/lib" + lib + ".so";
- final ZipEntry entry = zipFile.getEntry(libPath);
- if (entry == null) {
- Log.w(LOGTAG, libPath + " not found in APK " + apkPath);
- return false;
- }
-
- final InputStream in = zipFile.getInputStream(entry);
- try {
- final String outPath = outDir + "/lib" + lib + ".so";
- final FileOutputStream out = new FileOutputStream(outPath);
- final byte[] bytes = new byte[1024];
- int read;
-
- Log.d(LOGTAG, "Copying " + libPath + " to " + outPath);
- boolean failed = false;
- try {
- while ((read = in.read(bytes, 0, 1024)) != -1) {
- out.write(bytes, 0, read);
- }
- } catch (Exception e) {
- Log.w(LOGTAG, "Failing library copy.", e);
- failed = true;
- } finally {
- out.close();
- }
-
- if (failed) {
- // Delete the partial copy so we don't fail to load it.
- // Don't bother to check the return value -- there's nothing
- // we can do about a failure.
- new File(outPath).delete();
- } else {
- // Mark the file as executable. This doesn't seem to be
- // necessary for the loader, but it's the normal state of
- // affairs.
- Log.d(LOGTAG, "Marking " + outPath + " as executable.");
- new File(outPath).setExecutable(true);
- }
-
- return !failed;
- } finally {
- in.close();
- }
- } finally {
- zipFile.close();
- }
- } catch (Exception e) {
- Log.e(LOGTAG, "Failed to extract lib from APK.", e);
- return false;
- }
- }
-
- private static String getLoadDiagnostics(final Context context, final String lib) {
- final String androidPackageName = context.getPackageName();
-
- final StringBuilder message = new StringBuilder("LOAD ");
- message.append(lib);
-
- // These might differ. If so, we know why the library won't load!
- message.append(": ABI: " + AppConstants.MOZ_APP_ABI + ", " + getCPUABI());
- message.append(": Data: " + context.getApplicationInfo().dataDir);
- try {
- final boolean appLibExists = new File("/data/app-lib/" + androidPackageName + "/lib" + lib + ".so").exists();
- final boolean dataDataExists = new File("/data/data/" + androidPackageName + "/lib/lib" + lib + ".so").exists();
- message.append(", ax=" + appLibExists);
- message.append(", ddx=" + dataDataExists);
- } catch (Throwable e) {
- message.append(": ax/ddx fail, ");
- }
-
- try {
- final String dashOne = "/data/data/" + androidPackageName + "-1";
- final String dashTwo = "/data/data/" + androidPackageName + "-2";
- final boolean dashOneExists = new File(dashOne).exists();
- final boolean dashTwoExists = new File(dashTwo).exists();
- message.append(", -1x=" + dashOneExists);
- message.append(", -2x=" + dashTwoExists);
- } catch (Throwable e) {
- message.append(", dash fail, ");
- }
-
- try {
- if (Build.VERSION.SDK_INT >= 9) {
- final String nativeLibPath = context.getApplicationInfo().nativeLibraryDir;
- final boolean nativeLibDirExists = new File(nativeLibPath).exists();
- final boolean nativeLibLibExists = new File(nativeLibPath + "/lib" + lib + ".so").exists();
-
- message.append(", nativeLib: " + nativeLibPath);
- message.append(", dirx=" + nativeLibDirExists);
- message.append(", libx=" + nativeLibLibExists);
- } else {
- message.append(", <pre-9>");
- }
- } catch (Throwable e) {
- message.append(", nativeLib fail.");
- }
-
- return message.toString();
- }
-
- private static final boolean attemptLoad(final String path) {
- try {
- System.load(path);
- return true;
- } catch (Throwable e) {
- Log.wtf(LOGTAG, "Couldn't load " + path + ": " + e);
- }
-
- return false;
- }
-
- /**
- * The first two attempts at loading a library: directly, and
- * then using the app library path.
- *
- * Returns null or the cause exception.
- */
- private static final Throwable doLoadLibraryExpected(final Context context, final String lib) {
- try {
- // Attempt 1: the way that should work.
- System.loadLibrary(lib);
- return null;
- } catch (Throwable e) {
- Log.wtf(LOGTAG, "Couldn't load " + lib + ". Trying native library dir.");
-
- if (Build.VERSION.SDK_INT < 9) {
- // We can't use nativeLibraryDir.
- return e;
- }
-
- // Attempt 2: use nativeLibraryDir, which should also work.
- final String libDir = context.getApplicationInfo().nativeLibraryDir;
- final String libPath = libDir + "/lib" + lib + ".so";
-
- // Does it even exist?
- if (new File(libPath).exists()) {
- if (attemptLoad(libPath)) {
- // Success!
- return null;
- }
- Log.wtf(LOGTAG, "Library exists but couldn't load!");
- } else {
- Log.wtf(LOGTAG, "Library doesn't exist when it should.");
- }
-
- // We failed. Return the original cause.
- return e;
- }
- }
-
- public static void doLoadLibrary(final Context context, final String lib) {
- final Throwable e = doLoadLibraryExpected(context, lib);
- if (e == null) {
- // Success.
- return;
- }
-
- // If we're in a mismatched UID state (Bug 1042935 Comment 16) there's really
- // nothing we can do.
- if (Build.VERSION.SDK_INT >= 9) {
- final String nativeLibPath = context.getApplicationInfo().nativeLibraryDir;
- if (nativeLibPath.contains("mismatched_uid")) {
- throw new RuntimeException("Fatal: mismatched UID: cannot load.");
- }
- }
-
- // Attempt 3: try finding the path the pseudo-supported way using .dataDir.
- final String dataLibPath = context.getApplicationInfo().dataDir + "/lib/lib" + lib + ".so";
- if (attemptLoad(dataLibPath)) {
- return;
- }
-
- // Attempt 4: use /data/app-lib directly. This is a last-ditch effort.
- final String androidPackageName = context.getPackageName();
- if (attemptLoad("/data/app-lib/" + androidPackageName + "/lib" + lib + ".so")) {
- return;
- }
-
- // Attempt 5: even more optimistic.
- if (attemptLoad("/data/data/" + androidPackageName + "/lib/lib" + lib + ".so")) {
- return;
- }
-
- // Look in our files directory, copying from the APK first if necessary.
- final String filesLibDir = context.getFilesDir() + "/lib";
- final String filesLibPath = filesLibDir + "/lib" + lib + ".so";
- if (new File(filesLibPath).exists()) {
- if (attemptLoad(filesLibPath)) {
- return;
- }
- } else {
- // Try copying.
- if (extractLibrary(context, lib, filesLibDir)) {
- // Let's try it!
- if (attemptLoad(filesLibPath)) {
- return;
- }
- }
- }
-
- // Give up loudly, leaking information to debug the failure.
- final String message = getLoadDiagnostics(context, lib);
- Log.e(LOGTAG, "Load diagnostics: " + message);
-
- // Throw the descriptive message, using the original library load
- // failure as the cause.
- throw new RuntimeException(message, e);
- }
-
- public synchronized static void loadMozGlue(final Context context) {
- if (sMozGlueLoaded) {
- return;
- }
-
- doLoadLibrary(context, "mozglue");
- sMozGlueLoaded = true;
- }
-
- public synchronized static void loadGeckoLibs(final Context context, final String apkName) {
- loadLibsSetupLocked(context);
- loadGeckoLibsNative(apkName);
- }
-
- public synchronized static void extractGeckoLibs(final Context context, final String apkName) {
- loadLibsSetupLocked(context);
- try {
- extractGeckoLibsNative(apkName);
- } catch (Exception e) {
- Log.e(LOGTAG, "Failing library extraction.", e);
- }
- }
-
- private static void setupLocaleEnvironment() {
- putenv("LANG=" + Locale.getDefault().toString());
- NumberFormat nf = NumberFormat.getInstance();
- if (nf instanceof DecimalFormat) {
- DecimalFormat df = (DecimalFormat)nf;
- DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
-
- putenv("LOCALE_DECIMAL_POINT=" + dfs.getDecimalSeparator());
- putenv("LOCALE_THOUSANDS_SEP=" + dfs.getGroupingSeparator());
- putenv("LOCALE_GROUPING=" + (char)df.getGroupingSize());
- }
- }
-
- @SuppressWarnings("serial")
- public static class AbortException extends Exception {
- public AbortException(String msg) {
- super(msg);
- }
- }
-
- @JNITarget
- public static void abort(final String msg) {
- final Thread thread = Thread.currentThread();
- final Thread.UncaughtExceptionHandler uncaughtHandler =
- thread.getUncaughtExceptionHandler();
- if (uncaughtHandler != null) {
- uncaughtHandler.uncaughtException(thread, new AbortException(msg));
- }
- }
-
- // These methods are implemented in mozglue/android/nsGeckoUtils.cpp
- private static native void putenv(String map);
-
- // These methods are implemented in mozglue/android/APKOpen.cpp
- public static native void nativeRun(String args);
- private static native void loadGeckoLibsNative(String apkName);
- private static native void loadSQLiteLibsNative(String apkName);
- private static native void loadNSSLibsNative(String apkName);
- private static native void extractGeckoLibsNative(String apkName);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/JNIObject.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/JNIObject.java
deleted file mode 100644
index a3a127a1a..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/JNIObject.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.mozilla.gecko.mozglue;
-
-// Class that all classes with native methods extend from.
-public abstract class JNIObject
-{
- // Pointer to a WeakPtr object that refers to the native object.
- private long mHandle;
-
- // Dispose of any reference to a native object.
- protected abstract void disposeNative();
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeReference.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeReference.java
deleted file mode 100644
index 9d897d384..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeReference.java
+++ /dev/null
@@ -1,13 +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.mozglue;
-
-public interface NativeReference
-{
- public void release();
-
- public boolean isReleased();
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeZip.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeZip.java
deleted file mode 100644
index 11241c575..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/NativeZip.java
+++ /dev/null
@@ -1,84 +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.mozglue;
-
-import android.support.annotation.Keep;
-import org.mozilla.gecko.annotation.JNITarget;
-
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.util.zip.Inflater;
-import java.util.zip.InflaterInputStream;
-
-public class NativeZip implements NativeReference {
- private static final int DEFLATE = 8;
- private static final int STORE = 0;
-
- private volatile long mObj;
- @Keep
- private InputStream mInput;
-
- public NativeZip(String path) {
- mObj = getZip(path);
- }
-
- public NativeZip(InputStream input) {
- if (!(input instanceof ByteBufferInputStream)) {
- throw new IllegalArgumentException("Got " + input.getClass()
- + ", but expected ByteBufferInputStream!");
- }
- ByteBufferInputStream bbinput = (ByteBufferInputStream)input;
- mObj = getZipFromByteBuffer(bbinput.mBuf);
- mInput = input;
- }
-
- @Override
- protected void finalize() {
- release();
- }
-
- @Override
- public void release() {
- if (mObj != 0) {
- _release(mObj);
- mObj = 0;
- }
- mInput = null;
- }
-
- @Override
- public boolean isReleased() {
- return (mObj == 0);
- }
-
- public InputStream getInputStream(String path) {
- if (isReleased()) {
- throw new IllegalStateException("Can't get path \"" + path
- + "\" because NativeZip is closed!");
- }
- return _getInputStream(mObj, path);
- }
-
- private static native long getZip(String path);
- private static native long getZipFromByteBuffer(ByteBuffer buffer);
- private static native void _release(long obj);
- private native InputStream _getInputStream(long obj, String path);
-
- @JNITarget
- private InputStream createInputStream(ByteBuffer buffer, int compression) {
- if (compression != STORE && compression != DEFLATE) {
- throw new IllegalArgumentException("Unexpected compression: " + compression);
- }
-
- InputStream input = new ByteBufferInputStream(buffer, this);
- if (compression == DEFLATE) {
- Inflater inflater = new Inflater(true);
- input = new InflaterInputStream(input, inflater);
- }
-
- return input;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java
deleted file mode 100644
index 6942962fe..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java
+++ /dev/null
@@ -1,134 +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/.
- */
-
-// This should be in util/, but is here because of build dependency issues.
-package org.mozilla.gecko.mozglue;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.util.ArrayList;
-
-/**
- * External applications can pass values into Intents that can cause us to crash: in defense,
- * we wrap {@link Intent} and catch the exceptions they may force us to throw. See bug 1090385
- * for more.
- */
-public class SafeIntent {
- private static final String LOGTAG = "Gecko" + SafeIntent.class.getSimpleName();
-
- private final Intent intent;
-
- public SafeIntent(final Intent intent) {
- this.intent = intent;
- }
-
- public boolean hasExtra(String name) {
- try {
- return intent.hasExtra(name);
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't determine if intent had an extra: OOM. Malformed?");
- return false;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't determine if intent had an extra.", e);
- return false;
- }
- }
-
- public boolean getBooleanExtra(final String name, final boolean defaultValue) {
- try {
- return intent.getBooleanExtra(name, defaultValue);
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
- return defaultValue;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't get intent extras.", e);
- return defaultValue;
- }
- }
-
- public int getIntExtra(final String name, final int defaultValue) {
- try {
- return intent.getIntExtra(name, defaultValue);
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
- return defaultValue;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't get intent extras.", e);
- return defaultValue;
- }
- }
-
- public String getStringExtra(final String name) {
- try {
- return intent.getStringExtra(name);
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
- return null;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't get intent extras.", e);
- return null;
- }
- }
-
- public Bundle getBundleExtra(final String name) {
- try {
- return intent.getBundleExtra(name);
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?");
- return null;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't get intent extras.", e);
- return null;
- }
- }
-
- public String getAction() {
- return intent.getAction();
- }
-
- public String getDataString() {
- try {
- return intent.getDataString();
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't get intent data string: OOM. Malformed?");
- return null;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't get intent data string.", e);
- return null;
- }
- }
-
- public ArrayList<String> getStringArrayListExtra(final String name) {
- try {
- return intent.getStringArrayListExtra(name);
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't get intent data string: OOM. Malformed?");
- return null;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't get intent data string.", e);
- return null;
- }
- }
-
- public Uri getData() {
- try {
- return intent.getData();
- } catch (OutOfMemoryError e) {
- Log.w(LOGTAG, "Couldn't get intent data: OOM. Malformed?");
- return null;
- } catch (RuntimeException e) {
- Log.w(LOGTAG, "Couldn't get intent data.", e);
- return null;
- }
- }
-
- public Intent getUnsafe() {
- return intent;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionBlock.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionBlock.java
deleted file mode 100644
index a4d72f258..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionBlock.java
+++ /dev/null
@@ -1,133 +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.permissions;
-
-import org.mozilla.gecko.util.ThreadUtils;
-
-import android.app.Activity;
-import android.content.Context;
-import android.support.annotation.NonNull;
-
-/**
- * Helper class to run code blocks depending on whether a user has granted or denied certain runtime permissions.
- */
-public class PermissionBlock {
- private final PermissionsHelper helper;
-
- private Context context;
- private String[] permissions;
- private boolean onUIThread;
- private Runnable onPermissionsGranted;
- private Runnable onPermissionsDenied;
- private boolean doNotPrompt;
-
- /* package-private */ PermissionBlock(Context context, PermissionsHelper helper) {
- this.context = context;
- this.helper = helper;
- }
-
- /**
- * Determine whether the app has been granted the specified permissions.
- */
- public PermissionBlock withPermissions(@NonNull String... permissions) {
- this.permissions = permissions;
- return this;
- }
-
- /**
- * Execute all callbacks on the UI thread.
- */
- public PermissionBlock onUIThread() {
- this.onUIThread = true;
- return this;
- }
-
- /**
- * Do not prompt the user to accept the permission if it has not been granted yet.
- */
- public PermissionBlock doNotPrompt() {
- doNotPrompt = true;
- return this;
- }
-
- /**
- * If the condition is true then do not prompt the user to accept the permission if it has not
- * been granted yet.
- */
- public PermissionBlock doNotPromptIf(boolean condition) {
- if (condition) {
- doNotPrompt();
- }
-
- return this;
- }
-
- /**
- * Execute this permission block. Calling this method will prompt the user if needed.
- */
- public void run() {
- run(null);
- }
-
- /**
- * Execute the specified runnable if the app has been granted all permissions. Calling this method will prompt the
- * user if needed.
- */
- public void run(Runnable onPermissionsGranted) {
- if (!doNotPrompt && !(context instanceof Activity)) {
- throw new IllegalStateException("You need to either specify doNotPrompt() or pass in an Activity context");
- }
-
- this.onPermissionsGranted = onPermissionsGranted;
-
- if (hasPermissions(context)) {
- onPermissionsGranted();
- } else if (doNotPrompt) {
- onPermissionsDenied();
- } else {
- Permissions.prompt((Activity) context, this);
- }
-
- // This reference is no longer needed. Let's clear it now to avoid memory leaks.
- context = null;
- }
-
- /**
- * Execute this fallback if at least one permission has not been granted.
- */
- public PermissionBlock andFallback(@NonNull Runnable onPermissionsDenied) {
- this.onPermissionsDenied = onPermissionsDenied;
- return this;
- }
-
- /* package-private */ void onPermissionsGranted() {
- executeRunnable(onPermissionsGranted);
- }
-
- /* package-private */ void onPermissionsDenied() {
- executeRunnable(onPermissionsDenied);
- }
-
- private void executeRunnable(Runnable runnable) {
- if (runnable == null) {
- return;
- }
-
- if (onUIThread) {
- ThreadUtils.postToUiThread(runnable);
- } else {
- runnable.run();
- }
- }
-
- /* package-private */ String[] getPermissions() {
- return permissions;
- }
-
- /* packacge-private */ boolean hasPermissions(Context context) {
- return helper.hasPermissions(context, permissions);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/Permissions.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/Permissions.java
deleted file mode 100644
index c1b38f61c..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/Permissions.java
+++ /dev/null
@@ -1,210 +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.permissions;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.support.annotation.NonNull;
-
-import org.mozilla.gecko.util.ThreadUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Queue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.FutureTask;
-
-/**
- * Convenience class for checking and prompting for runtime permissions.
- *
- * Example:
- *
- * Permissions.from(activity)
- * .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
- * .onUiThread()
- * .andFallback(onPermissionDenied())
- * .run(onPermissionGranted())
- *
- * This example will run the runnable returned by onPermissionGranted() if the WRITE_EXTERNAL_STORAGE permission is
- * already granted. Otherwise it will prompt the user and run the runnable returned by onPermissionGranted() or
- * onPermissionDenied() depending on whether the user accepted or not. If onUiThread() is specified then all callbacks
- * will be run on the UI thread.
- */
-public class Permissions {
- private static final Queue<PermissionBlock> waiting = new LinkedList<>();
- private static final Queue<PermissionBlock> prompt = new LinkedList<>();
-
- private static PermissionsHelper permissionHelper = new PermissionsHelper();
-
- /**
- * Entry point for checking (and optionally prompting for) runtime permissions.
- *
- * Note: The provided context needs to be an Activity context in order to prompt. Use doNotPrompt()
- * for all other contexts.
- */
- public static PermissionBlock from(@NonNull Context context) {
- return new PermissionBlock(context, permissionHelper);
- }
-
- /**
- * This method will block until the specified permissions have been granted or denied by the user.
- * If needed the user will be prompted.
- *
- * @return true if all of the permissions have been granted. False if any of the permissions have been denied.
- */
- public static boolean waitFor(@NonNull Activity activity, String... permissions) {
- ThreadUtils.assertNotOnUiThread(); // We do not want to block the UI thread.
-
- // This task will block until all of the permissions have been granted
- final FutureTask<Boolean> blockingTask = new FutureTask<>(new Callable<Boolean>() {
- @Override
- public Boolean call() throws Exception {
- return true;
- }
- });
-
- // This runnable will cancel the task if any of the permissions have been denied
- Runnable cancelBlockingTask = new Runnable() {
- @Override
- public void run() {
- blockingTask.cancel(true);
- }
- };
-
- Permissions.from(activity)
- .withPermissions(permissions)
- .andFallback(cancelBlockingTask)
- .run(blockingTask);
-
- try {
- return blockingTask.get();
- } catch (InterruptedException | ExecutionException | CancellationException e) {
- return false;
- }
- }
-
- /**
- * Determine whether you have been granted particular permissions.
- */
- public static boolean has(Context context, String... permissions) {
- return permissionHelper.hasPermissions(context, permissions);
- }
-
- /* package-private */ static void setPermissionHelper(PermissionsHelper permissionHelper) {
- Permissions.permissionHelper = permissionHelper;
- }
-
- /**
- * Callback for Activity.onRequestPermissionsResult(). All activities that prompt for permissions using this class
- * should implement onRequestPermissionsResult() and call this method.
- */
- public static synchronized void onRequestPermissionsResult(@NonNull Activity activity, @NonNull String[] permissions, @NonNull int[] grantResults) {
- processGrantResults(permissions, grantResults);
-
- processQueue(activity, permissions, grantResults);
- }
-
- /* package-private */ static synchronized void prompt(Activity activity, PermissionBlock block) {
- if (prompt.isEmpty()) {
- prompt.add(block);
- showPrompt(activity);
- } else {
- waiting.add(block);
- }
- }
-
- private static synchronized void processGrantResults(@NonNull String[] permissions, @NonNull int[] grantResults) {
- final HashSet<String> grantedPermissions = collectGrantedPermissions(permissions, grantResults);
-
- while (!prompt.isEmpty()) {
- final PermissionBlock block = prompt.poll();
-
- if (allPermissionsGranted(block, grantedPermissions)) {
- block.onPermissionsGranted();
- } else {
- block.onPermissionsDenied();
- }
- }
- }
-
- private static synchronized void processQueue(Activity activity, String[] permissions, int[] grantResults) {
- final HashSet<String> deniedPermissions = collectDeniedPermissions(permissions, grantResults);
-
- while (!waiting.isEmpty()) {
- final PermissionBlock block = waiting.poll();
-
- if (block.hasPermissions(activity)) {
- block.onPermissionsGranted();
- } else {
- if (atLeastOnePermissionDenied(block, deniedPermissions)) {
- // We just prompted the user and one of the permissions of this block has been denied:
- // There's no reason to instantly prompt again; Just reject without prompting.
- block.onPermissionsDenied();
- } else {
- prompt.add(block);
- }
- }
- }
-
- if (!prompt.isEmpty()) {
- showPrompt(activity);
- }
- }
-
- private static synchronized void showPrompt(Activity activity) {
- HashSet<String> permissions = new HashSet<>();
-
- for (PermissionBlock block : prompt) {
- Collections.addAll(permissions, block.getPermissions());
- }
-
- permissionHelper.prompt(activity, permissions.toArray(new String[permissions.size()]));
- }
-
- private static HashSet<String> collectGrantedPermissions(@NonNull String[] permissions, @NonNull int[] grantResults) {
- return filterPermissionsByResult(permissions, grantResults, PackageManager.PERMISSION_GRANTED);
- }
-
- private static HashSet<String> collectDeniedPermissions(@NonNull String[] permissions, @NonNull int[] grantResults) {
- return filterPermissionsByResult(permissions, grantResults, PackageManager.PERMISSION_DENIED);
- }
-
- private static HashSet<String> filterPermissionsByResult(@NonNull String[] permissions, @NonNull int[] grantResults, int result) {
- HashSet<String> grantedPermissions = new HashSet<>(permissions.length);
- for (int i = 0; i < permissions.length; i++) {
- if (grantResults[i] == result) {
- grantedPermissions.add(permissions[i]);
- }
- }
- return grantedPermissions;
- }
-
- private static boolean allPermissionsGranted(PermissionBlock block, HashSet<String> grantedPermissions) {
- for (String permission : block.getPermissions()) {
- if (!grantedPermissions.contains(permission)) {
- return false;
- }
- }
-
- return true;
- }
-
- private static boolean atLeastOnePermissionDenied(PermissionBlock block, HashSet<String> deniedPermissions) {
- for (String permission : block.getPermissions()) {
- if (deniedPermissions.contains(permission)) {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionsHelper.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionsHelper.java
deleted file mode 100644
index 945a81f43..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/permissions/PermissionsHelper.java
+++ /dev/null
@@ -1,32 +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.permissions;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.content.ContextCompat;
-
-/* package-private */ class PermissionsHelper {
- private static final int PERMISSIONS_REQUEST_CODE = 212;
-
- public boolean hasPermissions(Context context, String... permissions) {
- for (String permission : permissions) {
- final int permissionCheck = ContextCompat.checkSelfPermission(context, permission);
-
- if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
- return false;
- }
- }
-
- return true;
- }
-
- public void prompt(Activity activity, String[] permissions) {
- ActivityCompat.requestPermissions(activity, permissions, PERMISSIONS_REQUEST_CODE);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/ByteBufferInputStream.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/ByteBufferInputStream.java
deleted file mode 100644
index f6b16619f..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/ByteBufferInputStream.java
+++ /dev/null
@@ -1,38 +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.sqlite;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-/*
- * Helper class to make the ByteBuffers returned by SQLite BLOB
- * easier to use.
- */
-public class ByteBufferInputStream extends InputStream {
- private final ByteBuffer mByteBuffer;
-
- public ByteBufferInputStream(ByteBuffer aByteBuffer) {
- mByteBuffer = aByteBuffer;
- }
-
- @Override
- public synchronized int read() throws IOException {
- if (!mByteBuffer.hasRemaining()) {
- return -1;
- }
- return mByteBuffer.get();
- }
-
- @Override
- public synchronized int read(byte[] aBytes, int aOffset, int aLen)
- throws IOException {
- int toRead = Math.min(aLen, mByteBuffer.remaining());
- mByteBuffer.get(aBytes, aOffset, toRead);
- return toRead;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/MatrixBlobCursor.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/MatrixBlobCursor.java
deleted file mode 100644
index 3e2023c86..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/MatrixBlobCursor.java
+++ /dev/null
@@ -1,366 +0,0 @@
-/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.mozilla.gecko.sqlite;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-
-import org.mozilla.gecko.annotation.JNITarget;
-import org.mozilla.gecko.AppConstants;
-
-import android.database.AbstractCursor;
-import android.database.CursorIndexOutOfBoundsException;
-import android.util.Log;
-
-/**
- * A mutable cursor implementation backed by an array of {@code Object}s. Use
- * {@link #newRow()} to add rows. Automatically expands internal capacity
- * as needed.
- *
- * This class provides one missing feature from Android's MatrixCursor:
- * the implementation of getBlob that was inadvertently omitted from API 9 (and
- * perhaps later; it's present in 14).
- *
- * MatrixCursor is all private, so we entirely duplicate it here.
- */
-public class MatrixBlobCursor extends AbstractCursor {
- private static final String LOGTAG = "GeckoMatrixCursor";
-
- private final String[] columnNames;
- private final int columnCount;
-
- private int rowCount;
- private Throwable allocationStack;
-
- Object[] data;
-
- /**
- * Constructs a new cursor with the given initial capacity.
- *
- * @param columnNames names of the columns, the ordering of which
- * determines column ordering elsewhere in this cursor
- * @param initialCapacity in rows
- */
- @JNITarget
- public MatrixBlobCursor(String[] columnNames, int initialCapacity) {
- this.columnNames = columnNames;
- this.columnCount = columnNames.length;
-
- if (initialCapacity < 1) {
- initialCapacity = 1;
- }
-
- this.data = new Object[columnCount * initialCapacity];
- if (AppConstants.DEBUG_BUILD) {
- this.allocationStack = new Throwable("allocationStack");
- }
- }
-
- /**
- * Constructs a new cursor.
- *
- * @param columnNames names of the columns, the ordering of which
- * determines column ordering elsewhere in this cursor
- */
- @JNITarget
- public MatrixBlobCursor(String[] columnNames) {
- this(columnNames, 16);
- }
-
- /**
- * Closes the Cursor, releasing all of its resources.
- */
- public void close() {
- this.allocationStack = null;
- this.data = null;
- super.close();
- }
-
- /**
- * Gets value at the given column for the current row.
- */
- protected Object get(int column) {
- if (column < 0 || column >= columnCount) {
- throw new CursorIndexOutOfBoundsException("Requested column: "
- + column + ", # of columns: " + columnCount);
- }
- if (mPos < 0) {
- throw new CursorIndexOutOfBoundsException("Before first row.");
- }
- if (mPos >= rowCount) {
- throw new CursorIndexOutOfBoundsException("After last row.");
- }
- return data[mPos * columnCount + column];
- }
-
- /**
- * Adds a new row to the end and returns a builder for that row. Not safe
- * for concurrent use.
- *
- * @return builder which can be used to set the column values for the new
- * row
- */
- public RowBuilder newRow() {
- rowCount++;
- int endIndex = rowCount * columnCount;
- ensureCapacity(endIndex);
- int start = endIndex - columnCount;
- return new RowBuilder(start, endIndex);
- }
-
- /**
- * Adds a new row to the end with the given column values. Not safe
- * for concurrent use.
- *
- * @throws IllegalArgumentException if {@code columnValues.length !=
- * columnNames.length}
- * @param columnValues in the same order as the the column names specified
- * at cursor construction time
- */
- @JNITarget
- public void addRow(Object[] columnValues) {
- if (columnValues.length != columnCount) {
- throw new IllegalArgumentException("columnNames.length = "
- + columnCount + ", columnValues.length = "
- + columnValues.length);
- }
-
- int start = rowCount++ * columnCount;
- ensureCapacity(start + columnCount);
- System.arraycopy(columnValues, 0, data, start, columnCount);
- }
-
- /**
- * Adds a new row to the end with the given column values. Not safe
- * for concurrent use.
- *
- * @throws IllegalArgumentException if {@code columnValues.size() !=
- * columnNames.length}
- * @param columnValues in the same order as the the column names specified
- * at cursor construction time
- */
- @JNITarget
- public void addRow(Iterable<?> columnValues) {
- final int start = rowCount * columnCount;
-
- if (columnValues instanceof ArrayList<?>) {
- addRow((ArrayList<?>) columnValues, start);
- return;
- }
-
- final int end = start + columnCount;
- int current = start;
-
- ensureCapacity(end);
- final Object[] localData = data;
- for (Object columnValue : columnValues) {
- if (current == end) {
- // TODO: null out row?
- throw new IllegalArgumentException(
- "columnValues.size() > columnNames.length");
- }
- localData[current++] = columnValue;
- }
-
- if (current != end) {
- // TODO: null out row?
- throw new IllegalArgumentException(
- "columnValues.size() < columnNames.length");
- }
-
- // Increase row count here in case we encounter an exception.
- rowCount++;
- }
-
- /** Optimization for {@link ArrayList}. */
- @JNITarget
- private void addRow(ArrayList<?> columnValues, int start) {
- final int size = columnValues.size();
- if (size != columnCount) {
- throw new IllegalArgumentException("columnNames.length = "
- + columnCount + ", columnValues.size() = " + size);
- }
-
- final int end = start + columnCount;
- ensureCapacity(end);
-
- // Take a reference just in case someone calls ensureCapacity
- // and `data` gets replaced by a new array!
- final Object[] localData = data;
- for (int i = 0; i < size; i++) {
- localData[start + i] = columnValues.get(i);
- }
-
- rowCount++;
- }
-
- /**
- * Ensures that this cursor has enough capacity. If it needs to allocate
- * a new array, the existing capacity will be at least doubled.
- */
- private void ensureCapacity(final int size) {
- if (size <= data.length) {
- return;
- }
-
- final Object[] oldData = this.data;
- this.data = new Object[Math.max(size, data.length * 2)];
- System.arraycopy(oldData, 0, this.data, 0, oldData.length);
- }
-
- /**
- * Builds a row, starting from the left-most column and adding one column
- * value at a time. Follows the same ordering as the column names specified
- * at cursor construction time.
- *
- * Not thread-safe.
- */
- public class RowBuilder {
- private int index;
- private final int endIndex;
-
- RowBuilder(int index, int endIndex) {
- this.index = index;
- this.endIndex = endIndex;
- }
-
- /**
- * Sets the next column value in this row.
- *
- * @throws CursorIndexOutOfBoundsException if you try to add too many
- * values
- * @return this builder to support chaining
- */
- public RowBuilder add(final Object columnValue) {
- if (index == endIndex) {
- throw new CursorIndexOutOfBoundsException("No more columns left.");
- }
-
- data[index++] = columnValue;
- return this;
- }
- }
-
- /**
- * Not thread safe.
- */
- public void set(int column, Object value) {
- if (column < 0 || column >= columnCount) {
- throw new CursorIndexOutOfBoundsException("Requested column: "
- + column + ", # of columns: " + columnCount);
- }
- if (mPos < 0) {
- throw new CursorIndexOutOfBoundsException("Before first row.");
- }
- if (mPos >= rowCount) {
- throw new CursorIndexOutOfBoundsException("After last row.");
- }
- data[mPos * columnCount + column] = value;
- }
-
- // AbstractCursor implementation.
- @Override
- public int getCount() {
- return rowCount;
- }
-
- @Override
- public String[] getColumnNames() {
- return columnNames;
- }
-
- @Override
- public String getString(int column) {
- Object value = get(column);
- if (value == null) return null;
- return value.toString();
- }
-
- @Override
- public short getShort(int column) {
- final Object value = get(column);
- if (value == null) return 0;
- if (value instanceof Number) return ((Number) value).shortValue();
- return Short.parseShort(value.toString());
- }
-
- @Override
- public int getInt(int column) {
- Object value = get(column);
- if (value == null) return 0;
- if (value instanceof Number) return ((Number) value).intValue();
- return Integer.parseInt(value.toString());
- }
-
- @Override
- public long getLong(int column) {
- Object value = get(column);
- if (value == null) return 0;
- if (value instanceof Number) return ((Number) value).longValue();
- return Long.parseLong(value.toString());
- }
-
- @Override
- public float getFloat(int column) {
- Object value = get(column);
- if (value == null) return 0.0f;
- if (value instanceof Number) return ((Number) value).floatValue();
- return Float.parseFloat(value.toString());
- }
-
- @Override
- public double getDouble(int column) {
- Object value = get(column);
- if (value == null) return 0.0d;
- if (value instanceof Number) return ((Number) value).doubleValue();
- return Double.parseDouble(value.toString());
- }
-
- @Override
- public byte[] getBlob(int column) {
- Object value = get(column);
- if (value == null) return null;
- if (value instanceof byte[]) {
- return (byte[]) value;
- }
-
- if (value instanceof ByteBuffer) {
- final ByteBuffer bytes = (ByteBuffer) value;
- byte[] byteArray = new byte[bytes.remaining()];
- bytes.get(byteArray);
- return byteArray;
- }
- throw new UnsupportedOperationException("BLOB Object not of known type");
- }
-
- @Override
- public boolean isNull(int column) {
- return get(column) == null;
- }
-
- @Override
- protected void finalize() {
- if (AppConstants.DEBUG_BUILD) {
- if (!isClosed()) {
- Log.e(LOGTAG, "Cursor finalized without being closed", this.allocationStack);
- }
- }
-
- super.finalize();
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridge.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridge.java
deleted file mode 100644
index 866b9e286..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridge.java
+++ /dev/null
@@ -1,387 +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.sqlite;
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.text.TextUtils;
-import android.util.Log;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Map.Entry;
-
-/*
- * This class allows using the mozsqlite3 library included with Firefox
- * to read SQLite databases, instead of the Android SQLiteDataBase API,
- * which might use whatever outdated DB is present on the Android system.
- */
-public class SQLiteBridge {
- private static final String LOGTAG = "SQLiteBridge";
-
- // Path to the database. If this database was not opened with openDatabase, we reopen it every query.
- private final String mDb;
-
- // Pointer to the database if it was opened with openDatabase. 0 implies closed.
- protected volatile long mDbPointer;
-
- // Values remembered after a query.
- private long[] mQueryResults;
-
- private boolean mTransactionSuccess;
- private boolean mInTransaction;
-
- private static final int RESULT_INSERT_ROW_ID = 0;
- private static final int RESULT_ROWS_CHANGED = 1;
-
- // Shamelessly cribbed from db/sqlite3/src/moz.build.
- private static final int DEFAULT_PAGE_SIZE_BYTES = 32768;
-
- // The same size we use elsewhere.
- private static final int MAX_WAL_SIZE_BYTES = 524288;
-
- // JNI code in $(topdir)/mozglue/android/..
- private static native MatrixBlobCursor sqliteCall(String aDb, String aQuery,
- String[] aParams,
- long[] aUpdateResult)
- throws SQLiteBridgeException;
- private static native MatrixBlobCursor sqliteCallWithDb(long aDb, String aQuery,
- String[] aParams,
- long[] aUpdateResult)
- throws SQLiteBridgeException;
- private static native long openDatabase(String aDb)
- throws SQLiteBridgeException;
- private static native void closeDatabase(long aDb);
-
- // Takes the path to the database we want to access.
- @RobocopTarget
- public SQLiteBridge(String aDb) throws SQLiteBridgeException {
- mDb = aDb;
- }
-
- // Executes a simple line of sql.
- public void execSQL(String sql)
- throws SQLiteBridgeException {
- Cursor cursor = internalQuery(sql, null);
- cursor.close();
- }
-
- // Executes a simple line of sql. Allow you to bind arguments
- public void execSQL(String sql, String[] bindArgs)
- throws SQLiteBridgeException {
- Cursor cursor = internalQuery(sql, bindArgs);
- cursor.close();
- }
-
- // Executes a DELETE statement on the database
- public int delete(String table, String whereClause, String[] whereArgs)
- throws SQLiteBridgeException {
- StringBuilder sb = new StringBuilder("DELETE from ");
- sb.append(table);
- if (whereClause != null) {
- sb.append(" WHERE " + whereClause);
- }
-
- execSQL(sb.toString(), whereArgs);
- return (int)mQueryResults[RESULT_ROWS_CHANGED];
- }
-
- public Cursor query(String table,
- String[] columns,
- String selection,
- String[] selectionArgs,
- String groupBy,
- String having,
- String orderBy,
- String limit)
- throws SQLiteBridgeException {
- StringBuilder sb = new StringBuilder("SELECT ");
- if (columns != null)
- sb.append(TextUtils.join(", ", columns));
- else
- sb.append(" * ");
-
- sb.append(" FROM ");
- sb.append(table);
-
- if (selection != null) {
- sb.append(" WHERE " + selection);
- }
-
- if (groupBy != null) {
- sb.append(" GROUP BY " + groupBy);
- }
-
- if (having != null) {
- sb.append(" HAVING " + having);
- }
-
- if (orderBy != null) {
- sb.append(" ORDER BY " + orderBy);
- }
-
- if (limit != null) {
- sb.append(" " + limit);
- }
-
- return rawQuery(sb.toString(), selectionArgs);
- }
-
- @RobocopTarget
- public Cursor rawQuery(String sql, String[] selectionArgs)
- throws SQLiteBridgeException {
- return internalQuery(sql, selectionArgs);
- }
-
- public long insert(String table, String nullColumnHack, ContentValues values)
- throws SQLiteBridgeException {
- if (values == null)
- return 0;
-
- ArrayList<String> valueNames = new ArrayList<String>();
- ArrayList<String> valueBinds = new ArrayList<String>();
- ArrayList<String> keyNames = new ArrayList<String>();
-
- for (Entry<String, Object> value : values.valueSet()) {
- keyNames.add(value.getKey());
-
- Object val = value.getValue();
- if (val == null) {
- valueNames.add("NULL");
- } else {
- valueNames.add("?");
- valueBinds.add(val.toString());
- }
- }
-
- StringBuilder sb = new StringBuilder("INSERT into ");
- sb.append(table);
-
- sb.append(" (");
- sb.append(TextUtils.join(", ", keyNames));
- sb.append(")");
-
- // XXX - Do we need to bind these values?
- sb.append(" VALUES (");
- sb.append(TextUtils.join(", ", valueNames));
- sb.append(") ");
-
- String[] binds = new String[valueBinds.size()];
- valueBinds.toArray(binds);
- execSQL(sb.toString(), binds);
- return mQueryResults[RESULT_INSERT_ROW_ID];
- }
-
- public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
- throws SQLiteBridgeException {
- if (values == null)
- return 0;
-
- ArrayList<String> valueNames = new ArrayList<String>();
-
- StringBuilder sb = new StringBuilder("UPDATE ");
- sb.append(table);
- sb.append(" SET ");
-
- boolean isFirst = true;
-
- for (Entry<String, Object> value : values.valueSet()) {
- if (isFirst)
- isFirst = false;
- else
- sb.append(", ");
-
- sb.append(value.getKey());
-
- Object val = value.getValue();
- if (val == null) {
- sb.append(" = NULL");
- } else {
- sb.append(" = ?");
- valueNames.add(val.toString());
- }
- }
-
- if (!TextUtils.isEmpty(whereClause)) {
- sb.append(" WHERE ");
- sb.append(whereClause);
- valueNames.addAll(Arrays.asList(whereArgs));
- }
-
- String[] binds = new String[valueNames.size()];
- valueNames.toArray(binds);
-
- execSQL(sb.toString(), binds);
- return (int)mQueryResults[RESULT_ROWS_CHANGED];
- }
-
- public int getVersion()
- throws SQLiteBridgeException {
- Cursor cursor = internalQuery("PRAGMA user_version", null);
- int ret = -1;
- if (cursor != null) {
- cursor.moveToFirst();
- String version = cursor.getString(0);
- ret = Integer.parseInt(version);
- cursor.close();
- }
- return ret;
- }
-
- // Do an SQL query, substituting the parameters in the query with the passed
- // parameters. The parameters are substituted in order: named parameters
- // are not supported.
- private Cursor internalQuery(String aQuery, String[] aParams)
- throws SQLiteBridgeException {
-
- mQueryResults = new long[2];
- if (isOpen()) {
- return sqliteCallWithDb(mDbPointer, aQuery, aParams, mQueryResults);
- }
- return sqliteCall(mDb, aQuery, aParams, mQueryResults);
- }
-
- /*
- * The second two parameters here are just provided for compatibility with SQLiteDatabase
- * Support for them is not currently implemented.
- */
- public static SQLiteBridge openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags)
- throws SQLiteException {
- if (factory != null) {
- throw new RuntimeException("factory not supported.");
- }
- if (flags != 0) {
- throw new RuntimeException("flags not supported.");
- }
-
- SQLiteBridge bridge = null;
- try {
- bridge = new SQLiteBridge(path);
- bridge.mDbPointer = SQLiteBridge.openDatabase(path);
- } catch (SQLiteBridgeException ex) {
- // Catch and rethrow as a SQLiteException to match SQLiteDatabase.
- throw new SQLiteException(ex.getMessage());
- }
-
- prepareWAL(bridge);
-
- return bridge;
- }
-
- public void close() {
- if (isOpen()) {
- closeDatabase(mDbPointer);
- }
- mDbPointer = 0L;
- }
-
- public boolean isOpen() {
- return mDbPointer != 0;
- }
-
- public void beginTransaction() throws SQLiteBridgeException {
- if (inTransaction()) {
- throw new SQLiteBridgeException("Nested transactions are not supported");
- }
- execSQL("BEGIN EXCLUSIVE");
- mTransactionSuccess = false;
- mInTransaction = true;
- }
-
- public void beginTransactionNonExclusive() throws SQLiteBridgeException {
- if (inTransaction()) {
- throw new SQLiteBridgeException("Nested transactions are not supported");
- }
- execSQL("BEGIN IMMEDIATE");
- mTransactionSuccess = false;
- mInTransaction = true;
- }
-
- public void endTransaction() {
- if (!inTransaction())
- return;
-
- try {
- if (mTransactionSuccess) {
- execSQL("COMMIT TRANSACTION");
- } else {
- execSQL("ROLLBACK TRANSACTION");
- }
- } catch (SQLiteBridgeException ex) {
- Log.e(LOGTAG, "Error ending transaction", ex);
- }
- mInTransaction = false;
- mTransactionSuccess = false;
- }
-
- public void setTransactionSuccessful() throws SQLiteBridgeException {
- if (!inTransaction()) {
- throw new SQLiteBridgeException("setTransactionSuccessful called outside a transaction");
- }
- mTransactionSuccess = true;
- }
-
- public boolean inTransaction() {
- return mInTransaction;
- }
-
- @Override
- public void finalize() {
- if (isOpen()) {
- Log.e(LOGTAG, "Bridge finalized without closing the database");
- close();
- }
- }
-
- private static void prepareWAL(final SQLiteBridge bridge) {
- // Prepare for WAL mode. If we can, we switch to journal_mode=WAL, then
- // set the checkpoint size appropriately. If we can't, then we fall back
- // to truncating and synchronous writes.
- final Cursor cursor = bridge.internalQuery("PRAGMA journal_mode=WAL", null);
- try {
- if (cursor.moveToFirst()) {
- String journalMode = cursor.getString(0);
- Log.d(LOGTAG, "Journal mode: " + journalMode);
- if ("wal".equals(journalMode)) {
- // Success! Let's make sure we autocheckpoint at a reasonable interval.
- final int pageSizeBytes = bridge.getPageSizeBytes();
- final int checkpointPageCount = MAX_WAL_SIZE_BYTES / pageSizeBytes;
- bridge.execSQL("PRAGMA wal_autocheckpoint=" + checkpointPageCount);
- } else {
- if (!"truncate".equals(journalMode)) {
- Log.w(LOGTAG, "Unable to activate WAL journal mode. Using truncate instead.");
- bridge.execSQL("PRAGMA journal_mode=TRUNCATE");
- }
- Log.w(LOGTAG, "Not using WAL mode: using synchronous=FULL instead.");
- bridge.execSQL("PRAGMA synchronous=FULL");
- }
- }
- } finally {
- cursor.close();
- }
- }
-
- private int getPageSizeBytes() {
- if (!isOpen()) {
- throw new IllegalStateException("Database not open.");
- }
-
- final Cursor cursor = internalQuery("PRAGMA page_size", null);
- try {
- if (!cursor.moveToFirst()) {
- Log.w(LOGTAG, "Unable to retrieve page size.");
- return DEFAULT_PAGE_SIZE_BYTES;
- }
-
- return cursor.getInt(0);
- } finally {
- cursor.close();
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridgeException.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridgeException.java
deleted file mode 100644
index c7999fc5c..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/sqlite/SQLiteBridgeException.java
+++ /dev/null
@@ -1,18 +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.sqlite;
-
-import org.mozilla.gecko.annotation.JNITarget;
-
-@JNITarget
-public class SQLiteBridgeException extends RuntimeException {
- static final long serialVersionUID = 1L;
-
- public SQLiteBridgeException() {}
- public SQLiteBridgeException(String msg) {
- super(msg);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandler.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandler.java
deleted file mode 100644
index 6f40ee96b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandler.java
+++ /dev/null
@@ -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.util;
-
-import android.content.Intent;
-
-public interface ActivityResultHandler {
- void onActivityResult(int resultCode, Intent data);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandlerMap.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandlerMap.java
deleted file mode 100644
index dc1d26cec..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityResultHandlerMap.java
+++ /dev/null
@@ -1,24 +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.util;
-
-import android.util.SparseArray;
-
-public final class ActivityResultHandlerMap {
- private final SparseArray<ActivityResultHandler> mMap = new SparseArray<ActivityResultHandler>();
- private int mCounter;
-
- public synchronized int put(ActivityResultHandler handler) {
- mMap.put(mCounter, handler);
- return mCounter++;
- }
-
- public synchronized ActivityResultHandler getAndRemove(int i) {
- ActivityResultHandler handler = mMap.get(i);
- mMap.delete(i);
-
- return handler;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityUtils.java
deleted file mode 100644
index 2f15e7868..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ActivityUtils.java
+++ /dev/null
@@ -1,72 +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.util;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-
-import org.mozilla.gecko.AppConstants.Versions;
-
-public class ActivityUtils {
- private ActivityUtils() {
- }
-
- public static void setFullScreen(Activity activity, boolean fullscreen) {
- // Hide/show the system notification bar
- Window window = activity.getWindow();
-
- if (Versions.feature16Plus) {
- int newVis;
- if (fullscreen) {
- newVis = View.SYSTEM_UI_FLAG_FULLSCREEN;
- if (Versions.feature19Plus) {
- newVis |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
- } else {
- newVis |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
- }
- } else {
- newVis = View.SYSTEM_UI_FLAG_VISIBLE;
- }
-
- window.getDecorView().setSystemUiVisibility(newVis);
- } else {
- window.setFlags(fullscreen ?
- WindowManager.LayoutParams.FLAG_FULLSCREEN : 0,
- WindowManager.LayoutParams.FLAG_FULLSCREEN);
- }
- }
-
- public static boolean isFullScreen(final Activity activity) {
- final Window window = activity.getWindow();
-
- if (Versions.feature16Plus) {
- final int vis = window.getDecorView().getSystemUiVisibility();
- return (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
- }
-
- final int flags = window.getAttributes().flags;
- return ((flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0);
- }
-
- /**
- * Finish this activity and launch the default home screen activity.
- */
- public static void goToHomeScreen(Activity activity) {
- Intent intent = new Intent(Intent.ACTION_MAIN);
-
- intent.addCategory(Intent.CATEGORY_HOME);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- activity.startActivity(intent);
-
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BundleEventListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BundleEventListener.java
deleted file mode 100644
index 9e9bb5a9e..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/BundleEventListener.java
+++ /dev/null
@@ -1,25 +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.util;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-import android.os.Bundle;
-
-@RobocopTarget
-public interface BundleEventListener {
- /**
- * Handles a message sent from Gecko.
- *
- * @param event The name of the event being sent.
- * @param message The message data.
- * @param callback The callback interface for this message. A callback is provided only if the
- * originating Messaging.sendRequest call included a callback argument;
- * otherwise, callback will be null. All listeners for a given event are given
- * the same callback object, and exactly one listener must handle the callback.
- */
- void handleMessage(String event, Bundle message, EventCallback callback);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java
deleted file mode 100644
index 02b07674f..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java
+++ /dev/null
@@ -1,117 +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.util;
-
-import java.util.concurrent.SynchronousQueue;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.AppConstants.Versions;
-
-import android.content.ClipData;
-import android.content.Context;
-import android.util.Log;
-
-public final class Clipboard {
- // Volatile but not synchronized: we don't care about the race condition in
- // init, because both app contexts will be the same, but we do care about a
- // thread having a stale null value of mContext.
- volatile static Context mContext;
- private final static String LOGTAG = "GeckoClipboard";
- private final static SynchronousQueue<String> sClipboardQueue = new SynchronousQueue<String>();
-
- private Clipboard() {
- }
-
- public static void init(final Context c) {
- if (mContext != null) {
- Log.w(LOGTAG, "Clipboard.init() called twice!");
- return;
- }
- mContext = c.getApplicationContext();
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public static String getText() {
- // If we're on the UI thread or the background thread, we have a looper on the thread
- // and can just call this directly. For any other threads, post the call to the
- // background thread.
-
- if (ThreadUtils.isOnUiThread() || ThreadUtils.isOnBackgroundThread()) {
- return getClipboardTextImpl();
- }
-
- ThreadUtils.postToBackgroundThread(new Runnable() {
- @Override
- public void run() {
- String text = getClipboardTextImpl();
- try {
- sClipboardQueue.put(text != null ? text : "");
- } catch (InterruptedException ie) { }
- }
- });
-
- try {
- return sClipboardQueue.take();
- } catch (InterruptedException ie) {
- return "";
- }
- }
-
- @WrapForJNI(calledFrom = "gecko")
- public static void setText(final CharSequence text) {
- ThreadUtils.postToBackgroundThread(new Runnable() {
- @Override
- public void run() {
- // In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager,
- // which is a subclass of android.text.ClipboardManager.
- final android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
- final ClipData clip = ClipData.newPlainText("Text", text);
- try {
- cm.setPrimaryClip(clip);
- } catch (NullPointerException e) {
- // Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
- // a NullPointerException if Samsung's /data/clipboard directory is full.
- // Fortunately, the text is still successfully copied to the clipboard.
- }
- return;
- }
- });
- }
-
- /**
- * @return true if the clipboard is nonempty, false otherwise.
- */
- @WrapForJNI(calledFrom = "gecko")
- public static boolean hasText() {
- android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
- return cm.hasPrimaryClip();
- }
-
- /**
- * Deletes all text from the clipboard.
- */
- @WrapForJNI(calledFrom = "gecko")
- public static void clearText() {
- setText(null);
- }
-
- /**
- * On some devices, access to the clipboard service needs to happen
- * on a thread with a looper, so this function requires a looper is
- * present on the thread.
- */
- @SuppressWarnings("deprecation")
- static String getClipboardTextImpl() {
- android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
- if (cm.hasPrimaryClip()) {
- ClipData clip = cm.getPrimaryClip();
- if (clip != null) {
- ClipData.Item item = clip.getItemAt(0);
- return item.coerceToText(mContext).toString();
- }
- }
- return null;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContextUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContextUtils.java
deleted file mode 100644
index 3a37911b0..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ContextUtils.java
+++ /dev/null
@@ -1,51 +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.util;
-
-import android.content.Context;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.text.TextUtils;
-
-public class ContextUtils {
- private static final String INSTALLER_GOOGLE_PLAY = "com.android.vending";
-
- private ContextUtils() {}
-
- /**
- * @return {@link android.content.pm.PackageInfo#firstInstallTime} for the context's package.
- * @throws PackageManager.NameNotFoundException Unexpected - we get the package name from the context so
- * it's expected to be found.
- */
- public static PackageInfo getCurrentPackageInfo(final Context context) {
- try {
- return context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
- } catch (PackageManager.NameNotFoundException e) {
- throw new AssertionError("Should not happen: Can't get package info of own package");
- }
- }
-
- public static boolean isPackageInstalled(final Context context, String packageName) {
- try {
- PackageManager pm = context.getPackageManager();
- pm.getPackageInfo(packageName, 0);
- return true;
- } catch (PackageManager.NameNotFoundException e) {
- return false;
- }
- }
-
- public static boolean isInstalledFromGooglePlay(final Context context) {
- final String installerPackageName = context.getPackageManager().getInstallerPackageName(context.getPackageName());
-
- if (TextUtils.isEmpty(installerPackageName)) {
- return false;
- }
-
- return INSTALLER_GOOGLE_PLAY.equals(installerPackageName);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/DateUtil.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/DateUtil.java
deleted file mode 100644
index 9d34a0fe8..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/DateUtil.java
+++ /dev/null
@@ -1,55 +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.util;
-
-import android.support.annotation.NonNull;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Utilities to help with manipulating Java's dates and calendars.
- */
-public class DateUtil {
- private DateUtil() {}
-
- /**
- * @param date the date to convert to HTTP format
- * @return the date as specified in rfc 1123, e.g. "Tue, 01 Feb 2011 14:00:00 GMT"
- */
- public static String getDateInHTTPFormat(@NonNull final Date date) {
- final DateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.US);
- df.setTimeZone(TimeZone.getTimeZone("GMT"));
- return df.format(date);
- }
-
- /**
- * Returns the timezone offset for the current date in minutes. See
- * {@link #getTimezoneOffsetInMinutesForGivenDate(Calendar)} for more details.
- */
- public static int getTimezoneOffsetInMinutes(@NonNull final TimeZone timezone) {
- return getTimezoneOffsetInMinutesForGivenDate(Calendar.getInstance(timezone));
- }
-
- /**
- * Returns the time zone offset for the given date in minutes. The date makes a difference due to daylight
- * savings time in some regions. We return minutes because we can accurately represent time zones that are
- * offset by non-integer hour values, e.g. parts of New Zealand at UTC+12:45.
- *
- * @param calendar A calendar with the appropriate time zone & date already set.
- */
- public static int getTimezoneOffsetInMinutesForGivenDate(@NonNull final Calendar calendar) {
- // via Date.getTimezoneOffset deprecated docs (note: it had incorrect order of operations).
- // Also, we cast to int because we should never overflow here - the max should be GMT+14 = 840.
- return (int) TimeUnit.MILLISECONDS.toMinutes(calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET));
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/EventCallback.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/EventCallback.java
deleted file mode 100644
index 099542666..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/EventCallback.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.mozilla.gecko.util;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-/**
- * Callback interface for Gecko requests.
- *
- * For each instance of EventCallback, exactly one of sendResponse, sendError, or sendCancel
- * must be called to prevent observer leaks. If more than one send* method is called, or if a
- * single send method is called multiple times, an {@link IllegalStateException} will be thrown.
- */
-@RobocopTarget
-public interface EventCallback {
- /**
- * Sends a success response with the given data.
- *
- * @param response The response data to send to Gecko. Can be any of the types accepted by
- * JSONObject#put(String, Object).
- */
- public void sendSuccess(Object response);
-
- /**
- * Sends an error response with the given data.
- *
- * @param response The response data to send to Gecko. Can be any of the types accepted by
- * JSONObject#put(String, Object).
- */
- public void sendError(Object response);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FileUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FileUtils.java
deleted file mode 100644
index 01cdd42bb..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FileUtils.java
+++ /dev/null
@@ -1,259 +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.util;
-
-import android.util.Log;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.FilenameFilter;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-import java.util.Comparator;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-public class FileUtils {
- private static final String LOGTAG = "GeckoFileUtils";
-
- /*
- * A basic Filter for checking a filename and age.
- **/
- static public class NameAndAgeFilter implements FilenameFilter {
- final private String mName;
- final private double mMaxAge;
-
- public NameAndAgeFilter(String name, double age) {
- mName = name;
- mMaxAge = age;
- }
-
- @Override
- public boolean accept(File dir, String filename) {
- if (mName == null || mName.matches(filename)) {
- File f = new File(dir, filename);
-
- if (mMaxAge < 0 || System.currentTimeMillis() - f.lastModified() > mMaxAge) {
- return true;
- }
- }
-
- return false;
- }
- }
-
- @RobocopTarget
- public static void delTree(File dir, FilenameFilter filter, boolean recurse) {
- String[] files = null;
-
- if (filter != null) {
- files = dir.list(filter);
- } else {
- files = dir.list();
- }
-
- if (files == null) {
- return;
- }
-
- for (String file : files) {
- File f = new File(dir, file);
- delete(f, recurse);
- }
- }
-
- public static boolean delete(File file) throws IOException {
- return delete(file, true);
- }
-
- public static boolean delete(File file, boolean recurse) {
- if (file.isDirectory() && recurse) {
- // If the quick delete failed and this is a dir, recursively delete the contents of the dir
- String files[] = file.list();
- for (String temp : files) {
- File fileDelete = new File(file, temp);
- try {
- delete(fileDelete);
- } catch (IOException ex) {
- Log.i(LOGTAG, "Error deleting " + fileDelete.getPath(), ex);
- }
- }
- }
-
- // Even if this is a dir, it should now be empty and delete should work
- return file.delete();
- }
-
- /**
- * A generic solution to read a JSONObject from a file. See
- * {@link #readStringFromFile(File)} for more details.
- *
- * @throws IOException if the file is empty, or another IOException occurs
- * @throws JSONException if the file could not be converted to a JSONObject.
- */
- public static JSONObject readJSONObjectFromFile(final File file) throws IOException, JSONException {
- if (file.length() == 0) {
- // Redirect this exception so it's clearer than when the JSON parser catches it.
- throw new IOException("Given file is empty - the JSON parser cannot create an object from an empty file");
- }
- return new JSONObject(readStringFromFile(file));
- }
-
- /**
- * A generic solution to read from a file. For more details,
- * see {@link #readStringFromInputStreamAndCloseStream(InputStream, int)}.
- *
- * This method loads the entire file into memory so will have the expected performance impact.
- * If you're trying to read a large file, you should be handling your own reading to avoid
- * out-of-memory errors.
- */
- public static String readStringFromFile(final File file) throws IOException {
- // FileInputStream will throw FileNotFoundException if the file does not exist, but
- // File.length will return 0 if the file does not exist so we catch it sooner.
- if (!file.exists()) {
- throw new FileNotFoundException("Given file, " + file + ", does not exist");
- } else if (file.length() == 0) {
- return "";
- }
- final int len = (int) file.length(); // includes potential EOF character.
- return readStringFromInputStreamAndCloseStream(new FileInputStream(file), len);
- }
-
- /**
- * A generic solution to read from an input stream in UTF-8. This function will read from the stream until it
- * is finished and close the stream - this is necessary to close the wrapping resources.
- *
- * For a higher-level method, see {@link #readStringFromFile(File)}.
- *
- * Since this is generic, it may not be the most performant for your use case.
- *
- * @param bufferSize Size of the underlying buffer for read optimizations - must be > 0.
- */
- public static String readStringFromInputStreamAndCloseStream(final InputStream inputStream, final int bufferSize)
- throws IOException {
- if (bufferSize <= 0) {
- // Safe close: it's more important to alert the programmer of
- // their error than to let them catch and continue on their way.
- IOUtils.safeStreamClose(inputStream);
- throw new IllegalArgumentException("Expected buffer size larger than 0. Got: " + bufferSize);
- }
-
- final StringBuilder stringBuilder = new StringBuilder(bufferSize);
- final InputStreamReader reader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
- try {
- int charsRead;
- final char[] buffer = new char[bufferSize];
- while ((charsRead = reader.read(buffer, 0, bufferSize)) != -1) {
- stringBuilder.append(buffer, 0, charsRead);
- }
- } finally {
- reader.close();
- }
- return stringBuilder.toString();
- }
-
- /**
- * A generic solution to write a JSONObject to a file.
- * See {@link #writeStringToFile(File, String)} for more details.
- */
- public static void writeJSONObjectToFile(final File file, final JSONObject obj) throws IOException {
- writeStringToFile(file, obj.toString());
- }
-
- /**
- * A generic solution to write to a File - the given file will be overwritten. If it does not exist yet, it will
- * be created. See {@link #writeStringToOutputStreamAndCloseStream(OutputStream, String)} for more details.
- */
- public static void writeStringToFile(final File file, final String str) throws IOException {
- writeStringToOutputStreamAndCloseStream(new FileOutputStream(file, false), str);
- }
-
- /**
- * A generic solution to write to an output stream in UTF-8. The stream will be closed at the
- * completion of this method - it's necessary in order to close the wrapping resources.
- *
- * For a higher-level method, see {@link #writeStringToFile(File, String)}.
- *
- * Since this is generic, it may not be the most performant for your use case.
- */
- public static void writeStringToOutputStreamAndCloseStream(final OutputStream outputStream, final String str)
- throws IOException {
- try {
- final OutputStreamWriter writer = new OutputStreamWriter(outputStream, Charset.forName("UTF-8"));
- try {
- writer.write(str);
- } finally {
- writer.close();
- }
- } finally {
- // OutputStreamWriter.close can throw before closing the
- // underlying stream. For safety, we close here too.
- outputStream.close();
- }
- }
-
- public static class FilenameWhitelistFilter implements FilenameFilter {
- private final Set<String> mFilenameWhitelist;
-
- public FilenameWhitelistFilter(final Set<String> filenameWhitelist) {
- mFilenameWhitelist = filenameWhitelist;
- }
-
- @Override
- public boolean accept(final File dir, final String filename) {
- return mFilenameWhitelist.contains(filename);
- }
- }
-
- public static class FilenameRegexFilter implements FilenameFilter {
- private final Pattern mPattern;
-
- // Each time `Pattern.matcher` is called, a new matcher is created. We can avoid the excessive object creation
- // by caching the returned matcher and calling `Matcher.reset` on it. Since Matcher's are not thread safe,
- // this assumes `FilenameFilter.accept` is not run in parallel (which, according to the source, it is not).
- private Matcher mCachedMatcher;
-
- public FilenameRegexFilter(final Pattern pattern) {
- mPattern = pattern;
- }
-
- @Override
- public boolean accept(final File dir, final String filename) {
- if (mCachedMatcher == null) {
- mCachedMatcher = mPattern.matcher(filename);
- } else {
- mCachedMatcher.reset(filename);
- }
- return mCachedMatcher.matches();
- }
- }
-
- public static class FileLastModifiedComparator implements Comparator<File> {
- @Override
- public int compare(final File lhs, final File rhs) {
- // Long.compare is API 19+.
- final long lhsModified = lhs.lastModified();
- final long rhsModified = rhs.lastModified();
- if (lhsModified < rhsModified) {
- return -1;
- } else if (lhsModified == rhsModified) {
- return 0;
- } else {
- return 1;
- }
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FloatUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FloatUtils.java
deleted file mode 100644
index fbcd7254f..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/FloatUtils.java
+++ /dev/null
@@ -1,43 +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.util;
-
-import android.graphics.PointF;
-
-import java.lang.IllegalArgumentException;
-
-public final class FloatUtils {
- private FloatUtils() {}
-
- public static boolean fuzzyEquals(float a, float b) {
- return (Math.abs(a - b) < 1e-6);
- }
-
- public static boolean fuzzyEquals(PointF a, PointF b) {
- return fuzzyEquals(a.x, b.x) && fuzzyEquals(a.y, b.y);
- }
-
- /*
- * Returns the value that represents a linear transition between `from` and `to` at time `t`,
- * which is on the scale [0, 1). Thus with t = 0.0f, this returns `from`; with t = 1.0f, this
- * returns `to`; with t = 0.5f, this returns the value halfway from `from` to `to`.
- */
- public static float interpolate(float from, float to, float t) {
- return from + (to - from) * t;
- }
-
- /**
- * Returns 'value', clamped so that it isn't any lower than 'low', and it
- * isn't any higher than 'high'.
- */
- public static float clamp(float value, float low, float high) {
- if (high < low) {
- throw new IllegalArgumentException(
- "clamp called with invalid parameters (" + high + " < " + low + ")" );
- }
- return Math.max(low, Math.min(high, value));
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GamepadUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GamepadUtils.java
deleted file mode 100644
index e22be8fd8..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GamepadUtils.java
+++ /dev/null
@@ -1,140 +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.util;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.view.InputDevice;
-import android.view.KeyCharacterMap;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-
-public final class GamepadUtils {
- private static final int SONY_XPERIA_GAMEPAD_DEVICE_ID = 196611;
-
- private static View.OnKeyListener sClickDispatcher;
- private static float sDeadZoneThresholdOverride = 1e-2f;
-
- private GamepadUtils() {
- }
-
- @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
- private static boolean isGamepadKey(KeyEvent event) {
- if (Build.VERSION.SDK_INT < 12) {
- return false;
- }
- return (event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD;
- }
-
- public static boolean isActionKey(KeyEvent event) {
- return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_A));
- }
-
- public static boolean isActionKeyDown(KeyEvent event) {
- return isActionKey(event) && event.getAction() == KeyEvent.ACTION_DOWN;
- }
-
- public static boolean isBackKey(KeyEvent event) {
- return (isGamepadKey(event) && (event.getKeyCode() == KeyEvent.KEYCODE_BUTTON_B));
- }
-
- public static void overrideDeadZoneThreshold(float threshold) {
- sDeadZoneThresholdOverride = threshold;
- }
-
- public static boolean isValueInDeadZone(MotionEvent event, int axis) {
- float threshold;
- if (sDeadZoneThresholdOverride >= 0) {
- threshold = sDeadZoneThresholdOverride;
- } else {
- InputDevice.MotionRange range = event.getDevice().getMotionRange(axis);
- threshold = range.getFlat() + range.getFuzz();
- }
- float value = event.getAxisValue(axis);
- return (Math.abs(value) < threshold);
- }
-
- public static boolean isPanningControl(MotionEvent event) {
- if (Build.VERSION.SDK_INT < 12) {
- return false;
- }
- if ((event.getSource() & InputDevice.SOURCE_CLASS_MASK) != InputDevice.SOURCE_CLASS_JOYSTICK) {
- return false;
- }
- if (isValueInDeadZone(event, MotionEvent.AXIS_X)
- && isValueInDeadZone(event, MotionEvent.AXIS_Y)
- && isValueInDeadZone(event, MotionEvent.AXIS_Z)
- && isValueInDeadZone(event, MotionEvent.AXIS_RZ)) {
- return false;
- }
- return true;
- }
-
- public static View.OnKeyListener getClickDispatcher() {
- if (sClickDispatcher == null) {
- sClickDispatcher = new View.OnKeyListener() {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (isActionKeyDown(event)) {
- return v.performClick();
- }
- return false;
- }
- };
- }
- return sClickDispatcher;
- }
-
- public static KeyEvent translateSonyXperiaGamepadKeys(int keyCode, KeyEvent event) {
- // The cross and circle button mappings may be swapped in the different regions so
- // determine if they are swapped so the proper key codes can be mapped to the keys
- boolean areKeysSwapped = areSonyXperiaGamepadKeysSwapped();
-
- // If a Sony Xperia, remap the cross and circle buttons to buttons
- // A and B for the gamepad API
- switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_A : KeyEvent.KEYCODE_BUTTON_B);
- break;
-
- case KeyEvent.KEYCODE_DPAD_CENTER:
- keyCode = (areKeysSwapped ? KeyEvent.KEYCODE_BUTTON_B : KeyEvent.KEYCODE_BUTTON_A);
- break;
-
- default:
- return event;
- }
-
- return new KeyEvent(event.getAction(), keyCode);
- }
-
- public static boolean isSonyXperiaGamepadKeyEvent(KeyEvent event) {
- return (event.getDeviceId() == SONY_XPERIA_GAMEPAD_DEVICE_ID &&
- "Sony Ericsson".equals(Build.MANUFACTURER) &&
- ("R800".equals(Build.MODEL) || "R800i".equals(Build.MODEL)));
- }
-
- private static boolean areSonyXperiaGamepadKeysSwapped() {
- // The cross and circle buttons on Sony Xperia phones are swapped
- // in different regions
- // http://developer.sonymobile.com/2011/02/13/xperia-play-game-keys/
- final char DEFAULT_O_BUTTON_LABEL = 0x25CB;
-
- boolean swapped = false;
- int[] deviceIds = InputDevice.getDeviceIds();
-
- for (int i = 0; deviceIds != null && i < deviceIds.length; i++) {
- KeyCharacterMap keyCharacterMap = KeyCharacterMap.load(deviceIds[i]);
- if (keyCharacterMap != null && DEFAULT_O_BUTTON_LABEL ==
- keyCharacterMap.getDisplayLabel(KeyEvent.KEYCODE_DPAD_CENTER)) {
- swapped = true;
- break;
- }
- }
- return swapped;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java
deleted file mode 100644
index 442f782e2..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoBackgroundThread.java
+++ /dev/null
@@ -1,76 +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.util;
-
-import android.os.Handler;
-import android.os.Looper;
-
-import java.util.concurrent.SynchronousQueue;
-
-final class GeckoBackgroundThread extends Thread {
- private static final String LOOPER_NAME = "GeckoBackgroundThread";
-
- // Guarded by 'GeckoBackgroundThread.class'.
- private static Handler handler;
- private static Thread thread;
-
- // The initial Runnable to run on the new thread. Its purpose
- // is to avoid us having to wait for the new thread to start.
- private Runnable initialRunnable;
-
- // Singleton, so private constructor.
- private GeckoBackgroundThread(final Runnable initialRunnable) {
- this.initialRunnable = initialRunnable;
- }
-
- @Override
- public void run() {
- setName(LOOPER_NAME);
- Looper.prepare();
-
- synchronized (GeckoBackgroundThread.class) {
- handler = new Handler();
- GeckoBackgroundThread.class.notify();
- }
-
- if (initialRunnable != null) {
- initialRunnable.run();
- initialRunnable = null;
- }
-
- Looper.loop();
- }
-
- private static void startThread(final Runnable initialRunnable) {
- thread = new GeckoBackgroundThread(initialRunnable);
- ThreadUtils.setBackgroundThread(thread);
-
- thread.setDaemon(true);
- thread.start();
- }
-
- // Get a Handler for a looper thread, or create one if it doesn't yet exist.
- /*package*/ static synchronized Handler getHandler() {
- if (thread == null) {
- startThread(null);
- }
-
- while (handler == null) {
- try {
- GeckoBackgroundThread.class.wait();
- } catch (final InterruptedException e) {
- }
- }
- return handler;
- }
-
- /*package*/ static synchronized void post(final Runnable runnable) {
- if (thread == null) {
- startThread(runnable);
- return;
- }
- getHandler().post(runnable);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoEventListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoEventListener.java
deleted file mode 100644
index 10336490b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoEventListener.java
+++ /dev/null
@@ -1,14 +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.util;
-
-import org.json.JSONObject;
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-@RobocopTarget
-public interface GeckoEventListener {
- void handleMessage(String event, JSONObject message);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoJarReader.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoJarReader.java
deleted file mode 100644
index 4e11592a4..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoJarReader.java
+++ /dev/null
@@ -1,261 +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.util;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.util.Log;
-import org.mozilla.gecko.annotation.RobocopTarget;
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.mozglue.GeckoLoader;
-import org.mozilla.gecko.mozglue.NativeZip;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Stack;
-
-/* Reads out of a multiple level deep jar file such as
- * jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png
- */
-public final class GeckoJarReader {
- private static final String LOGTAG = "GeckoJarReader";
-
- private GeckoJarReader() {}
-
- public static Bitmap getBitmap(Context context, Resources resources, String url) {
- BitmapDrawable drawable = getBitmapDrawable(context, resources, url);
- return (drawable != null) ? drawable.getBitmap() : null;
- }
-
- public static BitmapDrawable getBitmapDrawable(Context context, Resources resources,
- String url) {
- Stack<String> jarUrls = parseUrl(url);
- InputStream inputStream = null;
- BitmapDrawable bitmap = null;
-
- NativeZip zip = null;
- try {
- // Load the initial jar file as a zip
- zip = getZipFile(context, jarUrls.pop());
- inputStream = getStream(zip, jarUrls, url);
- if (inputStream != null) {
- bitmap = new BitmapDrawable(resources, inputStream);
- // BitmapDrawable created from a stream does not set the correct target density from resources.
- // In fact it discards the resources https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/graphics/java/android/graphics/drawable/BitmapDrawable.java#191
- bitmap.setTargetDensity(resources.getDisplayMetrics());
- }
- } catch (IOException | URISyntaxException ex) {
- Log.e(LOGTAG, "Exception ", ex);
- } finally {
- if (inputStream != null) {
- try {
- inputStream.close();
- } catch (IOException ex) {
- Log.e(LOGTAG, "Error closing stream", ex);
- }
- }
- }
-
- return bitmap;
- }
-
- public static String getText(Context context, String url) {
- Stack<String> jarUrls = parseUrl(url);
-
- NativeZip zip = null;
- BufferedReader reader = null;
- String text = null;
- try {
- zip = getZipFile(context, jarUrls.pop());
- InputStream input = getStream(zip, jarUrls, url);
- if (input != null) {
- reader = new BufferedReader(new InputStreamReader(input));
- text = reader.readLine();
- }
- } catch (IOException | URISyntaxException ex) {
- Log.e(LOGTAG, "Exception ", ex);
- } finally {
- if (reader != null) {
- try {
- reader.close();
- } catch (IOException ex) {
- Log.e(LOGTAG, "Error closing reader", ex);
- }
- }
- }
-
- return text;
- }
-
- private static NativeZip getZipFile(Context context, String url)
- throws IOException, URISyntaxException {
- URI fileUrl = new URI(url);
- GeckoLoader.loadMozGlue(context);
- return new NativeZip(fileUrl.getPath());
- }
-
- @RobocopTarget
- /**
- * Extract a (possibly nested) file from an archive and write it to a temporary file.
- *
- * @param context Android context.
- * @param url to open. Can include jar: to "reach into" nested archives.
- * @param dir to write temporary file to.
- * @return a <code>File</code>, if one could be written; otherwise null.
- * @throws IOException if an error occured.
- */
- public static File extractStream(Context context, String url, File dir, String suffix) throws IOException {
- InputStream input = null;
- try {
- try {
- final URI fileURI = new URI(url);
- // We don't check the scheme because we want to catch bare files, not just file:// URIs.
- // If we let bare files through, we'd try to open them as ZIP files later -- and crash in native code.
- if (fileURI != null && fileURI.getPath() != null) {
- final File inputFile = new File(fileURI.getPath());
- if (inputFile != null && inputFile.exists()) {
- input = new FileInputStream(inputFile);
- }
- }
- } catch (URISyntaxException e) {
- // Not a file:// URI.
- }
- if (input == null) {
- // No luck with file:// URI; maybe some other URI?
- input = getStream(context, url);
- }
- if (input == null) {
- // Not found!
- return null;
- }
-
- // n.b.: createTempFile does not in fact delete the file.
- final File file = File.createTempFile("extractStream", suffix, dir);
- OutputStream output = null;
- try {
- output = new FileOutputStream(file);
- byte[] buf = new byte[8192];
- int len;
- while ((len = input.read(buf)) >= 0) {
- output.write(buf, 0, len);
- }
- return file;
- } finally {
- if (output != null) {
- output.close();
- }
- }
- } finally {
- if (input != null) {
- try {
- input.close();
- } catch (IOException e) {
- Log.w(LOGTAG, "Got exception closing stream; ignoring.", e);
- }
- }
- }
- }
-
- @RobocopTarget
- public static InputStream getStream(Context context, String url) {
- Stack<String> jarUrls = parseUrl(url);
- try {
- NativeZip zip = getZipFile(context, jarUrls.pop());
- return getStream(zip, jarUrls, url);
- } catch (Exception ex) {
- // Some JNI code throws IllegalArgumentException on a bad file name;
- // swallow the error and return null. We could also see legitimate
- // IOExceptions here.
- Log.e(LOGTAG, "Exception getting input stream from jar URL: " + url, ex);
- return null;
- }
- }
-
- private static InputStream getStream(NativeZip zip, Stack<String> jarUrls, String origUrl) {
- InputStream inputStream = null;
-
- // loop through children jar files until we reach the innermost one
- while (!jarUrls.empty()) {
- String fileName = jarUrls.pop();
-
- if (inputStream != null) {
- // intermediate NativeZips and InputStreams will be garbage collected.
- try {
- zip = new NativeZip(inputStream);
- } catch (IllegalArgumentException e) {
- String description = "!!! BUG 849589 !!! origUrl=" + origUrl;
- Log.e(LOGTAG, description, e);
- throw new IllegalArgumentException(description);
- }
- }
-
- inputStream = zip.getInputStream(fileName);
- if (inputStream == null) {
- Log.d(LOGTAG, "No Entry for " + fileName);
- return null;
- }
- }
-
- return inputStream;
- }
-
- /* Returns a stack of strings breaking the url up into pieces. Each piece
- * is assumed to point to a jar file except for the final one. Callers should
- * pass in the url to parse, and null for the parent parameter (used for recursion)
- * For example, jar:jar:file:///data/app/org.mozilla.fennec.apk!/omni.ja!/chrome/chrome/content/branding/favicon32.png
- * will return:
- * file:///data/app/org.mozilla.fennec.apk
- * omni.ja
- * chrome/chrome/content/branding/favicon32.png
- */
- private static Stack<String> parseUrl(String url) {
- return parseUrl(url, null);
- }
-
- private static Stack<String> parseUrl(String url, Stack<String> results) {
- if (results == null) {
- results = new Stack<String>();
- }
-
- if (url.startsWith("jar:")) {
- int jarEnd = url.lastIndexOf("!");
- String subStr = url.substring(4, jarEnd);
- results.push(url.substring(jarEnd + 2)); // remove the !/ characters
- return parseUrl(subStr, results);
- } else {
- results.push(url);
- return results;
- }
- }
-
- public static String getJarURL(Context context, String pathInsideJAR) {
- // We need to encode the package resource path, because it might contain illegal characters. For example:
- // /mnt/asec2/[2]org.mozilla.fennec-1/pkg.apk
- // The round-trip through a URI does this for us.
- final String resourcePath = context.getPackageResourcePath();
- return computeJarURI(resourcePath, pathInsideJAR);
- }
-
- /**
- * Encodes its resource path correctly.
- */
- @RobocopTarget
- public static String computeJarURI(String resourcePath, String pathInsideJAR) {
- final String resURI = new File(resourcePath).toURI().toString();
-
- // TODO: do we need to encode the file path, too?
- return "jar:jar:" + resURI + "!/" + AppConstants.OMNIJAR_NAME + "!/" + pathInsideJAR;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoRequest.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoRequest.java
deleted file mode 100644
index a57ed7f08..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/GeckoRequest.java
+++ /dev/null
@@ -1,94 +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.util;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-import android.util.Log;
-
-public abstract class GeckoRequest {
- private static final String LOGTAG = "GeckoRequest";
- private static final AtomicInteger currentId = new AtomicInteger(0);
-
- private final int id = currentId.getAndIncrement();
- private final String name;
- private final String data;
-
- /**
- * Creates a request that can be dispatched using
- * {@link GeckoAppShell#sendRequestToGecko(GeckoRequest)}.
- *
- * @param name The name of the event associated with this request, which must have a
- * Gecko-side listener registered to respond to this request.
- * @param data Data to send with this request, which can be any object serializable by
- * {@link JSONObject#put(String, Object)}.
- */
- @RobocopTarget
- public GeckoRequest(String name, Object data) {
- this.name = name;
- final JSONObject message = new JSONObject();
- try {
- message.put("id", id);
- message.put("data", data);
- } catch (JSONException e) {
- Log.e(LOGTAG, "JSON error", e);
- }
- this.data = message.toString();
- }
-
- /**
- * Gets the ID for this request.
- *
- * @return The request ID
- */
- public int getId() {
- return id;
- }
-
- /**
- * Gets the event name associated with this request.
- *
- * @return The name of the event sent to Gecko
- */
- public String getName() {
- return name;
- }
-
- /**
- * Gets the stringified data associated with this request.
- *
- * @return The data being sent with the request
- */
- public String getData() {
- return data;
- }
-
- /**
- * Callback executed when the request succeeds.
- *
- * @param nativeJSObject The response data from Gecko
- */
- @RobocopTarget
- public abstract void onResponse(NativeJSObject nativeJSObject);
-
- /**
- * Callback executed when the request fails.
- *
- * By default, an exception is thrown. This should be overridden if the
- * GeckoRequest is able to recover from the error.
- *
- * @throws RuntimeException
- */
- @RobocopTarget
- public void onError(NativeJSObject error) {
- final String message = error.optString("message", "<no message>");
- final String stack = error.optString("stack", "<no stack>");
- throw new RuntimeException("Unhandled error for GeckoRequest " + name + ": " + message + "\nJS stack:\n" + stack);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareCodecCapabilityUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareCodecCapabilityUtils.java
deleted file mode 100644
index 864462d9b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareCodecCapabilityUtils.java
+++ /dev/null
@@ -1,169 +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.util;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.AppConstants.Versions;
-
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaCodecList;
-import android.util.Log;
-
-public final class HardwareCodecCapabilityUtils {
- private static final String LOGTAG = "GeckoHardwareCodecCapabilityUtils";
-
- // List of supported HW VP8 encoders.
- private static final String[] supportedVp8HwEncCodecPrefixes =
- {"OMX.qcom.", "OMX.Intel." };
- // List of supported HW VP8 decoders.
- private static final String[] supportedVp8HwDecCodecPrefixes =
- {"OMX.qcom.", "OMX.Nvidia.", "OMX.Exynos.", "OMX.Intel." };
- private static final String VP8_MIME_TYPE = "video/x-vnd.on2.vp8";
- private static final String VP9_MIME_TYPE = "video/x-vnd.on2.vp9";
- // NV12 color format supported by QCOM codec, but not declared in MediaCodec -
- // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
- private static final int
- COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04;
- // Allowable color formats supported by codec - in order of preference.
- private static final int[] supportedColorList = {
- CodecCapabilities.COLOR_FormatYUV420Planar,
- CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
- CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
- COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m
- };
-
- @WrapForJNI
- public static boolean findDecoderCodecInfoForMimeType(String aMimeType) {
- for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
- MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
- if (info.isEncoder()) {
- continue;
- }
- for (String mimeType : info.getSupportedTypes()) {
- if (mimeType.equals(aMimeType)) {
- return true;
- }
- }
- }
- return false;
- }
-
- public static boolean getHWEncoderCapability() {
- if (Versions.feature20Plus) {
- for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
- MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
- if (!info.isEncoder()) {
- continue;
- }
- String name = null;
- for (String mimeType : info.getSupportedTypes()) {
- if (mimeType.equals(VP8_MIME_TYPE)) {
- name = info.getName();
- break;
- }
- }
- if (name == null) {
- continue; // No HW support in this codec; try the next one.
- }
- Log.e(LOGTAG, "Found candidate encoder " + name);
-
- // Check if this is supported encoder.
- boolean supportedCodec = false;
- for (String codecPrefix : supportedVp8HwEncCodecPrefixes) {
- if (name.startsWith(codecPrefix)) {
- supportedCodec = true;
- break;
- }
- }
- if (!supportedCodec) {
- continue;
- }
-
- // Check if codec supports either yuv420 or nv12.
- CodecCapabilities capabilities =
- info.getCapabilitiesForType(VP8_MIME_TYPE);
- for (int colorFormat : capabilities.colorFormats) {
- Log.v(LOGTAG, " Color: 0x" + Integer.toHexString(colorFormat));
- }
- for (int supportedColorFormat : supportedColorList) {
- for (int codecColorFormat : capabilities.colorFormats) {
- if (codecColorFormat == supportedColorFormat) {
- // Found supported HW Encoder.
- Log.e(LOGTAG, "Found target encoder " + name +
- ". Color: 0x" + Integer.toHexString(codecColorFormat));
- return true;
- }
- }
- }
- }
- }
- // No HW encoder.
- return false;
- }
-
- public static boolean getHWDecoderCapability() {
- return getHWDecoderCapability(VP8_MIME_TYPE);
- }
-
- @WrapForJNI
- public static boolean HasHWVP9() {
- return getHWDecoderCapability(VP9_MIME_TYPE);
- }
-
- public static boolean getHWDecoderCapability(String aMimeType) {
- if (Versions.feature20Plus) {
- for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
- MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
- if (info.isEncoder()) {
- continue;
- }
- String name = null;
- for (String mimeType : info.getSupportedTypes()) {
- if (mimeType.equals(aMimeType)) {
- name = info.getName();
- break;
- }
- }
- if (name == null) {
- continue; // No HW support in this codec; try the next one.
- }
- Log.e(LOGTAG, "Found candidate decoder " + name);
-
- // Check if this is supported decoder.
- boolean supportedCodec = false;
- for (String codecPrefix : supportedVp8HwDecCodecPrefixes) {
- if (name.startsWith(codecPrefix)) {
- supportedCodec = true;
- break;
- }
- }
- if (!supportedCodec) {
- continue;
- }
-
- // Check if codec supports either yuv420 or nv12.
- CodecCapabilities capabilities =
- info.getCapabilitiesForType(aMimeType);
- for (int colorFormat : capabilities.colorFormats) {
- Log.v(LOGTAG, " Color: 0x" + Integer.toHexString(colorFormat));
- }
- for (int supportedColorFormat : supportedColorList) {
- for (int codecColorFormat : capabilities.colorFormats) {
- if (codecColorFormat == supportedColorFormat) {
- // Found supported HW decoder.
- Log.e(LOGTAG, "Found target decoder " + name +
- ". Color: 0x" + Integer.toHexString(codecColorFormat));
- return true;
- }
- }
- }
- }
- }
- return false; // No HW decoder.
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java
deleted file mode 100644
index ba92d08cb..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/HardwareUtils.java
+++ /dev/null
@@ -1,117 +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.util;
-
-import org.mozilla.gecko.AppConstants;
-import org.mozilla.gecko.SysInfo;
-
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.os.Build;
-import android.util.Log;
-import android.view.ViewConfiguration;
-
-public final class HardwareUtils {
- private static final String LOGTAG = "GeckoHardwareUtils";
-
- private static final boolean IS_AMAZON_DEVICE = Build.MANUFACTURER.equalsIgnoreCase("Amazon");
- public static final boolean IS_KINDLE_DEVICE = IS_AMAZON_DEVICE &&
- (Build.MODEL.equals("Kindle Fire") ||
- Build.MODEL.startsWith("KF"));
-
- private static volatile boolean sInited;
-
- // These are all set once, during init.
- private static volatile boolean sIsLargeTablet;
- private static volatile boolean sIsSmallTablet;
- private static volatile boolean sIsTelevision;
-
- private HardwareUtils() {
- }
-
- public static void init(Context context) {
- if (sInited) {
- // This is unavoidable, given that HardwareUtils is called from background services.
- Log.d(LOGTAG, "HardwareUtils already inited.");
- return;
- }
-
- // Pre-populate common flags from the context.
- final int screenLayoutSize = context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
- if (Build.VERSION.SDK_INT >= 11) {
- if (screenLayoutSize == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
- sIsLargeTablet = true;
- } else if (screenLayoutSize == Configuration.SCREENLAYOUT_SIZE_LARGE) {
- sIsSmallTablet = true;
- }
- if (Build.VERSION.SDK_INT >= 16) {
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
- sIsTelevision = true;
- }
- }
- }
-
- sInited = true;
- }
-
- public static boolean isTablet() {
- return sIsLargeTablet || sIsSmallTablet;
- }
-
- public static boolean isLargeTablet() {
- return sIsLargeTablet;
- }
-
- public static boolean isSmallTablet() {
- return sIsSmallTablet;
- }
-
- public static boolean isTelevision() {
- return sIsTelevision;
- }
-
- public static int getMemSize() {
- return SysInfo.getMemSize();
- }
-
- public static boolean isARMSystem() {
- return Build.CPU_ABI != null && Build.CPU_ABI.equals("armeabi-v7a");
- }
-
- public static boolean isX86System() {
- return Build.CPU_ABI != null && Build.CPU_ABI.equals("x86");
- }
-
- /**
- * @return false if the current system is not supported (e.g. APK/system ABI mismatch).
- */
- public static boolean isSupportedSystem() {
- if (Build.VERSION.SDK_INT < AppConstants.Versions.MIN_SDK_VERSION ||
- Build.VERSION.SDK_INT > AppConstants.Versions.MAX_SDK_VERSION) {
- return false;
- }
-
- // See http://developer.android.com/ndk/guides/abis.html
- final boolean isSystemARM = isARMSystem();
- final boolean isSystemX86 = isX86System();
-
- final boolean isAppARM = AppConstants.ANDROID_CPU_ARCH.startsWith("armeabi-v7a");
- final boolean isAppX86 = AppConstants.ANDROID_CPU_ARCH.startsWith("x86");
-
- // Only reject known incompatible ABIs. Better safe than sorry.
- if ((isSystemX86 && isAppARM) || (isSystemARM && isAppX86)) {
- return false;
- }
-
- if ((isSystemX86 && isAppX86) || (isSystemARM && isAppARM)) {
- return true;
- }
-
- Log.w(LOGTAG, "Unknown app/system ABI combination: " + AppConstants.MOZ_APP_ABI + " / " + Build.CPU_ABI);
- return true;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INIParser.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INIParser.java
deleted file mode 100644
index ed0706320..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INIParser.java
+++ /dev/null
@@ -1,176 +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.util;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-public final class INIParser extends INISection {
- // default file to read and write to
- private final File mFile;
-
- // List of sections in the current iniFile. null if the file has not been parsed yet
- private Hashtable<String, INISection> mSections;
-
- // create a parser. The file will not be read until you attempt to
- // access sections or properties inside it. At that point its read synchronously
- public INIParser(File iniFile) {
- super("");
- mFile = iniFile;
- }
-
- // write ini data to the default file. Will overwrite anything current inside
- public void write() {
- writeTo(mFile);
- }
-
- // write to the specified file. Will overwrite anything current inside
- public void writeTo(File f) {
- if (f == null)
- return;
-
- FileWriter outputStream = null;
- try {
- outputStream = new FileWriter(f);
- } catch (IOException e1) {
- e1.printStackTrace();
- }
-
- BufferedWriter writer = new BufferedWriter(outputStream);
- try {
- write(writer);
- writer.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public void write(BufferedWriter writer) throws IOException {
- super.write(writer);
-
- if (mSections != null) {
- for (Enumeration<INISection> e = mSections.elements(); e.hasMoreElements();) {
- INISection section = e.nextElement();
- section.write(writer);
- writer.newLine();
- }
- }
- }
-
- // return all of the sections inside this file
- public Hashtable<String, INISection> getSections() {
- if (mSections == null) {
- try {
- parse();
- } catch (IOException e) {
- debug("Error parsing: " + e);
- }
- }
- return mSections;
- }
-
- // parse the default file
- @Override
- protected void parse() throws IOException {
- super.parse();
- parse(mFile);
- }
-
- // parse a passed in file
- private void parse(File f) throws IOException {
- // Set up internal data members
- mSections = new Hashtable<String, INISection>();
-
- if (f == null || !f.exists())
- return;
-
- FileReader inputStream = null;
- try {
- inputStream = new FileReader(f);
- } catch (FileNotFoundException e1) {
- // If the file doesn't exist. Just return;
- return;
- }
-
- BufferedReader buf = new BufferedReader(inputStream);
- String line = null; // current line of text we are parsing
- INISection currentSection = null; // section we are currently parsing
-
- while ((line = buf.readLine()) != null) {
-
- if (line != null)
- line = line.trim();
-
- // blank line or a comment. ignore it
- if (line == null || line.length() == 0 || line.charAt(0) == ';') {
- debug("Ignore line: " + line);
- } else if (line.charAt(0) == '[') {
- debug("Parse as section: " + line);
- currentSection = new INISection(line.substring(1, line.length() - 1));
- mSections.put(currentSection.getName(), currentSection);
- } else {
- debug("Parse as property: " + line);
-
- String[] pieces = line.split("=");
- if (pieces.length != 2)
- continue;
-
- String key = pieces[0].trim();
- String value = pieces[1].trim();
- if (currentSection != null) {
- currentSection.setProperty(key, value);
- } else {
- mProperties.put(key, value);
- }
- }
- }
- buf.close();
- }
-
- // add a section to the file
- public void addSection(INISection sect) {
- // ensure that we have parsed the file
- getSections();
- mSections.put(sect.getName(), sect);
- }
-
- // get a section from the file. will return null if the section doesn't exist
- public INISection getSection(String key) {
- // ensure that we have parsed the file
- getSections();
- return mSections.get(key);
- }
-
- // remove an entire section from the file
- public void removeSection(String name) {
- // ensure that we have parsed the file
- getSections();
- mSections.remove(name);
- }
-
- // rename a section; nuking any previous section with the new
- // name in the process
- public void renameSection(String oldName, String newName) {
- // ensure that we have parsed the file
- getSections();
-
- mSections.remove(newName);
- INISection section = mSections.get(oldName);
- if (section == null)
- return;
-
- section.setName(newName);
- mSections.remove(oldName);
- mSections.put(newName, section);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INISection.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INISection.java
deleted file mode 100644
index af91ad410..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/INISection.java
+++ /dev/null
@@ -1,123 +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.util;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-public class INISection {
- private static final String LOGTAG = "INIParser";
-
- // default file to read and write to
- private String mName;
- public String getName() { return mName; }
- public void setName(String name) { mName = name; }
-
- // show or hide debug logging
- private boolean mDebug;
-
- // Global properties that aren't inside a section in the file
- protected Hashtable<String, Object> mProperties;
-
- // create a parser. The file will not be read until you attempt to
- // access sections or properties inside it. At that point its read synchronously
- public INISection(String name) {
- mName = name;
- }
-
- // log a debug string to the console
- protected void debug(String msg) {
- if (mDebug) {
- Log.i(LOGTAG, msg);
- }
- }
-
- // get a global property out of the hash table. will return null if the property doesn't exist
- public Object getProperty(String key) {
- getProperties(); // ensure that we have parsed the file
- return mProperties.get(key);
- }
-
- // get a global property out of the hash table. will return null if the property doesn't exist
- public int getIntProperty(String key) {
- Object val = getProperty(key);
- if (val == null)
- return -1;
-
- return Integer.parseInt(val.toString());
- }
-
- // get a global property out of the hash table. will return null if the property doesn't exist
- public String getStringProperty(String key) {
- Object val = getProperty(key);
- if (val == null)
- return null;
-
- return val.toString();
- }
-
- // get a hashtable of all the global properties in this file
- public Hashtable<String, Object> getProperties() {
- if (mProperties == null) {
- try {
- parse();
- } catch (IOException e) {
- debug("Error parsing: " + e);
- }
- }
- return mProperties;
- }
-
- // do nothing for generic sections
- protected void parse() throws IOException {
- mProperties = new Hashtable<String, Object>();
- }
-
- // set a property. Will erase the property if value = null
- public void setProperty(String key, Object value) {
- getProperties(); // ensure that we have parsed the file
- if (value == null)
- removeProperty(key);
- else
- mProperties.put(key.trim(), value);
- }
-
- // remove a property
- public void removeProperty(String name) {
- // ensure that we have parsed the file
- getProperties();
- mProperties.remove(name);
- }
-
- public void write(BufferedWriter writer) throws IOException {
- if (!TextUtils.isEmpty(mName)) {
- writer.write("[" + mName + "]");
- writer.newLine();
- }
-
- if (mProperties != null) {
- for (Enumeration<String> e = mProperties.keys(); e.hasMoreElements();) {
- String key = e.nextElement();
- writeProperty(writer, key, mProperties.get(key));
- }
- }
- writer.newLine();
- }
-
- // Helper function to write out a property
- private void writeProperty(BufferedWriter writer, String key, Object value) {
- try {
- writer.write(key + "=" + value);
- writer.newLine();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IOUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IOUtils.java
deleted file mode 100644
index 62eee5192..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IOUtils.java
+++ /dev/null
@@ -1,129 +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.util;
-
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * Static helper class containing useful methods for manipulating IO objects.
- */
-public class IOUtils {
- private static final String LOGTAG = "GeckoIOUtils";
-
- /**
- * Represents the result of consuming an input stream, holding the returned data as well
- * as the length of the data returned.
- * The byte[] is not guaranteed to be trimmed to the size of the data acquired from the stream:
- * hence the need for the length field. This strategy avoids the need to copy the data into a
- * trimmed buffer after consumption.
- */
- public static class ConsumedInputStream {
- public final int consumedLength;
- // Only reassigned in getTruncatedData.
- private byte[] consumedData;
-
- public ConsumedInputStream(int consumedLength, byte[] consumedData) {
- this.consumedLength = consumedLength;
- this.consumedData = consumedData;
- }
-
- /**
- * Get the data trimmed to the length of the actual payload read, caching the result.
- */
- public byte[] getTruncatedData() {
- if (consumedData.length == consumedLength) {
- return consumedData;
- }
-
- consumedData = truncateBytes(consumedData, consumedLength);
- return consumedData;
- }
-
- public byte[] getData() {
- return consumedData;
- }
- }
-
- /**
- * Fully read an InputStream into a byte array.
- * @param iStream the InputStream to consume.
- * @param bufferSize The initial size of the buffer to allocate. It will be grown as
- * needed, but if the caller knows something about the InputStream then
- * passing a good value here can improve performance.
- */
- public static ConsumedInputStream readFully(InputStream iStream, int bufferSize) {
- // Allocate a buffer to hold the raw data downloaded.
- byte[] buffer = new byte[bufferSize];
-
- // The offset of the start of the buffer's free space.
- int bPointer = 0;
-
- // The quantity of bytes the last call to read yielded.
- int lastRead = 0;
- try {
- // Fully read the data into the buffer.
- while (lastRead != -1) {
- // Read as many bytes as are currently available into the buffer.
- lastRead = iStream.read(buffer, bPointer, buffer.length - bPointer);
- bPointer += lastRead;
-
- // If buffer has overflowed, double its size and carry on.
- if (bPointer == buffer.length) {
- bufferSize *= 2;
- byte[] newBuffer = new byte[bufferSize];
-
- // Copy the contents of the old buffer into the new buffer.
- System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
- buffer = newBuffer;
- }
- }
-
- return new ConsumedInputStream(bPointer + 1, buffer);
- } catch (IOException e) {
- Log.e(LOGTAG, "Error consuming input stream.", e);
- } finally {
- try {
- iStream.close();
- } catch (IOException e) {
- Log.e(LOGTAG, "Error closing input stream.", e);
- }
- }
-
- return null;
- }
-
- /**
- * Truncate a given byte[] to a given length. Returns a new byte[] with the first length many
- * bytes of the input.
- */
- public static byte[] truncateBytes(byte[] bytes, int length) {
- byte[] newBytes = new byte[length];
- System.arraycopy(bytes, 0, newBytes, 0, length);
-
- return newBytes;
- }
-
- public static void safeStreamClose(Closeable stream) {
- try {
- if (stream != null)
- stream.close();
- } catch (IOException e) { }
- }
-
- public static void copy(InputStream in, OutputStream out) throws IOException {
- byte[] buffer = new byte[4096];
- int len;
-
- while ((len = in.read(buffer)) != -1) {
- out.write(buffer, 0, len);
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/InputOptionsUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/InputOptionsUtils.java
deleted file mode 100644
index 55c02e4da..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/InputOptionsUtils.java
+++ /dev/null
@@ -1,45 +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.util;
-
-import android.content.Context;
-import android.content.Intent;
-import android.speech.RecognizerIntent;
-
-public class InputOptionsUtils {
- public static boolean supportsVoiceRecognizer(Context context, String prompt) {
- final Intent intent = createVoiceRecognizerIntent(prompt);
- return intent.resolveActivity(context.getPackageManager()) != null;
- }
-
- public static Intent createVoiceRecognizerIntent(String prompt) {
- final Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
- intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
- intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
- intent.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt);
- return intent;
- }
-
- public static boolean supportsIntent(Intent intent, Context context) {
- return intent.resolveActivity(context.getPackageManager()) != null;
- }
-
- public static boolean supportsQrCodeReader(Context context) {
- final Intent intent = createQRCodeReaderIntent();
- return supportsIntent(intent, context);
- }
-
- public static Intent createQRCodeReaderIntent() {
- // Bug 602818 enables QR code input if you have the particular app below installed in your device
- final String appPackage = "com.google.zxing.client.android";
-
- Intent intent = new Intent(appPackage + ".SCAN");
- intent.setPackage(appPackage);
- intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- return intent;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java
deleted file mode 100644
index d4fe297da..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java
+++ /dev/null
@@ -1,109 +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.util;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.CheckResult;
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-
-import org.mozilla.gecko.mozglue.SafeIntent;
-
-import java.util.HashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Utilities for Intents.
- */
-public class IntentUtils {
- public static final String ENV_VAR_IN_AUTOMATION = "MOZ_IN_AUTOMATION";
-
- private static final String ENV_VAR_REGEX = "(.+)=(.*)";
-
- private IntentUtils() {}
-
- /**
- * Returns a list of environment variables and their values. These are parsed from an Intent extra
- * with the key -> value format:
- * env# -> ENV_VAR=VALUE
- *
- * # in env# is expected to be increasing from 0.
- *
- * @return A Map of environment variable name to value, e.g. ENV_VAR -> VALUE
- */
- public static HashMap<String, String> getEnvVarMap(@NonNull final SafeIntent intent) {
- // Optimization: get matcher for re-use. Pattern.matcher creates a new object every time so it'd be great
- // to avoid the unnecessary allocation, particularly because we expect to be called on the startup path.
- final Pattern envVarPattern = Pattern.compile(ENV_VAR_REGEX);
- final Matcher matcher = envVarPattern.matcher(""); // argument does not matter here.
-
- // This is expected to be an external intent so we should use SafeIntent to prevent crashing.
- final HashMap<String, String> out = new HashMap<>();
- int i = 0;
- while (true) {
- final String envKey = "env" + i;
- i += 1;
- if (!intent.hasExtra(envKey)) {
- break;
- }
-
- maybeAddEnvVarToEnvVarMap(out, intent, envKey, matcher);
- }
- return out;
- }
-
- /**
- * @param envVarMap the map to add the env var to
- * @param intent the intent from which to extract the env var
- * @param envKey the key at which the env var resides
- * @param envVarMatcher a matcher initialized with the env var pattern to extract
- */
- private static void maybeAddEnvVarToEnvVarMap(@NonNull final HashMap<String, String> envVarMap,
- @NonNull final SafeIntent intent, @NonNull final String envKey, @NonNull final Matcher envVarMatcher) {
- final String envValue = intent.getStringExtra(envKey);
- if (envValue == null) {
- return; // nothing to do here!
- }
-
- envVarMatcher.reset(envValue);
- if (envVarMatcher.matches()) {
- final String envVarName = envVarMatcher.group(1);
- final String envVarValue = envVarMatcher.group(2);
- envVarMap.put(envVarName, envVarValue);
- }
- }
-
- public static Bundle getBundleExtraSafe(final Intent intent, final String name) {
- return new SafeIntent(intent).getBundleExtra(name);
- }
-
- public static String getStringExtraSafe(final Intent intent, final String name) {
- return new SafeIntent(intent).getStringExtra(name);
- }
-
- public static boolean getBooleanExtraSafe(final Intent intent, final String name, final boolean defaultValue) {
- return new SafeIntent(intent).getBooleanExtra(name, defaultValue);
- }
-
- /**
- * Gets whether or not we're in automation from the passed in environment variables.
- *
- * We need to read environment variables from the intent string
- * extra because environment variables from our test harness aren't set
- * until Gecko is loaded, and we need to know this before then.
- *
- * The return value of this method should be used early since other
- * initialization may depend on its results.
- */
- @CheckResult
- public static boolean getIsInAutomationFromEnvironment(final SafeIntent intent) {
- final HashMap<String, String> envVars = IntentUtils.getEnvVarMap(intent);
- return !TextUtils.isEmpty(envVars.get(IntentUtils.ENV_VAR_IN_AUTOMATION));
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/JSONUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/JSONUtils.java
deleted file mode 100644
index 4ec98ec9e..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/JSONUtils.java
+++ /dev/null
@@ -1,69 +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.util;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.os.Bundle;
-import android.util.Log;
-
-import java.util.HashSet;
-import java.util.Set;
-import java.util.UUID;
-
-public final class JSONUtils {
- private static final String LOGTAG = "GeckoJSONUtils";
-
- private JSONUtils() {}
-
- public static UUID getUUID(String name, JSONObject json) {
- String uuid = json.optString(name, null);
- return (uuid != null) ? UUID.fromString(uuid) : null;
- }
-
- public static void putUUID(String name, UUID uuid, JSONObject json) {
- String uuidString = uuid.toString();
- try {
- json.put(name, uuidString);
- } catch (JSONException e) {
- throw new IllegalArgumentException(name + "=" + uuidString, e);
- }
- }
-
- public static JSONObject bundleToJSON(Bundle bundle) {
- if (bundle == null || bundle.isEmpty()) {
- return null;
- }
-
- JSONObject json = new JSONObject();
- for (String key : bundle.keySet()) {
- try {
- json.put(key, bundle.get(key));
- } catch (JSONException e) {
- Log.w(LOGTAG, "Error building JSON response.", e);
- }
- }
-
- return json;
- }
-
- // Handles conversions between a JSONArray and a Set<String>
- public static Set<String> parseStringSet(JSONArray json) {
- final Set<String> ret = new HashSet<String>();
-
- for (int i = 0; i < json.length(); i++) {
- try {
- ret.add(json.getString(i));
- } catch (JSONException ex) {
- Log.i(LOGTAG, "Error parsing json", ex);
- }
- }
-
- return ret;
- }
-
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/MenuUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/MenuUtils.java
deleted file mode 100644
index e44fdd541..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/MenuUtils.java
+++ /dev/null
@@ -1,33 +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.util;
-
-import android.view.Menu;
-import android.view.MenuItem;
-
-public class MenuUtils {
- /*
- * This method looks for a menuitem and sets it's visible state, if
- * it exists.
- */
- public static void safeSetVisible(Menu menu, int id, boolean visible) {
- MenuItem item = menu.findItem(id);
- if (item != null) {
- item.setVisible(visible);
- }
- }
-
- /*
- * This method looks for a menuitem and sets it's enabled state, if
- * it exists.
- */
- public static void safeSetEnabled(Menu menu, int id, boolean enabled) {
- MenuItem item = menu.findItem(id);
- if (item != null) {
- item.setEnabled(enabled);
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeEventListener.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeEventListener.java
deleted file mode 100644
index 2a1b6e89a..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeEventListener.java
+++ /dev/null
@@ -1,23 +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.util;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-@RobocopTarget
-public interface NativeEventListener {
- /**
- * Handles a message sent from Gecko.
- *
- * @param event The name of the event being sent.
- * @param message The message data.
- * @param callback The callback interface for this message. A callback is provided only if the
- * originating Messaging.sendRequest call included a callback argument; otherwise,
- * callback will be null. All listeners for a given event are given the same
- * callback object, and exactly one listener must handle the callback.
- */
- void handleMessage(String event, NativeJSObject message, EventCallback callback);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSContainer.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSContainer.java
deleted file mode 100644
index daefe6de0..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSContainer.java
+++ /dev/null
@@ -1,37 +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.util;
-
-import org.mozilla.gecko.annotation.WrapForJNI;
-
-/**
- * NativeJSContainer is a wrapper around the SpiderMonkey JSAPI to make it possible to
- * access Javascript objects in Java.
- *
- * A container must only be used on the thread it is attached to. To use it on another
- * thread, call {@link #clone()} to make a copy, and use the copy on the other thread.
- * When a copy is first used, it becomes attached to the thread using it.
- */
-@WrapForJNI(calledFrom = "gecko")
-public final class NativeJSContainer extends NativeJSObject
-{
- private NativeJSContainer() {
- }
-
- /**
- * Make a copy of this container for use by another thread. When the copy is first used,
- * it becomes attached to the thread using it.
- */
- @Override
- public native NativeJSContainer clone();
-
- /**
- * Dispose all associated native objects. Subsequent use of any objects derived from
- * this container will throw a NullPointerException.
- */
- @Override
- public native void disposeNative();
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSObject.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSObject.java
deleted file mode 100644
index 0d1f0a037..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NativeJSObject.java
+++ /dev/null
@@ -1,533 +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.util;
-
-import org.mozilla.gecko.annotation.JNITarget;
-import org.mozilla.gecko.annotation.WrapForJNI;
-import org.mozilla.gecko.mozglue.JNIObject;
-
-import android.os.Bundle;
-
-/**
- * NativeJSObject is a wrapper around the SpiderMonkey JSAPI to make it possible to
- * access Javascript objects in Java.
- */
-@WrapForJNI(calledFrom = "gecko")
-public class NativeJSObject extends JNIObject
-{
- @SuppressWarnings("serial")
- @JNITarget
- public static final class InvalidPropertyException extends RuntimeException {
- public InvalidPropertyException(final String msg) {
- super(msg);
- }
- }
-
- protected NativeJSObject() {
- }
-
- @Override
- protected void disposeNative() {
- // NativeJSObject is disposed as part of NativeJSContainer disposal.
- }
-
- /**
- * Returns the value of a boolean property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native boolean getBoolean(String name);
-
- /**
- * Returns the value of a boolean property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native boolean optBoolean(String name, boolean fallback);
-
- /**
- * Returns the value of a boolean array property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native boolean[] getBooleanArray(String name);
-
- /**
- * Returns the value of a boolean array property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native boolean[] optBooleanArray(String name, boolean[] fallback);
-
- /**
- * Returns the value of an object property as a Bundle.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native Bundle getBundle(String name);
-
- /**
- * Returns the value of an object property as a Bundle.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native Bundle optBundle(String name, Bundle fallback);
-
- /**
- * Returns the value of an object array property as a Bundle array.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native Bundle[] getBundleArray(String name);
-
- /**
- * Returns the value of an object array property as a Bundle array.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native Bundle[] optBundleArray(String name, Bundle[] fallback);
-
- /**
- * Returns the value of a double property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native double getDouble(String name);
-
- /**
- * Returns the value of a double property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native double optDouble(String name, double fallback);
-
- /**
- * Returns the value of a double array property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native double[] getDoubleArray(String name);
-
- /**
- * Returns the value of a double array property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native double[] optDoubleArray(String name, double[] fallback);
-
- /**
- * Returns the value of an int property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native int getInt(String name);
-
- /**
- * Returns the value of an int property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native int optInt(String name, int fallback);
-
- /**
- * Returns the value of an int array property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native int[] getIntArray(String name);
-
- /**
- * Returns the value of an int array property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native int[] optIntArray(String name, int[] fallback);
-
- /**
- * Returns the value of an object property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native NativeJSObject getObject(String name);
-
- /**
- * Returns the value of an object property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native NativeJSObject optObject(String name, NativeJSObject fallback);
-
- /**
- * Returns the value of an object array property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native NativeJSObject[] getObjectArray(String name);
-
- /**
- * Returns the value of an object array property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native NativeJSObject[] optObjectArray(String name, NativeJSObject[] fallback);
-
- /**
- * Returns the value of a string property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native String getString(String name);
-
- /**
- * Returns the value of a string property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native String optString(String name, String fallback);
-
- /**
- * Returns the value of a string array property.
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property does not exist or if its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native String[] getStringArray(String name);
-
- /**
- * Returns the value of a string array property.
- *
- * @param name
- * Property name
- * @param fallback
- * Value to return if property does not exist
- * @throws IllegalArgumentException
- * If name is null
- * @throws InvalidPropertyException
- * If the property exists and its type does not match the return type
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native String[] optStringArray(String name, String[] fallback);
-
- /**
- * Returns whether a property exists in this object
- *
- * @param name
- * Property name
- * @throws IllegalArgumentException
- * If name is null
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native boolean has(String name);
-
- /**
- * Returns the Bundle representation of this object.
- *
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- public native Bundle toBundle();
-
- /**
- * Returns the JSON representation of this object.
- *
- * @throws NullPointerException
- * If this JS object has been disposed
- * @throws IllegalThreadStateException
- * If not called on the thread this object is attached to
- * @throws UnsupportedOperationException
- * If an internal JSAPI call failed
- */
- @Override
- public native String toString();
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NetworkUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NetworkUtils.java
deleted file mode 100644
index 2210e43ed..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NetworkUtils.java
+++ /dev/null
@@ -1,177 +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.util;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.support.annotation.Nullable;
-import android.support.annotation.NonNull;
-import android.telephony.TelephonyManager;
-
-public class NetworkUtils {
- /*
- * Keep the below constants in sync with
- * http://dxr.mozilla.org/mozilla-central/source/netwerk/base/nsINetworkLinkService.idl
- */
- public enum ConnectionSubType {
- CELL_2G("2g"),
- CELL_3G("3g"),
- CELL_4G("4g"),
- ETHERNET("ethernet"),
- WIFI("wifi"),
- WIMAX("wimax"),
- UNKNOWN("unknown");
-
- public final String value;
- ConnectionSubType(String value) {
- this.value = value;
- }
- }
-
- /*
- * Keep the below constants in sync with
- * http://dxr.mozilla.org/mozilla-central/source/netwerk/base/nsINetworkLinkService.idl
- */
- public enum NetworkStatus {
- UP("up"),
- DOWN("down"),
- UNKNOWN("unknown");
-
- public final String value;
-
- NetworkStatus(String value) {
- this.value = value;
- }
- }
-
- // Connection Type defined in Network Information API v3.
- // See Bug 1270401 - current W3C Spec (Editor's Draft) is different, it also contains wimax, mixed, unknown.
- // W3C spec: http://w3c.github.io/netinfo/#the-connectiontype-enum
- public enum ConnectionType {
- CELLULAR(0),
- BLUETOOTH(1),
- ETHERNET(2),
- WIFI(3),
- OTHER(4),
- NONE(5);
-
- public final int value;
-
- ConnectionType(int value) {
- this.value = value;
- }
- }
-
- /**
- * Indicates whether network connectivity exists and it is possible to establish connections and pass data.
- */
- public static boolean isConnected(@NonNull Context context) {
- return isConnected((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
- }
-
- public static boolean isConnected(ConnectivityManager connectivityManager) {
- if (connectivityManager == null) {
- return false;
- }
-
- final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
- return networkInfo != null && networkInfo.isConnected();
- }
-
- /**
- * For mobile connections, maps particular connection subtype to a general 2G, 3G, 4G bucket.
- */
- public static ConnectionSubType getConnectionSubType(ConnectivityManager connectivityManager) {
- if (connectivityManager == null) {
- return ConnectionSubType.UNKNOWN;
- }
-
- final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
-
- if (networkInfo == null) {
- return ConnectionSubType.UNKNOWN;
- }
-
- switch (networkInfo.getType()) {
- case ConnectivityManager.TYPE_ETHERNET:
- return ConnectionSubType.ETHERNET;
- case ConnectivityManager.TYPE_MOBILE:
- return getGenericMobileSubtype(networkInfo.getSubtype());
- case ConnectivityManager.TYPE_WIMAX:
- return ConnectionSubType.WIMAX;
- case ConnectivityManager.TYPE_WIFI:
- return ConnectionSubType.WIFI;
- default:
- return ConnectionSubType.UNKNOWN;
- }
- }
-
- public static ConnectionType getConnectionType(ConnectivityManager connectivityManager) {
- if (connectivityManager == null) {
- return ConnectionType.NONE;
- }
-
- final NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
- if (networkInfo == null) {
- return ConnectionType.NONE;
- }
-
- switch (networkInfo.getType()) {
- case ConnectivityManager.TYPE_BLUETOOTH:
- return ConnectionType.BLUETOOTH;
- case ConnectivityManager.TYPE_ETHERNET:
- return ConnectionType.ETHERNET;
- // Fallthrough, MOBILE and WIMAX both map to CELLULAR.
- case ConnectivityManager.TYPE_MOBILE:
- case ConnectivityManager.TYPE_WIMAX:
- return ConnectionType.CELLULAR;
- case ConnectivityManager.TYPE_WIFI:
- return ConnectionType.WIFI;
- default:
- return ConnectionType.OTHER;
- }
- }
-
- public static NetworkStatus getNetworkStatus(ConnectivityManager connectivityManager) {
- if (connectivityManager == null) {
- return NetworkStatus.UNKNOWN;
- }
-
- if (isConnected(connectivityManager)) {
- return NetworkStatus.UP;
- }
- return NetworkStatus.DOWN;
- }
-
- private static ConnectionSubType getGenericMobileSubtype(int subtype) {
- switch (subtype) {
- // 2G types: fallthrough 5x
- case TelephonyManager.NETWORK_TYPE_GPRS:
- case TelephonyManager.NETWORK_TYPE_EDGE:
- case TelephonyManager.NETWORK_TYPE_CDMA:
- case TelephonyManager.NETWORK_TYPE_1xRTT:
- case TelephonyManager.NETWORK_TYPE_IDEN:
- return ConnectionSubType.CELL_2G;
- // 3G types: fallthrough 9x
- case TelephonyManager.NETWORK_TYPE_UMTS:
- case TelephonyManager.NETWORK_TYPE_EVDO_0:
- case TelephonyManager.NETWORK_TYPE_EVDO_A:
- case TelephonyManager.NETWORK_TYPE_HSDPA:
- case TelephonyManager.NETWORK_TYPE_HSUPA:
- case TelephonyManager.NETWORK_TYPE_HSPA:
- case TelephonyManager.NETWORK_TYPE_EVDO_B:
- case TelephonyManager.NETWORK_TYPE_EHRPD:
- case TelephonyManager.NETWORK_TYPE_HSPAP:
- return ConnectionSubType.CELL_3G;
- // 4G - just one type!
- case TelephonyManager.NETWORK_TYPE_LTE:
- return ConnectionSubType.CELL_4G;
- default:
- return ConnectionSubType.UNKNOWN;
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NonEvictingLruCache.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NonEvictingLruCache.java
deleted file mode 100644
index 793b39b81..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/NonEvictingLruCache.java
+++ /dev/null
@@ -1,44 +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.util;
-
-import android.util.LruCache;
-
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * An LruCache that also supports a set of items that will never be evicted.
- *
- * Alas, LruCache is final, so we compose rather than inherit.
- */
-public class NonEvictingLruCache<K, V> {
- private final ConcurrentHashMap<K, V> permanent = new ConcurrentHashMap<K, V>();
- private final LruCache<K, V> evictable;
-
- public NonEvictingLruCache(final int evictableSize) {
- evictable = new LruCache<K, V>(evictableSize);
- }
-
- public V get(K key) {
- V val = permanent.get(key);
- if (val == null) {
- return evictable.get(key);
- }
- return val;
- }
-
- public void putWithoutEviction(K key, V value) {
- permanent.put(key, value);
- }
-
- public void put(K key, V value) {
- evictable.put(key, value);
- }
-
- public void evictAll() {
- evictable.evictAll();
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/PrefUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/PrefUtils.java
deleted file mode 100644
index 217e40b91..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/PrefUtils.java
+++ /dev/null
@@ -1,70 +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.util;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.mozilla.gecko.AppConstants.Versions;
-
-import android.content.SharedPreferences;
-import android.util.Log;
-
-
-public class PrefUtils {
- private static final String LOGTAG = "GeckoPrefUtils";
-
- // Cross version compatible way to get a string set from a pref
- public static Set<String> getStringSet(final SharedPreferences prefs,
- final String key,
- final Set<String> defaultVal) {
- if (!prefs.contains(key)) {
- return defaultVal;
- }
-
- // If this is Android version >= 11, try to use a Set<String>.
- try {
- return prefs.getStringSet(key, new HashSet<String>());
- } catch (ClassCastException ex) {
- // A ClassCastException means we've upgraded from a pre-v11 Android to a new one
- final Set<String> val = getFromJSON(prefs, key);
- SharedPreferences.Editor edit = prefs.edit();
- putStringSet(edit, key, val).apply();
- return val;
- }
- }
-
- private static Set<String> getFromJSON(SharedPreferences prefs, String key) {
- try {
- final String val = prefs.getString(key, "[]");
- return JSONUtils.parseStringSet(new JSONArray(val));
- } catch (JSONException ex) {
- Log.i(LOGTAG, "Unable to parse JSON", ex);
- }
-
- return new HashSet<String>();
- }
-
- /**
- * Cross version compatible way to save a set of strings.
- * <p>
- * This method <b>does not commit</b> any transaction. It is up to callers
- * to commit.
- *
- * @param editor to write to.
- * @param key to write.
- * @param vals comprising string set.
- * @return
- */
- public static SharedPreferences.Editor putStringSet(final SharedPreferences.Editor editor,
- final String key,
- final Set<String> vals) {
- editor.putStringSet(key, vals);
- return editor;
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
deleted file mode 100644
index 35010242b..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ProxySelector.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// This code is based on AOSP /libcore/luni/src/main/java/java/net/ProxySelectorImpl.java
-
-package org.mozilla.gecko.util;
-
-import android.support.annotation.Nullable;
-import android.text.TextUtils;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.URI;
-import java.net.URLConnection;
-import java.util.List;
-
-public class ProxySelector {
- public static URLConnection openConnectionWithProxy(URI uri) throws IOException {
- java.net.ProxySelector ps = java.net.ProxySelector.getDefault();
- Proxy proxy = Proxy.NO_PROXY;
- if (ps != null) {
- List<Proxy> proxies = ps.select(uri);
- if (proxies != null && !proxies.isEmpty()) {
- proxy = proxies.get(0);
- }
- }
-
- return uri.toURL().openConnection(proxy);
- }
-
- public ProxySelector() {
- }
-
- public Proxy select(String scheme, String host) {
- int port = -1;
- Proxy proxy = null;
- String nonProxyHostsKey = null;
- boolean httpProxyOkay = true;
- if ("http".equalsIgnoreCase(scheme)) {
- port = 80;
- nonProxyHostsKey = "http.nonProxyHosts";
- proxy = lookupProxy("http.proxyHost", "http.proxyPort", Proxy.Type.HTTP, port);
- } else if ("https".equalsIgnoreCase(scheme)) {
- port = 443;
- nonProxyHostsKey = "https.nonProxyHosts"; // RI doesn't support this
- proxy = lookupProxy("https.proxyHost", "https.proxyPort", Proxy.Type.HTTP, port);
- } else if ("ftp".equalsIgnoreCase(scheme)) {
- port = 80; // not 21 as you might guess
- nonProxyHostsKey = "ftp.nonProxyHosts";
- proxy = lookupProxy("ftp.proxyHost", "ftp.proxyPort", Proxy.Type.HTTP, port);
- } else if ("socket".equalsIgnoreCase(scheme)) {
- httpProxyOkay = false;
- } else {
- return Proxy.NO_PROXY;
- }
-
- if (nonProxyHostsKey != null
- && isNonProxyHost(host, System.getProperty(nonProxyHostsKey))) {
- return Proxy.NO_PROXY;
- }
-
- if (proxy != null) {
- return proxy;
- }
-
- if (httpProxyOkay) {
- proxy = lookupProxy("proxyHost", "proxyPort", Proxy.Type.HTTP, port);
- if (proxy != null) {
- return proxy;
- }
- }
-
- proxy = lookupProxy("socksProxyHost", "socksProxyPort", Proxy.Type.SOCKS, 1080);
- if (proxy != null) {
- return proxy;
- }
-
- return Proxy.NO_PROXY;
- }
-
- /**
- * Returns the proxy identified by the {@code hostKey} system property, or
- * null.
- */
- @Nullable
- private Proxy lookupProxy(String hostKey, String portKey, Proxy.Type type, int defaultPort) {
- final String host = System.getProperty(hostKey);
- if (TextUtils.isEmpty(host)) {
- return null;
- }
-
- final int port = getSystemPropertyInt(portKey, defaultPort);
- if (port == -1) {
- // Port can be -1. See bug 1270529.
- return null;
- }
-
- return new Proxy(type, InetSocketAddress.createUnresolved(host, port));
- }
-
- private int getSystemPropertyInt(String key, int defaultValue) {
- String string = System.getProperty(key);
- if (string != null) {
- try {
- return Integer.parseInt(string);
- } catch (NumberFormatException ignored) {
- }
- }
- return defaultValue;
- }
-
- /**
- * Returns true if the {@code nonProxyHosts} system property pattern exists
- * and matches {@code host}.
- */
- private boolean isNonProxyHost(String host, String nonProxyHosts) {
- if (host == null || nonProxyHosts == null) {
- return false;
- }
-
- // construct pattern
- StringBuilder patternBuilder = new StringBuilder();
- for (int i = 0; i < nonProxyHosts.length(); i++) {
- char c = nonProxyHosts.charAt(i);
- switch (c) {
- case '.':
- patternBuilder.append("\\.");
- break;
- case '*':
- patternBuilder.append(".*");
- break;
- default:
- patternBuilder.append(c);
- }
- }
- // check whether the host is the nonProxyHosts.
- String pattern = patternBuilder.toString();
- return host.matches(pattern);
- }
-}
-
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/RawResource.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/RawResource.java
deleted file mode 100644
index 5bcad1c60..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/RawResource.java
+++ /dev/null
@@ -1,52 +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.util;
-
-import android.content.Context;
-import android.content.res.Resources;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.StringWriter;
-
-/**
- * {@code RawResource} provides API to load raw resources in different
- * forms. For now, we only load them as strings. We're using raw resources
- * as localizable 'assets' as opposed to a string that can be directly
- * translatable e.g. JSON file vs string.
- *
- * This is just a utility class to avoid code duplication for the different
- * cases where need to read such assets.
- */
-public final class RawResource {
- public static String getAsString(Context context, int id) throws IOException {
- InputStreamReader reader = null;
-
- try {
- final Resources res = context.getResources();
- final InputStream is = res.openRawResource(id);
- if (is == null) {
- return null;
- }
-
- reader = new InputStreamReader(is);
-
- final char[] buffer = new char[1024];
- final StringWriter s = new StringWriter();
-
- int n;
- while ((n = reader.read(buffer, 0, buffer.length)) != -1) {
- s.write(buffer, 0, n);
- }
-
- return s.toString();
- } finally {
- if (reader != null) {
- reader.close();
- }
- }
- }
-} \ No newline at end of file
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java
deleted file mode 100644
index 308168f43..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/StringUtils.java
+++ /dev/null
@@ -1,293 +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.util;
-
-import android.net.Uri;
-import android.support.annotation.NonNull;
-import android.text.TextUtils;
-
-import org.mozilla.gecko.AppConstants.Versions;
-
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-public class StringUtils {
- private static final String LOGTAG = "GeckoStringUtils";
-
- private static final String FILTER_URL_PREFIX = "filter://";
- private static final String USER_ENTERED_URL_PREFIX = "user-entered:";
-
- /*
- * This method tries to guess if the given string could be a search query or URL,
- * and returns a previous result if there is ambiguity
- *
- * Search examples:
- * foo
- * foo bar.com
- * foo http://bar.com
- *
- * URL examples
- * foo.com
- * foo.c
- * :foo
- * http://foo.com bar
- *
- * wasSearchQuery specifies whether text was a search query before the latest change
- * in text. In ambiguous cases where the new text can be either a search or a URL,
- * wasSearchQuery is returned
- */
- public static boolean isSearchQuery(String text, boolean wasSearchQuery) {
- // We remove leading and trailing white spaces when decoding URLs
- text = text.trim();
- if (text.length() == 0)
- return wasSearchQuery;
-
- int colon = text.indexOf(':');
- int dot = text.indexOf('.');
- int space = text.indexOf(' ');
-
- // If a space is found before any dot and colon, we assume this is a search query
- if (space > -1 && (colon == -1 || space < colon) && (dot == -1 || space < dot)) {
- return true;
- }
- // Otherwise, if a dot or a colon is found, we assume this is a URL
- if (dot > -1 || colon > -1) {
- return false;
- }
- // Otherwise, text is ambiguous, and we keep its status unchanged
- return wasSearchQuery;
- }
-
- /**
- * Strip the ref from a URL, if present
- *
- * @return The base URL, without the ref. The original String is returned if it has no ref,
- * of if the input is malformed.
- */
- public static String stripRef(final String inputURL) {
- if (inputURL == null) {
- return null;
- }
-
- final int refIndex = inputURL.indexOf('#');
-
- if (refIndex >= 0) {
- return inputURL.substring(0, refIndex);
- }
-
- return inputURL;
- }
-
- public static class UrlFlags {
- public static final int NONE = 0;
- public static final int STRIP_HTTPS = 1;
- }
-
- public static String stripScheme(String url) {
- return stripScheme(url, UrlFlags.NONE);
- }
-
- public static String stripScheme(String url, int flags) {
- if (url == null) {
- return url;
- }
-
- String newURL = url;
-
- if (newURL.startsWith("http://")) {
- newURL = newURL.replace("http://", "");
- } else if (newURL.startsWith("https://") && flags == UrlFlags.STRIP_HTTPS) {
- newURL = newURL.replace("https://", "");
- }
-
- if (newURL.endsWith("/")) {
- newURL = newURL.substring(0, newURL.length()-1);
- }
-
- return newURL;
- }
-
- public static boolean isHttpOrHttps(String url) {
- if (TextUtils.isEmpty(url)) {
- return false;
- }
-
- return url.startsWith("http://") || url.startsWith("https://");
- }
-
- public static String stripCommonSubdomains(String host) {
- if (host == null) {
- return host;
- }
-
- // In contrast to desktop, we also strip mobile subdomains,
- // since its unlikely users are intentionally typing them
- int start = 0;
-
- if (host.startsWith("www.")) {
- start = 4;
- } else if (host.startsWith("mobile.")) {
- start = 7;
- } else if (host.startsWith("m.")) {
- start = 2;
- }
-
- return host.substring(start);
- }
-
- /**
- * Searches the url query string for the first value with the given key.
- */
- public static String getQueryParameter(String url, String desiredKey) {
- if (TextUtils.isEmpty(url) || TextUtils.isEmpty(desiredKey)) {
- return null;
- }
-
- final String[] urlParts = url.split("\\?");
- if (urlParts.length < 2) {
- return null;
- }
-
- final String query = urlParts[1];
- for (final String param : query.split("&")) {
- final String pair[] = param.split("=");
- final String key = Uri.decode(pair[0]);
-
- // Key is empty or does not match the key we're looking for, discard
- if (TextUtils.isEmpty(key) || !key.equals(desiredKey)) {
- continue;
- }
- // No value associated with key, discard
- if (pair.length < 2) {
- continue;
- }
- final String value = Uri.decode(pair[1]);
- if (TextUtils.isEmpty(value)) {
- return null;
- }
- return value;
- }
-
- return null;
- }
-
- public static boolean isFilterUrl(String url) {
- if (TextUtils.isEmpty(url)) {
- return false;
- }
-
- return url.startsWith(FILTER_URL_PREFIX);
- }
-
- public static String getFilterFromUrl(String url) {
- if (TextUtils.isEmpty(url)) {
- return null;
- }
-
- return url.substring(FILTER_URL_PREFIX.length());
- }
-
- public static boolean isShareableUrl(final String url) {
- final String scheme = Uri.parse(url).getScheme();
- return !("about".equals(scheme) || "chrome".equals(scheme) ||
- "file".equals(scheme) || "resource".equals(scheme));
- }
-
- public static boolean isUserEnteredUrl(String url) {
- return (url != null && url.startsWith(USER_ENTERED_URL_PREFIX));
- }
-
- /**
- * Given a url with a user-entered scheme, extract the
- * scheme-specific component. For e.g, given "user-entered://www.google.com",
- * this method returns "//www.google.com". If the passed url
- * does not have a user-entered scheme, the same url will be returned.
- *
- * @param url to be decoded
- * @return url component entered by user
- */
- public static String decodeUserEnteredUrl(String url) {
- Uri uri = Uri.parse(url);
- if ("user-entered".equals(uri.getScheme())) {
- return uri.getSchemeSpecificPart();
- }
- return url;
- }
-
- public static String encodeUserEnteredUrl(String url) {
- return Uri.fromParts("user-entered", url, null).toString();
- }
-
- /**
- * Compatibility layer for API < 11.
- *
- * Returns a set of the unique names of all query parameters. Iterating
- * over the set will return the names in order of their first occurrence.
- *
- * @param uri
- * @throws UnsupportedOperationException if this isn't a hierarchical URI
- *
- * @return a set of decoded names
- */
- public static Set<String> getQueryParameterNames(Uri uri) {
- return uri.getQueryParameterNames();
- }
-
- public static String safeSubstring(@NonNull final String str, final int start, final int end) {
- return str.substring(
- Math.max(0, start),
- Math.min(end, str.length()));
- }
-
- /**
- * Check if this might be a RTL (right-to-left) text by looking at the first character.
- */
- public static boolean isRTL(String text) {
- if (TextUtils.isEmpty(text)) {
- return false;
- }
-
- final char character = text.charAt(0);
- final byte directionality = Character.getDirectionality(character);
-
- return directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT
- || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC
- || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING
- || directionality == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE;
- }
-
- /**
- * Force LTR (left-to-right) by prepending the text with the "left-to-right mark" (U+200E) if needed.
- */
- public static String forceLTR(String text) {
- if (!isRTL(text)) {
- return text;
- }
-
- return "\u200E" + text;
- }
-
- /**
- * Joining together a sequence of strings with a separator.
- */
- public static String join(@NonNull String separator, @NonNull List<String> parts) {
- if (parts.size() == 0) {
- return "";
- }
-
- final StringBuilder builder = new StringBuilder();
- builder.append(parts.get(0));
-
- for (int i = 1; i < parts.size(); i++) {
- builder.append(separator);
- builder.append(parts.get(i));
- }
-
- return builder.toString();
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java
deleted file mode 100644
index 884a56dc4..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/ThreadUtils.java
+++ /dev/null
@@ -1,247 +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.util;
-
-import org.mozilla.gecko.annotation.RobocopTarget;
-
-import java.util.Map;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.util.Log;
-
-public final class ThreadUtils {
- private static final String LOGTAG = "ThreadUtils";
-
- /**
- * Controls the action taken when a method like
- * {@link ThreadUtils#assertOnUiThread(AssertBehavior)} detects a problem.
- */
- public static enum AssertBehavior {
- NONE,
- THROW,
- }
-
- private static final Thread sUiThread = Looper.getMainLooper().getThread();
- private static final Handler sUiHandler = new Handler(Looper.getMainLooper());
-
- private static volatile Thread sBackgroundThread;
-
- // Referenced directly from GeckoAppShell in highly performance-sensitive code (The extra
- // function call of the getter was harming performance. (Bug 897123))
- // Once Bug 709230 is resolved we should reconsider this as ProGuard should be able to optimise
- // this out at compile time.
- public static Handler sGeckoHandler;
- public static volatile Thread sGeckoThread;
-
- // Delayed Runnable that resets the Gecko thread priority.
- private static final Runnable sPriorityResetRunnable = new Runnable() {
- @Override
- public void run() {
- resetGeckoPriority();
- }
- };
-
- private static boolean sIsGeckoPriorityReduced;
-
- @SuppressWarnings("serial")
- public static class UiThreadBlockedException extends RuntimeException {
- public UiThreadBlockedException() {
- super();
- }
-
- public UiThreadBlockedException(String msg) {
- super(msg);
- }
-
- public UiThreadBlockedException(String msg, Throwable e) {
- super(msg, e);
- }
-
- public UiThreadBlockedException(Throwable e) {
- super(e);
- }
- }
-
- public static void dumpAllStackTraces() {
- Log.w(LOGTAG, "Dumping ALL the threads!");
- Map<Thread, StackTraceElement[]> allStacks = Thread.getAllStackTraces();
- for (Thread t : allStacks.keySet()) {
- Log.w(LOGTAG, t.toString());
- for (StackTraceElement ste : allStacks.get(t)) {
- Log.w(LOGTAG, ste.toString());
- }
- Log.w(LOGTAG, "----");
- }
- }
-
- public static void setBackgroundThread(Thread thread) {
- sBackgroundThread = thread;
- }
-
- public static Thread getUiThread() {
- return sUiThread;
- }
-
- public static Handler getUiHandler() {
- return sUiHandler;
- }
-
- public static void postToUiThread(Runnable runnable) {
- sUiHandler.post(runnable);
- }
-
- public static void postDelayedToUiThread(Runnable runnable, long timeout) {
- sUiHandler.postDelayed(runnable, timeout);
- }
-
- public static void removeCallbacksFromUiThread(Runnable runnable) {
- sUiHandler.removeCallbacks(runnable);
- }
-
- public static Thread getBackgroundThread() {
- return sBackgroundThread;
- }
-
- public static Handler getBackgroundHandler() {
- return GeckoBackgroundThread.getHandler();
- }
-
- public static void postToBackgroundThread(Runnable runnable) {
- GeckoBackgroundThread.post(runnable);
- }
-
- public static void assertOnUiThread(final AssertBehavior assertBehavior) {
- assertOnThread(getUiThread(), assertBehavior);
- }
-
- public static void assertOnUiThread() {
- assertOnThread(getUiThread(), AssertBehavior.THROW);
- }
-
- public static void assertNotOnUiThread() {
- assertNotOnThread(getUiThread(), AssertBehavior.THROW);
- }
-
- @RobocopTarget
- public static void assertOnGeckoThread() {
- assertOnThread(sGeckoThread, AssertBehavior.THROW);
- }
-
- public static void assertNotOnGeckoThread() {
- if (sGeckoThread == null) {
- // Cannot be on Gecko thread if Gecko thread is not live yet.
- return;
- }
- assertNotOnThread(sGeckoThread, AssertBehavior.THROW);
- }
-
- public static void assertOnBackgroundThread() {
- assertOnThread(getBackgroundThread(), AssertBehavior.THROW);
- }
-
- public static void assertOnThread(final Thread expectedThread) {
- assertOnThread(expectedThread, AssertBehavior.THROW);
- }
-
- public static void assertOnThread(final Thread expectedThread, AssertBehavior behavior) {
- assertOnThreadComparison(expectedThread, behavior, true);
- }
-
- public static void assertNotOnThread(final Thread expectedThread, AssertBehavior behavior) {
- assertOnThreadComparison(expectedThread, behavior, false);
- }
-
- private static void assertOnThreadComparison(final Thread expectedThread, AssertBehavior behavior, boolean expected) {
- final Thread currentThread = Thread.currentThread();
- final long currentThreadId = currentThread.getId();
- final long expectedThreadId = expectedThread.getId();
-
- if ((currentThreadId == expectedThreadId) == expected) {
- return;
- }
-
- final String message;
- if (expected) {
- message = "Expected thread " + expectedThreadId +
- " (\"" + expectedThread.getName() + "\"), but running on thread " +
- currentThreadId + " (\"" + currentThread.getName() + "\")";
- } else {
- message = "Expected anything but " + expectedThreadId +
- " (\"" + expectedThread.getName() + "\"), but running there.";
- }
-
- final IllegalThreadStateException e = new IllegalThreadStateException(message);
-
- switch (behavior) {
- case THROW:
- throw e;
- default:
- Log.e(LOGTAG, "Method called on wrong thread!", e);
- }
- }
-
- public static boolean isOnGeckoThread() {
- if (sGeckoThread != null) {
- return isOnThread(sGeckoThread);
- }
- return false;
- }
-
- public static boolean isOnUiThread() {
- return isOnThread(getUiThread());
- }
-
- @RobocopTarget
- public static boolean isOnBackgroundThread() {
- if (sBackgroundThread == null) {
- return false;
- }
-
- return isOnThread(sBackgroundThread);
- }
-
- @RobocopTarget
- public static boolean isOnThread(Thread thread) {
- return (Thread.currentThread().getId() == thread.getId());
- }
-
- /**
- * Reduces the priority of the Gecko thread, allowing other operations
- * (such as those related to the UI and database) to take precedence.
- *
- * Note that there are no guards in place to prevent multiple calls
- * to this method from conflicting with each other.
- *
- * @param timeout Timeout in ms after which the priority will be reset
- */
- public static void reduceGeckoPriority(long timeout) {
- if (Runtime.getRuntime().availableProcessors() > 1) {
- // Don't reduce priority for multicore devices. We use availableProcessors()
- // for its fast performance. It may give false negatives (i.e. multicore
- // detected as single-core), but we can tolerate this behavior.
- return;
- }
- if (!sIsGeckoPriorityReduced && sGeckoThread != null) {
- sIsGeckoPriorityReduced = true;
- sGeckoThread.setPriority(Thread.MIN_PRIORITY);
- getUiHandler().postDelayed(sPriorityResetRunnable, timeout);
- }
- }
-
- /**
- * Resets the priority of a thread whose priority has been reduced
- * by reduceGeckoPriority.
- */
- public static void resetGeckoPriority() {
- if (sIsGeckoPriorityReduced) {
- sIsGeckoPriorityReduced = false;
- sGeckoThread.setPriority(Thread.NORM_PRIORITY);
- getUiHandler().removeCallbacks(sPriorityResetRunnable);
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UIAsyncTask.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UIAsyncTask.java
deleted file mode 100644
index 26cc32a99..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UIAsyncTask.java
+++ /dev/null
@@ -1,121 +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.util;
-
-import android.os.Handler;
-import android.os.Looper;
-
-/**
- * Executes a background task and publishes the result on the UI thread.
- *
- * The standard {@link android.os.AsyncTask} only runs onPostExecute on the
- * thread it is constructed on, so this is a convenience class for creating
- * tasks off the UI thread.
- *
- * We use generics differently to Android's AsyncTask.
- * Android uses a "Params" type parameter to represent the type of all the parameters to this task.
- * It then uses arguments of type Params... to permit arbitrarily-many of these to be passed
- * fluently.
- *
- * Unfortunately, since Java does not support generic array types (and since varargs desugars to a
- * single array parameter) that behaviour exposes a hole in the type system. See:
- * http://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html#vulnerabilities
- *
- * Instead, we equivalently have a single type parameter "Param". A UiAsyncTask may take exactly one
- * parameter of type Param. Since Param can be an array type, this no more restrictive than the
- * other approach, it just provides additional type safety.
- */
-public abstract class UIAsyncTask<Param, Result> {
- /**
- * Provide a convenient API for parameter-free UiAsyncTasks by wrapping parameter-taking methods
- * from UiAsyncTask in parameterless equivalents.
- */
- public static abstract class WithoutParams<InnerResult> extends UIAsyncTask<Void, InnerResult> {
- public WithoutParams(Handler backgroundThreadHandler) {
- super(backgroundThreadHandler);
- }
-
- public void execute() {
- execute(null);
- }
-
- @Override
- protected InnerResult doInBackground(Void unused) {
- return doInBackground();
- }
-
- protected abstract InnerResult doInBackground();
- }
-
- final Handler mBackgroundThreadHandler;
- private volatile boolean mCancelled;
- private static Handler sHandler;
-
- /**
- * Creates a new asynchronous task.
- *
- * @param backgroundThreadHandler the handler to execute the background task on
- */
- public UIAsyncTask(Handler backgroundThreadHandler) {
- mBackgroundThreadHandler = backgroundThreadHandler;
- }
-
- private static synchronized Handler getUiHandler() {
- if (sHandler == null) {
- sHandler = new Handler(Looper.getMainLooper());
- }
-
- return sHandler;
- }
-
- private final class BackgroundTaskRunnable implements Runnable {
- private final Param mParam;
-
- public BackgroundTaskRunnable(Param param) {
- mParam = param;
- }
-
- @Override
- public void run() {
- final Result result = doInBackground(mParam);
-
- getUiHandler().post(new Runnable() {
- @Override
- public void run() {
- if (mCancelled) {
- onCancelled();
- } else {
- onPostExecute(result);
- }
- }
- });
- }
- }
-
- protected void execute(final Param param) {
- getUiHandler().post(new Runnable() {
- @Override
- public void run() {
- onPreExecute();
- mBackgroundThreadHandler.post(new BackgroundTaskRunnable(param));
- }
- });
- }
-
- public final boolean cancel() {
- mCancelled = true;
- return mCancelled;
- }
-
- public final boolean isCancelled() {
- return mCancelled;
- }
-
- protected void onPreExecute() { }
- protected void onPostExecute(Result result) { }
- protected void onCancelled() { }
- protected abstract Result doInBackground(Param param);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UUIDUtil.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UUIDUtil.java
deleted file mode 100644
index cef303a87..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/UUIDUtil.java
+++ /dev/null
@@ -1,19 +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.util;
-
-import java.util.regex.Pattern;
-
-/**
- * Utilities for UUIDs.
- */
-public class UUIDUtil {
- private UUIDUtil() {}
-
- public static final String UUID_REGEX = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
- public static final Pattern UUID_PATTERN = Pattern.compile(UUID_REGEX);
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WeakReferenceHandler.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WeakReferenceHandler.java
deleted file mode 100644
index 3e8508bce..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WeakReferenceHandler.java
+++ /dev/null
@@ -1,27 +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.util;
-
-import android.os.Handler;
-
-import java.lang.ref.WeakReference;
-
-/**
- * A Handler to help prevent memory leaks when using Handlers as inner classes.
- *
- * To use, extend the Handler, if it's an inner class, make it static,
- * and reference `this` via the associated WeakReference.
- *
- * For additional context, see the "HandlerLeak" android lint item and this post by Romain Guy:
- * https://groups.google.com/forum/#!msg/android-developers/1aPZXZG6kWk/lIYDavGYn5UJ
- */
-public class WeakReferenceHandler<T> extends Handler {
- public final WeakReference<T> mTarget;
-
- public WeakReferenceHandler(final T that) {
- super();
- mTarget = new WeakReference<>(that);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WindowUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WindowUtils.java
deleted file mode 100644
index 5298f846a..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/WindowUtils.java
+++ /dev/null
@@ -1,59 +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.util;
-
-import org.mozilla.gecko.AppConstants.Versions;
-
-import android.content.Context;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Display;
-import android.view.WindowManager;
-
-import java.lang.reflect.Method;
-
-public class WindowUtils {
- private static final String LOGTAG = "Gecko" + WindowUtils.class.getSimpleName();
-
- private WindowUtils() { /* To prevent instantiation */ }
-
- /**
- * Returns the best-guess physical device dimensions, including the system status bars. Note
- * that DisplayMetrics.height/widthPixels does not include the system bars.
- *
- * via http://stackoverflow.com/a/23861333
- *
- * @param context the calling Activity's Context
- * @return The number of pixels of the device's largest dimension, ignoring software status bars
- */
- public static int getLargestDimension(final Context context) {
- final Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
-
- if (Versions.feature17Plus) {
- final DisplayMetrics realMetrics = new DisplayMetrics();
- display.getRealMetrics(realMetrics);
- return Math.max(realMetrics.widthPixels, realMetrics.heightPixels);
-
- } else {
- int tempWidth;
- int tempHeight;
- try {
- final Method getRawH = Display.class.getMethod("getRawHeight");
- final Method getRawW = Display.class.getMethod("getRawWidth");
- tempWidth = (Integer) getRawW.invoke(display);
- tempHeight = (Integer) getRawH.invoke(display);
- } catch (Exception e) {
- // This is the best we can do.
- tempWidth = display.getWidth();
- tempHeight = display.getHeight();
- Log.w(LOGTAG, "Couldn't use reflection to get the real display metrics.");
- }
-
- return Math.max(tempWidth, tempHeight);
-
- }
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffix.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffix.java
deleted file mode 100644
index 6a146cfcf..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffix.java
+++ /dev/null
@@ -1,121 +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.util.publicsuffix;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.annotation.WorkerThread;
-
-import org.mozilla.gecko.util.StringUtils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Helper methods for the public suffix part of a domain.
- *
- * A "public suffix" is one under which Internet users can (or historically could) directly register
- * names. Some examples of public suffixes are .com, .co.uk and pvt.k12.ma.us.
- *
- * https://publicsuffix.org/
- *
- * Some parts of the implementation of this class are based on InternetDomainName class of the Guava
- * project: https://github.com/google/guava
- */
-public class PublicSuffix {
- /**
- * Strip the public suffix from the domain. Returns the original domain if no public suffix
- * could be found.
- *
- * www.mozilla.org -> www.mozilla
- * independent.co.uk -> independent
- */
- @NonNull
- @WorkerThread // This method might need to load data from disk
- public static String stripPublicSuffix(Context context, @NonNull String domain) {
- if (domain.length() == 0) {
- return domain;
- }
-
- final int index = findPublicSuffixIndex(context, domain);
- if (index == -1) {
- return domain;
- }
-
- return domain.substring(0, index);
- }
-
- /**
- * Returns the index of the leftmost part of the public suffix, or -1 if not found.
- */
- @WorkerThread
- private static int findPublicSuffixIndex(Context context, String domain) {
- final List<String> parts = normalizeAndSplit(domain);
- final int partsSize = parts.size();
- final Set<String> exact = PublicSuffixPatterns.getExactSet(context);
-
- for (int i = 0; i < partsSize; i++) {
- String ancestorName = StringUtils.join(".", parts.subList(i, partsSize));
-
- if (exact.contains(ancestorName)) {
- return joinIndex(parts, i);
- }
-
- // Excluded domains (e.g. !nhs.uk) use the next highest
- // domain as the effective public suffix (e.g. uk).
- if (PublicSuffixPatterns.EXCLUDED.contains(ancestorName)) {
- return joinIndex(parts, i + 1);
- }
-
- if (matchesWildcardPublicSuffix(ancestorName)) {
- return joinIndex(parts, i);
- }
- }
-
- return -1;
- }
-
- /**
- * Normalize domain and split into domain parts (www.mozilla.org -> [www, mozilla, org]).
- */
- private static List<String> normalizeAndSplit(String domain) {
- domain = domain.replaceAll("[.\u3002\uFF0E\uFF61]", "."); // All dot-like characters to '.'
- domain = domain.toLowerCase();
-
- if (domain.endsWith(".")) {
- domain = domain.substring(0, domain.length() - 1); // Strip trailing '.'
- }
-
- List<String> parts = new ArrayList<>();
- Collections.addAll(parts, domain.split("\\."));
-
- return parts;
- }
-
- /**
- * Translate the index of the leftmost part of the public suffix to the index of the domain string.
- *
- * [www, mozilla, org] and 2 => 12 (www.mozilla)
- */
- private static int joinIndex(List<String> parts, int index) {
- int actualIndex = parts.get(0).length();
-
- for (int i = 1; i < index; i++) {
- actualIndex += parts.get(i).length() + 1; // Add one for the "." that is not part of the list elements
- }
-
- return actualIndex;
- }
-
- /**
- * Does the domain name match one of the "wildcard" patterns (e.g. {@code "*.ar"})?
- */
- private static boolean matchesWildcardPublicSuffix(String domain) {
- final String[] pieces = domain.split("\\.", 2);
- return pieces.length == 2 && PublicSuffixPatterns.UNDER.contains(pieces[1]);
- }
-}
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffixPatterns.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffixPatterns.java
deleted file mode 100644
index 8c4b80ce1..000000000
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/publicsuffix/PublicSuffixPatterns.java
+++ /dev/null
@@ -1,117 +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.util.publicsuffix;
-
-import android.content.Context;
-import android.util.Log;
-
-import org.mozilla.gecko.util.IOUtils;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.HashSet;
-import java.util.Set;
-
-class PublicSuffixPatterns {
- /** If a hostname is contained as a key in this map, it is a public suffix. */
- private static Set<String> EXACT = null;
-
- static synchronized Set<String> getExactSet(Context context) {
- if (EXACT != null) {
- return EXACT;
- }
-
- EXACT = new HashSet<>();
-
- InputStream stream = null;
-
- try {
- stream = context.getAssets().open("publicsuffixlist");
- BufferedReader reader = new BufferedReader(new InputStreamReader(
- new BufferedInputStream(stream)));
-
- String line;
- while ((line = reader.readLine()) != null) {
- EXACT.add(line);
- }
-
- } catch (IOException e) {
- Log.e("Patterns", "IOException during loading public suffix list");
- } finally {
- IOUtils.safeStreamClose(stream);
- }
-
- return EXACT;
- }
-
-
- /**
- * If a hostname is not a key in the EXCLUDE map, and if removing its
- * leftmost component results in a name which is a key in this map, it is a
- * public suffix.
- */
- static final Set<String> UNDER = new HashSet<>();
- static {
- UNDER.add("bd");
- UNDER.add("magentosite.cloud");
- UNDER.add("ke");
- UNDER.add("triton.zone");
- UNDER.add("compute.estate");
- UNDER.add("ye");
- UNDER.add("pg");
- UNDER.add("kh");
- UNDER.add("platform.sh");
- UNDER.add("fj");
- UNDER.add("ck");
- UNDER.add("fk");
- UNDER.add("alces.network");
- UNDER.add("sch.uk");
- UNDER.add("jm");
- UNDER.add("mm");
- UNDER.add("api.githubcloud.com");
- UNDER.add("ext.githubcloud.com");
- UNDER.add("0emm.com");
- UNDER.add("githubcloudusercontent.com");
- UNDER.add("cns.joyent.com");
- UNDER.add("bn");
- UNDER.add("yokohama.jp");
- UNDER.add("nagoya.jp");
- UNDER.add("kobe.jp");
- UNDER.add("sendai.jp");
- UNDER.add("kawasaki.jp");
- UNDER.add("sapporo.jp");
- UNDER.add("kitakyushu.jp");
- UNDER.add("np");
- UNDER.add("nom.br");
- UNDER.add("er");
- UNDER.add("cryptonomic.net");
- UNDER.add("gu");
- UNDER.add("kw");
- UNDER.add("zw");
- UNDER.add("mz");
- }
-
- /**
- * The elements in this map would pass the UNDER test, but are known not to
- * be public suffixes and are thus excluded from consideration. Since it
- * refers to elements in UNDER of the same type, the type is actually not
- * important here. The map is simply used for consistency reasons.
- */
- static final Set<String> EXCLUDED = new HashSet<>();
- static {
- EXCLUDED.add("www.ck");
- EXCLUDED.add("city.yokohama.jp");
- EXCLUDED.add("city.nagoya.jp");
- EXCLUDED.add("city.kobe.jp");
- EXCLUDED.add("city.sendai.jp");
- EXCLUDED.add("city.kawasaki.jp");
- EXCLUDED.add("city.sapporo.jp");
- EXCLUDED.add("city.kitakyushu.jp");
- EXCLUDED.add("teledata.mz");
- }
-}
diff --git a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java
deleted file mode 100644
index 3bdd8c450..000000000
--- a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java
+++ /dev/null
@@ -1,147 +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 com.googlecode.eyesfree.braille.selfbraille;
-
-/**
- * Interface for a client to control braille output for a part of the
- * accessibility node tree.
- */
-public interface ISelfBrailleService extends android.os.IInterface {
- /** Local-side IPC implementation stub class. */
- public static abstract class Stub extends android.os.Binder implements
- com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService {
- private static final java.lang.String DESCRIPTOR = "com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService";
-
- /** Construct the stub at attach it to the interface. */
- public Stub() {
- this.attachInterface(this, DESCRIPTOR);
- }
-
- /**
- * Cast an IBinder object into an
- * com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService
- * interface, generating a proxy if needed.
- */
- public static com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService asInterface(
- android.os.IBinder obj) {
- if ((obj == null)) {
- return null;
- }
- android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
- if (((iin != null) && (iin instanceof com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService))) {
- return ((com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService) iin);
- }
- return new com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService.Stub.Proxy(
- obj);
- }
-
- @Override
- public android.os.IBinder asBinder() {
- return this;
- }
-
- @Override
- public boolean onTransact(int code, android.os.Parcel data,
- android.os.Parcel reply, int flags)
- throws android.os.RemoteException {
- switch (code) {
- case INTERFACE_TRANSACTION: {
- reply.writeString(DESCRIPTOR);
- return true;
- }
- case TRANSACTION_write: {
- data.enforceInterface(DESCRIPTOR);
- android.os.IBinder _arg0;
- _arg0 = data.readStrongBinder();
- com.googlecode.eyesfree.braille.selfbraille.WriteData _arg1;
- if ((0 != data.readInt())) {
- _arg1 = com.googlecode.eyesfree.braille.selfbraille.WriteData.CREATOR
- .createFromParcel(data);
- } else {
- _arg1 = null;
- }
- this.write(_arg0, _arg1);
- reply.writeNoException();
- return true;
- }
- case TRANSACTION_disconnect: {
- data.enforceInterface(DESCRIPTOR);
- android.os.IBinder _arg0;
- _arg0 = data.readStrongBinder();
- this.disconnect(_arg0);
- return true;
- }
- }
- return super.onTransact(code, data, reply, flags);
- }
-
- private static class Proxy implements
- com.googlecode.eyesfree.braille.selfbraille.ISelfBrailleService {
- private android.os.IBinder mRemote;
-
- Proxy(android.os.IBinder remote) {
- mRemote = remote;
- }
-
- @Override
- public android.os.IBinder asBinder() {
- return mRemote;
- }
-
- public java.lang.String getInterfaceDescriptor() {
- return DESCRIPTOR;
- }
-
- @Override
- public void write(
- android.os.IBinder clientToken,
- com.googlecode.eyesfree.braille.selfbraille.WriteData writeData)
- throws android.os.RemoteException {
- android.os.Parcel _data = android.os.Parcel.obtain();
- android.os.Parcel _reply = android.os.Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder(clientToken);
- if ((writeData != null)) {
- _data.writeInt(1);
- writeData.writeToParcel(_data, 0);
- } else {
- _data.writeInt(0);
- }
- mRemote.transact(Stub.TRANSACTION_write, _data, _reply, 0);
- _reply.readException();
- } finally {
- _reply.recycle();
- _data.recycle();
- }
- }
-
- @Override
- public void disconnect(android.os.IBinder clientToken)
- throws android.os.RemoteException {
- android.os.Parcel _data = android.os.Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeStrongBinder(clientToken);
- mRemote.transact(Stub.TRANSACTION_disconnect, _data, null,
- android.os.IBinder.FLAG_ONEWAY);
- } finally {
- _data.recycle();
- }
- }
- }
-
- static final int TRANSACTION_write = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
- static final int TRANSACTION_disconnect = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
- }
-
- public void write(android.os.IBinder clientToken,
- com.googlecode.eyesfree.braille.selfbraille.WriteData writeData)
- throws android.os.RemoteException;
-
- public void disconnect(android.os.IBinder clientToken)
- throws android.os.RemoteException;
-}
diff --git a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java
deleted file mode 100644
index e4a363aca..000000000
--- a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.googlecode.eyesfree.braille.selfbraille;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.Signature;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-/**
- * Client-side interface to the self brailling interface.
- *
- * Threading: Instances of this object should be created and shut down
- * in a thread with a {@link Looper} associated with it. Other methods may
- * be called on any thread.
- */
-public class SelfBrailleClient {
- private static final String LOG_TAG =
- SelfBrailleClient.class.getSimpleName();
- private static final String ACTION_SELF_BRAILLE_SERVICE =
- "com.googlecode.eyesfree.braille.service.ACTION_SELF_BRAILLE_SERVICE";
- private static final String BRAILLE_BACK_PACKAGE =
- "com.googlecode.eyesfree.brailleback";
- private static final Intent mServiceIntent =
- new Intent(ACTION_SELF_BRAILLE_SERVICE)
- .setPackage(BRAILLE_BACK_PACKAGE);
- /**
- * SHA-1 hash value of the Eyes-Free release key certificate, used to sign
- * BrailleBack. It was generated from the keystore with:
- * $ keytool -exportcert -keystore <keystorefile> -alias android.keystore \
- * > cert
- * $ keytool -printcert -file cert
- */
- // The typecasts are to silence a compiler warning about loss of precision
- private static final byte[] EYES_FREE_CERT_SHA1 = new byte[] {
- (byte) 0x9B, (byte) 0x42, (byte) 0x4C, (byte) 0x2D,
- (byte) 0x27, (byte) 0xAD, (byte) 0x51, (byte) 0xA4,
- (byte) 0x2A, (byte) 0x33, (byte) 0x7E, (byte) 0x0B,
- (byte) 0xB6, (byte) 0x99, (byte) 0x1C, (byte) 0x76,
- (byte) 0xEC, (byte) 0xA4, (byte) 0x44, (byte) 0x61
- };
- /**
- * Delay before the first rebind attempt on bind error or service
- * disconnect.
- */
- private static final int REBIND_DELAY_MILLIS = 500;
- private static final int MAX_REBIND_ATTEMPTS = 5;
-
- private final Binder mIdentity = new Binder();
- private final Context mContext;
- private final boolean mAllowDebugService;
- private final SelfBrailleHandler mHandler = new SelfBrailleHandler();
- private boolean mShutdown = false;
-
- /**
- * Written in handler thread, read in any thread calling methods on the
- * object.
- */
- private volatile Connection mConnection;
- /** Protected by synchronizing on mHandler. */
- private int mNumFailedBinds = 0;
-
- /**
- * Constructs an instance of this class. {@code context} is used to bind
- * to the self braille service. The current thread must have a Looper
- * associated with it. If {@code allowDebugService} is true, this instance
- * will connect to a BrailleBack service without requiring it to be signed
- * by the release key used to sign BrailleBack.
- */
- public SelfBrailleClient(Context context, boolean allowDebugService) {
- mContext = context;
- mAllowDebugService = allowDebugService;
- doBindService();
- }
-
- /**
- * Shuts this instance down, deallocating any global resources it is using.
- * This method must be called on the same thread that created this object.
- */
- public void shutdown() {
- mShutdown = true;
- doUnbindService();
- }
-
- public void write(WriteData writeData) {
- writeData.validate();
- ISelfBrailleService localService = getSelfBrailleService();
- if (localService != null) {
- try {
- localService.write(mIdentity, writeData);
- } catch (RemoteException ex) {
- Log.e(LOG_TAG, "Self braille write failed", ex);
- }
- }
- }
-
- private void doBindService() {
- Connection localConnection = new Connection();
- if (!mContext.bindService(mServiceIntent, localConnection,
- Context.BIND_AUTO_CREATE)) {
- Log.e(LOG_TAG, "Failed to bind to service");
- mHandler.scheduleRebind();
- return;
- }
- mConnection = localConnection;
- Log.i(LOG_TAG, "Bound to self braille service");
- }
-
- private void doUnbindService() {
- if (mConnection != null) {
- ISelfBrailleService localService = getSelfBrailleService();
- if (localService != null) {
- try {
- localService.disconnect(mIdentity);
- } catch (RemoteException ex) {
- // Nothing to do.
- }
- }
- mContext.unbindService(mConnection);
- mConnection = null;
- }
- }
-
- private ISelfBrailleService getSelfBrailleService() {
- Connection localConnection = mConnection;
- if (localConnection != null) {
- return localConnection.mService;
- }
- return null;
- }
-
- private boolean verifyPackage() {
- PackageManager pm = mContext.getPackageManager();
- PackageInfo pi;
- try {
- pi = pm.getPackageInfo(BRAILLE_BACK_PACKAGE,
- PackageManager.GET_SIGNATURES);
- } catch (PackageManager.NameNotFoundException ex) {
- Log.w(LOG_TAG, "Can't verify package " + BRAILLE_BACK_PACKAGE,
- ex);
- return false;
- }
- MessageDigest digest;
- try {
- digest = MessageDigest.getInstance("SHA-1");
- } catch (NoSuchAlgorithmException ex) {
- Log.e(LOG_TAG, "SHA-1 not supported", ex);
- return false;
- }
- // Check if any of the certificates match our hash.
- for (Signature signature : pi.signatures) {
- digest.update(signature.toByteArray());
- if (MessageDigest.isEqual(EYES_FREE_CERT_SHA1, digest.digest())) {
- return true;
- }
- digest.reset();
- }
- if (mAllowDebugService) {
- Log.w(LOG_TAG, String.format(
- "*** %s connected to BrailleBack with invalid (debug?) "
- + "signature ***",
- mContext.getPackageName()));
- return true;
- }
- return false;
- }
- private class Connection implements ServiceConnection {
- // Read in application threads, written in main thread.
- private volatile ISelfBrailleService mService;
-
- @Override
- public void onServiceConnected(ComponentName className,
- IBinder binder) {
- if (!verifyPackage()) {
- Log.w(LOG_TAG, String.format("Service certificate mismatch "
- + "for %s, dropping connection",
- BRAILLE_BACK_PACKAGE));
- mHandler.unbindService();
- return;
- }
- Log.i(LOG_TAG, "Connected to self braille service");
- mService = ISelfBrailleService.Stub.asInterface(binder);
- synchronized (mHandler) {
- mNumFailedBinds = 0;
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- Log.e(LOG_TAG, "Disconnected from self braille service");
- mService = null;
- // Retry by rebinding.
- mHandler.scheduleRebind();
- }
- }
-
- private class SelfBrailleHandler extends Handler {
- private static final int MSG_REBIND_SERVICE = 1;
- private static final int MSG_UNBIND_SERVICE = 2;
-
- public void scheduleRebind() {
- synchronized (this) {
- if (mNumFailedBinds < MAX_REBIND_ATTEMPTS) {
- int delay = REBIND_DELAY_MILLIS << mNumFailedBinds;
- sendEmptyMessageDelayed(MSG_REBIND_SERVICE, delay);
- ++mNumFailedBinds;
- }
- }
- }
-
- public void unbindService() {
- sendEmptyMessage(MSG_UNBIND_SERVICE);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_REBIND_SERVICE:
- handleRebindService();
- break;
- case MSG_UNBIND_SERVICE:
- handleUnbindService();
- break;
- }
- }
-
- private void handleRebindService() {
- if (mShutdown) {
- return;
- }
- if (mConnection != null) {
- doUnbindService();
- }
- doBindService();
- }
-
- private void handleUnbindService() {
- doUnbindService();
- }
- }
-}
diff --git a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java b/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java
deleted file mode 100644
index ef81a2990..000000000
--- a/mobile/android/geckoview/src/thirdparty/java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2012 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.googlecode.eyesfree.braille.selfbraille;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-/**
- * Represents what should be shown on the braille display for a
- * part of the accessibility node tree.
- */
-public class WriteData implements Parcelable {
-
- private static final String PROP_SELECTION_START = "selectionStart";
- private static final String PROP_SELECTION_END = "selectionEnd";
-
- private AccessibilityNodeInfo mAccessibilityNodeInfo;
- private CharSequence mText;
- private Bundle mProperties = Bundle.EMPTY;
-
- /**
- * Returns a new {@link WriteData} instance for the given {@code view}.
- */
- public static WriteData forView(View view) {
- AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(view);
- WriteData writeData = new WriteData();
- writeData.mAccessibilityNodeInfo = node;
- return writeData;
- }
-
- public static WriteData forInfo(AccessibilityNodeInfo info){
- WriteData writeData = new WriteData();
- writeData.mAccessibilityNodeInfo = info;
- return writeData;
- }
-
-
- public AccessibilityNodeInfo getAccessibilityNodeInfo() {
- return mAccessibilityNodeInfo;
- }
-
- /**
- * Sets the text to be displayed when the accessibility node associated
- * with this instance has focus. If this method is not called (or
- * {@code text} is {@code null}), this client relinquishes control over
- * this node.
- */
- public WriteData setText(CharSequence text) {
- mText = text;
- return this;
- }
-
- public CharSequence getText() {
- return mText;
- }
-
- /**
- * Sets the start position in the text of a text selection or cursor that
- * should be marked on the display. A negative value (the default) means
- * no selection will be added.
- */
- public WriteData setSelectionStart(int v) {
- writableProperties().putInt(PROP_SELECTION_START, v);
- return this;
- }
-
- /**
- * @see {@link #setSelectionStart}.
- */
- public int getSelectionStart() {
- return mProperties.getInt(PROP_SELECTION_START, -1);
- }
-
- /**
- * Sets the end of the text selection to be marked on the display. This
- * value should only be non-negative if the selection start is
- * non-negative. If this value is <= the selection start, the selection
- * is a cursor. Otherwise, the selection covers the range from
- * start(inclusive) to end (exclusive).
- *
- * @see {@link android.text.Selection}.
- */
- public WriteData setSelectionEnd(int v) {
- writableProperties().putInt(PROP_SELECTION_END, v);
- return this;
- }
-
- /**
- * @see {@link #setSelectionEnd}.
- */
- public int getSelectionEnd() {
- return mProperties.getInt(PROP_SELECTION_END, -1);
- }
-
- private Bundle writableProperties() {
- if (mProperties == Bundle.EMPTY) {
- mProperties = new Bundle();
- }
- return mProperties;
- }
-
- /**
- * Checks constraints on the fields that must be satisfied before sending
- * this instance to the self braille service.
- * @throws IllegalStateException
- */
- public void validate() throws IllegalStateException {
- if (mAccessibilityNodeInfo == null) {
- throw new IllegalStateException(
- "Accessibility node info can't be null");
- }
- int selectionStart = getSelectionStart();
- int selectionEnd = getSelectionEnd();
- if (mText == null) {
- if (selectionStart > 0 || selectionEnd > 0) {
- throw new IllegalStateException(
- "Selection can't be set without text");
- }
- } else {
- if (selectionStart < 0 && selectionEnd >= 0) {
- throw new IllegalStateException(
- "Selection end without start");
- }
- int textLength = mText.length();
- if (selectionStart > textLength || selectionEnd > textLength) {
- throw new IllegalStateException("Selection out of bounds");
- }
- }
- }
-
- // For Parcelable support.
-
- public static final Parcelable.Creator<WriteData> CREATOR =
- new Parcelable.Creator<WriteData>() {
- @Override
- public WriteData createFromParcel(Parcel in) {
- return new WriteData(in);
- }
-
- @Override
- public WriteData[] newArray(int size) {
- return new WriteData[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- /**
- * {@inheritDoc}
- * <strong>Note:</strong> The {@link AccessibilityNodeInfo} will be
- * recycled by this method, don't try to use this more than once.
- */
- @Override
- public void writeToParcel(Parcel out, int flags) {
- mAccessibilityNodeInfo.writeToParcel(out, flags);
- // The above call recycles the node, so make sure we don't use it
- // anymore.
- mAccessibilityNodeInfo = null;
- out.writeString(mText.toString());
- out.writeBundle(mProperties);
- }
-
- private WriteData() {
- }
-
- private WriteData(Parcel in) {
- mAccessibilityNodeInfo =
- AccessibilityNodeInfo.CREATOR.createFromParcel(in);
- mText = in.readString();
- mProperties = in.readBundle();
- }
-}