diff --git a/BLE Indoor Positioning/build.gradle b/BLE Indoor Positioning/build.gradle index 2c5c061..8fbe31b 100644 --- a/BLE Indoor Positioning/build.gradle +++ b/BLE Indoor Positioning/build.gradle @@ -8,8 +8,8 @@ buildscript { } plugins { - id 'net.researchgate.release' version '2.4.0' - id 'com.jfrog.bintray' version '1.7.3' + id 'net.researchgate.release' version '2.7.0' + id 'com.jfrog.bintray' version '1.8.4' } apply plugin: 'java-library' @@ -21,6 +21,7 @@ apply plugin: 'jacoco' dependencies { compile 'com.lemmingapex.trilateration:trilateration:1.0.2' compile 'com.google.code.gson:gson:2.8.5' + testCompile 'net.steppschuh.markdowngenerator:markdowngenerator:1.3.0.0' testImplementation 'junit:junit:4.12' } @@ -97,6 +98,21 @@ def pomConfig = { } } +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allJava +} + +task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir +} + +artifacts { + archives sourcesJar + archives javadocJar +} + publishing { publications { mavenJava(MavenPublication) { @@ -123,21 +139,6 @@ publishing { } } -task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allJava -} - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} - // configure release plugin. See https://github.com/researchgate/gradle-release#configuration release { failOnUnversionedFiles = false diff --git a/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/BeaconInfo.java b/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/BeaconInfo.java index 7b2e19c..355b578 100644 --- a/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/BeaconInfo.java +++ b/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/BeaconInfo.java @@ -2,13 +2,19 @@ public class BeaconInfo { + public static final String KEY_BEACON_NAME = "beaconName"; + public static final String KEY_BEACON_MODEL = "beaconModel"; + public static final String KEY_BEACON_MANUFACTURER = "beaconManufacturer"; + public static final String KEY_BEACON_ADVERTISING_FREQUENCY = "beaconAdvertisingFrequency"; + public static final String KEY_BEACON_TRANSMISSION_POWER = "beaconTransmissionPower"; + private String name; private String model; private String manufacturer; - private int advertizingFrequency; + private int advertisingFrequency; private int transmissionPower; @@ -39,12 +45,12 @@ public void setManufacturer(String manufacturer) { this.manufacturer = manufacturer; } - public int getAdvertizingFrequency() { - return advertizingFrequency; + public int getAdvertisingFrequency() { + return advertisingFrequency; } - public void setAdvertizingFrequency(int advertizingFrequency) { - this.advertizingFrequency = advertizingFrequency; + public void setAdvertisingFrequency(int advertisingFrequency) { + this.advertisingFrequency = advertisingFrequency; } public int getTransmissionPower() { diff --git a/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/DeviceInfo.java b/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/DeviceInfo.java index c40d57f..da14621 100644 --- a/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/DeviceInfo.java +++ b/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/DeviceInfo.java @@ -2,7 +2,12 @@ public class DeviceInfo { - private String name; + public static final String KEY_DEVICE_ID = "deviceName"; + public static final String KEY_DEVICE_MODEL = "deviceModel"; + public static final String KEY_DEVICE_MANUFACTURER = "deviceManufacturer"; + public static final String KEY_DEVICE_OS_VERSION = "deviceOsVersion"; + + private String id; private String model; @@ -13,12 +18,12 @@ public class DeviceInfo { public DeviceInfo() { } - public String getName() { - return name; + public String getId() { + return id; } - public void setName(String name) { - this.name = name; + public void setId(String id) { + this.id = id; } public String getModel() { diff --git a/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/RssiMeasurements.java b/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/RssiMeasurements.java index 187aa24..9a3dc4a 100644 --- a/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/RssiMeasurements.java +++ b/BLE Indoor Positioning/src/main/java/com/nexenio/bleindoorpositioning/testutil/benchmark/RssiMeasurements.java @@ -2,11 +2,21 @@ public class RssiMeasurements { + public static final String KEY_DEVICE_INFO = "deviceInfo"; + public static final String KEY_BEACON_INFO = "beaconInfo"; + public static final String KEY_START_TIMESTAMP = "startTimestamp"; + public static final String KEY_END_TIMESTAMP = "endTimestamp"; + public static final String KEY_NOTES = "notes"; + public static final String KEY_DISTANCE = "distance"; + public static final String KEY_RSSIS = "rssis"; + private DeviceInfo deviceInfo; private BeaconInfo beaconInfo; - private long timestamp; + private long startTimestamp; + + private long endTimestamp; private String notes; @@ -33,12 +43,20 @@ public void setBeaconInfo(BeaconInfo beaconInfo) { this.beaconInfo = beaconInfo; } - public long getTimestamp() { - return timestamp; + public long getStartTimestamp() { + return startTimestamp; + } + + public void setStartTimestamp(long startTimestamp) { + this.startTimestamp = startTimestamp; + } + + public long getEndTimestamp() { + return endTimestamp; } - public void setTimestamp(long timestamp) { - this.timestamp = timestamp; + public void setEndTimestamp(long endTimestamp) { + this.endTimestamp = endTimestamp; } public String getNotes() { diff --git a/app/build.gradle b/app/build.gradle index 42415c1..0fbfd8e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { targetSdkVersion 28 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -31,19 +31,21 @@ android { dependencies { implementation project(':BLE Indoor Positioning') - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support:support-v4:28.0.0' - implementation 'com.android.support:support-media-compat:28.0.0' - implementation 'com.android.support:design:28.0.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.media:media:1.0.0' + implementation 'com.google.android.material:material:1.1.0-alpha01' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + + implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.gms:play-services-location:16.0.0' implementation "com.polidea.rxandroidble:rxandroidble:1.4.3" testImplementation 'junit:junit:4.12' - testImplementation "com.android.support.test:runner:1.0.2" - testImplementation "com.android.support.test:rules:1.0.2" - testImplementation 'org.robolectric:robolectric:4.0-beta-1' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + testImplementation 'androidx.test:runner:1.1.0' + testImplementation 'androidx.test:rules:1.1.0' + testImplementation 'org.robolectric:robolectric:4.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b4d2e16..a12268f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + + + @@ -26,6 +32,16 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ExternalStorageUtils.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ExternalStorageUtils.java new file mode 100644 index 0000000..3fb9a42 --- /dev/null +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ExternalStorageUtils.java @@ -0,0 +1,228 @@ +package com.nexenio.bleindoorpositioningdemo; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Environment; +import android.os.StatFs; +import android.widget.Toast; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import androidx.annotation.NonNull; +import androidx.core.content.FileProvider; + +/** + * Created by steppschuh on 04/11/2016. + */ + +public abstract class ExternalStorageUtils { + + public static final int BUFFER_SIZE = 2048; + + /** + * Checks if external storage is available for read and write + */ + public static boolean isExternalStorageWritable() { + String state; + try { + state = Environment.getExternalStorageState(); + } catch (ArrayIndexOutOfBoundsException e) { + return false; + } + return Environment.MEDIA_MOUNTED.equals(state); + } + + /** + * Checks if external storage is available to at least read + */ + public static boolean isExternalStorageReadable() { + String state = Environment.getExternalStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state) || + Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) { + return true; + } + return false; + } + + public static File getDocumentsDirectory(String subFolder) { + File file; + if (subFolder != null) { + file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS), subFolder); + } else { + file = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS); + } + file.mkdirs(); + return file; + } + + public static File getCacheDirectory(Context context, String subFolder) { + File file; + if (subFolder != null) { + file = new File(context.getExternalCacheDir(), subFolder); + } else { + file = context.getExternalCacheDir(); + } + file.mkdirs(); + return file; + } + + public static long getFileSize(@NonNull File file) { + long size = file.length(); + if (file.isDirectory()) { + for (File f : file.listFiles()) { + if (f.isDirectory()) { + size += getFileSize(f); + } + size += f.length(); + } + } + return size; + } + + /** + * Emits all {@link File}s (that are not directories) in the specified directory. Also includes + * files in sub directories. + */ + public static List getFilesInDirectory(@NonNull File directory) { + List files = new ArrayList<>(); + for (File file : directory.listFiles()) { + if (file.isDirectory()) { + files.addAll(getFilesInDirectory(file)); + } else { + files.add(file); + } + } + return files; + } + + public static void zip(List files, String zipFile) throws IOException { + BufferedInputStream origin; + try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)))) { + byte[] data = new byte[BUFFER_SIZE]; + + for (File file : files) { + FileInputStream fi = new FileInputStream(file); + origin = new BufferedInputStream(fi, BUFFER_SIZE); + + String filePath = file.getAbsolutePath(); + + try { + ZipEntry entry = new ZipEntry(filePath.substring(filePath.lastIndexOf("/") + 1)); + out.putNextEntry(entry); + int count; + while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) { + out.write(data, 0, count); + } + } finally { + origin.close(); + } + } + } + } + + /** + * Returns a list of {@link File}s which have the .json extension in the specified directory. + * Also includes files in subfolders. + */ + public static List getJsonFilesInDirectory(File directory) { + return getFilesInDirectoryForExtension(directory, ".json"); + } + + /** + * Returns a list of {@link File}s which have the specified extension in the specified + * directory. Also includes files in subfolders. + */ + public static List getFilesInDirectoryForExtension(File directory, String extension) { + ArrayList jsonFiles = new ArrayList<>(); + File[] files = directory.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + jsonFiles.addAll(getJsonFilesInDirectory(file)); + } else { + if (file.getName().endsWith(extension)) { + jsonFiles.add(file); + } + } + } + return jsonFiles; + } + + /** + * Removes the given files from the external storage. + */ + public static boolean removeFiles(List files) { + boolean allFilesRemoved = true; + for (File file : files) { + allFilesRemoved &= file.delete(); + } + return allFilesRemoved; + } + + /** + * Invokes an implicit share intent for the specified file. + */ + public static void shareFile(File file, Context context) { + shareFile(file, context, file.getName(), file.getName()); + } + + /** + * Invokes an implicit share intent for the specified file. + */ + public static void shareFile(File file, Context context, String subject, String text) { + if (file == null || !file.canRead()) { + return; + } + + Intent shareIntent = new Intent(Intent.ACTION_SEND); + Uri contentUri = FileProvider.getUriForFile(context, "com.nexenio.bleindoorpositioning.fileprovider", file); + + shareIntent.setTypeAndNormalize(context.getContentResolver().getType(contentUri)); + shareIntent.putExtra(Intent.EXTRA_SUBJECT, subject); + shareIntent.putExtra(Intent.EXTRA_TEXT, text); + shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); + // Todo: Remove + shareIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"marvin.mirtschin@nexenio.com"}); + + Intent chooserIntent = Intent.createChooser(shareIntent, "Share file"); + chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + if (shareIntent.resolveActivity(context.getPackageManager()) != null) { + context.startActivity(chooserIntent); + } else { + Toast.makeText(context, "Unable to share file", Toast.LENGTH_LONG).show(); + } + } + + /** + * Writes the contents of a string to the specified file. + * + * @param data the string that the file should contain + * @param file the file that the string should be written to + * @param append if false, file contents will be overwritten + */ + public static void writeStringToFile(String data, File file, boolean append) throws IOException { + try (FileWriter fw = new FileWriter(file, append)) { + fw.write(data); + } + } + + /** + * Returns the amount of bytes that are available in the specified directory. + */ + public static long getAvailableMemory(@NonNull File directory) { + StatFs stats = new StatFs(directory.getAbsolutePath()); + return stats.getAvailableBlocksLong() * stats.getBlockSizeLong(); + } + +} diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/HomeActivity.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/HomeActivity.java index 4aa8109..2c9c148 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/HomeActivity.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/HomeActivity.java @@ -3,17 +3,18 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.design.widget.BottomNavigationView; -import android.support.design.widget.CoordinatorLayout; -import android.support.design.widget.Snackbar; -import android.support.v4.app.Fragment; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import com.google.android.material.bottomnavigation.BottomNavigationView; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import com.google.android.material.snackbar.Snackbar; +import androidx.fragment.app.Fragment; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.widget.EditText; import com.nexenio.bleindoorpositioningdemo.bluetooth.BluetoothClient; import com.nexenio.bleindoorpositioningdemo.location.AndroidLocationProvider; @@ -56,8 +57,12 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case R.id.menu_record: + Intent intent = new Intent(this, RecordingActivity.class); + startActivity(intent); + return true; case R.id.menu_filter: - Log.w(TAG, "BeaconFilter"); + Log.w(TAG, "Filter"); return true; default: break; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/RecordingActivity.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/RecordingActivity.java new file mode 100644 index 0000000..c90b421 --- /dev/null +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/RecordingActivity.java @@ -0,0 +1,488 @@ +package com.nexenio.bleindoorpositioningdemo; + +import com.google.android.material.button.MaterialButton; +import com.google.android.material.snackbar.BaseTransientBottomBar; +import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.textfield.TextInputEditText; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.provider.Settings; +import android.text.Editable; +import android.text.InputFilter; +import android.util.Log; +import android.view.View; +import android.widget.Toast; + +import com.nexenio.bleindoorpositioning.ble.advertising.IBeaconAdvertisingPacket; +import com.nexenio.bleindoorpositioning.ble.beacon.BeaconManager; +import com.nexenio.bleindoorpositioning.ble.beacon.FilteredBeaconUpdateListener; +import com.nexenio.bleindoorpositioning.ble.beacon.IBeacon; +import com.nexenio.bleindoorpositioning.ble.beacon.filter.IBeaconFilter; +import com.nexenio.bleindoorpositioning.testutil.benchmark.BeaconInfo; +import com.nexenio.bleindoorpositioning.testutil.benchmark.DeviceInfo; +import com.nexenio.bleindoorpositioning.testutil.benchmark.RssiMeasurements; +import com.nexenio.bleindoorpositioningdemo.bluetooth.BluetoothClient; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +public class RecordingActivity extends AppCompatActivity { + + private static final String TAG = RecordingActivity.class.getSimpleName(); + private static final String RECORDING_DIRECTORY_NAME = "Rssi_Recordings"; + public static final int REQUEST_CODE_STORAGE_PERMISSIONS = 1; + + private final static UUID RECORDING_UUID = UUID.fromString("61a0523a-a733-4789-ae8f-4f55fcff64f2"); + + private RssiMeasurements rssiMeasurements = new RssiMeasurements(); + + @Nullable + private List recordedRssiValues; + private FilteredBeaconUpdateListener> recordingBeaconUpdateListener; + + private SharedPreferences sharedPreferences; + + private TextInputEditText distanceEditText; + private TextInputEditText notesEditText; + + private TextInputEditText deviceIdEditText; + private TextInputEditText deviceModelEditText; + private TextInputEditText deviceManufacturerEditText; + private TextInputEditText deviceOsVersionEditText; + + private TextInputEditText beaconNameEditText; + private TextInputEditText beaconModelEditText; + private TextInputEditText beaconManufacturerEditText; + private TextInputEditText beaconTransmissionPowerEditText; + private TextInputEditText beaconAdvertisingFrequencyEditText; + + private TextInputEditText recordingUuidCopyEditText; + + private MaterialButton recordButton; + + private boolean isRecording; + + @Nullable + protected Snackbar errorSnackBar; + protected CoordinatorLayout coordinatorLayout; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_recording); + + sharedPreferences = getPreferences(Context.MODE_PRIVATE); + + initializeLayout(); + restoreFormValues(); + initializeBluetoothScanning(); + + if (!hasStoragePermission()) { + requestStoragePermission(); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + switch (requestCode) { + case REQUEST_CODE_STORAGE_PERMISSIONS: { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Storage permission granted"); + } else { + showStoragePermissionMissingError(); + } + break; + } + } + } + + private void initializeLayout() { + coordinatorLayout = getWindow().getDecorView().findViewById(R.id.coordinatorLayout); + + recordButton = findViewById(R.id.recordButton); + distanceEditText = findViewById(R.id.recordDistanceEditText); + notesEditText = findViewById(R.id.recordNotesEditText); + + deviceIdEditText = findViewById(R.id.recordDeviceIdEditText); + deviceModelEditText = findViewById(R.id.recordDeviceModelEditText); + deviceManufacturerEditText = findViewById(R.id.recordDeviceManufacturerEditText); + deviceOsVersionEditText = findViewById(R.id.recordDeviceOsVersionEditText); + + beaconNameEditText = findViewById(R.id.recordBeaconNameEditText); + beaconModelEditText = findViewById(R.id.recordBeaconModelEditText); + beaconManufacturerEditText = findViewById(R.id.recordBeaconManufacturerEditText); + beaconTransmissionPowerEditText = findViewById(R.id.recordBeaconTransmissionPowerEditText); + beaconAdvertisingFrequencyEditText = findViewById(R.id.recordBeaconAdvertisingFrequencyEditText); + + recordingUuidCopyEditText = findViewById(R.id.recordingUuidCopyEditText); + setupUuidEditText(); + + // Limit numbers to avoid overflow + InputFilter[] FilterArray = new InputFilter[1]; + FilterArray[0] = new InputFilter.LengthFilter(6); + + distanceEditText.setFilters(FilterArray); + beaconTransmissionPowerEditText.setFilters(FilterArray); + beaconAdvertisingFrequencyEditText.setFilters(FilterArray); + } + + private void initializeBluetoothScanning() { + Log.d(TAG, "Initializing Bluetooth scanning"); + + BluetoothClient.initialize(this); + IBeaconFilter> recordingBeaconFilter = new IBeaconFilter<>(RECORDING_UUID); + recordingBeaconUpdateListener = new FilteredBeaconUpdateListener>(recordingBeaconFilter) { + @Override + public void onMatchingBeaconUpdated(IBeacon beacon) { + onRecordingBeaconUpdated(beacon); + } + }; + } + + @Override + protected void onStop() { + stopRecording(); + super.onStop(); + } + + public void onRecordButtonClicked(View view) { + persistFormValues(); + if (isRecording) { + stopRecording(); + } else { + startRecording(); + } + } + + public void onClearRecordingsButtonClicked(View view) { + new AlertDialog.Builder(this) + .setTitle("Remove Recordings") + .setMessage("Do you really want to remove all recordings?") + .setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + File documentsDirectory = ExternalStorageUtils.getDocumentsDirectory(RECORDING_DIRECTORY_NAME); + List files = ExternalStorageUtils.getFilesInDirectory(documentsDirectory); + ExternalStorageUtils.removeFiles(files); + Toast.makeText(RecordingActivity.this, "Removed " + files.size() + " file(s)", Toast.LENGTH_SHORT).show(); + } + }) + .setNegativeButton(android.R.string.no, null).show(); + } + + private RssiMeasurements createRssiMeasurements() { + RssiMeasurements rssiMeasurements = new RssiMeasurements(); + rssiMeasurements.setDeviceInfo(createDeviceInfo()); + rssiMeasurements.setBeaconInfo(createBeaconInfo()); + rssiMeasurements.setDistance(Float.parseFloat(distanceEditText.getText().toString())); + + Editable optionalNotes = notesEditText.getText(); + if (optionalNotes != null) { + rssiMeasurements.setNotes(optionalNotes.toString()); + } + + rssiMeasurements.setStartTimestamp(System.currentTimeMillis()); + return rssiMeasurements; + } + + private DeviceInfo createDeviceInfo() { + DeviceInfo deviceInfo = new DeviceInfo(); + deviceInfo.setId(deviceIdEditText.getText().toString()); + deviceInfo.setModel(deviceModelEditText.getText().toString()); + deviceInfo.setManufacturer(deviceManufacturerEditText.getText().toString()); + deviceInfo.setOsVersion(deviceOsVersionEditText.getText().toString()); + return deviceInfo; + } + + private BeaconInfo createBeaconInfo() { + BeaconInfo beaconInfo = new BeaconInfo(); + beaconInfo.setName(beaconNameEditText.getText().toString()); + beaconInfo.setModel(beaconModelEditText.getText().toString()); + beaconInfo.setManufacturer(beaconManufacturerEditText.getText().toString()); + beaconInfo.setTransmissionPower(Integer.valueOf(beaconTransmissionPowerEditText.getText().toString())); + beaconInfo.setAdvertisingFrequency(Integer.valueOf(beaconAdvertisingFrequencyEditText.getText().toString())); + return beaconInfo; + } + + private void startRecording() { + List invalidTextEdits = getInvalidTextEdits(); + if (!invalidTextEdits.isEmpty()) { + showInvalidFields(invalidTextEdits); + return; + } + + rssiMeasurements = createRssiMeasurements(); + + if (!hasStoragePermission()) { + requestStoragePermission(); + return; + } + + Log.d(TAG, "Starting recording"); + recordedRssiValues = new ArrayList<>(); + + BluetoothClient.startScanning(); + BeaconManager.registerBeaconUpdateListener(recordingBeaconUpdateListener); + + isRecording = true; + recordButton.setText(getString(R.string.action_stop_recording)); + } + + private void stopRecording() { + if (!isRecording) { + return; + } + Log.d(TAG, "Stopping recording"); + isRecording = false; + recordButton.setText(getString(R.string.action_start_recording)); + + BluetoothClient.stopScanning(); + BeaconManager.unregisterBeaconUpdateListener(recordingBeaconUpdateListener); + + rssiMeasurements.setEndTimestamp(System.currentTimeMillis()); + rssiMeasurements.setRssis(convertIntArray(recordedRssiValues)); + + if (recordedRssiValues != null) { + recordedRssiValues.clear(); + } + + Log.i(TAG, "RSSI measurements:\n" + rssiMeasurements); + exportMeasurements(); + } + + private void setupUuidEditText() { + recordingUuidCopyEditText.setText(RECORDING_UUID.toString()); + recordingUuidCopyEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Recording Uuid", ((TextInputEditText) v).getText()); + clipboard.setPrimaryClip(clip); + Toast.makeText(RecordingActivity.this, "Copied to Clipboard", Toast.LENGTH_SHORT).show(); + } + } + }); + } + + private List getInvalidTextEdits() { + List textInputEditTexts = getNecessaryTextInputEditTexts(); + List invalidInputEdits = new ArrayList<>(); + for (TextInputEditText textInputEditText : textInputEditTexts) { + if (textInputEditText.getText() != null && textInputEditText.getText().toString().equals("")) { + invalidInputEdits.add(textInputEditText); + } + } + return invalidInputEdits; + } + + /** + * Indicate text edit fields with missing text. + */ + private void showInvalidFields(List textInputEditTexts) { + Toast.makeText(this, "Not all information were provided", Toast.LENGTH_LONG).show(); + String errorString = "This field cannot be empty"; + for (TextInputEditText textInputEditText : textInputEditTexts) { + textInputEditText.setError(errorString); + } + } + + private List getNecessaryTextInputEditTexts() { + return Arrays.asList(distanceEditText, + deviceIdEditText, + deviceModelEditText, + deviceManufacturerEditText, + deviceOsVersionEditText, + beaconNameEditText, + beaconModelEditText, + beaconManufacturerEditText, + beaconTransmissionPowerEditText, + beaconAdvertisingFrequencyEditText); + } + + public void onExportAllRecordingsButtonClicked(View view) { + exportAllRecordings(); + } + + private void exportAllRecordings() { + File documentsDirectory = ExternalStorageUtils.getDocumentsDirectory(RECORDING_DIRECTORY_NAME); + List jsonFiles = ExternalStorageUtils.getJsonFilesInDirectory(documentsDirectory); + + String fileName = "measurements.zip"; + File zipFile = new File(documentsDirectory, fileName); + + if (zipFile.exists()) { + zipFile.delete(); + } + + try { + ExternalStorageUtils.zip(jsonFiles, zipFile.getAbsolutePath()); + } catch (IOException e) { + Toast.makeText(this, "Unable to export files", Toast.LENGTH_LONG).show(); + Log.e(TAG, "Unable to export measurements", e); + return; + } + ExternalStorageUtils.shareFile(zipFile, this); + } + + private void exportMeasurements() { + String jsonString = createJsonString(rssiMeasurements); + String fileName = "rssiMeasurements_" + rssiMeasurements.getStartTimestamp() + "_" + rssiMeasurements.getEndTimestamp() + ".json"; + File file = persistJsonFile(fileName, jsonString); + + ExternalStorageUtils.shareFile(file, this); + } + + private static String createJsonString(RssiMeasurements rssiMeasurements) { + GsonBuilder gsonBuilder = new GsonBuilder() + .setPrettyPrinting(); + + Gson gson = gsonBuilder.create(); + return gson.toJson(rssiMeasurements); + } + + private File persistJsonFile(String fileName, String jsonString) { + File documentsDirectory = ExternalStorageUtils.getDocumentsDirectory(RECORDING_DIRECTORY_NAME); + File file = new File(documentsDirectory, fileName); + try { + if (!documentsDirectory.exists()) { + documentsDirectory.mkdirs(); + } + ExternalStorageUtils.writeStringToFile(jsonString, file, false); + } catch (IOException e) { + // TODO: log + toast + e.printStackTrace(); + } + return file; + } + + private boolean hasStoragePermission() { + int permissionResult = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); + return permissionResult == PackageManager.PERMISSION_GRANTED; + } + + @SuppressLint("CheckResult") + private void requestStoragePermission() { + Log.d(TAG, "Requesting storage permission"); + ActivityCompat.requestPermissions(this, new String[]{ + Manifest.permission.WRITE_EXTERNAL_STORAGE + }, REQUEST_CODE_STORAGE_PERMISSIONS); + } + + private void showStoragePermissionMissingError() { + // find a container for the snackbar + if (errorSnackBar != null) { + errorSnackBar.dismiss(); + errorSnackBar = null; + } + + View container = coordinatorLayout; + if (container == null) { + container = getWindow().getDecorView(); + } + // create the snackbar + errorSnackBar = Snackbar.make(container, "Permission not granted", Snackbar.LENGTH_LONG); + + errorSnackBar.setDuration(BaseTransientBottomBar.LENGTH_INDEFINITE) + .setAction(getString(R.string.record_retry), new View.OnClickListener() { + @Override + public void onClick(View view) { + requestStoragePermission(); + } + }).show(); + } + + private void onRecordingBeaconUpdated(IBeacon beacon) { + IBeaconAdvertisingPacket latestAdvertisingPacket = beacon.getLatestAdvertisingPacket(); + recordedRssiValues.add(latestAdvertisingPacket.getRssi()); + } + + private void persistFormValues() { + Log.d(TAG, "Persisting form values"); + + SharedPreferences.Editor editor = sharedPreferences.edit(); + + editor.putString(RssiMeasurements.KEY_DISTANCE, distanceEditText.getText().toString()); + editor.putString(RssiMeasurements.KEY_NOTES, notesEditText.getText().toString()); + + // don't persist dynamic fields + //editor.putString(DeviceInfo.KEY_DEVICE_ID, deviceIdEditText.getText().toString()); + //editor.putString(DeviceInfo.KEY_DEVICE_MODEL, deviceModelEditText.getText().toString()); + //editor.putString(DeviceInfo.KEY_DEVICE_MANUFACTURER, deviceManufacturerEditText.getText().toString()); + //editor.putString(DeviceInfo.KEY_DEVICE_OS_VERSION, deviceOsVersionEditText.getText().toString()); + + editor.putString(BeaconInfo.KEY_BEACON_NAME, beaconNameEditText.getText().toString()); + editor.putString(BeaconInfo.KEY_BEACON_MODEL, beaconModelEditText.getText().toString()); + editor.putString(BeaconInfo.KEY_BEACON_MANUFACTURER, beaconManufacturerEditText.getText().toString()); + editor.putString(BeaconInfo.KEY_BEACON_TRANSMISSION_POWER, beaconTransmissionPowerEditText.getText().toString()); + editor.putString(BeaconInfo.KEY_BEACON_ADVERTISING_FREQUENCY, beaconAdvertisingFrequencyEditText.getText().toString()); + + editor.apply(); + } + + private void restoreFormValues() { + Log.d(TAG, "Restoring form values"); + + distanceEditText.setText(sharedPreferences.getString(RssiMeasurements.KEY_DISTANCE, "")); + notesEditText.setText(sharedPreferences.getString(RssiMeasurements.KEY_NOTES, "")); + + deviceIdEditText.setText(sharedPreferences.getString(DeviceInfo.KEY_DEVICE_ID, getDeviceId(this))); + deviceModelEditText.setText(sharedPreferences.getString(DeviceInfo.KEY_DEVICE_MODEL, Build.MODEL)); + deviceManufacturerEditText.setText(sharedPreferences.getString(DeviceInfo.KEY_DEVICE_MANUFACTURER, Build.MANUFACTURER)); + deviceOsVersionEditText.setText(sharedPreferences.getString(DeviceInfo.KEY_DEVICE_OS_VERSION, getOsVersion())); + + beaconNameEditText.setText(sharedPreferences.getString(BeaconInfo.KEY_BEACON_NAME, "")); + beaconModelEditText.setText(sharedPreferences.getString(BeaconInfo.KEY_BEACON_MODEL, "")); + beaconManufacturerEditText.setText(sharedPreferences.getString(BeaconInfo.KEY_BEACON_MANUFACTURER, "")); + beaconTransmissionPowerEditText.setText(sharedPreferences.getString(BeaconInfo.KEY_BEACON_TRANSMISSION_POWER, "")); + beaconAdvertisingFrequencyEditText.setText(sharedPreferences.getString(BeaconInfo.KEY_BEACON_ADVERTISING_FREQUENCY, "")); + } + + private static int[] convertIntArray(@Nullable List integerList) { + if (integerList == null) { + Log.w(TAG, "Can't convert null to int array."); + return new int[0]; + } + + int[] intArray = new int[integerList.size()]; + Iterator iterator = integerList.iterator(); + for (int i = 0; i < intArray.length; i++) { + intArray[i] = iterator.next(); + } + return intArray; + } + + @SuppressLint("HardwareIds") + private static String getDeviceId(Context context) { + return Settings.Secure.getString(context.getContentResolver(), "android_id"); + } + + private static String getOsVersion() { + return "Android " + Build.VERSION.RELEASE + " (SDK " + Build.VERSION.SDK_INT + ")"; + } + +} diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/bluetooth/BluetoothClient.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/bluetooth/BluetoothClient.java index 50ac5b0..c5b8c43 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/bluetooth/BluetoothClient.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/bluetooth/BluetoothClient.java @@ -5,7 +5,7 @@ import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.Intent; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import com.nexenio.bleindoorpositioning.ble.advertising.AdvertisingPacket; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/location/AndroidLocationProvider.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/location/AndroidLocationProvider.java index cfaeb4e..dbe92db 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/location/AndroidLocationProvider.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/location/AndroidLocationProvider.java @@ -25,8 +25,8 @@ import android.content.pm.PackageManager; import android.os.Build; import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.v4.app.ActivityCompat; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; import android.text.TextUtils; import android.util.Log; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/LocationAnimator.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/LocationAnimator.java index 8c1cfed..85c51c1 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/LocationAnimator.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/LocationAnimator.java @@ -2,7 +2,7 @@ import android.animation.Animator; import android.animation.ValueAnimator; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.nexenio.bleindoorpositioning.location.Location; import com.nexenio.bleindoorpositioning.location.LocationListener; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconView.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconView.java index 91f56e0..ded8ec6 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconView.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconView.java @@ -5,10 +5,10 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.PointF; -import android.support.annotation.CallSuper; -import android.support.annotation.ColorInt; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; +import androidx.annotation.CallSuper; +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import android.util.AttributeSet; import android.view.View; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconViewFragment.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconViewFragment.java index 0692f4b..b48fa35 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconViewFragment.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/BeaconViewFragment.java @@ -2,11 +2,11 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.CallSuper; -import android.support.annotation.LayoutRes; -import android.support.annotation.Nullable; -import android.support.design.widget.CoordinatorLayout; -import android.support.v4.app.Fragment; +import androidx.annotation.CallSuper; +import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/ColorUtil.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/ColorUtil.java index 0364089..b1ea7c0 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/ColorUtil.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/ColorUtil.java @@ -2,10 +2,10 @@ import android.content.Context; import android.content.res.Resources; -import android.support.annotation.ColorInt; -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; -import android.support.v4.content.res.ResourcesCompat; +import androidx.annotation.ColorInt; +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.core.content.res.ResourcesCompat; import com.nexenio.bleindoorpositioningdemo.R; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChart.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChart.java index 20c9d68..d122036 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChart.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChart.java @@ -1,8 +1,8 @@ package com.nexenio.bleindoorpositioningdemo.ui.beaconview.chart; import android.content.Context; -import android.support.annotation.IntDef; -import android.support.annotation.Nullable; +import androidx.annotation.IntDef; +import androidx.annotation.Nullable; import android.util.AttributeSet; import com.nexenio.bleindoorpositioningdemo.ui.beaconview.BeaconView; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChartFragment.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChartFragment.java index ad366ab..4369134 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChartFragment.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconChartFragment.java @@ -3,7 +3,7 @@ import android.annotation.SuppressLint; import android.os.Bundle; -import android.support.annotation.CallSuper; +import androidx.annotation.CallSuper; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconLineChart.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconLineChart.java index 20cb750..afa96bd 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconLineChart.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/BeaconLineChart.java @@ -8,8 +8,8 @@ import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Shader; -import android.support.annotation.ColorInt; -import android.support.annotation.Nullable; +import androidx.annotation.ColorInt; +import androidx.annotation.Nullable; import android.util.AttributeSet; import com.nexenio.bleindoorpositioning.ble.advertising.AdvertisingPacket; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/RssiFilterLineChart.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/RssiFilterLineChart.java index 1088ed7..373743c 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/RssiFilterLineChart.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/chart/RssiFilterLineChart.java @@ -3,7 +3,7 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.PointF; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.AttributeSet; import com.nexenio.bleindoorpositioning.ble.advertising.AdvertisingPacket; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMap.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMap.java index d242910..fdeb134 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMap.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMap.java @@ -10,8 +10,8 @@ import android.graphics.RadialGradient; import android.graphics.RectF; import android.graphics.Shader; -import android.support.annotation.CallSuper; -import android.support.annotation.Nullable; +import androidx.annotation.CallSuper; +import androidx.annotation.Nullable; import android.util.AttributeSet; import com.nexenio.bleindoorpositioning.ble.advertising.AdvertisingPacket; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapBackground.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapBackground.java index 4035fa8..f0ccd5e 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapBackground.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapBackground.java @@ -2,7 +2,7 @@ import android.graphics.Bitmap; import android.graphics.Point; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.nexenio.bleindoorpositioning.location.Location; import com.nexenio.bleindoorpositioning.location.projection.CanvasProjection; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapFragment.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapFragment.java index 20b80db..63ef77a 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapFragment.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/map/BeaconMapFragment.java @@ -5,7 +5,7 @@ import android.graphics.BitmapFactory; import android.graphics.Point; import android.os.Bundle; -import android.support.annotation.CallSuper; +import androidx.annotation.CallSuper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadar.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadar.java index f554264..f4fb24e 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadar.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadar.java @@ -7,7 +7,7 @@ import android.graphics.Paint; import android.graphics.PointF; import android.graphics.RectF; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import android.util.AttributeSet; import com.nexenio.bleindoorpositioning.ble.advertising.AdvertisingPacket; diff --git a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadarFragment.java b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadarFragment.java index ccfe7e8..c9eefa2 100644 --- a/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadarFragment.java +++ b/app/src/main/java/com/nexenio/bleindoorpositioningdemo/ui/beaconview/radar/BeaconRadarFragment.java @@ -7,7 +7,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; -import android.support.annotation.CallSuper; +import androidx.annotation.CallSuper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 85773df..2451f42 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -9,7 +9,7 @@ android:layout_height="match_parent" tools:context="com.nexenio.bleindoorpositioningdemo.HomeActivity"> - - + - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_recording_beacon.xml b/app/src/main/res/layout/content_recording_beacon.xml new file mode 100644 index 0000000..0ce1e8e --- /dev/null +++ b/app/src/main/res/layout/content_recording_beacon.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_recording_device.xml b/app/src/main/res/layout/content_recording_device.xml new file mode 100644 index 0000000..127aa12 --- /dev/null +++ b/app/src/main/res/layout/content_recording_device.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/content_recording_general.xml b/app/src/main/res/layout/content_recording_general.xml new file mode 100644 index 0000000..09c1006 --- /dev/null +++ b/app/src/main/res/layout/content_recording_general.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/beacon_view.xml b/app/src/main/res/menu/beacon_view.xml index f1444f0..0ddc130 100644 --- a/app/src/main/res/menu/beacon_view.xml +++ b/app/src/main/res/menu/beacon_view.xml @@ -2,9 +2,14 @@ + + 16dp 16dp + 16dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fffde19..d997379 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -27,6 +27,33 @@ Frequency in Hz Variance in dBm + + Record + Start Recording + Stop Recording + Export all recordings + Delete all recordings + General Info + Device Info + Beacon Info + Notes + An optional comment + ID + Name + Manufacturer + Model + OS Version + TX Power + The transmission power in dBm (e.g. -20) + Advertising Frequency + Advertising frequency in Hz (e.g. 10) + Distance + The measured distance in meters (e.g. 3) + Export json file + Retry + Recording Uuid + Copy recording uuid to clipboard + Filter Beacon Type @@ -39,6 +66,7 @@ @string/beacon_major + @string/title_record @string/title_filter Coloring Instances diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e0ae3b7..83f4b4f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,10 +1,37 @@ - + + + + + + + + diff --git a/app/src/main/res/xml/file_paths.xml b/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..9fb12db --- /dev/null +++ b/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index e5a39a8..5682d63 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.4.0-alpha01' + classpath 'com.android.tools.build:gradle:3.6.1' } } diff --git a/gradle.properties b/gradle.properties index f1c49f3..1e0c1d3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b2f1a62..4e678bb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thu Oct 25 13:40:28 CEST 2018 +#Fri May 01 12:14:23 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip