diff options
Diffstat (limited to 'android/WALT/app/src/main/java/org/chromium/latency/walt')
24 files changed, 563 insertions, 94 deletions
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/AboutFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/AboutFragment.java index 08b4e4f..2dce815 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/AboutFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/AboutFragment.java @@ -19,11 +19,13 @@ package org.chromium.latency.walt; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import java.util.Locale; + +import androidx.fragment.app.Fragment; /** @@ -46,7 +48,7 @@ public class AboutFragment extends Fragment { public void onResume() { super.onResume(); TextView textView = (TextView) getActivity().findViewById(R.id.txt_build_info); - String text = String.format("WALT v%s (versionCode=%d)\n", + String text = String.format(Locale.US, "WALT v%s (versionCode=%d)\n", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE); text += "WALT protocol version: " + WaltDevice.PROTOCOL_VERSION + "\n"; text += "Android Build ID: " + Build.DISPLAY + "\n"; diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/AccelerometerFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/AccelerometerFragment.java new file mode 100644 index 0000000..4f39547 --- /dev/null +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/AccelerometerFragment.java @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2017 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.chromium.latency.walt; + +import static org.chromium.latency.walt.Utils.argmax; +import static org.chromium.latency.walt.Utils.interp; +import static org.chromium.latency.walt.Utils.max; +import static org.chromium.latency.walt.Utils.mean; +import static org.chromium.latency.walt.Utils.min; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.graphics.Color; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.SystemClock; +import android.text.method.ScrollingMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.fragment.app.Fragment; + +import com.github.mikephil.charting.charts.ScatterChart; +import com.github.mikephil.charting.components.Description; +import com.github.mikephil.charting.data.Entry; +import com.github.mikephil.charting.data.ScatterData; +import com.github.mikephil.charting.data.ScatterDataSet; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class AccelerometerFragment extends Fragment implements + View.OnClickListener, SensorEventListener { + + private static final int MAX_TEST_LENGTH_MS = 10000; + private SimpleLogger logger; + private WaltDevice waltDevice; + private TextView logTextView; + private View startButton; + private ScatterChart latencyChart; + private View latencyChartLayout; + private StringBuilder accelerometerData; + private List<AccelerometerEvent> phoneAccelerometerData = new ArrayList<>(); + private Handler handler = new Handler(); + private SensorManager sensorManager; + private Sensor accelerometer; + private double realTimeOffsetMs; + private boolean isTestRunning = false; + + Runnable finishAccelerometer = new Runnable() { + @Override + public void run() { + isTestRunning = false; + waltDevice.stopListener(); + waltDevice.clearTriggerHandler(); + calculateAndDrawLatencyChart(accelerometerData.toString()); + startButton.setEnabled(true); + accelerometerData = new StringBuilder(); + LogUploader.uploadIfAutoEnabled(getContext()); + } + }; + + private BroadcastReceiver logReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String msg = intent.getStringExtra("message"); + AccelerometerFragment.this.appendLogText(msg); + } + }; + + private WaltDevice.TriggerHandler triggerHandler = new WaltDevice.TriggerHandler() { + @Override + public void onReceive(WaltDevice.TriggerMessage tmsg) { + logger.log("ERROR: Accelerometer trigger got a trigger message, " + + "this should never happen."); + } + + @Override + public void onReceiveRaw(String s) { + if (s.trim().equals("end")) { + // Remove the delayed callback and run it now + handler.removeCallbacks(finishAccelerometer); + handler.post(finishAccelerometer); + } else { + accelerometerData.append(s); + } + } + }; + + Runnable startAccelerometer = new Runnable() { + @Override + public void run() { + waltDevice.setTriggerHandler(triggerHandler); + try { + waltDevice.command(WaltDevice.CMD_ACCELEROMETER); + } catch (IOException e) { + logger.log("Error sending command CMD_ACCELEROMETER: " + e.getMessage()); + startButton.setEnabled(true); + return; + } + + logger.log("=== Accelerometer Test ===\n"); + isTestRunning = true; + handler.postDelayed(finishAccelerometer, MAX_TEST_LENGTH_MS); + } + }; + + public AccelerometerFragment() { + // Required empty public constructor + } + + static List<Entry> getEntriesFromString(final String latencyString) { + List<Entry> entries = new ArrayList<>(); + // "o" marks the start of the accelerometer data + int startIndex = latencyString.indexOf("o") + 1; + + String[] brightnessStrings = + latencyString.substring(startIndex).trim().split("\n"); + for (String str : brightnessStrings) { + String[] arr = str.split(" "); + final float timestampMs = Integer.parseInt(arr[0]) / 1000f; + final float value = Integer.parseInt(arr[1]); + entries.add(new Entry(timestampMs, value)); + } + return entries; + } + + static List<Entry> smoothEntries(List<Entry> entries, int windowSize) { + List<Entry> smoothEntries = new ArrayList<>(); + for (int i = windowSize; i < entries.size() - windowSize; i++) { + final float time = entries.get(i).getX(); + float avg = 0; + for (int j = i - windowSize; j <= i + windowSize; j++) { + avg += entries.get(j).getY() / (2 * windowSize + 1); + } + smoothEntries.add(new Entry(time, avg)); + } + return smoothEntries; + } + + static double[] findShifts(List<Entry> phoneEntries, List<Entry> waltEntries) { + double[] phoneTimes = new double[phoneEntries.size()]; + double[] phoneValues = new double[phoneEntries.size()]; + double[] waltTimes = new double[waltEntries.size()]; + double[] waltValues = new double[waltEntries.size()]; + + for (int i = 0; i < phoneTimes.length; i++) { + phoneTimes[i] = phoneEntries.get(i).getX(); + phoneValues[i] = phoneEntries.get(i).getY(); + } + + for (int i = 0; i < waltTimes.length; i++) { + waltTimes[i] = waltEntries.get(i).getX(); + waltValues[i] = waltEntries.get(i).getY(); + } + + double[] shiftCorrelations = new double[401]; + for (int i = 0; i < shiftCorrelations.length; i++) { + double shift = i / 10.; + final double[] shiftedPhoneTimes = new double[phoneTimes.length]; + for (int j = 0; j < phoneTimes.length; j++) { + shiftedPhoneTimes[j] = phoneTimes[j] - shift; + } + final double[] interpolatedValues = interp(shiftedPhoneTimes, waltTimes, waltValues); + double sum = 0; + for (int j = 0; j < shiftedPhoneTimes.length; j++) { + // Calculate square dot product of phone and walt values + sum += Math.pow(phoneValues[j] * interpolatedValues[j], 2); + } + shiftCorrelations[i] = sum; + } + return shiftCorrelations; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + logger = SimpleLogger.getInstance(getContext()); + waltDevice = WaltDevice.getInstance(getContext()); + + // Inflate the layout for this fragment + final View view = inflater.inflate(R.layout.fragment_accelerometer, container, false); + logTextView = (TextView) view.findViewById(R.id.txt_log); + startButton = view.findViewById(R.id.button_start); + latencyChart = (ScatterChart) view.findViewById(R.id.latency_chart); + latencyChartLayout = view.findViewById(R.id.latency_chart_layout); + logTextView.setMovementMethod(new ScrollingMovementMethod()); + view.findViewById(R.id.button_close_chart).setOnClickListener(this); + sensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE); + accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + if (accelerometer == null) { + logger.log("ERROR! Accelerometer sensor not found"); + } + return view; + } + + @Override + public void onResume() { + super.onResume(); + logTextView.setText(logger.getLogText()); + logger.registerReceiver(logReceiver); + startButton.setOnClickListener(this); + sensorManager.registerListener( + AccelerometerFragment.this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST); + } + + @Override + public void onPause() { + logger.unregisterReceiver(logReceiver); + sensorManager.unregisterListener(AccelerometerFragment.this, accelerometer); + super.onPause(); + } + + public void appendLogText(String msg) { + logTextView.append(msg + "\n"); + } + + void startMeasurement() { + logger.log("Starting accelerometer latency measurement"); + try { + accelerometerData = new StringBuilder(); + phoneAccelerometerData.clear(); + waltDevice.syncClock(); + waltDevice.startListener(); + realTimeOffsetMs = + SystemClock.elapsedRealtimeNanos() / 1e6 - waltDevice.clock.micros() / 1e3; + } catch (IOException e) { + logger.log("Error syncing clocks: " + e.getMessage()); + startButton.setEnabled(true); + return; + } + Toast.makeText(getContext(), "Start shaking the phone and WALT!", Toast.LENGTH_LONG).show(); + handler.postDelayed(startAccelerometer, 500); + } + + /** + * Handler for all the button clicks on this screen. + */ + @Override + public void onClick(View v) { + if (v.getId() == R.id.button_start) { + latencyChartLayout.setVisibility(View.GONE); + startButton.setEnabled(false); + startMeasurement(); + return; + } + + if (v.getId() == R.id.button_close_chart) { + latencyChartLayout.setVisibility(View.GONE); + } + } + + private void calculateAndDrawLatencyChart(final String latencyString) { + List<Entry> phoneEntries = new ArrayList<>(); + List<Entry> waltEntries = getEntriesFromString(latencyString); + List<Entry> waltSmoothEntries = smoothEntries(waltEntries, 4); + + for (AccelerometerEvent e : phoneAccelerometerData) { + phoneEntries.add(new Entry(e.callbackTimeMs, e.value)); + } + + while (phoneEntries.get(0).getX() < waltSmoothEntries.get(0).getX()) { + // This event is earlier than any walt event, so discard it + phoneEntries.remove(0); + } + + while (phoneEntries.get(phoneEntries.size() - 1).getX() > + waltSmoothEntries.get(waltSmoothEntries.size() - 1).getX()) { + // This event is later than any walt event, so discard it + phoneEntries.remove(phoneEntries.size() - 1); + } + + // Adjust waltEntries so min and max is the same as phoneEntries + float phoneMean = mean(phoneEntries); + float phoneMax = max(phoneEntries); + float phoneMin = min(phoneEntries); + float waltMin = min(waltSmoothEntries); + float phoneRange = phoneMax - phoneMin; + float waltRange = max(waltSmoothEntries) - waltMin; + for (Entry e : waltSmoothEntries) { + e.setY((e.getY() - waltMin) * (phoneRange / waltRange) + phoneMin - phoneMean); + } + + // Adjust phoneEntries so mean=0 + for (Entry e : phoneEntries) { + e.setY(e.getY() - phoneMean); + } + + double[] shifts = findShifts(phoneEntries, waltSmoothEntries); + double bestShift = argmax(shifts) / 10d; + logger.log(String.format(Locale.US, "Accelerometer latency: %.1fms", bestShift)); + + double[] deltasKernelToCallback = new double[phoneAccelerometerData.size()]; + for (int i = 0; i < deltasKernelToCallback.length; i++) { + deltasKernelToCallback[i] = phoneAccelerometerData.get(i).callbackTimeMs - + phoneAccelerometerData.get(i).kernelTimeMs; + } + + logger.log(String.format( + Locale.US, "Mean kernel-to-callback latency: %.1fms", mean(deltasKernelToCallback))); + + List<Entry> phoneEntriesShifted = new ArrayList<>(); + for (Entry e : phoneEntries) { + phoneEntriesShifted.add(new Entry((float) (e.getX() - bestShift), e.getY())); + } + + drawLatencyChart(phoneEntriesShifted, waltSmoothEntries); + } + + private void drawLatencyChart(List<Entry> phoneEntriesShifted, List<Entry> waltEntries) { + final ScatterDataSet dataSetWalt = + new ScatterDataSet(waltEntries, "WALT Events"); + dataSetWalt.setColor(Color.BLUE); + dataSetWalt.setScatterShape(ScatterChart.ScatterShape.CIRCLE); + dataSetWalt.setScatterShapeSize(8f); + + final ScatterDataSet dataSetPhoneShifted = + new ScatterDataSet(phoneEntriesShifted, "Phone Events Shifted"); + dataSetPhoneShifted.setColor(Color.RED); + dataSetPhoneShifted.setScatterShapeSize(10f); + dataSetPhoneShifted.setScatterShape(ScatterChart.ScatterShape.X); + + final ScatterData scatterData = new ScatterData(dataSetWalt, dataSetPhoneShifted); + final Description desc = new Description(); + desc.setText(""); + desc.setTextSize(12f); + latencyChart.setDescription(desc); + latencyChart.setData(scatterData); + latencyChart.invalidate(); + latencyChartLayout.setVisibility(View.VISIBLE); + } + + @Override + public void onSensorChanged(SensorEvent event) { + if (isTestRunning) { + phoneAccelerometerData.add(new AccelerometerEvent(event)); + } + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + private class AccelerometerEvent { + float callbackTimeMs; + float kernelTimeMs; + float value; + + AccelerometerEvent(SensorEvent event) { + callbackTimeMs = waltDevice.clock.micros() / 1e3f; + kernelTimeMs = (float) (event.timestamp / 1e6f - realTimeOffsetMs); + value = event.values[2]; + } + } +} diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java index 65452ff..3db3723 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/AudioFragment.java @@ -16,6 +16,8 @@ package org.chromium.latency.walt; +import static org.chromium.latency.walt.Utils.getIntPreference; + import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; @@ -24,8 +26,6 @@ import android.content.pm.PackageManager; import android.graphics.Color; import android.media.AudioManager; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.content.ContextCompat; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.View; @@ -34,6 +34,9 @@ import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; + import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Description; import com.github.mikephil.charting.components.LimitLine; @@ -45,8 +48,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import static org.chromium.latency.walt.Utils.getIntPreference; - /** * A simple {@link Fragment} subclass. */ diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/AutoRunFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/AutoRunFragment.java index f2f2a7f..71f5979 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/AutoRunFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/AutoRunFragment.java @@ -21,14 +21,15 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + import java.io.FileWriter; import java.io.IOException; import java.util.Iterator; diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/BaseUsbConnection.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/BaseUsbConnection.java index f0e6c62..4726d12 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/BaseUsbConnection.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/BaseUsbConnection.java @@ -24,7 +24,8 @@ import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; -import android.support.v4.content.LocalBroadcastManager; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import java.util.HashMap; import java.util.Locale; @@ -44,7 +45,7 @@ public abstract class BaseUsbConnection { protected UsbDeviceConnection usbConnection; public BaseUsbConnection(Context context) { - this.context = context; + this.context = context.getApplicationContext(); usbManager = (UsbManager) this.context.getSystemService(Context.USB_SERVICE); logger = SimpleLogger.getInstance(context); broadcastManager = LocalBroadcastManager.getInstance(context); @@ -184,7 +185,8 @@ public abstract class BaseUsbConnection { public UsbDevice findUsbDevice() { - logger.log(String.format("Looking for TeensyUSB VID=0x%x PID=0x%x", getVid(), getPid())); + logger.log(String.format(Locale.US, + "Looking for TeensyUSB VID=0x%x PID=0x%x", getVid(), getPid())); HashMap<String, UsbDevice> deviceHash = usbManager.getDeviceList(); if (deviceHash.size() == 0) { diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/CrashLogActivity.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/CrashLogActivity.java index 00e80ed..f2ce3d1 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/CrashLogActivity.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/CrashLogActivity.java @@ -17,10 +17,11 @@ package org.chromium.latency.walt; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; import android.text.method.ScrollingMovementMethod; import android.widget.TextView; +import androidx.appcompat.app.AppCompatActivity; + /** * A separate activity to display exception trace on the screen in case of a crash. diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/DiagnosticsFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/DiagnosticsFragment.java index 65ec3bf..17b0f2f 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/DiagnosticsFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/DiagnosticsFragment.java @@ -21,13 +21,14 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; + /** * This screen allows to perform different tasks useful for diagnostics. diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java index af03e36..9453c04 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/DragLatencyFragment.java @@ -16,12 +16,12 @@ package org.chromium.latency.walt; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -29,6 +29,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; + import com.github.mikephil.charting.charts.ScatterChart; import com.github.mikephil.charting.components.Description; import com.github.mikephil.charting.data.Entry; @@ -67,6 +69,7 @@ public class DragLatencyFragment extends Fragment } }; + @SuppressLint("ClickableViewAccessibility") private View.OnTouchListener touchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { @@ -143,6 +146,7 @@ public class DragLatencyFragment extends Fragment /** * @return true if measurement was successfully started */ + @SuppressLint("ClickableViewAccessibility") boolean startMeasurement() { logger.log("Starting drag latency test"); try { @@ -185,6 +189,7 @@ public class DragLatencyFragment extends Fragment updateCountsDisplay(); } + @SuppressLint("ClickableViewAccessibility") void finishAndShowStats() { touchCatcher.stopAnimation(); waltDevice.stopListener(); diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/FastPathSurfaceView.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/FastPathSurfaceView.java index 449627f..0c5ad59 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/FastPathSurfaceView.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/FastPathSurfaceView.java @@ -22,13 +22,14 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; -import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.widget.Toast; +import androidx.annotation.RequiresApi; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/FrontPageFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/FrontPageFragment.java index cb125e3..84fbbbe 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/FrontPageFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/FrontPageFragment.java @@ -18,13 +18,14 @@ package org.chromium.latency.walt; import android.graphics.Color; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import androidx.fragment.app.Fragment; + /** * A simple {@link Fragment} subclass. diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/LogFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/LogFragment.java index 069d032..1e26b32 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/LogFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/LogFragment.java @@ -17,25 +17,26 @@ package org.chromium.latency.walt; -import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; + /** * A screen that shows the log. */ public class LogFragment extends Fragment { - private Activity activity; + private AppCompatActivity activity; private SimpleLogger logger; TextView textView; @@ -56,7 +57,7 @@ public class LogFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - activity = getActivity(); + activity = (AppCompatActivity) getActivity(); logger = SimpleLogger.getInstance(getContext()); // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_log, container, false); diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/LogUploader.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/LogUploader.java index a73f456..aa10f74 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/LogUploader.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/LogUploader.java @@ -17,7 +17,8 @@ package org.chromium.latency.walt; import android.content.Context; -import android.support.v4.content.AsyncTaskLoader; + +import androidx.loader.content.AsyncTaskLoader; import java.io.BufferedOutputStream; import java.io.IOException; diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java index ac1df47..81a637b 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/MainActivity.java @@ -16,6 +16,8 @@ package org.chromium.latency.walt; +import static org.chromium.latency.walt.Utils.getBooleanPreference; + import android.Manifest; import android.content.DialogInterface; import android.content.Intent; @@ -30,18 +32,6 @@ import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.StrictMode; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.Loader; -import android.support.v4.content.LocalBroadcastManager; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -49,7 +39,18 @@ import android.view.View; import android.widget.EditText; import android.widget.Toast; -import org.chromium.latency.walt.programmer.Programmer; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.loader.content.Loader; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import androidx.preference.PreferenceManager; import java.io.File; import java.io.FileOutputStream; @@ -59,7 +60,7 @@ import java.io.StringWriter; import java.util.Date; import java.util.Locale; -import static org.chromium.latency.walt.Utils.getBooleanPreference; +import org.chromium.latency.walt.programmer.Programmer; public class MainActivity extends AppCompatActivity { private static final String TAG = "WALT"; @@ -218,7 +219,7 @@ public class MainActivity extends AppCompatActivity { broadcastManager = LocalBroadcastManager.getInstance(this); // Add basic version and device info to the log - logger.log(String.format("WALT v%s (versionCode=%d)", + logger.log(String.format(Locale.US, "WALT v%s (versionCode=%d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)); logger.log("WALT protocol version " + WaltDevice.PROTOCOL_VERSION); logger.log("DEVICE INFO:"); @@ -325,6 +326,11 @@ public class MainActivity extends AppCompatActivity { switchScreen(newFragment, "Drag Latency"); } + public void onClickAccelerometer(View view) { + AccelerometerFragment newFragment = new AccelerometerFragment(); + switchScreen(newFragment, "Accelerometer Latency"); + } + public void onClickOpenLog(View view) { LogFragment logFragment = new LogFragment(); // menu.findItem(R.id.action_help).setVisible(false); @@ -349,13 +355,8 @@ public class MainActivity extends AppCompatActivity { } public void onClickPing(View view) { - long t1 = waltDevice.clock.micros(); try { - waltDevice.command(WaltDevice.CMD_PING); - long dt = waltDevice.clock.micros() - t1; - logger.log(String.format(Locale.US, - "Ping reply in %.1fms", dt / 1000. - )); + waltDevice.ping(); } catch (IOException e) { logger.log("Error sending ping: " + e.getMessage()); } @@ -487,7 +488,7 @@ public class MainActivity extends AppCompatActivity { // A reasonable world readable location,on many phones it's /storage/emulated/Documents // TODO: make this location configurable? - File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); + File path = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS); File file = null; FileOutputStream outStream = null; @@ -511,7 +512,7 @@ public class MainActivity extends AppCompatActivity { } public void clearLogFile() { - File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); + File path = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS); try { File file = new File(path, LOG_FILENAME); file.delete(); @@ -541,7 +542,8 @@ public class MainActivity extends AppCompatActivity { } private static boolean startsWithHttp(String url) { - return url.toLowerCase().startsWith("http://") || url.toLowerCase().startsWith("https://"); + return url.toLowerCase(Locale.getDefault()).startsWith("http://") || + url.toLowerCase(Locale.getDefault()).startsWith("https://"); } private void showUploadLogDialog() { diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/MidiFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/MidiFragment.java index c6f1118..8d3fe6a 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/MidiFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/MidiFragment.java @@ -20,13 +20,14 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.fragment.app.Fragment; + import java.util.Locale; public class MidiFragment extends Fragment diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/NumberPickerPreference.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/NumberPickerPreference.java index 9d71d42..cf4ef55 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/NumberPickerPreference.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/NumberPickerPreference.java @@ -19,13 +19,15 @@ package org.chromium.latency.walt; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.preference.DialogPreference; -import android.support.v7.preference.PreferenceDialogFragmentCompat; import android.util.AttributeSet; import android.view.View; +import androidx.annotation.NonNull; +import androidx.preference.DialogPreference; +import androidx.preference.PreferenceDialogFragmentCompat; + public class NumberPickerPreference extends DialogPreference { + private boolean isInitSet = false; private int currentValue; private int maxValue; private int minValue; @@ -62,9 +64,13 @@ public class NumberPickerPreference extends DialogPreference { } public void setValue(int value) { - currentValue = value; - persistInt(currentValue); - setSummary(String.format(defaultSummary, getValue())); + boolean changed = (currentValue != value); + if (changed || !isInitSet) { + isInitSet = true; + currentValue = value; + persistInt(currentValue); + setSummary(String.format(defaultSummary, getValue())); + } } @Override @@ -73,8 +79,8 @@ public class NumberPickerPreference extends DialogPreference { } @Override - protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) { - setValue(restorePersistedValue ? getPersistedInt(currentValue) : (Integer) defaultValue); + protected void onSetInitialValue(Object defaultValue) { + setValue((Integer) defaultValue); } public static class NumberPickerPreferenceDialogFragmentCompat diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java index 629ed7d..d1ebfac 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/ScreenResponseFragment.java @@ -16,16 +16,15 @@ package org.chromium.latency.walt; +import static org.chromium.latency.walt.Utils.getBooleanPreference; +import static org.chromium.latency.walt.Utils.getIntPreference; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; -import android.support.v4.app.Fragment; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; import android.text.method.ScrollingMovementMethod; import android.view.Choreographer; import android.view.LayoutInflater; @@ -36,6 +35,11 @@ import android.widget.ArrayAdapter; import android.widget.Spinner; import android.widget.TextView; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; + import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.Description; import com.github.mikephil.charting.data.Entry; @@ -47,9 +51,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import static org.chromium.latency.walt.Utils.getBooleanPreference; -import static org.chromium.latency.walt.Utils.getIntPreference; - /** * Measurement of screen response time when switching between black and white. */ diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/SettingsFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/SettingsFragment.java index 4f74fc4..ace30cc 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/SettingsFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/SettingsFragment.java @@ -18,19 +18,21 @@ package org.chromium.latency.walt; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.content.ContextCompat; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceFragmentCompat; -import android.support.v7.preference.PreferenceScreen; -import android.support.v7.widget.Toolbar; import android.view.View; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; -public class SettingsFragment extends PreferenceFragmentCompat implements PreferenceFragmentCompat.OnPreferenceStartScreenCallback { + +public class SettingsFragment extends PreferenceFragmentCompat implements + PreferenceFragmentCompat.OnPreferenceStartScreenCallback { private Toolbar toolbar; @@ -70,8 +72,8 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer DialogFragment fragment = NumberPickerPreference. NumberPickerPreferenceDialogFragmentCompat.newInstance(preference.getKey()); fragment.setTargetFragment(this, 0); - fragment.show(getFragmentManager(), - "android.support.v7.preference.PreferenceFragment.DIALOG"); + fragment.show(getParentFragmentManager(), + "androidx.preference.PreferenceFragment.DIALOG"); } else { super.onDisplayPreferenceDialog(preference); } @@ -90,7 +92,8 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, preferenceScreen.getKey()); fragment.setArguments(args); - FragmentTransaction ft = preferenceFragmentCompat.getFragmentManager().beginTransaction(); + FragmentTransaction ft = preferenceFragmentCompat. + getParentFragmentManager().beginTransaction(); ft.add(R.id.fragment_container, fragment, preferenceScreen.getKey()); ft.addToBackStack(preferenceScreen.getTitle().toString()); ft.commit(); diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/SimpleLogger.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/SimpleLogger.java index 6059e0f..9244377 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/SimpleLogger.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/SimpleLogger.java @@ -20,7 +20,9 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.support.v4.content.LocalBroadcastManager; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + import android.util.Log; /** diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java index e26a328..ca0e80b 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/TapLatencyFragment.java @@ -16,11 +16,11 @@ package org.chromium.latency.walt; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.text.method.ScrollingMovementMethod; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -29,6 +29,8 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.TextView; +import androidx.fragment.app.Fragment; + import java.io.IOException; import java.util.ArrayList; import java.util.Locale; @@ -71,6 +73,7 @@ public class TapLatencyFragment extends Fragment } }; + @SuppressLint("ClickableViewAccessibility") private View.OnTouchListener touchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { @@ -212,6 +215,7 @@ public class TapLatencyFragment extends Fragment moveCountsView.setText(String.format(Locale.US, "⇄ %d", moveCount)); } + @SuppressLint("ClickableViewAccessibility") void restartMeasurement() { logger.log("\n## Restarting tap latency measurement. Re-sync clocks ..."); try { @@ -241,6 +245,7 @@ public class TapLatencyFragment extends Fragment tapCatcherView.setOnTouchListener(touchListener); } + @SuppressLint("ClickableViewAccessibility") void finishAndShowStats() { tapCatcherView.setOnTouchListener(null); waltDevice.checkDrift(); diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/TraceLogger.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/TraceLogger.java index 6fdb8d9..964e9c6 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/TraceLogger.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/TraceLogger.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.Locale; /** * Used to log events for Android systrace @@ -58,12 +59,13 @@ class TraceLogger { StringBuilder sb = new StringBuilder(); int pid = android.os.Process.myPid(); for (TraceEvent e : traceEvents) { - sb.append(String.format( - "WALTThread-1234 (%d) [000] ...1 %s: tracing_mark_write: B|%d|%s|description=%s|WALT\n", - pid, df.format(e.startTimeMicros / 1e6), pid, e.title, e.description)); - sb.append(String.format( - "WALTThread-1234 (%d) [000] ...1 %s: tracing_mark_write: E|%d|%s||WALT\n", - pid, df.format(e.finishTimeMicros / 1e6), pid, e.title)); + sb.append(String.format(Locale.US, + "WALTThread-1234 (%d) [000] ...1 %s: tracing_mark_write: " + + "B|%d|%s|description=%s|WALT\n", + pid, df.format(e.startTimeMicros / 1e6), pid, e.title, e.description)); + sb.append(String.format(Locale.US, + "WALTThread-1234 (%d) [000] ...1 %s: tracing_mark_write: E|%d|%s||WALT\n", + pid, df.format(e.finishTimeMicros / 1e6), pid, e.title)); } return sb.toString(); } @@ -85,7 +87,7 @@ class TraceLogger { OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file, true)); writer.write(getLogText()); writer.close(); - logger.log(String.format("TraceLogger wrote %d events to %s", + logger.log(String.format(Locale.US, "TraceLogger wrote %d events to %s", traceEvents.size(), file.getAbsolutePath())); } catch (IOException e) { logger.log("ERROR: IOException writing to trace.txt"); diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/UsMotionEvent.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/UsMotionEvent.java index e961949..f68e461 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/UsMotionEvent.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/UsMotionEvent.java @@ -20,6 +20,7 @@ import android.util.Log; import android.view.MotionEvent; import java.lang.reflect.Method; +import java.util.Locale; /** * A convenient representation of MotionEvent events @@ -71,13 +72,13 @@ public class UsMotionEvent { public String toString() { - return String.format("%d %f %f", + return String.format(Locale.US, "%d %f %f", kernelTime, x, y); } public String toStringLong() { - return String.format("Event: t=%d x=%.1f y=%.1f slot=%d num=%d %s", + return String.format(Locale.US, "Event: t=%d x=%.1f y=%.1f slot=%d num=%d %s", kernelTime, x, y, slot, num, actionToString(action)); } @@ -119,7 +120,7 @@ public class UsMotionEvent { private long getEventTimeMicro(MotionEvent event) { long t_nanos = -1; try { - Class cls = Class.forName("android.view.MotionEvent"); + Class<?> cls = Class.forName("android.view.MotionEvent"); Method myTimeGetter = cls.getMethod("getEventTimeNano"); t_nanos = (long) myTimeGetter.invoke(event); } catch (Exception e) { @@ -132,7 +133,7 @@ public class UsMotionEvent { private long getHistoricalEventTimeMicro(MotionEvent event, int pos) { long t_nanos = -1; try { - Class cls = Class.forName("android.view.MotionEvent"); + Class<?> cls = Class.forName("android.view.MotionEvent"); Method myTimeGetter = cls.getMethod("getHistoricalEventTimeNano", new Class[] {int.class}); t_nanos = (long) myTimeGetter.invoke(event, new Object[]{pos}); } catch (Exception e) { diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java index 19c7488..46796e2 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java @@ -18,11 +18,15 @@ package org.chromium.latency.walt; import android.content.Context; import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.support.annotation.StringRes; + +import androidx.preference.PreferenceManager; +import androidx.annotation.StringRes; + +import com.github.mikephil.charting.data.Entry; import java.util.ArrayList; import java.util.Collections; +import java.util.List; /** * Kitchen sink for small utility functions @@ -120,6 +124,11 @@ public class Utils { return sb.toString(); } + public static int argmax(double[] a) { + int imax = 0; + for (int i=1; i<a.length; i++) if (a[i] > a[imax]) imax = i; + return imax; + } public static int argmin(double[] a) { int imin = 0; @@ -177,6 +186,30 @@ public class Utils { return preferences.getString(context.getString(keyId), defaultValue); } + static float min(List<Entry> entries) { + float min = Float.MAX_VALUE; + for (Entry e : entries) { + min = Math.min(min, e.getY()); + } + return min; + } + + static float max(List<Entry> entries) { + float max = Float.MIN_VALUE; + for (Entry e : entries) { + max = Math.max(max, e.getY()); + } + return max; + } + + static float mean(List<Entry> entries) { + float mean = 0; + for (Entry e : entries) { + mean += e.getY()/entries.size(); + } + return mean; + } + public enum ListenerState { RUNNING, STARTING, diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java index 8ec2cb4..631afb8 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/WaltDevice.java @@ -23,6 +23,7 @@ import android.os.Handler; import android.util.Log; import java.io.IOException; +import java.util.Locale; /** * A singleton used as an interface for the physical WALT device. @@ -31,7 +32,7 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener { private static final int DEFAULT_DRIFT_LIMIT_US = 1500; private static final String TAG = "WaltDevice"; - public static final String PROTOCOL_VERSION = "5"; + public static final String PROTOCOL_VERSION = "6"; // Teensy side commands. Each command is a single char // Based on #defines section in walt.ino @@ -56,6 +57,7 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener { static final char CMD_BEEP_STOP = 'S'; // Stop generating tone static final char CMD_MIDI = 'M'; // Start listening for a MIDI message static final char CMD_NOTE = 'N'; // Generate a MIDI NoteOn message + static final char CMD_ACCELEROMETER = 'O'; // Generate a MIDI NoteOn message private static final int BYTE_BUFFER_SIZE = 1024 * 4; private byte[] buffer = new byte[BYTE_BUFFER_SIZE]; @@ -221,6 +223,19 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener { } } + public void ping() throws IOException { + if (!isConnected() || clock == null) { + throw new IOException("Not connected to WALT"); + } + + long t1 = clock.micros(); + command(CMD_PING); + long dt = clock.micros() - t1; + logger.log(String.format(Locale.US, + "Ping reply in %.1fms", dt / 1000. + )); + } + public void syncClock() throws IOException { clock = connection.syncClock(); } @@ -247,7 +262,7 @@ public class WaltDevice implements WaltConnection.ConnectionStateListener { return; } int drift = Math.abs(clock.getMeanLag()); - String msg = String.format("Remote clock delayed between %d and %d us", + String msg = String.format(Locale.US, "Remote clock delayed between %d and %d us", clock.minLag, clock.maxLag); // TODO: Convert the limit to user editable preference if (drift > DEFAULT_DRIFT_LIMIT_US) { diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/programmer/FirmwareImage.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/programmer/FirmwareImage.java index d2feb01..0f8b1ef 100644 --- a/android/WALT/app/src/main/java/org/chromium/latency/walt/programmer/FirmwareImage.java +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/programmer/FirmwareImage.java @@ -24,6 +24,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.text.ParseException; import java.util.Arrays; +import java.util.Locale; class FirmwareImage { private static final String TAG = "FirmwareImage"; @@ -89,7 +90,8 @@ class FirmwareImage { return; } default: { - throw new ParseException(String.format("Unknown code '%x'", code), cur); + throw new ParseException(String.format( + Locale.US, "Unknown code '%x'", code), cur); } } } @@ -119,8 +121,8 @@ class FirmwareImage { dest[addr + i] = parseByte(line, pos + i * 2); mask[addr + i] = true; } catch (ArrayIndexOutOfBoundsException e) { - throw new ParseException(String.format("Address '%x' out of range", addr + i), - pos + i * 2); + throw new ParseException(String.format(Locale.US, + "Address '%x' out of range", addr + i), pos + i * 2); } } } |