mRetainedFiles;
+
+ public FileToDeleteFilter(File... retainedFiles) {
+ this.mRetainedFiles = Arrays.asList(retainedFiles);
+ }
+
+ /**
+ * Returns {@code true} to delete the file, and {@code false} to keep the file.
+ *
+ * Files are deleted if they are not in the {@link FileToDeleteFilter#mRetainedFiles} list.
+ */
+ @Override
+ public boolean accept(File pathname) {
+ if (pathname == null || !pathname.exists()) {
+ return false;
+ }
+ if (mRetainedFiles.contains(pathname)) {
+ return false;
+ }
+ return pathname.length() < MINIMUM_USABLE_FILE_SIZE_BYTES;
+ }
+ }
+}
diff --git a/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssContainer.java b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssContainer.java
new file mode 100644
index 0000000..df0d53a
--- /dev/null
+++ b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssContainer.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2016 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 com.google.android.apps.location.gps.gnsslogger;
+
+import android.content.Context;
+import android.location.GnssMeasurementsEvent;
+import android.location.GnssNavigationMessage;
+import android.location.GnssStatus;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.location.OnNmeaMessageListener;
+import android.os.Bundle;
+import android.os.SystemClock;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A container for GPS related API calls, it binds the {@link LocationManager} with {@link UiLogger}
+ */
+public class GnssContainer {
+
+ public static final String TAG = "GnssLogger";
+
+ private static final long LOCATION_RATE_GPS_MS = TimeUnit.SECONDS.toMillis(1L);
+ private static final long LOCATION_RATE_NETWORK_MS = TimeUnit.SECONDS.toMillis(60L);
+
+ private boolean mLogLocations = true;
+ private boolean mLogNavigationMessages = true;
+ private boolean mLogMeasurements = true;
+ private boolean mLogStatuses = true;
+ private boolean mLogNmeas = true;
+
+ private final List mLoggers;
+
+ private final LocationManager mLocationManager;
+ private final LocationListener mLocationListener =
+ new LocationListener() {
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ if (mLogLocations) {
+ for (GnssListener logger : mLoggers) {
+ logger.onProviderEnabled(provider);
+ }
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ if (mLogLocations) {
+ for (GnssListener logger : mLoggers) {
+ logger.onProviderDisabled(provider);
+ }
+ }
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ if (mLogLocations) {
+ for (GnssListener logger : mLoggers) {
+ logger.onLocationChanged(location);
+ }
+ }
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ if (mLogLocations) {
+ for (GnssListener logger : mLoggers) {
+ logger.onLocationStatusChanged(provider, status, extras);
+ }
+ }
+ }
+ };
+
+ private final GnssMeasurementsEvent.Callback gnssMeasurementsEventListener =
+ new GnssMeasurementsEvent.Callback() {
+ @Override
+ public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
+ if (mLogMeasurements) {
+ for (GnssListener logger : mLoggers) {
+ logger.onGnssMeasurementsReceived(event);
+ }
+ }
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ if (mLogMeasurements) {
+ for (GnssListener logger : mLoggers) {
+ logger.onGnssMeasurementsStatusChanged(status);
+ }
+ }
+ }
+ };
+
+ private final GnssNavigationMessage.Callback gnssNavigationMessageListener =
+ new GnssNavigationMessage.Callback() {
+ @Override
+ public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
+ if (mLogNavigationMessages) {
+ for (GnssListener logger : mLoggers) {
+ logger.onGnssNavigationMessageReceived(event);
+ }
+ }
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ if (mLogNavigationMessages) {
+ for (GnssListener logger : mLoggers) {
+ logger.onGnssNavigationMessageStatusChanged(status);
+ }
+ }
+ }
+ };
+
+ private final GnssStatus.Callback gnssStatusListener =
+ new GnssStatus.Callback() {
+ @Override
+ public void onStarted() {}
+
+ @Override
+ public void onStopped() {}
+
+ @Override
+ public void onSatelliteStatusChanged(GnssStatus status) {
+ for (GnssListener logger : mLoggers) {
+ logger.onGnssStatusChanged(status);
+ }
+ }
+ };
+
+ private final OnNmeaMessageListener nmeaListener =
+ new OnNmeaMessageListener() {
+ @Override
+ public void onNmeaMessage(String s, long l) {
+ if (mLogNmeas) {
+ for (GnssListener logger : mLoggers) {
+ logger.onNmeaReceived(l, s);
+ }
+ }
+ }
+ };
+
+ public GnssContainer(Context context, GnssListener... loggers) {
+ this.mLoggers = Arrays.asList(loggers);
+ mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ }
+
+ public LocationManager getLocationManager() {
+ return mLocationManager;
+ }
+
+ public void setLogLocations(boolean value) {
+ mLogLocations = value;
+ }
+
+ public boolean canLogLocations() {
+ return mLogLocations;
+ }
+
+ public void setLogNavigationMessages(boolean value) {
+ mLogNavigationMessages = value;
+ }
+
+ public boolean canLogNavigationMessages() {
+ return mLogNavigationMessages;
+ }
+
+ public void setLogMeasurements(boolean value) {
+ mLogMeasurements = value;
+ }
+
+ public boolean canLogMeasurements() {
+ return mLogMeasurements;
+ }
+
+ public void setLogStatuses(boolean value) {
+ mLogStatuses = value;
+ }
+
+ public boolean canLogStatuses() {
+ return mLogStatuses;
+ }
+
+ public void setLogNmeas(boolean value) {
+ mLogNmeas = value;
+ }
+
+ public boolean canLogNmeas() {
+ return mLogNmeas;
+ }
+
+ public void registerLocation() {
+ boolean isGpsProviderEnabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
+ if (isGpsProviderEnabled) {
+ mLocationManager.requestLocationUpdates(
+ LocationManager.NETWORK_PROVIDER,
+ LOCATION_RATE_NETWORK_MS,
+ 0.0f /* minDistance */,
+ mLocationListener);
+ mLocationManager.requestLocationUpdates(
+ LocationManager.GPS_PROVIDER,
+ LOCATION_RATE_GPS_MS,
+ 0.0f /* minDistance */,
+ mLocationListener);
+ }
+ logRegistration("LocationUpdates", isGpsProviderEnabled);
+ }
+
+ public void unregisterLocation() {
+ mLocationManager.removeUpdates(mLocationListener);
+ }
+
+ public void registerMeasurements() {
+ logRegistration(
+ "GnssMeasurements",
+ mLocationManager.registerGnssMeasurementsCallback(gnssMeasurementsEventListener));
+ }
+
+ public void unregisterMeasurements() {
+ mLocationManager.unregisterGnssMeasurementsCallback(gnssMeasurementsEventListener);
+ }
+
+ public void registerNavigation() {
+ logRegistration(
+ "GpsNavigationMessage",
+ mLocationManager.registerGnssNavigationMessageCallback(gnssNavigationMessageListener));
+ }
+
+ public void unregisterNavigation() {
+ mLocationManager.unregisterGnssNavigationMessageCallback(gnssNavigationMessageListener);
+ }
+
+ public void registerGnssStatus() {
+ logRegistration("GnssStatus", mLocationManager.registerGnssStatusCallback(gnssStatusListener));
+ }
+
+ public void unregisterGpsStatus() {
+ mLocationManager.unregisterGnssStatusCallback(gnssStatusListener);
+ }
+
+ public void registerNmea() {
+ logRegistration("Nmea", mLocationManager.addNmeaListener(nmeaListener));
+ }
+
+ public void unregisterNmea() {
+ mLocationManager.removeNmeaListener(nmeaListener);
+ }
+
+ public void registerAll() {
+ registerLocation();
+ registerMeasurements();
+ registerNavigation();
+ registerGnssStatus();
+ registerNmea();
+ }
+
+ public void unregisterAll() {
+ unregisterLocation();
+ unregisterMeasurements();
+ unregisterNavigation();
+ unregisterGpsStatus();
+ unregisterNmea();
+ }
+
+ private void logRegistration(String listener, boolean result) {
+ for (GnssListener logger : mLoggers) {
+ logger.onListenerRegistration(listener, result);
+ }
+ }
+}
diff --git a/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssListener.java b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssListener.java
new file mode 100644
index 0000000..9065c8f
--- /dev/null
+++ b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/GnssListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 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 com.google.android.apps.location.gps.gnsslogger;
+
+import android.location.GnssMeasurementsEvent;
+import android.location.GnssNavigationMessage;
+import android.location.GnssStatus;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.OnNmeaMessageListener;
+import android.os.Bundle;
+
+/** A class representing an interface for logging GPS information. */
+public interface GnssListener {
+
+ /** @see LocationListener#onProviderEnabled(String) */
+ void onProviderEnabled(String provider);
+ /** @see LocationListener#onProviderDisabled(String) */
+ void onProviderDisabled(String provider);
+ /** @see LocationListener#onLocationChanged(Location) */
+ void onLocationChanged(Location location);
+ /** @see LocationListener#onStatusChanged(String, int, Bundle) */
+ void onLocationStatusChanged(String provider, int status, Bundle extras);
+ /**
+ * @see android.location.GnssMeasurementsEvent.Callback#
+ * onGnssMeasurementsReceived(GnssMeasurementsEvent)
+ */
+ void onGnssMeasurementsReceived(GnssMeasurementsEvent event);
+ /** @see GnssMeasurementsEvent.Callback#onStatusChanged(int) */
+ void onGnssMeasurementsStatusChanged(int status);
+ /**
+ * @see GnssNavigationMessage.Callback#
+ * onGnssNavigationMessageReceived(GnssNavigationMessage)
+ */
+ void onGnssNavigationMessageReceived(GnssNavigationMessage event);
+ /** @see GnssNavigationMessage.Callback#onStatusChanged(int) */
+ void onGnssNavigationMessageStatusChanged(int status);
+ /** @see GnssStatus.Callback#onSatelliteStatusChanged(GnssStatus) */
+ void onGnssStatusChanged(GnssStatus gnssStatus);
+ /** Called when the listener is registered to listen to GNSS events */
+ void onListenerRegistration(String listener, boolean result);
+ /** @see OnNmeaMessageListener#onNmeaMessage(String, long) */
+ void onNmeaReceived(long l, String s);
+}
diff --git a/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/HelpDialog.java b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/HelpDialog.java
new file mode 100644
index 0000000..4938a5c
--- /dev/null
+++ b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/HelpDialog.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 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 com.google.android.apps.location.gps.gnsslogger;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.net.MailTo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+
+public class HelpDialog extends Dialog {
+
+ private static Context mContext = null;
+
+ public HelpDialog(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ setContentView(R.layout.help);
+ WebView help = (WebView)findViewById(R.id.helpView);
+ help.setWebViewClient(new WebViewClient(){
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if(url.startsWith("mailto:")){
+ MailTo mt = MailTo.parse(url);
+ Intent emailIntent = new Intent(Intent.ACTION_SEND);
+ emailIntent.setType("*/*");
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, "GNSSLogger Feedback");
+ emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { mt.getTo()});
+ emailIntent.putExtra(Intent.EXTRA_TEXT, "");
+ mContext.startActivity(Intent.createChooser(emailIntent, "Send Feedback..."));
+ return true;
+ }
+ else{
+ view.loadUrl(url);
+ }
+ return true;
+ }
+ });
+
+ String helpText = readRawTextFile(R.raw.help_contents);
+ help.loadData(helpText, "text/html; charset=utf-8", "utf-8");
+ }
+
+ private String readRawTextFile(int id) {
+ InputStream inputStream = mContext.getResources().openRawResource(id);
+ InputStreamReader in = new InputStreamReader(inputStream);
+ BufferedReader buf = new BufferedReader(in);
+ String line;
+ StringBuilder text = new StringBuilder();
+ try {
+ while (( line = buf.readLine()) != null)
+ text.append(line);
+ } catch (IOException e) {
+ return null;
+ }
+ return text.toString();
+ }
+}
diff --git a/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/LoggerFragment.java b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/LoggerFragment.java
new file mode 100644
index 0000000..e0445a3
--- /dev/null
+++ b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/LoggerFragment.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2016 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 com.google.android.apps.location.gps.gnsslogger;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.SpannableStringBuilder;
+import android.text.style.ForegroundColorSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/** The UI fragment that hosts a logging view. */
+public class LoggerFragment extends Fragment {
+
+ private TextView mLogView;
+ private ScrollView mScrollView;
+ private FileLogger mFileLogger;
+ private UiLogger mUiLogger;
+
+ private final UIFragmentComponent mUiComponent = new UIFragmentComponent();
+
+ public void setUILogger(UiLogger value) {
+ mUiLogger = value;
+ }
+
+ public void setFileLogger(FileLogger value) {
+ mFileLogger = value;
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View newView = inflater.inflate(R.layout.fragment_log, container, false /* attachToRoot */);
+ mLogView = (TextView) newView.findViewById(R.id.log_view);
+ mScrollView = (ScrollView) newView.findViewById(R.id.log_scroll);
+
+ UiLogger currentUiLogger = mUiLogger;
+ if (currentUiLogger != null) {
+ currentUiLogger.setUiFragmentComponent(mUiComponent);
+ }
+ FileLogger currentFileLogger = mFileLogger;
+ if (currentFileLogger != null) {
+ currentFileLogger.setUiComponent(mUiComponent);
+ }
+
+ Button start = (Button) newView.findViewById(R.id.start_log);
+ start.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mScrollView.fullScroll(View.FOCUS_UP);
+ }
+ });
+
+ Button end = (Button) newView.findViewById(R.id.end_log);
+ end.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mScrollView.fullScroll(View.FOCUS_DOWN);
+ }
+ });
+
+ Button clear = (Button) newView.findViewById(R.id.clear_log);
+ clear.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mLogView.setText("");
+ }
+ });
+
+ final Button startLog = (Button) newView.findViewById(R.id.start_logs);
+ final Button sendFile = (Button) newView.findViewById(R.id.send_file);
+
+ startLog.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startLog.setEnabled(false);
+ sendFile.setEnabled(true);
+ Toast.makeText(getContext(), "Starting log...", Toast.LENGTH_LONG).show();
+ mFileLogger.startNewLog();
+ }
+ });
+
+ sendFile.setOnClickListener(
+ new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ startLog.setEnabled(true);
+ sendFile.setEnabled(false);
+ Toast.makeText(getContext(), "Sending file...", Toast.LENGTH_LONG).show();
+ mFileLogger.send();
+ }
+ });
+
+ return newView;
+ }
+
+ /**
+ * A facade for UI and Activity related operations that are required for {@link GnssListener}s.
+ */
+ public class UIFragmentComponent {
+
+ private static final int MAX_LENGTH = 12000;
+ private static final int LOWER_THRESHOLD = (int) (MAX_LENGTH * 0.5);
+
+ public synchronized void logTextFragment(final String tag, final String text, int color) {
+ final SpannableStringBuilder builder = new SpannableStringBuilder();
+ builder.append(tag).append(" | ").append(text).append("\n");
+ builder.setSpan(
+ new ForegroundColorSpan(color),
+ 0 /* start */,
+ builder.length(),
+ SpannableStringBuilder.SPAN_INCLUSIVE_EXCLUSIVE);
+
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ activity.runOnUiThread(
+ new Runnable() {
+ @Override
+ public void run() {
+ mLogView.append(builder);
+ Editable editable = mLogView.getEditableText();
+ int length = editable.length();
+ if (length > MAX_LENGTH) {
+ editable.delete(0, length - LOWER_THRESHOLD);
+ }
+ }
+ });
+ }
+
+ public void startActivity(Intent intent) {
+ getActivity().startActivity(intent);
+ }
+ }
+}
diff --git a/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/MainActivity.java b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/MainActivity.java
new file mode 100644
index 0000000..737d5a7
--- /dev/null
+++ b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/MainActivity.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 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 com.google.android.apps.location.gps.gnsslogger;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.support.design.widget.TabLayout;
+import android.support.design.widget.TabLayout.TabLayoutOnPageChangeListener;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v13.app.FragmentStatePagerAdapter;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.Locale;
+
+/** The activity for the application. */
+public class MainActivity extends AppCompatActivity {
+
+ private static final int LOCATION_REQUEST_ID = 1;
+ private static final String[] REQUIRED_PERMISSIONS = {
+ Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.WRITE_EXTERNAL_STORAGE
+ };
+ private static final int NUMBER_OF_FRAGMENTS = 2;
+ private static final int FRAGMENT_INDEX_SETTING = 0;
+ private static final int FRAGMENT_INDEX_LOGGER = 1;
+
+ private GnssContainer mGnssContainer;
+ private UiLogger mUiLogger;
+ private FileLogger mFileLogger;
+ private Fragment[] mFragments;
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ requestPermissionAndSetupFragments(this);
+ }
+
+ /**
+ * A {@link FragmentPagerAdapter} that returns a fragment corresponding to one of the
+ * sections/tabs/pages.
+ */
+ public class ViewPagerAdapter extends FragmentStatePagerAdapter {
+
+ public ViewPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ switch (position) {
+ case FRAGMENT_INDEX_SETTING:
+ return mFragments[FRAGMENT_INDEX_SETTING];
+ case FRAGMENT_INDEX_LOGGER:
+ return mFragments[FRAGMENT_INDEX_LOGGER];
+ default:
+ throw new IllegalArgumentException("Invalid section: " + position);
+ }
+ }
+
+ @Override
+ public int getCount() {
+ // Show total pages.
+ return 2;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ Locale locale = Locale.getDefault();
+ switch (position) {
+ case 0:
+ return getString(R.string.title_settings).toUpperCase(locale);
+ case 1:
+ return getString(R.string.title_log).toUpperCase(locale);
+ default:
+ return super.getPageTitle(position);
+ }
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(
+ int requestCode, String permissions[], int[] grantResults) {
+ if (requestCode == LOCATION_REQUEST_ID) {
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ setupFragments();
+ }
+ }
+ }
+
+ private void setupFragments() {
+ mUiLogger = new UiLogger();
+ mFileLogger = new FileLogger(getApplicationContext());
+ mGnssContainer = new GnssContainer(getApplicationContext(), mUiLogger, mFileLogger);
+ mFragments = new Fragment[NUMBER_OF_FRAGMENTS];
+ SettingsFragment settingsFragment = new SettingsFragment();
+ settingsFragment.setGpsContainer(mGnssContainer);
+ mFragments[FRAGMENT_INDEX_SETTING] = settingsFragment;
+
+ LoggerFragment loggerFragment = new LoggerFragment();
+ loggerFragment.setUILogger(mUiLogger);
+ loggerFragment.setFileLogger(mFileLogger);
+ mFragments[FRAGMENT_INDEX_LOGGER] = loggerFragment;
+
+
+ // The viewpager that will host the section contents.
+ ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
+ viewPager.setOffscreenPageLimit(2);
+ ViewPagerAdapter adapter = new ViewPagerAdapter(getFragmentManager());
+ viewPager.setAdapter(adapter);
+
+ TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
+ tabLayout.setTabsFromPagerAdapter(adapter);
+
+ // Set a listener via setOnTabSelectedListener(OnTabSelectedListener) to be notified when any
+ // tab's selection state has been changed.
+ tabLayout.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(viewPager));
+
+ // Use a TabLayout.TabLayoutOnPageChangeListener to forward the scroll and selection changes to
+ // this layout
+ viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(tabLayout));
+ }
+
+ private boolean hasPermissions(Activity activity) {
+ if (Build.VERSION.SDK_INT < VERSION_CODES.M) {
+ // Permissions granted at install time.
+ return true;
+ }
+ for (String p : REQUIRED_PERMISSIONS) {
+ if (ContextCompat.checkSelfPermission(activity, p) != PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void requestPermissionAndSetupFragments(final Activity activity) {
+ if (hasPermissions(activity)) {
+ setupFragments();
+ } else {
+ ActivityCompat.requestPermissions(activity, REQUIRED_PERMISSIONS, LOCATION_REQUEST_ID);
+ }
+ }
+}
diff --git a/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/SettingsFragment.java b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/SettingsFragment.java
new file mode 100644
index 0000000..33f002e
--- /dev/null
+++ b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/SettingsFragment.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 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 com.google.android.apps.location.gps.gnsslogger;
+
+import android.app.Fragment;
+import android.location.LocationManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.google.android.apps.location.gps.gnsslogger.GnssContainer;
+import java.lang.reflect.InvocationTargetException;
+import android.widget.Button;
+
+/**
+ * The UI fragment showing a set of configurable settings for the client to request GPS data.
+ */
+public class SettingsFragment extends Fragment {
+
+ public static final String TAG = ":SettingsFragment";
+ private GnssContainer mGpsContainer;
+ private HelpDialog helpDialog;
+
+ public void setGpsContainer(GnssContainer value) {
+ mGpsContainer = value;
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_main, container, false /* attachToRoot */);
+
+ final Switch registerLocation = (Switch) view.findViewById(R.id.register_location);
+ final TextView registerLocationLabel =
+ (TextView) view.findViewById(R.id.register_location_label);
+ //set the switch to OFF
+ registerLocation.setChecked(false);
+ registerLocationLabel.setText("Switch is OFF");
+ registerLocation.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+ if (isChecked) {
+ mGpsContainer.registerLocation();
+ registerLocationLabel.setText("Switch is ON");
+ } else {
+ mGpsContainer.unregisterLocation();
+ registerLocationLabel.setText("Switch is OFF");
+ }
+ }
+ });
+
+ final Switch registerMeasurements = (Switch) view.findViewById(R.id.register_measurements);
+ final TextView registerMeasurementsLabel =
+ (TextView) view.findViewById(R.id.register_measurement_label);
+ //set the switch to OFF
+ registerMeasurements.setChecked(false);
+ registerMeasurementsLabel.setText("Switch is OFF");
+ registerMeasurements.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+ if (isChecked) {
+ mGpsContainer.registerMeasurements();
+ registerMeasurementsLabel.setText("Switch is ON");
+ } else {
+ mGpsContainer.unregisterMeasurements();
+ registerMeasurementsLabel.setText("Switch is OFF");
+ }
+ }
+ });
+
+ final Switch registerNavigation = (Switch) view.findViewById(R.id.register_navigation);
+ final TextView registerNavigationLabel =
+ (TextView) view.findViewById(R.id.register_navigation_label);
+ //set the switch to OFF
+ registerNavigation.setChecked(false);
+ registerNavigationLabel.setText("Switch is OFF");
+ registerNavigation.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+ if (isChecked) {
+ mGpsContainer.registerNavigation();
+ registerNavigationLabel.setText("Switch is ON");
+ } else {
+ mGpsContainer.unregisterNavigation();
+ registerNavigationLabel.setText("Switch is OFF");
+ }
+ }
+ });
+
+ final Switch registerGpsStatus = (Switch) view.findViewById(R.id.register_status);
+ final TextView registerGpsStatusLabel =
+ (TextView) view.findViewById(R.id.register_status_label);
+ //set the switch to OFF
+ registerGpsStatus.setChecked(false);
+ registerGpsStatusLabel.setText("Switch is OFF");
+ registerGpsStatus.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+ if (isChecked) {
+ mGpsContainer.registerGnssStatus();
+ registerGpsStatusLabel.setText("Switch is ON");
+ } else {
+ mGpsContainer.unregisterGpsStatus();
+ registerGpsStatusLabel.setText("Switch is OFF");
+ }
+ }
+ });
+
+ final Switch registerNmea = (Switch) view.findViewById(R.id.register_nmea);
+ final TextView registerNmeaLabel = (TextView) view.findViewById(R.id.register_nmea_label);
+ //set the switch to OFF
+ registerNmea.setChecked(false);
+ registerNmeaLabel.setText("Switch is OFF");
+ registerNmea.setOnCheckedChangeListener(
+ new OnCheckedChangeListener() {
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+
+ if (isChecked) {
+ mGpsContainer.registerNmea();
+ registerNmeaLabel.setText("Switch is ON");
+ } else {
+ mGpsContainer.unregisterNmea();
+ registerNmeaLabel.setText("Switch is OFF");
+ }
+ }
+ });
+
+ Button help = (Button) view.findViewById(R.id.help);
+ helpDialog = new HelpDialog(getContext());
+ helpDialog.setTitle("Help contents");
+ helpDialog.create();
+
+ help.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ helpDialog.show();
+ }
+ });
+
+ Button exit = (Button) view.findViewById(R.id.exit);
+ exit.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ getActivity().finishAffinity();
+ }
+ });
+
+ TextView swInfo = (TextView) view.findViewById(R.id.sw_info);
+
+ java.lang.reflect.Method method;
+ LocationManager locationManager = mGpsContainer.getLocationManager();
+ try {
+ method = locationManager.getClass().getMethod("getGnssYearOfHardware");
+ int hwYear = (int) method.invoke(locationManager);
+ if (hwYear == 0) {
+ swInfo.append("HW Year: " + "2015 or older \n");
+ } else {
+ swInfo.append("HW Year: " + hwYear + "\n");
+ }
+
+ } catch (NoSuchMethodException e) {
+ logException("No such method exception: ", e);
+ return null;
+ } catch (IllegalAccessException e) {
+ logException("Illegal Access exception: ", e);
+ return null;
+ } catch (InvocationTargetException e) {
+ logException("Invocation Target Exception: ", e);
+ return null;
+ }
+
+ String platfromVersionString = Build.VERSION.RELEASE;
+ swInfo.append("Platform: " + platfromVersionString + "\n");
+ int apiLivelInt = Build.VERSION.SDK_INT;
+ swInfo.append("Api Level: " + apiLivelInt);
+
+ return view;
+ }
+
+ private void logException(String errorMessage, Exception e) {
+ Log.e(GnssContainer.TAG + TAG, errorMessage, e);
+ Toast.makeText(getContext(), errorMessage, Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/UiLogger.java b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/UiLogger.java
new file mode 100644
index 0000000..3fddf42
--- /dev/null
+++ b/GNSSLogger/app/src/main/java/com/google/android/apps/location/gps/gnsslogger/UiLogger.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2016 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 com.google.android.apps.location.gps.gnsslogger;
+
+import android.graphics.Color;
+import android.location.GnssMeasurementsEvent;
+import android.location.GnssNavigationMessage;
+import android.location.GnssStatus;
+import android.location.Location;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.util.Log;
+import com.google.android.apps.location.gps.gnsslogger.LoggerFragment.UIFragmentComponent;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A class representing a UI logger for the application. Its responsibility is show information in
+ * the UI.
+ */
+public class UiLogger implements GnssListener {
+
+ private static final long EARTH_RADIUS_METERS = 6371000;
+ private static final int USED_COLOR = Color.rgb(0x4a, 0x5f, 0x70);
+
+ public UiLogger() {}
+
+ private UIFragmentComponent mUiFragmentComponent;
+
+ public synchronized UIFragmentComponent getUiFragmentComponent() {
+ return mUiFragmentComponent;
+ }
+
+ public synchronized void setUiFragmentComponent(UIFragmentComponent value) {
+ mUiFragmentComponent = value;
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ logLocationEvent("onProviderEnabled: " + provider);
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ logLocationEvent("onProviderDisabled: " + provider);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ logLocationEvent("onLocationChanged: " + location);
+ }
+
+ @Override
+ public void onLocationStatusChanged(String provider, int status, Bundle extras) {
+ String message =
+ String.format(
+ "onStatusChanged: provider=%s, status=%s, extras=%s",
+ provider, locationStatusToString(status), extras);
+ logLocationEvent(message);
+ }
+
+ @Override
+ public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
+ logMeasurementEvent("onGnsssMeasurementsReceived: " + event);
+ }
+
+ @Override
+ public void onGnssMeasurementsStatusChanged(int status) {
+ logMeasurementEvent("onStatusChanged: " + gnssMeasurementsStatusToString(status));
+ }
+
+ @Override
+ public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
+ logNavigationMessageEvent("onGnssNavigationMessageReceived: " + event);
+ }
+
+ @Override
+ public void onGnssNavigationMessageStatusChanged(int status) {
+ logNavigationMessageEvent("onStatusChanged: " + getGnssNavigationMessageStatus(status));
+ }
+
+ @Override
+ public void onGnssStatusChanged(GnssStatus gnssStatus) {
+ logStatusEvent("onGnssStatusChanged: " + gnssStatusToString(gnssStatus));
+ }
+
+ @Override
+ public void onNmeaReceived(long timestamp, String s) {
+ logNmeaEvent(String.format("onNmeaReceived: timestamp=%d, %s", timestamp, s));
+ }
+
+ @Override
+ public void onListenerRegistration(String listener, boolean result) {
+ logEvent("Registration", String.format("add%sListener: %b", listener, result), USED_COLOR);
+ }
+
+ private void logMeasurementEvent(String event) {
+ logEvent("Measurement", event, USED_COLOR);
+ }
+
+ private void logNavigationMessageEvent(String event) {
+ logEvent("NavigationMsg", event, USED_COLOR);
+ }
+
+ private void logStatusEvent(String event) {
+ logEvent("Status", event, USED_COLOR);
+ }
+
+ private void logNmeaEvent(String event) {
+ logEvent("Nmea", event, USED_COLOR);
+ }
+
+ private void logEvent(String tag, String message, int color) {
+ String composedTag = GnssContainer.TAG + tag;
+ Log.d(composedTag, message);
+ logText(tag, message, color);
+ }
+
+ private void logText(String tag, String text, int color) {
+ UIFragmentComponent component = getUiFragmentComponent();
+ if (component != null) {
+ component.logTextFragment(tag, text, color);
+ }
+ }
+
+ private String locationStatusToString(int status) {
+ switch (status) {
+ case LocationProvider.AVAILABLE:
+ return "AVAILABLE";
+ case LocationProvider.OUT_OF_SERVICE:
+ return "OUT_OF_SERVICE";
+ case LocationProvider.TEMPORARILY_UNAVAILABLE:
+ return "TEMPORARILY_UNAVAILABLE";
+ default:
+ return "";
+ }
+ }
+
+ private String gnssMeasurementsStatusToString(int status) {
+ switch (status) {
+ case GnssMeasurementsEvent.Callback.STATUS_NOT_SUPPORTED:
+ return "NOT_SUPPORTED";
+ case GnssMeasurementsEvent.Callback.STATUS_READY:
+ return "READY";
+ case GnssMeasurementsEvent.Callback.STATUS_LOCATION_DISABLED:
+ return "GNSS_LOCATION_DISABLED";
+ default:
+ return "";
+ }
+ }
+
+ private String getGnssNavigationMessageStatus(int status) {
+ switch (status) {
+ case GnssNavigationMessage.STATUS_UNKNOWN:
+ return "Status Unknown";
+ case GnssNavigationMessage.STATUS_PARITY_PASSED:
+ return "READY";
+ case GnssNavigationMessage.STATUS_PARITY_REBUILT:
+ return "Status Parity Rebuilt";
+ default:
+ return "";
+ }
+ }
+
+ private String gnssStatusToString(GnssStatus gnssStatus) {
+
+ StringBuilder builder = new StringBuilder("SATELLITE_STATUS | [Satellites:\n");
+ for (int i = 0; i < gnssStatus.getSatelliteCount(); i++) {
+ builder
+ .append("Constellation = ")
+ .append(getConstellationName(gnssStatus.getConstellationType(i)))
+ .append(", ");
+ builder.append("Svid = ").append(gnssStatus.getSvid(i)).append(", ");
+ builder.append("Cn0DbHz = ").append(gnssStatus.getCn0DbHz(i)).append(", ");
+ builder.append("Elevation = ").append(gnssStatus.getElevationDegrees(i)).append(", ");
+ builder.append("Azimuth = ").append(gnssStatus.getAzimuthDegrees(i)).append(", ");
+ builder.append("hasEphemeris = ").append(gnssStatus.hasEphemerisData(i)).append(", ");
+ builder.append("hasAlmanac = ").append(gnssStatus.hasAlmanacData(i)).append(", ");
+ builder.append("usedInFix = ").append(gnssStatus.usedInFix(i)).append("\n");
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ private void logLocationEvent(String event) {
+ logEvent("Location", event, USED_COLOR);
+ }
+
+ private String getConstellationName(int id) {
+ switch (id) {
+ case 1:
+ return "GPS";
+ case 2:
+ return "SBAS";
+ case 3:
+ return "GLONASS";
+ case 4:
+ return "QZSS";
+ case 5:
+ return "BEIDOU";
+ case 6:
+ return "GALILEO";
+ default:
+ return "UNKNOWN";
+ }
+ }
+}
diff --git a/GNSSLogger/app/src/main/res/drawable/ic_launcher.png b/GNSSLogger/app/src/main/res/drawable/ic_launcher.png
new file mode 100644
index 0000000..a1019b2
Binary files /dev/null and b/GNSSLogger/app/src/main/res/drawable/ic_launcher.png differ
diff --git a/GNSSLogger/app/src/main/res/layout/activity_main.xml b/GNSSLogger/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..b95955c
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GNSSLogger/app/src/main/res/layout/fragment_log.xml b/GNSSLogger/app/src/main/res/layout/fragment_log.xml
new file mode 100644
index 0000000..16b47b4
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/layout/fragment_log.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GNSSLogger/app/src/main/res/layout/fragment_main.xml b/GNSSLogger/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 0000000..6c0e680
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,155 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GNSSLogger/app/src/main/res/layout/help.xml b/GNSSLogger/app/src/main/res/layout/help.xml
new file mode 100644
index 0000000..5ff799f
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/layout/help.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/GNSSLogger/app/src/main/res/mipmap-hdpi/ic_launcher.png b/GNSSLogger/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/GNSSLogger/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/GNSSLogger/app/src/main/res/mipmap-mdpi/ic_launcher.png b/GNSSLogger/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..a1019b2
Binary files /dev/null and b/GNSSLogger/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/GNSSLogger/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/GNSSLogger/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/GNSSLogger/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/GNSSLogger/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/GNSSLogger/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/GNSSLogger/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/GNSSLogger/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/GNSSLogger/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/GNSSLogger/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/GNSSLogger/app/src/main/res/raw/help_contents.txt b/GNSSLogger/app/src/main/res/raw/help_contents.txt
new file mode 100644
index 0000000..585ce94
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/raw/help_contents.txt
@@ -0,0 +1,20 @@
+About AppHelp
+
+Feedback
+
+
+About AppHelp
+
+ Start Log button: Starts logging when pushed.
+ Stop & Send button: Stops logging and shows dialog to send the file via email or other options.
+
+
+
+
+
+Feedback
+Any comments, feedback, suggestions on GNSSLogger please write to us using the link below.
+
+ Contact support
+
+
\ No newline at end of file
diff --git a/GNSSLogger/app/src/main/res/values-w820dp/dimens.xml b/GNSSLogger/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/GNSSLogger/app/src/main/res/values/colors.xml b/GNSSLogger/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/GNSSLogger/app/src/main/res/values/dimens.xml b/GNSSLogger/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..d5593db
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/values/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 16dp
+ 16dp
+ 12sp
+
diff --git a/GNSSLogger/app/src/main/res/values/strings.xml b/GNSSLogger/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f0d6032
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/values/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ GnssLogger
+ v1.4.0.0
+
+ Settings
+ Log
+ Device
+ 8487000
+ Location
+ Measurements
+ Navigation Messages
+ GnssStatus
+ Nmea
+ HELP
+ Exit
+
+
diff --git a/GNSSLogger/app/src/main/res/values/styles.xml b/GNSSLogger/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..f0665fa
--- /dev/null
+++ b/GNSSLogger/app/src/main/res/values/styles.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/GNSSLogger/build.gradle b/GNSSLogger/build.gradle
new file mode 100644
index 0000000..a3330d4
--- /dev/null
+++ b/GNSSLogger/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.2.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/GNSSLogger/gradle.properties b/GNSSLogger/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/GNSSLogger/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/GNSSLogger/gradlew b/GNSSLogger/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/GNSSLogger/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/GNSSLogger/gradlew.bat b/GNSSLogger/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/GNSSLogger/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/GNSSLogger/local.properties b/GNSSLogger/local.properties
new file mode 100644
index 0000000..fd43cc3
--- /dev/null
+++ b/GNSSLogger/local.properties
@@ -0,0 +1,10 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file should *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=/media/build/master/prebuilts/fullsdk/linux
\ No newline at end of file
diff --git a/GNSSLogger/settings.gradle b/GNSSLogger/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/GNSSLogger/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/opensource/README.md b/opensource/README.md
index 1b5d556..c59bdf0 100644
--- a/opensource/README.md
+++ b/opensource/README.md
@@ -13,7 +13,9 @@ This code is maintained on GitHub at the following link:
https://github.com/google/gps-measurement-tools
-# Initial setup:
+# Matlab
+
+## Initial setup:
1. Extract the contents of the zip file to a directory, for example:
@@ -34,7 +36,7 @@ https://github.com/google/gps-measurement-tools
3. Run ProcessGnssMeasScript.m, it will run with pre-recorded log files.
-## To process a log file you collected from GnssLogger:
+### To process a log file you collected from GnssLogger:
1. save the log file in a directory
2. edit ProcessGpsMeasScript.m, specify the file name and directory path
@@ -50,12 +52,12 @@ site (GetNasaHourlyEphemeris.m will tell you the correct url and filename),
copy the file to the directory where your log file is,
and GetNasaHourlyEphemeris.m will read it from there.
-## For a summary of the open source GNSS Measurements Tools
+### For a summary of the open source GNSS Measurements Tools
See ~/gpstools/opensource/Contents.m or type 'help opensource' in matlab
command window.
-# Platform specific notes:
+## Platform specific notes:
For Windows: use '\' (backslash), instead of '/' for directories.
@@ -75,6 +77,16 @@ edit the name of the unzip utility:
If you uncompress the file 'by hand' and rerun GetNasaHourlyEphemeris.m, it will
read the uncompressed file.
+# GNSSLogger
+
+Sample App that allows registering for various Android location related measurements
+and log the measurements to the screen and optionally to a text file.
+
+This source code is supplied as an Android Studio project that can be built and run
+with [Android Studio](https://developer.android.com/studio/index.html).
+
+The APK is also provided for convience.
+
# Copyright Notice
Copyright 2016 Google Inc.